UNPKG

shamir

Version:

A JavaScript implementation of Shamir's Secret Sharing algorithm over GF(256).

227 lines (192 loc) 7.2 kB
/* * Copyright © 2019 Simon Massey (massey1905@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ const test = require('tape-catch'); const { randomBytes } = require('crypto'); /* eslint-disable prefer-arrow-callback */ /* eslint-disable func-names */ /* eslint-disable no-plusplus */ /* eslint-disable no-bitwise */ // https://codereview.stackexchange.com/a/3589/75693 function bytesToSring(bytes) { const chars = []; for (let i = 0, n = bytes.length; i < n;) { chars.push(((bytes[i++] & 0xff) << 8) | (bytes[i++] & 0xff)); } return String.fromCharCode.apply(null, chars); } // https://codereview.stackexchange.com/a/3589/75693 function stringToBytes(str) { const bytes = []; for (let i = 0, n = str.length; i < n; i++) { const char = str.charCodeAt(i); bytes.push(char >>> 8, char & 0xff); } return bytes; } // eslint-disable-next-line no-undef const ByteArray = Java.type('byte[]'); // eslint-disable-next-line no-undef const JavaScriptUtils = Java.type('com.codahale.shamir.polygot.JavaScriptUtils'); function jsByteArrayToJavaByteArray(jsarray) { const jbarray = new ByteArray(jsarray.length); for (let i = 0; i < jsarray.length; i++) { jbarray[i] = JavaScriptUtils.unsignedToSignedByteMap.get(jsarray[i]); } return jbarray; } function jBytesArrayToJavaScriptByteArray(barray) { const jsarray = []; for (let i = 0; i < barray.length; i++) { jsarray.push(JavaScriptUtils.signedToUnsignedByteMap.get(barray[i])); } return jsarray; } test('PolygotTests Java and JavaScript byte array roundtrip', function (t) { t.plan(1); const secretUtf8 = 'ᚠᛇᚻ'; const jsBytes = stringToBytes(secretUtf8); const jbytes = jsByteArrayToJavaByteArray(jsBytes); const jsRoundTripBytes = jBytesArrayToJavaScriptByteArray(jbytes); t.equal(secretUtf8, bytesToSring(jsRoundTripBytes)); }); // eslint-disable-next-line no-undef const Scheme = Java.type('com.codahale.shamir.Scheme'); // eslint-disable-next-line no-undef const SecureRandom = Java.type('java.security.SecureRandom'); const secureRandom = new SecureRandom(); test('PolygotTests JavaScript strings with all Java logic', function (t) { t.plan(1); const scheme = new Scheme(secureRandom, 5, 3); const secretUtf8 = 'ᚠᛇᚻ'; const parts = scheme.split(jsByteArrayToJavaByteArray(stringToBytes(secretUtf8))); const joined = scheme.join(parts); t.equal(secretUtf8, bytesToSring(jBytesArrayToJavaScriptByteArray(joined))); }); const { split } = require('../../main/js/Scheme.js'); // eslint-disable-next-line no-undef const HashMap = Java.type('java.util.HashMap'); function javaScriptToJavaParts(parts) { const map = new HashMap(); // eslint-disable-next-line no-restricted-syntax, guard-for-in for (const key in parts) { const bytes = parts[key]; const jbarr = jsByteArrayToJavaByteArray(bytes); map.put(Number(key), jbarr); } return map; } function javaToJavaScriptParts(javaMap) { const result = {}; const entrySetIterator = javaMap.entrySet().iterator(); while (entrySetIterator.hasNext()) { const pair = entrySetIterator.next(); const key = pair.getKey(); const value = pair.getValue(); result[key] = jBytesArrayToJavaScriptByteArray(value); } return result; } // eslint-disable-next-line no-undef const Collectors = Java.type('java.util.stream.Collectors'); function equalParts(jParts, jsParts) { // js keys are strings const jsKeysNumbers = Object.keys(jsParts); // j keys are java integers that we map to strings const jKeysSet = jParts .keySet() .stream() .map(n => n.toString()) .collect(Collectors.toList()); // check that all js keys are in the j keys // eslint-disable-next-line no-restricted-syntax for (const jsk of jsKeysNumbers) { if (!jKeysSet.contains(jsk)) { throw new Error(`jKeysSet ${jKeysSet} does not contain jsk ${jsk}`); } } // check that all j keys are in the js keys // eslint-disable-next-line no-restricted-syntax for (const jk of jKeysSet) { if (!jsKeysNumbers.includes(jk)) { throw new Error(`jsKeysNumbers ${jsKeysNumbers} does not contain jk ${jk}`); } } // eslint-disable-next-line no-restricted-syntax for (const k of Object.keys(jsParts)) { const jArray = jBytesArrayToJavaScriptByteArray(jParts.get(Number(k))); const jsArray = jsParts[k]; if (jArray.length !== jsArray.length) { throw new Error(`unequal lengths ${jArray.length} != ${jsArray.length}`); } for (let l = 0; l < jArray.length; l++) { if (jArray[l] !== jsArray[l]) { throw new Error(`at index ${l}: ${jArray[l]} != ${jsArray[l]}`); } } } return true; } test('PolygotTests roundrip parts between JavaScript and Java', function (t) { t.plan(1); const secretUtf8 = 'ᚠᛇᚻ'; const secret = stringToBytes(secretUtf8); const parts = split(randomBytes, 3, 2, secret); const jParts = javaScriptToJavaParts(parts); const jsParts = javaToJavaScriptParts(jParts); t.ok(equalParts(jParts, jsParts), 'roundtrip parts'); }); // eslint-disable-next-line no-undef const NotRandomSource = Java.type('com.codahale.shamir.polygot.NotRandomSource'); const notRandomSource = new NotRandomSource(); function notRandomSourceJavaScript(len) { const bytes = []; for (let i = 0; i < len; i++) { bytes[i] = i + 1; } return bytes; } test('PolygotTests compare Java and JavaScript split', function (t) { t.plan(1); const secretUtf8 = 'ᚠᛇᚻ'; const secret = stringToBytes(secretUtf8); const jsParts = split(notRandomSourceJavaScript, 3, 2, secret); const jscheme = new Scheme(notRandomSource, 3, 2); const jParts = jscheme.split(jsByteArrayToJavaByteArray(secret)); t.ok(equalParts(jParts, jsParts), 'splits match'); }); test('PolygotTests JavaScript split with Java join', function (t) { t.plan(1); const secretUtf8 = 'ᚠᛇᚻ'; const secret = stringToBytes(secretUtf8); const jsParts = split(randomBytes, 3, 2, secret); const jscheme = new Scheme(secureRandom, 3, 2); const joined = jscheme.join(javaScriptToJavaParts(jsParts)); t.equal( bytesToSring(jBytesArrayToJavaScriptByteArray(joined)), secretUtf8, 'java joined js parts', ); }); const { join } = require('../../main/js/Scheme.js'); test('PolygotTests Java split with JavaScript join', function (t) { t.plan(1); const secretUtf8 = 'ᚠᛇᚻ'; const secret = stringToBytes(secretUtf8); const jscheme = new Scheme(secureRandom, 3, 2); const jParts = jscheme.split(jsByteArrayToJavaByteArray(secret)); const joined = join(javaToJavaScriptParts(jParts)); t.equal(bytesToSring(joined), secretUtf8, 'java joined js parts'); });