From a05bc04c728f2ec9a2f708557104e164d38b57b3 Mon Sep 17 00:00:00 2001 From: Hadas Zeilberger Date: Mon, 5 Nov 2018 15:48:32 -0500 Subject: [PATCH 1/3] WIP shamir integration --- ExampleGrinder/App.js | 15 ++- e2e/firstTest.spec.js | 4 + lib/config.js | 46 ++++++++ lib/galoisField.js | 83 ++++++++++++++ lib/galoisField.test.js | 65 +++++++++++ lib/randomBitGenerator.js | 16 +++ lib/sssa.js | 226 ++++++++++++++++++++++++++++++++++++++ lib/sssa.test.js | 41 +++++++ lib/utils.js | 76 +++++++++++++ package.json | 3 +- yarn.lock | 5 + 11 files changed, 576 insertions(+), 4 deletions(-) create mode 100644 lib/config.js create mode 100644 lib/galoisField.js create mode 100644 lib/galoisField.test.js create mode 100644 lib/randomBitGenerator.js create mode 100644 lib/sssa.js create mode 100644 lib/sssa.test.js create mode 100644 lib/utils.js diff --git a/ExampleGrinder/App.js b/ExampleGrinder/App.js index 69ce0c3..823c484 100644 --- a/ExampleGrinder/App.js +++ b/ExampleGrinder/App.js @@ -8,7 +8,8 @@ import React, { Component } from 'react'; import { Platform, StyleSheet, Text, View } from 'react-native'; -import { generateSecureRandom } from '../RNSecureRandom/index'; +import { randomBitGenerator } from '../lib/randomBitGenerator.js'; +import { bytesToBits } from '../lib/utils'; const instructions = Platform.select({ ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu', android: @@ -17,15 +18,23 @@ const instructions = Platform.select({ }); type Props = {}; + export default class App extends Component { + state = { randomBits: '' }; + async componentDidMount() { + let randomNumber = await randomBitGenerator(14); + this.setState({ randomBits: randomNumber }); + } render() { return ( - Welcome to React Native! + {this.state.randomBits} To get started, edit App.js - {instructions} + + {this.state.randomBits.length} + ); } diff --git a/e2e/firstTest.spec.js b/e2e/firstTest.spec.js index 8c50844..ae1b723 100644 --- a/e2e/firstTest.spec.js +++ b/e2e/firstTest.spec.js @@ -6,4 +6,8 @@ describe('Example', () => { it('should have welcome screen', async () => { await expect(element(by.id('welcome'))).toBeVisible(); }); + + it('should have the correct length', async () => { + await expect(element(by.id('randombits'))).toHaveText('14'); + }); }); diff --git a/lib/config.js b/lib/config.js new file mode 100644 index 0000000..3e580a3 --- /dev/null +++ b/lib/config.js @@ -0,0 +1,46 @@ +/*eslint no-undef: "off"*/ +export default (config = { + bits: 8, // default number of bits + radix: 16, // work with hex by default + minbits: 3, + maxbits: 20, // this permits 1,048,575 shares, though going this high is not recommended in js! + bytesperchar: 2, + maxbytesperchar: 6, // math.pow(256,7) > math.pow(2,53) + + // primitive polynomials (in decimal form) for galois fields gf(2^n), for 2 <= n <= 30 + // the index of each term in the array corresponds to the n for that polynomial + // i.e. to get the polynomial for n=16, use primitivepolynomials[16] + primitivepolynomials: [ + null, + null, + 1, + 3, + 3, + 5, + 3, + 3, + 29, + 17, + 9, + 5, + 83, + 27, + 43, + 3, + 45, + 9, + 39, + 39, + 9, + 5, + 3, + 33, + 27, + 9, + 71, + 39, + 9, + 5, + 83 + ] +}); diff --git a/lib/galoisField.js b/lib/galoisField.js new file mode 100644 index 0000000..b2d4b9f --- /dev/null +++ b/lib/galoisField.js @@ -0,0 +1,83 @@ +import config from './config'; + +//class that represents polynomials as bit arrays +export class Polynomial { + constructor(value) { + this.value = value; + } + getValue() { + return this.value; + } + setValue(value) { + this.value = value; + } + plus(rightHandPolynomial) { + this.value = this.value ^ rightHandPolynomial; + return this; + } + timesMonomialOfDegree(n) { + this.value = this.value << n; + return this; + } + subtractTermsAboveDegree(n) { + this.value = this.value & (Math.pow(2, n + 1) - 1); + return this; + } + static toIntegerForm(bitArray) { + return parseInt(bitArray, 2); + } + static toPolynomialForm(integer) { + return new Number(integer).toString(2); + } +} + +export class CharacteristicTwoGaloisField { + constructor(numElements) { + this.n = Math.log2(numElements); + if ( + this.n && + (this.n % 1 !== 0 || this.n < config.minBits || this.n > config.maxBits) + ) { + throw new Error( + 'Number of n must be an integer between ' + + config.minBits + + ' and ' + + config.maxBits + + ', inclusive.' + ); + } + this.numberOfElementsInField = numElements; + this.computeLogAndExpTables(); + } + fieldContains(value) { + return value < this.numberOfElementsInField; + } + getExponentOfNextDegree(expOfCurrentDegree) { + let primitivePolynomial = config.primitivepolynomials[this.n]; + var polynomial = expOfCurrentDegree.timesMonomialOfDegree(1); + if (!this.fieldContains(polynomial.getValue())) { + polynomial = polynomial + .plus(primitivePolynomial) + .subtractTermsAboveDegree(this.n - 1); + } + return polynomial; + } + computeLogAndExpTables() { + this.exps = []; + this.logs = []; + let polynomial = new Polynomial(1); + for (var i = 0; i < this.numberOfElementsInField; i++) { + this.exps[i] = polynomial.getValue(); + this.logs[polynomial.getValue()] = i; + polynomial = this.getExponentOfNextDegree(polynomial); + } + } + multiply(polynomialOne, polynomialTwo) { + return new Polynomial( + this.exps[ + (this.logs[polynomialOne] + this.logs[polynomialTwo]) % + (this.numberOfElementsInField - 1) + ] + ); + } +} diff --git a/lib/galoisField.test.js b/lib/galoisField.test.js new file mode 100644 index 0000000..8a0ff63 --- /dev/null +++ b/lib/galoisField.test.js @@ -0,0 +1,65 @@ +import { Polynomial, CharacteristicTwoGaloisField } from './galoisField'; +//Testing Polynomial Class +describe('adds two polynomials in galois field of characteristic two', () => { + it('should output the correct XOR value of two polynomials represened as bit arrays', () => { + var polynomialOne = Polynomial.toIntegerForm('1010'); //equivelent of x^3 + x (^ is exponent in this context) + var polynomialTwo = Polynomial.toIntegerForm('0110'); //equivelent of x^2 + x + var expectedResult = Polynomial.toIntegerForm('1100'); //equivelent of x^3 + x^2 + var actualResult = new Polynomial(polynomialOne).plus(polynomialTwo); + expect(expectedResult).toBe(actualResult.getValue()); + expect(4).toBe(new Polynomial(1).plus(5).getValue()); + }); +}); + +describe('multiplies a polynomial by a monomial of the given degree', () => { + it('should multiply the valueToMultiply by monomial', () => { + var valueToMultiply = Polynomial.toIntegerForm('11100'); //x^4 + x^3 + x^2 in polynomial form, and 28 in integer form + var monomial = '10'; //equivelently x^1 in polynomial form and 2 in integer form + var degreeOfMonomial = Math.log2(Polynomial.toIntegerForm(monomial)); //degree is 1 + var expectedResult = Polynomial.toIntegerForm('111000'); + var actualResult = new Polynomial(valueToMultiply).timesMonomialOfDegree( + degreeOfMonomial + ); + expect(expectedResult).toBe(actualResult.getValue()); + }); +}); + +describe('subtract terms above degree', () => { + it('bitwise AND with bit array containing all 1 values up to given degree', () => { + var polynomialWithBigDegree = Polynomial.toIntegerForm('11100'); + var expectedResult = Polynomial.toIntegerForm('100'); + var actualResult = new Polynomial( + polynomialWithBigDegree + ).subtractTermsAboveDegree(2); + expect(expectedResult).toBe(actualResult.getValue()); + }); +}); + +//Testing ChracteristicTwoGaloisField class +describe('determines if an element is contained within the field', () => { + it('should return false if the degree of the element is above n in 2^n', () => { + var polynomial = Polynomial.toIntegerForm('1111110'); + var gf = new CharacteristicTwoGaloisField(Math.pow(2, 5)); + var expectedResult = false; + var actualResult = gf.fieldContains(polynomial); + expect(expectedResult).toBe(actualResult); + }); +}); +describe('create exponent and log tables for the field', () => { + it('should output the correct exp and log tables for GF(2^3)', () => { + var gf = new CharacteristicTwoGaloisField(Math.pow(2, 3)); + expect(gf.exps[0]).toBe(1); + expect(gf.exps[1]).toBe(2); + expect(gf.exps[2]).toBe(4); + expect(gf.exps[3]).toBe(3); + expect(gf.exps[4]).toBe(6); + expect(gf.exps[5]).toBe(7); + expect(gf.exps[6]).toBe(5); + expect(gf.exps[7]).toBe(1); + for (var i = 1; i < Math.pow(2, 3); i++) { + expect(gf.exps[gf.logs[i]]).toBe(i); + } + expect(gf.logs[1]).toBe(7); + expect(gf.logs[0]).toBeFalsy(); + }); +}); diff --git a/lib/randomBitGenerator.js b/lib/randomBitGenerator.js new file mode 100644 index 0000000..cbf68da --- /dev/null +++ b/lib/randomBitGenerator.js @@ -0,0 +1,16 @@ +import { generateSecureRandom } from '../RNSecureRandom/index'; +import { bytesToBits } from './utils'; + +export async function randomBitGenerator(numBits) { + var buf, + numBytes, + str = null; + + numBytes = Math.ceil(numBits / 8); + while (str === null) { + let uIntByteArr = await generateSecureRandom(numBytes); + str = bytesToBits(uIntByteArr); + } + str = str.substr(-numBits); + return str; +} diff --git a/lib/sssa.js b/lib/sssa.js new file mode 100644 index 0000000..30ddd9c --- /dev/null +++ b/lib/sssa.js @@ -0,0 +1,226 @@ +import { randomBitGenerator } from './randomBitGenerator'; +import { Polynomial, CharacteristicTwoGaloisField } from './galoisField'; +var isBase64 = require('is-base64'); +import { + splitBitsToIntArray, + base64ToBits, + padLeft, + bin2hex +} from './utils.js'; +export class SSSA { + constructor(coeffLength) { + this.fieldSize = Math.pow(2, coeffLength); + this.char2GF = new CharacteristicTwoGaloisField(this.fieldSize); + this.coeffLength = coeffLength; + } + // Polynomial evaluation at `x` using Horner's Method + // NOTE: fx=fx * x + coeff[i] -> exp(log(fx) + log(x)) + coeff[i], + // so if fx===0, just set fx to coeff[i] because + // using the exp/log form will result in incorrect value + horner(x, coeffs) { + var galoisField = this.char2GF; + var fx = 0; + + for (var i = coeffs.length - 1; i >= 0; i--) { + var coefficient = Polynomial.toIntegerForm(coeffs[i]); + if (fx !== 0) { + fx = galoisField + .multiply(x, fx) + .plus(coefficient) + .getValue(); + } else { + fx = coefficient; + } + } + + return fx; + } + + getPointsOnPolynomialFor(secretByte, numShares, threshold) { + var shares = [], + coeffs = [secretByte], + i, + len; + + for (i = 1; i < threshold; i++) { + coeffs[i] = randomBitGenerator(this.coeffLength, 2); + } + + for (i = 1, len = numShares + 1; i < len; i++) { + shares[i - 1] = { + x: i, + y: this.horner(i, coeffs) + }; + } + + return shares; + } + + generateShares(secret, numShares, threshold, padLength) { + var neededBits, + subShares, + x = new Array(numShares), + y = new Array(numShares), + maxShares = this.fieldSize - 1, + i, + j, + len; + + // Security: + // For additional security, pad in multiples of 128 bits by default. + // A small trade-off in larger share size to help prevent leakage of information + // about small-ish secrets and increase the difficulty of attacking them. + padLength = padLength || 128; + + if (typeof secret !== 'string') { + throw new Error('Secret must be a string.'); + } + + if (typeof numShares !== 'number' || numShares % 1 !== 0 || numShares < 2) { + throw new Error( + 'Number of shares must be an integer between 2 and (' + + this.fieldSize - + 1 + + '), inclusive.' + ); + } + + if (numShares > maxShares) { + neededBits = Math.ceil(Math.log(numShares + 1) / Math.LN2); + throw new Error( + 'Number of shares must be an integer between 2 and (' + + this.fieldSize - + 1 + + '), inclusive. To create ' + + numShares + + ' shares, use at least ' + + neededBits + + ' bits.' + ); + } + + if (typeof threshold !== 'number' || threshold % 1 !== 0 || threshold < 2) { + throw new Error( + 'Threshold number of shares must be an integer between 2 and 2^bits-1 (' + + maxShares + + '), inclusive.' + ); + } + + if (threshold > maxShares) { + neededBits = Math.ceil(Math.log(threshold + 1) / Math.LN2); + throw new Error( + 'Threshold number of shares must be an integer between 2 and 2^bits-1 (' + + maxShares + + '), inclusive. To use a threshold of ' + + threshold + + ', create an SSSA instance with a coefficient size of atleast' + + neededBits + + ' bits.' + ); + } + + if (threshold > numShares) { + throw new Error( + 'Threshold number of shares was ' + + threshold + + ' but must be less than or equal to the ' + + numShares + + ' shares specified as the total to generate.' + ); + } + + if ( + typeof padLength !== 'number' || + padLength % 1 !== 0 || + padLength < 0 || + padLength > 1024 + ) { + throw new Error( + 'Zero-pad length must be an integer between 0 and 1024 inclusive.' + ); + } + + if (!isBase64(secret)) { + throw new Error('secret but be base-64'); + } + + secret = '1' + base64ToBits(secret); // append a 1 as a marker so that we can preserve the correct number of leading zeros in our secret + secret = splitBitsToIntArray(secret, padLength, this.coeffLength); //uses global coefficient length + + for (i = 0, len = secret.length; i < len; i++) { + subShares = this.getPointsOnPolynomialFor( + secret[i], + numShares, + threshold + ); + for (j = 0; j < numShares; j++) { + x[j] = x[j] || subShares[j].x; + y[j] = padLeft(subShares[j].y.toString(2)) + (y[j] || ''); + } + } + + for (i = 0; i < numShares; i++) { + x[i] = this.constructPublicShareString(x[i], bin2hex(y[i])); //changed to hex so it can be used with RegEx + } + + return x; + } + constructPublicShareString(id, data) { + var idHex, idMax, idPaddingLen, newShareString; + + idMax = this.fieldSize - 1; + idPaddingLen = idMax.toString(16).length; + idHex = padLeft(id.toString(16), idPaddingLen); + + if (typeof id !== 'number' || id % 1 !== 0 || id < 1 || id > idMax) { + throw new Error( + 'Share id must be an integer between 1 and ' + idMax + ', inclusive.' + ); + } + + newShareString = idHex + data; + + return newShareString; + } + + deconstructPublicShareString(share) { + var bits, + id, + idLen, + max, + obj = {}, + regexStr, + shareComponents; + + max = this.fieldSize - 1; + + // Determine the ID length which is variable and based on the bit count. + idLen = max.toString(16).length; + + // Extract all the parts now that the segment sizes are known. + regexStr = '^([a-fA-F0-9]{' + idLen + '})([a-fA-F0-9]+)$'; + shareComponents = new RegExp(regexStr).exec(share); //first element of output array is entire share, second element is the first part, up to idLen chars, and the third element is the second component (ie the data) + + // The ID is a Hex number and needs to be converted to an Integer + if (shareComponents) { + id = parseInt(shareComponents[1]); + } + + if (typeof id !== 'number' || id % 1 !== 0 || id < 1 || id > max) { + throw new Error( + 'Invalid share : Share id must be an integer between 1 and ' + + max + + ', inclusive.' + ); + } + + if (shareComponents && shareComponents[2]) { + obj.id = id; + obj.data = shareComponents[2]; + return obj; + } + + throw new Error('The share data provided is invalid : ' + share); + } +} diff --git a/lib/sssa.test.js b/lib/sssa.test.js new file mode 100644 index 0000000..6c0d883 --- /dev/null +++ b/lib/sssa.test.js @@ -0,0 +1,41 @@ +import { SSSA } from './sssa.js'; +var isBase64 = require('is-base64'); + +describe('horner method of polynomial evaluation', () => { + it('evaluates 7x + 1 at x=2 in GF(8)', () => { + var sssa = new SSSA(3); + var coeffs = ['111', '1']; + var xCoordinate = 2; + var expectedValue = 5; + expect(sssa.horner(xCoordinate, coeffs)).toBe(expectedValue); + }); + it('evalutes 5x^2 + 4x + 3 at x=3 in GF(8)', () => { + var sssa = new SSSA(3); + var coeffs = ['101', '100', '11']; + var xCoordinate = 3; + var expectedValue = 6; + expect(sssa.horner(xCoordinate, coeffs)).toBe(expectedValue); + }); +}); +describe('public share construction and deconstruction', () => { + it('combines x-coordinate and corresponding data into one string', () => { + var sssa = new SSSA(3); + var xCoordinate = 1; //new Number(1).toString(16); + var data = 'afd'; + var publicShareString = sssa.constructPublicShareString(xCoordinate, data); + var deconstructedShareString = sssa.deconstructPublicShareString( + publicShareString + ); + expect(publicShareString.length).toBe(4); + expect(deconstructedShareString.data).toBe(data); + expect(deconstructedShareString.id).toBe(parseInt(xCoordinate, 16)); + }); +}); +describe('split base64 secret into specified number of shares and threshold', () => { + it('will output an array of shares', () => { + var sssa = new SSSA(4); + var secret = 'aaA='; + var shares = sssa.generateShares(secret, 4, 2); + expect(shares.length).toBe(6); + }); +}); diff --git a/lib/utils.js b/lib/utils.js new file mode 100644 index 0000000..8f67895 --- /dev/null +++ b/lib/utils.js @@ -0,0 +1,76 @@ +import { toByteArray } from 'base64-js'; + +export function splitBitsToIntArray(str, padLength, intSize) { + var parts = [], + i; + + if (padLength) { + str = padLeft(str, padLength); + } + + for (i = str.length; i > intSize; i -= intSize) { + parts.push(parseInt(str.slice(i - intSize, i), 2)); + } + + parts.push(parseInt(str.slice(0, i), 2)); + + return parts; +} +export function padLeft(str, endLength) { + var missing; + var pregenpadding = new Array(1024).join('0'); // Pre-generate a string of 1024 0's for use by padLeft(). + + if (str) { + missing = endLength - (str.length % endLength); + } + + if (missing !== endLength) { + return (pregenpadding + str).slice(-(missing + str.length)); + } + + return str; +} +export function base64ToBits(base64) { + return bytesToBits(toByteArray(base64)); +} +export function bytesToBits(byteArray) { + var i = 0, + len, + str = '', + parsedInt; + + if (byteArray) { + len = byteArray.length; + } + + while (i < len) { + //converts it to a byte array and pads left with up to 8 0's + //make sure we are adding increments of 8 bits + str = str + padLeft(byteArray[i].toString(2), 8); + i++; + } + + // return null so this result can be re-processed if the result is all 0's. + if ((str.match(/0/g) || []).length === str.length) { + return null; + } + return str; +} + +export function bin2hex(str) { + var hex = '', + num, + i; + + str = padLeft(str, 4); + + for (i = str.length; i >= 4; i -= 4) { + num = parseInt(str.slice(i - 4, i), 2); + if (isNaN(num)) { + throw new Error('Invalid binary character.'); + } + hex = num.toString(16) + hex; + } + + return hex; +} diff --git a/package.json b/package.json index dd5146a..3f24de9 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,8 @@ ], "homepage": "https://github.com/rh389/react-native-securerandom#readme", "dependencies": { - "base64-js": "*" + "base64-js": "*", + "is-base64": "^0.1.0" }, "devDependencies": { "@babel/core": "^7.1.2", diff --git a/yarn.lock b/yarn.lock index bb1dddf..1655ffa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3547,6 +3547,11 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= +is-base64@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-base64/-/is-base64-0.1.0.tgz#a6f20610c6ef4863a51cba32bc0222544b932622" + integrity sha512-WRRyllsGXJM7ZN7gPTCCQ/6wNPTRDwiWdPK66l5sJzcU/oOzcIcRRf0Rux8bkpox/1yjt0F6VJRsQOIG2qz5sg== + is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" From d06003ad78485ef44d4762aef5ab52289326f8d1 Mon Sep 17 00:00:00 2001 From: Hadas Zeilberger Date: Mon, 5 Nov 2018 17:16:17 -0500 Subject: [PATCH 2/3] get rid of unused vars, fix test case --- ExampleGrinder/App.js | 1 - lib/randomBitGenerator.js | 3 +-- lib/sssa.js | 3 +-- lib/sssa.test.js | 3 +-- lib/utils.js | 3 +-- 5 files changed, 4 insertions(+), 9 deletions(-) diff --git a/ExampleGrinder/App.js b/ExampleGrinder/App.js index 7faf520..2780d51 100644 --- a/ExampleGrinder/App.js +++ b/ExampleGrinder/App.js @@ -8,7 +8,6 @@ import React, { Component } from 'react'; import { Platform, StyleSheet, Text, View } from 'react-native'; import { randomBitGenerator } from '../lib/randomBitGenerator.js'; -import { bytesToBits } from '../lib/utils'; const instructions = Platform.select({ ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu', android: diff --git a/lib/randomBitGenerator.js b/lib/randomBitGenerator.js index cbf68da..43f1387 100644 --- a/lib/randomBitGenerator.js +++ b/lib/randomBitGenerator.js @@ -2,8 +2,7 @@ import { generateSecureRandom } from '../RNSecureRandom/index'; import { bytesToBits } from './utils'; export async function randomBitGenerator(numBits) { - var buf, - numBytes, + var numBytes, str = null; numBytes = Math.ceil(numBits / 8); diff --git a/lib/sssa.js b/lib/sssa.js index 30ddd9c..9bbf40e 100644 --- a/lib/sssa.js +++ b/lib/sssa.js @@ -185,8 +185,7 @@ export class SSSA { } deconstructPublicShareString(share) { - var bits, - id, + var id, idLen, max, obj = {}, diff --git a/lib/sssa.test.js b/lib/sssa.test.js index 6c0d883..72e8b40 100644 --- a/lib/sssa.test.js +++ b/lib/sssa.test.js @@ -1,5 +1,4 @@ import { SSSA } from './sssa.js'; -var isBase64 = require('is-base64'); describe('horner method of polynomial evaluation', () => { it('evaluates 7x + 1 at x=2 in GF(8)', () => { @@ -36,6 +35,6 @@ describe('split base64 secret into specified number of shares and threshold', () var sssa = new SSSA(4); var secret = 'aaA='; var shares = sssa.generateShares(secret, 4, 2); - expect(shares.length).toBe(6); + expect(shares.length).toBe(4); }); }); diff --git a/lib/utils.js b/lib/utils.js index 8f67895..3d231e0 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -36,8 +36,7 @@ export function base64ToBits(base64) { export function bytesToBits(byteArray) { var i = 0, len, - str = '', - parsedInt; + str = ''; if (byteArray) { len = byteArray.length; From 6cbdf65d695d27b1711e187b27c52c0500efb4d8 Mon Sep 17 00:00:00 2001 From: Hadas Zeilberger Date: Fri, 9 Nov 2018 14:30:35 -0500 Subject: [PATCH 3/3] small changes --- ExampleGrinder/App.js | 8 +------- lib/config.js | 6 +++--- lib/galoisField.js | 8 ++++++-- lib/galoisField.test.js | 20 ++++++++++---------- lib/sssa.test.js | 8 -------- lib/utils.js | 3 ++- package.json | 2 +- 7 files changed, 23 insertions(+), 32 deletions(-) diff --git a/ExampleGrinder/App.js b/ExampleGrinder/App.js index 2780d51..5d41d73 100644 --- a/ExampleGrinder/App.js +++ b/ExampleGrinder/App.js @@ -6,14 +6,8 @@ */ /*eslint no-unused-vars: "warn"*/ import React, { Component } from 'react'; -import { Platform, StyleSheet, Text, View } from 'react-native'; +import { StyleSheet, Text, View } from 'react-native'; import { randomBitGenerator } from '../lib/randomBitGenerator.js'; -const instructions = Platform.select({ - ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu', - android: - 'Double tap R on your keyboard to reload,\n' + - 'Shake or press menu button for dev menu' -}); type Props = {}; diff --git a/lib/config.js b/lib/config.js index 3e580a3..69e25e2 100644 --- a/lib/config.js +++ b/lib/config.js @@ -1,5 +1,4 @@ -/*eslint no-undef: "off"*/ -export default (config = { +const config = { bits: 8, // default number of bits radix: 16, // work with hex by default minbits: 3, @@ -43,4 +42,5 @@ export default (config = { 5, 83 ] -}); +}; +export default config; diff --git a/lib/galoisField.js b/lib/galoisField.js index b2d4b9f..2d6d958 100644 --- a/lib/galoisField.js +++ b/lib/galoisField.js @@ -3,6 +3,9 @@ import config from './config'; //class that represents polynomials as bit arrays export class Polynomial { constructor(value) { + if (value % 1 !== 0) { + throw new Error('value must be an integer, got ' + value); + } this.value = value; } getValue() { @@ -33,6 +36,7 @@ export class Polynomial { export class CharacteristicTwoGaloisField { constructor(numElements) { + //this.n represents n in GF(2^n) this.n = Math.log2(numElements); if ( this.n && @@ -54,7 +58,7 @@ export class CharacteristicTwoGaloisField { } getExponentOfNextDegree(expOfCurrentDegree) { let primitivePolynomial = config.primitivepolynomials[this.n]; - var polynomial = expOfCurrentDegree.timesMonomialOfDegree(1); + let polynomial = expOfCurrentDegree.timesMonomialOfDegree(1); if (!this.fieldContains(polynomial.getValue())) { polynomial = polynomial .plus(primitivePolynomial) @@ -66,7 +70,7 @@ export class CharacteristicTwoGaloisField { this.exps = []; this.logs = []; let polynomial = new Polynomial(1); - for (var i = 0; i < this.numberOfElementsInField; i++) { + for (let i = 0; i < this.numberOfElementsInField; i++) { this.exps[i] = polynomial.getValue(); this.logs[polynomial.getValue()] = i; polynomial = this.getExponentOfNextDegree(polynomial); diff --git a/lib/galoisField.test.js b/lib/galoisField.test.js index 8a0ff63..c22f7ec 100644 --- a/lib/galoisField.test.js +++ b/lib/galoisField.test.js @@ -7,7 +7,7 @@ describe('adds two polynomials in galois field of characteristic two', () => { var expectedResult = Polynomial.toIntegerForm('1100'); //equivelent of x^3 + x^2 var actualResult = new Polynomial(polynomialOne).plus(polynomialTwo); expect(expectedResult).toBe(actualResult.getValue()); - expect(4).toBe(new Polynomial(1).plus(5).getValue()); + expect(new Polynomial(1).plus(5).getValue()).toBe(4); }); }); @@ -48,16 +48,16 @@ describe('determines if an element is contained within the field', () => { describe('create exponent and log tables for the field', () => { it('should output the correct exp and log tables for GF(2^3)', () => { var gf = new CharacteristicTwoGaloisField(Math.pow(2, 3)); - expect(gf.exps[0]).toBe(1); - expect(gf.exps[1]).toBe(2); - expect(gf.exps[2]).toBe(4); - expect(gf.exps[3]).toBe(3); - expect(gf.exps[4]).toBe(6); - expect(gf.exps[5]).toBe(7); - expect(gf.exps[6]).toBe(5); - expect(gf.exps[7]).toBe(1); + expect(gf.exps[0]).toEqual(1); + expect(gf.exps[1]).toEqual(2); + expect(gf.exps[2]).toEqual(4); + expect(gf.exps[3]).toEqual(3); + expect(gf.exps[4]).toEqual(6); + expect(gf.exps[5]).toEqual(7); + expect(gf.exps[6]).toEqual(5); + expect(gf.exps[7]).toEqual(1); for (var i = 1; i < Math.pow(2, 3); i++) { - expect(gf.exps[gf.logs[i]]).toBe(i); + expect(gf.exps[gf.logs[i]]).toEqual(i); } expect(gf.logs[1]).toBe(7); expect(gf.logs[0]).toBeFalsy(); diff --git a/lib/sssa.test.js b/lib/sssa.test.js index 72e8b40..84f7d8e 100644 --- a/lib/sssa.test.js +++ b/lib/sssa.test.js @@ -30,11 +30,3 @@ describe('public share construction and deconstruction', () => { expect(deconstructedShareString.id).toBe(parseInt(xCoordinate, 16)); }); }); -describe('split base64 secret into specified number of shares and threshold', () => { - it('will output an array of shares', () => { - var sssa = new SSSA(4); - var secret = 'aaA='; - var shares = sssa.generateShares(secret, 4, 2); - expect(shares.length).toBe(4); - }); -}); diff --git a/lib/utils.js b/lib/utils.js index 3d231e0..c675868 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -50,7 +50,8 @@ export function bytesToBits(byteArray) { } // return null so this result can be re-processed if the result is all 0's. - if ((str.match(/0/g) || []).length === str.length) { + const allZeroPattern = /^0+$/; + if (str.match(allZeroPattern)) { return null; } return str; diff --git a/package.json b/package.json index 3f24de9..8fdc8e9 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ ], "homepage": "https://github.com/rh389/react-native-securerandom#readme", "dependencies": { - "base64-js": "*", + "base64-js": "^1.3.0", "is-base64": "^0.1.0" }, "devDependencies": {