mirror of
https://github.com/paritytech/banana_split.git
synced 2026-09-12 19:51:03 +05:00
Fix linter issues
Now that the rules are much more pedantic there are quite some places where we are inconsistent with codestyle; all security issues detected were false positives, though.
This commit is contained in:
parent
ff93057bd3
commit
e27865e53b
19
.eslintrc.js
19
.eslintrc.js
@ -18,5 +18,24 @@ module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
node: true
|
||||
},
|
||||
rules: {
|
||||
"vue/html-self-closing": [
|
||||
"warning",
|
||||
{
|
||||
html: "never"
|
||||
}
|
||||
],
|
||||
"vue/v-on-style": ["warning", "longform"],
|
||||
"vue/max-attributes-per-line": [
|
||||
"warning",
|
||||
{
|
||||
singleline: 3,
|
||||
multiline: {
|
||||
max: 1,
|
||||
allowFirstLine: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,24 +1,18 @@
|
||||
module.exports = {
|
||||
moduleFileExtensions: [
|
||||
'js',
|
||||
'jsx',
|
||||
'json',
|
||||
'vue'
|
||||
],
|
||||
moduleFileExtensions: ["js", "jsx", "json", "vue"],
|
||||
transform: {
|
||||
'^.+\\.vue$': 'vue-jest',
|
||||
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
|
||||
'^.+\\.jsx?$': 'babel-jest'
|
||||
"^.+\\.vue$": "vue-jest",
|
||||
".+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$":
|
||||
"jest-transform-stub",
|
||||
"^.+\\.jsx?$": "babel-jest"
|
||||
},
|
||||
moduleNameMapper: {
|
||||
'^@/(.*)$': '<rootDir>/src/$1'
|
||||
"^@/(.*)$": "<rootDir>/src/$1"
|
||||
},
|
||||
snapshotSerializers: [
|
||||
'jest-serializer-vue'
|
||||
],
|
||||
snapshotSerializers: ["jest-serializer-vue"],
|
||||
testMatch: [
|
||||
'**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
|
||||
"**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"
|
||||
],
|
||||
testURL: 'http://localhost/',
|
||||
reporters: [ 'default', 'jest-junit' ]
|
||||
}
|
||||
testURL: "http://localhost/",
|
||||
reporters: ["default", "jest-junit"]
|
||||
};
|
||||
|
||||
@ -5,8 +5,12 @@
|
||||
<div v-if="secure">
|
||||
<router-view />
|
||||
<nav>
|
||||
<router-link to="/share">Create</router-link>
|
||||
<router-link to="/combine">Restore </router-link>
|
||||
<router-link to="/share">
|
||||
Create
|
||||
</router-link>
|
||||
<router-link to="/combine">
|
||||
Restore
|
||||
</router-link>
|
||||
</nav>
|
||||
</div>
|
||||
<SavePageInfo v-else-if="!localFile" />
|
||||
|
||||
@ -1,30 +1,33 @@
|
||||
<template>
|
||||
<canvas class="canvasText" width="0" height="28px" v-canvas-message="text"></canvas>
|
||||
<canvas v-canvas-message="text" class="canvasText" width="0" height="28px" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'CanvasText',
|
||||
props: {
|
||||
text: String
|
||||
},
|
||||
directives: {
|
||||
canvasMessage: function(canvasElement, binding) {
|
||||
var context = canvasElement.getContext("2d");
|
||||
context.clearRect(0, 0, canvasElement.width, canvasElement.height);
|
||||
context.fillStyle = "black";
|
||||
context.font = "20px Arial";
|
||||
var textSize = context.measureText(binding.value);
|
||||
canvasElement.setAttribute("width", (textSize.width+30)+"px");
|
||||
context.font = "20px Arial";
|
||||
context.fillText(binding.value, 15, 20)
|
||||
}
|
||||
name: "CanvasText",
|
||||
directives: {
|
||||
canvasMessage: function(canvasElement, binding) {
|
||||
var context = canvasElement.getContext("2d");
|
||||
context.clearRect(0, 0, canvasElement.width, canvasElement.height);
|
||||
context.fillStyle = "black";
|
||||
context.font = "20px Arial";
|
||||
var textSize = context.measureText(binding.value);
|
||||
canvasElement.setAttribute("width", textSize.width + 30 + "px");
|
||||
context.font = "20px Arial";
|
||||
context.fillText(binding.value, 15, 20);
|
||||
}
|
||||
}
|
||||
},
|
||||
props: {
|
||||
text: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.canvasText {
|
||||
margin-bottom: -8px;
|
||||
}
|
||||
.canvasText {
|
||||
margin-bottom: -8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,79 +1,82 @@
|
||||
<template>
|
||||
<span id="forkongithub">
|
||||
<a v-bind:href="url">🥄Fork me on GitHub</a>
|
||||
</span>
|
||||
<span id="forkongithub">
|
||||
<a :href="url">🥄Fork me on GitHub</a>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "ForkMe",
|
||||
props: {
|
||||
url: String
|
||||
name: "ForkMe",
|
||||
props: {
|
||||
url: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
>
|
||||
|
||||
<style>
|
||||
#forkongithub a {
|
||||
background: #000;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
font-family: arial, sans-serif;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
padding: 5px 40px;
|
||||
font-size: 1rem;
|
||||
line-height: 2rem;
|
||||
position: relative;
|
||||
transition: 0.5s;
|
||||
background: #000;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
font-family: arial, sans-serif;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
padding: 5px 40px;
|
||||
font-size: 1rem;
|
||||
line-height: 2rem;
|
||||
position: relative;
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
#forkongithub a:hover {
|
||||
background: #c11;
|
||||
color: #fff;
|
||||
background: #c11;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#forkongithub a::before,
|
||||
#forkongithub a::after {
|
||||
content: "";
|
||||
width: 100%;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
left: 0;
|
||||
height: 1px;
|
||||
background: #fff;
|
||||
content: "";
|
||||
width: 100%;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
left: 0;
|
||||
height: 1px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
#forkongithub a::after {
|
||||
bottom: 1px;
|
||||
top: auto;
|
||||
bottom: 1px;
|
||||
top: auto;
|
||||
}
|
||||
|
||||
@media screen and (min-width:800px) {
|
||||
#forkongithub {
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 200px;
|
||||
overflow: hidden;
|
||||
height: 200px;
|
||||
z-index: 9999;
|
||||
}
|
||||
@media screen and (min-width: 800px) {
|
||||
#forkongithub {
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 200px;
|
||||
overflow: hidden;
|
||||
height: 200px;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
#forkongithub a {
|
||||
width: 200px;
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
right: -60px;
|
||||
transform: rotate(45deg);
|
||||
-webkit-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
-moz-transform: rotate(45deg);
|
||||
-o-transform: rotate(45deg);
|
||||
box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
#forkongithub a {
|
||||
width: 200px;
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
right: -60px;
|
||||
transform: rotate(45deg);
|
||||
-webkit-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
-moz-transform: rotate(45deg);
|
||||
-o-transform: rotate(45deg);
|
||||
box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
<template>
|
||||
<div class="fold">
|
||||
<h1 class="main-title">Banana split</h1>
|
||||
<input id="fold-header" type="checkbox" name="fold" v-model="unfolded" />
|
||||
<h1 class="main-title">
|
||||
Banana split
|
||||
</h1>
|
||||
<input id="fold-header" v-model="unfolded" type="checkbox" name="fold" />
|
||||
<label for="fold-header">
|
||||
<h2 v-if="unfolded">Shamir Secret Sharing for people with friends</h2>
|
||||
<p v-else>What is this?</p>
|
||||
@ -9,9 +11,9 @@
|
||||
<div class="fold-content">
|
||||
<p>
|
||||
Banana Split uses
|
||||
<a href="https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing"
|
||||
>Shamir's secret sharing</a
|
||||
>
|
||||
<a href="https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing">
|
||||
Shamir's secret sharing
|
||||
</a>
|
||||
to make your paper backups more resilient and secure.
|
||||
</p>
|
||||
<p>
|
||||
|
||||
@ -1,31 +1,40 @@
|
||||
<script>
|
||||
import Vue from 'vue'
|
||||
import Vue from "vue";
|
||||
|
||||
import ShardQrCode from "./ShardQrCode";
|
||||
|
||||
export default {
|
||||
name: 'ShardInfo',
|
||||
props: {
|
||||
title: String,
|
||||
shard: String,
|
||||
requiredShards: Number
|
||||
name: "ShardInfo",
|
||||
components: { ShardQrCode },
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
components: {ShardQrCode},
|
||||
render: function () {
|
||||
var element = document.createElement('div');
|
||||
document.body.appendChild(element);
|
||||
var passedProps = this.$props;
|
||||
this.vm = new Vue({
|
||||
el: element,
|
||||
render: function(h) {
|
||||
return h(ShardQrCode, {props: passedProps})
|
||||
}
|
||||
});
|
||||
return this.vm;
|
||||
shard: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
beforeDestroy: function() {
|
||||
this.vm.$el.remove();
|
||||
this.vm.$destroy();
|
||||
requiredShards: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
</script>
|
||||
},
|
||||
beforeDestroy: function() {
|
||||
this.vm.$el.remove();
|
||||
this.vm.$destroy();
|
||||
},
|
||||
render: function() {
|
||||
var element = document.createElement("div");
|
||||
document.body.appendChild(element);
|
||||
var passedProps = this.$props;
|
||||
this.vm = new Vue({
|
||||
el: element,
|
||||
render: function(h) {
|
||||
return h(ShardQrCode, { props: passedProps });
|
||||
}
|
||||
});
|
||||
return this.vm;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -7,15 +7,13 @@
|
||||
to reconstruct the secret
|
||||
</h3>
|
||||
</div>
|
||||
<qriously
|
||||
class="qr-code print-only"
|
||||
v-bind:value="shard"
|
||||
v-bind:size="600"
|
||||
/>
|
||||
<qriously class="screen-only" v-bind:value="shard" v-bind:size="200" />
|
||||
<qriously class="qr-code print-only" :value="shard" :size="600" />
|
||||
<qriously class="screen-only" :value="shard" :size="200" />
|
||||
<div class="print-only">
|
||||
<div class="recovery-field">
|
||||
<div class="recovery-title">Recovery passphrase is </div>
|
||||
<div class="recovery-title">
|
||||
Recovery passphrase is
|
||||
</div>
|
||||
<div class="recovery-blank" />
|
||||
</div>
|
||||
<p class="version">
|
||||
@ -33,9 +31,18 @@ import { version } from "../../package.json";
|
||||
export default {
|
||||
name: "ShardQrCode",
|
||||
props: {
|
||||
title: String,
|
||||
shard: String,
|
||||
requiredShards: Number
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
shard: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
requiredShards: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
pluralizeCode: function() {
|
||||
|
||||
28
src/main.js
28
src/main.js
@ -1,22 +1,24 @@
|
||||
import Vue from 'vue'
|
||||
import Vue from "vue";
|
||||
|
||||
import VueQriously from 'vue-qriously'
|
||||
Vue.use(VueQriously)
|
||||
import VueQriously from "vue-qriously";
|
||||
Vue.use(VueQriously);
|
||||
|
||||
import QrcodeStream from "vue-qrcode-reader"
|
||||
Vue.use(QrcodeStream)
|
||||
import QrcodeStream from "vue-qrcode-reader";
|
||||
Vue.use(QrcodeStream);
|
||||
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import App from "./App.vue";
|
||||
import router from "./router";
|
||||
|
||||
import OnlinePlugin from './plugins/online'
|
||||
Vue.use(OnlinePlugin)
|
||||
import OnlinePlugin from "./plugins/online";
|
||||
Vue.use(OnlinePlugin);
|
||||
|
||||
Vue.config.productionTip = false
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
Vue.prototype.$eventHub = new Vue()
|
||||
Vue.prototype.$eventHub = new Vue();
|
||||
|
||||
new Vue({
|
||||
router,
|
||||
render: function (h) { return h(App) }
|
||||
}).$mount('#app')
|
||||
render: function(h) {
|
||||
return h(App);
|
||||
}
|
||||
}).$mount("#app");
|
||||
|
||||
@ -1,26 +1,26 @@
|
||||
const plugin = {
|
||||
install (Vue) {
|
||||
const vm = new Vue({
|
||||
data: {
|
||||
online: window.navigator.onLine
|
||||
install(Vue) {
|
||||
const vm = new Vue({
|
||||
data: {
|
||||
online: window.navigator.onLine
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("online", function handleOnline() {
|
||||
vm.$data.online = true;
|
||||
});
|
||||
|
||||
window.addEventListener("offline", function handleOffline() {
|
||||
vm.$data.online = false;
|
||||
});
|
||||
|
||||
Vue.mixin({
|
||||
computed: {
|
||||
isOnline() {
|
||||
return vm.$data.online;
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('online', function handleOnline() {
|
||||
vm.$data.online = true;
|
||||
});
|
||||
|
||||
window.addEventListener('offline', function handleOffline() {
|
||||
vm.$data.online = false;
|
||||
});
|
||||
|
||||
Vue.mixin({
|
||||
computed: {
|
||||
isOnline() {
|
||||
return vm.$data.online;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
export default plugin
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
export default plugin;
|
||||
|
||||
@ -1,28 +1,28 @@
|
||||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
import Vue from "vue";
|
||||
import Router from "vue-router";
|
||||
|
||||
import Info from './views/Info'
|
||||
import Share from './views/Share'
|
||||
import Combine from './views/Combine'
|
||||
import Info from "./views/Info";
|
||||
import Share from "./views/Share";
|
||||
import Combine from "./views/Combine";
|
||||
|
||||
Vue.use(Router)
|
||||
Vue.use(Router);
|
||||
|
||||
export default new Router({
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'info',
|
||||
path: "/",
|
||||
name: "info",
|
||||
component: Info
|
||||
},
|
||||
{
|
||||
path: '/share',
|
||||
name: 'share',
|
||||
path: "/share",
|
||||
name: "share",
|
||||
component: Share
|
||||
},
|
||||
{
|
||||
path: '/combine',
|
||||
name: 'combine',
|
||||
path: "/combine",
|
||||
name: "combine",
|
||||
component: Combine
|
||||
}
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -2,138 +2,167 @@ const BASE64 = require("base64-js");
|
||||
const CRYPTO = require("tweetnacl");
|
||||
const SCRYPT = require("scryptsy");
|
||||
|
||||
const SECRETS = require('secrets.js-grempe');
|
||||
const SECRETS = require("secrets.js-grempe");
|
||||
|
||||
const HexEncodeArray = [
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
"d",
|
||||
"e",
|
||||
"f"
|
||||
];
|
||||
|
||||
function strToUint8Array(str) {
|
||||
return new TextEncoder("utf-8").encode(str);
|
||||
return new TextEncoder("utf-8").encode(str);
|
||||
}
|
||||
|
||||
function uint8ArrayToStr(arr) {
|
||||
return new TextDecoder("utf-8").decode(arr);
|
||||
return new TextDecoder("utf-8").decode(arr);
|
||||
}
|
||||
|
||||
function hashString(str) {
|
||||
return CRYPTO.hash(strToUint8Array(str));
|
||||
return CRYPTO.hash(strToUint8Array(str));
|
||||
}
|
||||
|
||||
function hexify(arr) {
|
||||
var s = '';
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
var code = arr[i];
|
||||
s += HexEncodeArray[code >>> 4];
|
||||
s += HexEncodeArray[code & 0x0F];
|
||||
}
|
||||
return s;
|
||||
var s = "";
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
// `i` is a numerical counter for the loop and is never changed outside of there
|
||||
// therefore `i` is numerical, and is safe to use as an array index
|
||||
// eslint-disable-next-line security/detect-object-injection
|
||||
var code = arr[i];
|
||||
s += HexEncodeArray[code >>> 4];
|
||||
s += HexEncodeArray[code & 0x0f];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function dehexify(str) {
|
||||
var arr = new Uint8Array(str.length / 2);
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
arr[i] = parseInt(str.slice(2 * i, 2 * i + 2), 16);
|
||||
}
|
||||
return arr;
|
||||
var arr = new Uint8Array(str.length / 2);
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
// `i` is a numerical counter for the loop and is never changed outside of there
|
||||
// therefore `i` is numerical, and is safe to use as an array index
|
||||
// eslint-disable-next-line security/detect-object-injection
|
||||
arr[i] = parseInt(str.slice(2 * i, 2 * i + 2), 16);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
function encrypt(data, salt, passphrase) {
|
||||
var key = SCRYPT(passphrase, Buffer.from(salt), 1 << 15, 8, 1, 32);
|
||||
var nonce = CRYPTO.randomBytes(24);
|
||||
return {
|
||||
nonce,
|
||||
salt,
|
||||
value: CRYPTO.secretbox(strToUint8Array(data), nonce, key)
|
||||
}
|
||||
var key = SCRYPT(passphrase, Buffer.from(salt), 1 << 15, 8, 1, 32);
|
||||
var nonce = CRYPTO.randomBytes(24);
|
||||
return {
|
||||
nonce,
|
||||
salt,
|
||||
value: CRYPTO.secretbox(strToUint8Array(data), nonce, key)
|
||||
};
|
||||
}
|
||||
|
||||
function decrypt(data, salt, passphrase, nonce) {
|
||||
var key = SCRYPT(passphrase, Buffer.from(salt.buffer), 1 << 15, 8, 1, 32);
|
||||
return CRYPTO.secretbox.open(data, nonce, key);
|
||||
var key = SCRYPT(passphrase, Buffer.from(salt.buffer), 1 << 15, 8, 1, 32);
|
||||
// This is a false positive, `secretbox.open` is unrelated to `fs.open`
|
||||
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
||||
return CRYPTO.secretbox.open(data, nonce, key);
|
||||
}
|
||||
|
||||
function share(data, title, passphrase, totalShards, requiredShards) {
|
||||
var salt = hashString(title);
|
||||
var encrypted = encrypt(data, salt, passphrase);
|
||||
var nonce = BASE64.fromByteArray(encrypted.nonce);
|
||||
var hexEncrypted = hexify(encrypted.value);
|
||||
return SECRETS.share(hexEncrypted, totalShards, requiredShards).map(function (shard) {
|
||||
// First char is non-hex (base36) and signifies the bitfield size of our share
|
||||
var encodedShard = shard[0] + BASE64.fromByteArray(dehexify(shard.slice(1)));
|
||||
var salt = hashString(title);
|
||||
var encrypted = encrypt(data, salt, passphrase);
|
||||
var nonce = BASE64.fromByteArray(encrypted.nonce);
|
||||
var hexEncrypted = hexify(encrypted.value);
|
||||
return SECRETS.share(hexEncrypted, totalShards, requiredShards).map(function(
|
||||
shard
|
||||
) {
|
||||
// First char is non-hex (base36) and signifies the bitfield size of our share
|
||||
var encodedShard =
|
||||
shard[0] + BASE64.fromByteArray(dehexify(shard.slice(1)));
|
||||
|
||||
return JSON.stringify({
|
||||
v: 1,
|
||||
t: title,
|
||||
r: requiredShards,
|
||||
d: encodedShard,
|
||||
n: nonce
|
||||
}).replace(/[\u007F-\uFFFF]/g, function (chr) {
|
||||
return "\\u" + ("0000" + chr.charCodeAt(0).toString(16)).substr(-4)
|
||||
});
|
||||
return JSON.stringify({
|
||||
v: 1,
|
||||
t: title,
|
||||
r: requiredShards,
|
||||
d: encodedShard,
|
||||
n: nonce
|
||||
}).replace(/[\u007F-\uFFFF]/g, function(chr) {
|
||||
return "\\u" + ("0000" + chr.charCodeAt(0).toString(16)).substr(-4);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function parse(payload) {
|
||||
let parsed = JSON.parse(payload);
|
||||
return {
|
||||
version: parsed.v || 0, // 'undefined' version is treated as 0
|
||||
title: parsed.t,
|
||||
requiredShards: parsed.r,
|
||||
data: parsed.d,
|
||||
nonce: parsed.n
|
||||
}
|
||||
let parsed = JSON.parse(payload);
|
||||
return {
|
||||
version: parsed.v || 0, // 'undefined' version is treated as 0
|
||||
title: parsed.t,
|
||||
requiredShards: parsed.r,
|
||||
data: parsed.d,
|
||||
nonce: parsed.n
|
||||
};
|
||||
}
|
||||
|
||||
function reconstruct(shardObjects, passphrase) {
|
||||
var shardsRequirements = shardObjects.map(shard => shard.requiredShards);
|
||||
if (!shardsRequirements.every(r => r===shardsRequirements[0])) {
|
||||
throw "Mismatching min shards requirement among shards!"
|
||||
}
|
||||
if (shardObjects.length < shardsRequirements[0]) {
|
||||
throw `Not enough shards, need ${shardsRequirements[0]} but only ${shardObjects.length} provided`
|
||||
}
|
||||
var shardsRequirements = shardObjects.map(shard => shard.requiredShards);
|
||||
if (!shardsRequirements.every(r => r === shardsRequirements[0])) {
|
||||
throw "Mismatching min shards requirement among shards!";
|
||||
}
|
||||
if (shardObjects.length < shardsRequirements[0]) {
|
||||
throw `Not enough shards, need ${shardsRequirements[0]} but only ${
|
||||
shardObjects.length
|
||||
} provided`;
|
||||
}
|
||||
|
||||
var nonces = shardObjects.map(shard => shard.nonce);
|
||||
if (!nonces.every(n => n===nonces[0])) {
|
||||
throw "Nonces mismatch among shards!"
|
||||
}
|
||||
var nonces = shardObjects.map(shard => shard.nonce);
|
||||
if (!nonces.every(n => n === nonces[0])) {
|
||||
throw "Nonces mismatch among shards!";
|
||||
}
|
||||
|
||||
var titles = shardObjects.map((shard) => shard.title);
|
||||
if (!titles.every(t => t===titles[0])) {
|
||||
throw "Titles mismatch among shards!"
|
||||
}
|
||||
var titles = shardObjects.map(shard => shard.title);
|
||||
if (!titles.every(t => t === titles[0])) {
|
||||
throw "Titles mismatch among shards!";
|
||||
}
|
||||
|
||||
var versions = shardObjects.map(shard => shard.version);
|
||||
if (!versions.every(v => v===versions[0])) {
|
||||
throw "Versions mismatch along shards!"
|
||||
}
|
||||
var versions = shardObjects.map(shard => shard.version);
|
||||
if (!versions.every(v => v === versions[0])) {
|
||||
throw "Versions mismatch along shards!";
|
||||
}
|
||||
|
||||
switch (versions[0]) {
|
||||
case 0:
|
||||
var shardData = shardObjects.map(shard => shard.data);
|
||||
var encryptedSecret = SECRETS.combine(shardData);
|
||||
var secret = dehexify(encryptedSecret);
|
||||
var nonce = dehexify(nonces[0]);
|
||||
var salt = hashString(titles[0]);
|
||||
return uint8ArrayToStr(decrypt(secret, salt, passphrase, nonce));
|
||||
switch (versions[0]) {
|
||||
case 0:
|
||||
var shardData = shardObjects.map(shard => shard.data);
|
||||
var encryptedSecret = SECRETS.combine(shardData);
|
||||
var secret = dehexify(encryptedSecret);
|
||||
var nonce = dehexify(nonces[0]);
|
||||
var salt = hashString(titles[0]);
|
||||
return uint8ArrayToStr(decrypt(secret, salt, passphrase, nonce));
|
||||
|
||||
case 1:
|
||||
var shardDataV1 = shardObjects.map(shard => shard.data[0]+hexify(BASE64.toByteArray(shard.data.slice(1))));
|
||||
var encryptedSecretV1 = SECRETS.combine(shardDataV1);
|
||||
var secretV1 = dehexify(encryptedSecretV1);
|
||||
var nonceV1 = BASE64.toByteArray(nonces[0]);
|
||||
var saltV1 = hashString(titles[0]);
|
||||
return uint8ArrayToStr(decrypt(secretV1, saltV1, passphrase, nonceV1));
|
||||
case 1:
|
||||
var shardDataV1 = shardObjects.map(
|
||||
shard => shard.data[0] + hexify(BASE64.toByteArray(shard.data.slice(1)))
|
||||
);
|
||||
var encryptedSecretV1 = SECRETS.combine(shardDataV1);
|
||||
var secretV1 = dehexify(encryptedSecretV1);
|
||||
var nonceV1 = BASE64.toByteArray(nonces[0]);
|
||||
var saltV1 = hashString(titles[0]);
|
||||
return uint8ArrayToStr(decrypt(secretV1, saltV1, passphrase, nonceV1));
|
||||
|
||||
default:
|
||||
throw "Version is not supported!";
|
||||
}
|
||||
default:
|
||||
throw "Version is not supported!";
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
share,
|
||||
parse,
|
||||
reconstruct
|
||||
}
|
||||
|
||||
share,
|
||||
parse,
|
||||
reconstruct
|
||||
};
|
||||
|
||||
@ -4,38 +4,42 @@
|
||||
Combine shards for <em v-if="title"> {{ title }}</em>
|
||||
</h1>
|
||||
<div v-if="needMoreShards">
|
||||
<qrcode-stream v-on:decode="onDecode" />
|
||||
<qrcode-stream @decode="onDecode" />
|
||||
<div class="codes-row">
|
||||
<qriously
|
||||
class="qrcode"
|
||||
v-for="code in qrCodes"
|
||||
v-bind:key="code"
|
||||
v-bind:value="code"
|
||||
v-bind:size="200"
|
||||
v-bind:padding="0"
|
||||
:key="code"
|
||||
class="qrcode"
|
||||
:value="code"
|
||||
:size="200"
|
||||
:padding="0"
|
||||
/>
|
||||
<qriously
|
||||
class="qrcode remaining"
|
||||
v-for="n in remainingCodes"
|
||||
v-bind:key="n"
|
||||
value='{"t":"Very secret info","r":2,"d":"803c12929ba469d720a63fc8ca6f6ef1cc441f8f0b830ea04f8a484169ec800e4a7","n":"29abbb2c509adedb470f6d3fd3d083362b8c1a9283adf987"}'
|
||||
v-bind:size="200"
|
||||
:key="n"
|
||||
class="qrcode remaining"
|
||||
:value="PLACEHOLDER_QR_DATA"
|
||||
:size="200"
|
||||
foreground="#aaa"
|
||||
v-bind:padding="0"
|
||||
:padding="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<input
|
||||
type="text"
|
||||
v-model="passphrase"
|
||||
v-on:keyup.enter="reconstruct"
|
||||
type="text"
|
||||
placeholder="type your passphrase"
|
||||
autofocus
|
||||
@keyup.enter="reconstruct"
|
||||
/>
|
||||
<button v-on:click="reconstruct">Reconstruct Secret</button>
|
||||
<button @click="reconstruct">
|
||||
Reconstruct Secret
|
||||
</button>
|
||||
</div>
|
||||
<h2 class="secret" v-if="recoveredSecret">{{ recoveredSecret }}</h2>
|
||||
<h2 v-if="recoveredSecret" class="secret">
|
||||
{{ recoveredSecret }}
|
||||
</h2>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -44,6 +48,11 @@
|
||||
|
||||
import crypto from "../util/crypto";
|
||||
|
||||
// It's actually used in the template
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const PLACEHOLDER_QR_DATA =
|
||||
'{"t":"Very secret info","r":2,"d":"803c12929ba469d720a63fc8ca6f6ef1cc441f8f0b830ea04f8a484169ec800e4a7","n":"29abbb2c509adedb470f6d3fd3d083362b8c1a9283adf987"}';
|
||||
|
||||
export default {
|
||||
name: "Combine",
|
||||
data: function() {
|
||||
@ -69,6 +78,9 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted: function() {
|
||||
this.$eventHub.$emit("foldGeneralInfo");
|
||||
},
|
||||
methods: {
|
||||
onDecode: function(result) {
|
||||
var parsed = crypto.parse(result);
|
||||
@ -111,9 +123,6 @@ export default {
|
||||
var shards = Array.from(this.shards);
|
||||
this.recoveredSecret = crypto.reconstruct(shards, this.passphrase);
|
||||
}
|
||||
},
|
||||
mounted: function() {
|
||||
this.$eventHub.$emit("foldGeneralInfo");
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -1,14 +1,9 @@
|
||||
<template>
|
||||
<div>
|
||||
</div>
|
||||
<div />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
}
|
||||
export default {};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
<style></style>
|
||||
|
||||
@ -5,48 +5,52 @@
|
||||
<p>
|
||||
Name of your split
|
||||
<input
|
||||
v-model="title"
|
||||
type="text"
|
||||
:disabled="encryptionMode"
|
||||
v-model="title"
|
||||
placeholder="Ex: 'My Bitcoin seed phrase'"
|
||||
autofocus
|
||||
/>
|
||||
</p>
|
||||
<textarea
|
||||
v-model="secret"
|
||||
v-bind:class="{ tooLong: secretTooLong }"
|
||||
:class="{ tooLong: secretTooLong }"
|
||||
:disabled="encryptionMode"
|
||||
placeholder="Your secret goes here"
|
||||
></textarea>
|
||||
<div v-if="this.secretTooLong">
|
||||
/>
|
||||
<div v-if="secretTooLong">
|
||||
Inputs longer than 1024 characters make QR codes illegible
|
||||
</div>
|
||||
<p>
|
||||
Will require any {{ requiredShards }} shards out of
|
||||
<input type="number" v-model.number="totalShards" min="3" /> to
|
||||
<input v-model.number="totalShards" type="number" min="3" /> to
|
||||
reconstruct
|
||||
</p>
|
||||
<button :disabled="secretTooLong" v-on:click="toggleMode">
|
||||
<span v-if="this.encryptionMode">Back to editing data</span>
|
||||
<span v-if="encryptionMode">Back to editing data</span>
|
||||
<span v-else>Generate QR codes!</span>
|
||||
</button>
|
||||
<div v-if="this.encryptionMode">
|
||||
<div v-if="encryptionMode">
|
||||
<p>Your passphrase for the recovery is:</p>
|
||||
<p>
|
||||
<canvas-text v-bind:text="recoveryPassphrase" />
|
||||
<button v-on:click="regenPassphrase">↺</button>
|
||||
<canvas-text :text="recoveryPassphrase" />
|
||||
<button @click="regenPassphrase">
|
||||
↺
|
||||
</button>
|
||||
</p>
|
||||
<button v-on:click="print">Print us!</button>
|
||||
<button @click="print">
|
||||
Print us!
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="qr-tiles">
|
||||
<shard-info
|
||||
v-for="shard in shards"
|
||||
v-bind:key="shard"
|
||||
v-bind:shard="shard"
|
||||
v-bind:requiredShards="requiredShards"
|
||||
v-bind:title="title"
|
||||
:key="shard"
|
||||
:shard="shard"
|
||||
:required-shards="requiredShards"
|
||||
:title="title"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -61,6 +65,7 @@ import CanvasText from "../components/CanvasText";
|
||||
|
||||
export default {
|
||||
name: "Share",
|
||||
components: { ShardInfo, CanvasText },
|
||||
data: function() {
|
||||
return {
|
||||
title: "",
|
||||
@ -70,7 +75,6 @@ export default {
|
||||
encryptionMode: false
|
||||
};
|
||||
},
|
||||
components: { ShardInfo, CanvasText },
|
||||
computed: {
|
||||
secretTooLong: function() {
|
||||
return this.secret.length > 1024;
|
||||
@ -91,6 +95,9 @@ export default {
|
||||
);
|
||||
}
|
||||
},
|
||||
mounted: function() {
|
||||
this.$eventHub.$emit("foldGeneralInfo");
|
||||
},
|
||||
methods: {
|
||||
regenPassphrase: function() {
|
||||
this.recoveryPassphrase = bipPhrase.generate(4);
|
||||
@ -101,9 +108,6 @@ export default {
|
||||
toggleMode: function() {
|
||||
this.encryptionMode = !this.encryptionMode;
|
||||
}
|
||||
},
|
||||
mounted: function() {
|
||||
this.$eventHub.$emit("foldGeneralInfo");
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -1,98 +1,134 @@
|
||||
require('fast-text-encoding'); // NodeJS requires this polyfill
|
||||
/* eslint-disable security/detect-object-injection */
|
||||
require("fast-text-encoding"); // NodeJS requires this polyfill
|
||||
|
||||
import crypto from "../../src/util/crypto";
|
||||
|
||||
test('encodes-decodes empty values', () => {
|
||||
var shards = crypto.share('', '', '', 3, 2);
|
||||
var parsed = shards.map(s => crypto.parse(s));
|
||||
var reconstructed = crypto.reconstruct(parsed, '');
|
||||
expect(reconstructed).toBe('');
|
||||
})
|
||||
test("encodes-decodes empty values", () => {
|
||||
var shards = crypto.share("", "", "", 3, 2);
|
||||
var parsed = shards.map(s => crypto.parse(s));
|
||||
var reconstructed = crypto.reconstruct(parsed, "");
|
||||
expect(reconstructed).toBe("");
|
||||
});
|
||||
|
||||
test('properly deserializes metadata', () => {
|
||||
var shards = crypto.share('Secret message', 'Secret title', 'correct-horse-battery-staple', 5, 3);
|
||||
var parsed = shards.map(s => crypto.parse(s));
|
||||
parsed.forEach(message => {
|
||||
expect(message.title).toBe('Secret title');
|
||||
expect(message.requiredShards).toBe(3);
|
||||
expect(message.nonce).toBe(parsed[0].nonce);
|
||||
});
|
||||
})
|
||||
test("properly deserializes metadata", () => {
|
||||
var shards = crypto.share(
|
||||
"Secret message",
|
||||
"Secret title",
|
||||
"correct-horse-battery-staple",
|
||||
5,
|
||||
3
|
||||
);
|
||||
var parsed = shards.map(s => crypto.parse(s));
|
||||
parsed.forEach(message => {
|
||||
expect(message.title).toBe("Secret title");
|
||||
expect(message.requiredShards).toBe(3);
|
||||
expect(message.nonce).toBe(parsed[0].nonce);
|
||||
});
|
||||
});
|
||||
|
||||
test('works with any 2 shards out of 3', () => {
|
||||
var shards = crypto.share('Secret message', 'Secret title', 'correct-horse-battery-staple', 3, 2);
|
||||
var parsed = shards.map(s => crypto.parse(s));
|
||||
[
|
||||
test("works with any 2 shards out of 3", () => {
|
||||
var shards = crypto.share(
|
||||
"Secret message",
|
||||
"Secret title",
|
||||
"correct-horse-battery-staple",
|
||||
3,
|
||||
2
|
||||
);
|
||||
var parsed = shards.map(s => crypto.parse(s));
|
||||
[[0, 1], [0, 2], [1, 2], [1, 0], [2, 0], [2, 1]].forEach(([i, j]) => {
|
||||
expect(
|
||||
crypto.reconstruct([parsed[i], parsed[j]], "correct-horse-battery-staple")
|
||||
).toBe("Secret message");
|
||||
});
|
||||
});
|
||||
|
||||
[0, 1], [0, 2], [1, 2],
|
||||
[1, 0], [2, 0], [2, 1]
|
||||
].forEach(([i, j]) => {
|
||||
expect(crypto.reconstruct([parsed[i], parsed[j]], 'correct-horse-battery-staple')).toBe('Secret message')
|
||||
});
|
||||
})
|
||||
test("fails with incorrect password", () => {
|
||||
var shards = crypto.share(
|
||||
"Secret message",
|
||||
"Secret title",
|
||||
"correct-horse-battery-staple",
|
||||
5,
|
||||
3
|
||||
);
|
||||
var parsed = shards.map(s => crypto.parse(s));
|
||||
var reconstructed = crypto.reconstruct(parsed, "");
|
||||
expect(reconstructed).not.toBe("Secret message");
|
||||
expect(reconstructed).toBe("");
|
||||
});
|
||||
|
||||
test('fails with incorrect password', () => {
|
||||
var shards = crypto.share('Secret message', 'Secret title', 'correct-horse-battery-staple', 5, 3);
|
||||
var parsed = shards.map(s => crypto.parse(s));
|
||||
var reconstructed = crypto.reconstruct(parsed, '');
|
||||
expect(reconstructed).not.toBe('Secret message');
|
||||
expect(reconstructed).toBe('');
|
||||
})
|
||||
test("works with unicode strings", () => {
|
||||
var shards = crypto.share(
|
||||
"Текст сообщения",
|
||||
"Это секрет",
|
||||
"correct-horse-battery-staple",
|
||||
5,
|
||||
3
|
||||
);
|
||||
var parsed = shards.map(s => crypto.parse(s));
|
||||
parsed.forEach(message => {
|
||||
expect(message.title).toBe("Это секрет");
|
||||
});
|
||||
|
||||
test('works with unicode strings', () => {
|
||||
var shards = crypto.share('Текст сообщения', 'Это секрет', 'correct-horse-battery-staple', 5, 3);
|
||||
var parsed = shards.map(s => crypto.parse(s));
|
||||
parsed.forEach(message => {
|
||||
expect(message.title).toBe('Это секрет')
|
||||
});
|
||||
var reconstructed = crypto.reconstruct(
|
||||
parsed,
|
||||
"correct-horse-battery-staple"
|
||||
);
|
||||
expect(reconstructed).toBe("Текст сообщения");
|
||||
});
|
||||
|
||||
var reconstructed = crypto.reconstruct(parsed, 'correct-horse-battery-staple');
|
||||
expect(reconstructed).toBe('Текст сообщения');
|
||||
})
|
||||
test("reconstructs the reference example of v0 serialization", () => {
|
||||
var shards = [
|
||||
'{"t":"Very secret info","r":2,"d":"803c12929ba469d720a63fc8ca6f6ef1cc441f8f0b830ea04f8a484169ec800e4a7","n":"29abbb2c509adedb470f6d3fd3d083362b8c1a9283adf987"}',
|
||||
'{"t":"Very secret info","r":2,"d":"801b4196813bb664141169f939414fec30c7f343326d8df45e557b25b0249fd43e2","n":"29abbb2c509adedb470f6d3fd3d083362b8c1a9283adf987"}',
|
||||
'{"t":"Very secret info","r":2,"d":"80275318760b66ee5a1d7430dbf8769fda05e9e1ff7447eaa78539fbed006f0390b","n":"29abbb2c509adedb470f6d3fd3d083362b8c1a9283adf987"}'
|
||||
].map(s => crypto.parse(s));
|
||||
|
||||
test('reconstructs the reference example of v0 serialization', () => {
|
||||
var shards = [
|
||||
'{"t":"Very secret info","r":2,"d":"803c12929ba469d720a63fc8ca6f6ef1cc441f8f0b830ea04f8a484169ec800e4a7","n":"29abbb2c509adedb470f6d3fd3d083362b8c1a9283adf987"}',
|
||||
'{"t":"Very secret info","r":2,"d":"801b4196813bb664141169f939414fec30c7f343326d8df45e557b25b0249fd43e2","n":"29abbb2c509adedb470f6d3fd3d083362b8c1a9283adf987"}',
|
||||
'{"t":"Very secret info","r":2,"d":"80275318760b66ee5a1d7430dbf8769fda05e9e1ff7447eaa78539fbed006f0390b","n":"29abbb2c509adedb470f6d3fd3d083362b8c1a9283adf987"}'
|
||||
].map(s => crypto.parse(s));
|
||||
[[0, 1], [0, 2], [1, 2], [1, 0], [2, 0], [2, 1]].forEach(([i, j]) => {
|
||||
expect(
|
||||
crypto.reconstruct([shards[i], shards[j]], "amazing-daring-panda-horror")
|
||||
).toBe("Any questions?");
|
||||
});
|
||||
});
|
||||
|
||||
[
|
||||
[0, 1], [0, 2], [1, 2],
|
||||
[1, 0], [2, 0], [2, 1]
|
||||
].forEach(([i,j]) => {
|
||||
expect(crypto.reconstruct([shards[i], shards[j]], 'amazing-daring-panda-horror')).toBe('Any questions?')
|
||||
});
|
||||
})
|
||||
test("reconstructs the reference example of v1 serialization", () => {
|
||||
var shards = [
|
||||
'{"v":1,"t":"Pssst","r":2,"d":"8AdI3F1Xn4CK9mXEVWLWgg2gzho5WV38E/hn1OYyRMZenL/Jm6dmrZoiji2ZlMSVEW+XN9WW1I/ilDC1yiu4oBa4=","n":"a17TDZHP2iL/sdPHgFJUP3NlAC7bDgrp"}',
|
||||
'{"v":1,"t":"Pssst","r":2,"d":"8ArluLqrT3URnL+IqsHddG9MpXqvSNt5JBLfTqSJmCg4raIFLg2XhfbnFLCTgCumI4qByThq9bBxnwLy8EgEHYiw=","n":"a17TDZHP2iL/sdPHgFJUP3NlAC7bDgrp"}',
|
||||
'{"v":1,"t":"Pssst","r":2,"d":"8A2tZOf80PWbatpM/6ML9mLrUFkOu4kpyUiY62bPA6HmkVVtQpfosdF3nuhpo6K3MfmjsJ8ROokDShDgNka/ptFI=","n":"a17TDZHP2iL/sdPHgFJUP3NlAC7bDgrp"}'
|
||||
].map(s => crypto.parse(s));
|
||||
|
||||
test('reconstructs the reference example of v1 serialization', () => {
|
||||
var shards = [
|
||||
'{"v":1,"t":"Pssst","r":2,"d":"8AdI3F1Xn4CK9mXEVWLWgg2gzho5WV38E/hn1OYyRMZenL/Jm6dmrZoiji2ZlMSVEW+XN9WW1I/ilDC1yiu4oBa4=","n":"a17TDZHP2iL/sdPHgFJUP3NlAC7bDgrp"}',
|
||||
'{"v":1,"t":"Pssst","r":2,"d":"8ArluLqrT3URnL+IqsHddG9MpXqvSNt5JBLfTqSJmCg4raIFLg2XhfbnFLCTgCumI4qByThq9bBxnwLy8EgEHYiw=","n":"a17TDZHP2iL/sdPHgFJUP3NlAC7bDgrp"}',
|
||||
'{"v":1,"t":"Pssst","r":2,"d":"8A2tZOf80PWbatpM/6ML9mLrUFkOu4kpyUiY62bPA6HmkVVtQpfosdF3nuhpo6K3MfmjsJ8ROokDShDgNka/ptFI=","n":"a17TDZHP2iL/sdPHgFJUP3NlAC7bDgrp"}'
|
||||
].map(s => crypto.parse(s));
|
||||
[[0, 1], [0, 2], [1, 2], [1, 0], [2, 0], [2, 1]].forEach(([i, j]) => {
|
||||
expect(
|
||||
crypto.reconstruct([shards[i], shards[j]], "excess-torch-unfold-fix")
|
||||
).toBe("Version one is all-around better");
|
||||
});
|
||||
});
|
||||
|
||||
[
|
||||
[0, 1], [0, 2], [1, 2],
|
||||
[1, 0], [2, 0], [2, 1]
|
||||
].forEach(([i,j]) => {
|
||||
expect(crypto.reconstruct([shards[i], shards[j]], 'excess-torch-unfold-fix')).toBe('Version one is all-around better')
|
||||
});
|
||||
})
|
||||
test("throws an error if nonces mismatch", () => {
|
||||
var shards = crypto
|
||||
.share("Message", "Title", "correct-horse-battery-staple", 3, 2)
|
||||
.map(s => crypto.parse(s));
|
||||
shards[0].nonce = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
|
||||
expect(() =>
|
||||
crypto.reconstruct([shards[0], shards[1]], "correct-horse-battery-staple")
|
||||
).toThrow(/Nonces mismatch/);
|
||||
});
|
||||
|
||||
test('throws an error if nonces mismatch', () => {
|
||||
var shards = crypto.share('Message', 'Title', 'correct-horse-battery-staple', 3, 2).map(s => crypto.parse(s));
|
||||
shards[0].nonce = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
expect(() => crypto.reconstruct([shards[0], shards[1]], 'correct-horse-battery-staple')).toThrow(/Nonces mismatch/)
|
||||
})
|
||||
|
||||
test('throws an error if titles mismatch', () => {
|
||||
var shards = crypto.share('Message', 'Title', 'correct-horse-battery-staple', 3, 2).map(s => crypto.parse(s));
|
||||
shards[0].title = "Completely different title"
|
||||
expect(() => crypto.reconstruct([shards[0], shards[1]], 'correct-horse-battery-staple')).toThrow(/Titles mismatch/)
|
||||
})
|
||||
test("throws an error if titles mismatch", () => {
|
||||
var shards = crypto
|
||||
.share("Message", "Title", "correct-horse-battery-staple", 3, 2)
|
||||
.map(s => crypto.parse(s));
|
||||
shards[0].title = "Completely different title";
|
||||
expect(() =>
|
||||
crypto.reconstruct([shards[0], shards[1]], "correct-horse-battery-staple")
|
||||
).toThrow(/Titles mismatch/);
|
||||
});
|
||||
|
||||
test("doesn't work if less shards than necessary are supplied", () => {
|
||||
var shards = crypto.share('Message', 'Title', 'correct-horse-battery-staple', 5, 3).map(s => crypto.parse(s));
|
||||
expect(() => crypto.reconstruct([shards[0], shards[1]], 'correct-horse-battery-staple')).toThrow(/Not enough shards/)
|
||||
})
|
||||
var shards = crypto
|
||||
.share("Message", "Title", "correct-horse-battery-staple", 5, 3)
|
||||
.map(s => crypto.parse(s));
|
||||
expect(() =>
|
||||
crypto.reconstruct([shards[0], shards[1]], "correct-horse-battery-staple")
|
||||
).toThrow(/Not enough shards/);
|
||||
});
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
let HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
let HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin');
|
||||
let Webpack = require('webpack');
|
||||
let HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||
let HtmlWebpackInlineSourcePlugin = require("html-webpack-inline-source-plugin");
|
||||
let Webpack = require("webpack");
|
||||
|
||||
let childProcess = require('child_process');
|
||||
let GIT_REVISION = childProcess.execSync('git rev-parse HEAD').toString();
|
||||
// eslint-disable-next-line security/detect-child-process
|
||||
let childProcess = require("child_process");
|
||||
let GIT_REVISION = childProcess.execSync("git rev-parse HEAD").toString();
|
||||
|
||||
module.exports = {
|
||||
productionSourceMap: false,
|
||||
@ -13,15 +14,15 @@ module.exports = {
|
||||
configureWebpack: {
|
||||
plugins: [
|
||||
new HtmlWebpackPlugin({
|
||||
template: 'public/index.html',
|
||||
inlineSource: '.(js|css)$'
|
||||
template: "public/index.html",
|
||||
inlineSource: ".(js|css)$"
|
||||
}),
|
||||
new HtmlWebpackInlineSourcePlugin(HtmlWebpackPlugin),
|
||||
new Webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
'GIT_REVISION': JSON.stringify(GIT_REVISION)
|
||||
"process.env": {
|
||||
GIT_REVISION: JSON.stringify(GIT_REVISION)
|
||||
}
|
||||
})
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user