Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ipfs #11

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open

Ipfs #11

Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
jsdoc
Hadas Zeilberger authored and Hadas Zeilberger committed Nov 11, 2018
commit 29655806dab71984ea9d4769eb6e689b61a802e3
2 changes: 1 addition & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
node_modules/
e2e/
RNSecureRandom/__tests__
RNSecureRandom/
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ module.exports = {
'plugin:react/recommended',
'plugin:prettier/recommended'
],
plugins: ['react', 'react-native', 'jasmine', 'detox','jsdoc'],
plugins: ['react', 'react-native', 'jasmine', 'detox', 'jsdoc'],
settings: {
react: {
pragma: 'React',
2 changes: 1 addition & 1 deletion ExampleGrinder/App.js
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@ export default class App extends Component<Props> {
*/
async componentDidMount() {
let sssa = new SSSA(3);
let shares = await sssa.generateShares(secret, 7, 2, 1);
let shares = await sssa.generateShares(secret, 7, 2, 1, 100);
let combinedShares = sssa.combine(shares);
this.setState({
shamirShares: JSON.stringify(shares),
71 changes: 53 additions & 18 deletions lib/galoisField.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,71 @@
import config from './config'; //class that represents polynomials as bit arrays export class Polynomial { /*represents a polynomial whose coefficients are binary * @constructor *@param {number} value - an integer, that when converted to binary, represents a polynomial */ constructor(value) { this.value = value; } /*returns the integer value of the polynomial *For example: If the value is 3, then the binary value is 011 *and the polynomial is x+1 */ getValue() { return this.value; } /*sets the value of the polynomial. For * example, setting the value to 3, * sets the polynomial to 011, which is * x + 1 *@param {number} value : an integer, that when converted to binary represents a polynomial */
import config from './config';

/**
* class that represents polynomials as bit arrays
*/
export class Polynomial {
/**@class
* @param {number} value - integer to construct polynomial from
*/
constructor(value) {
this.value = value;
}
/**
* returns the integer value of the polynomial
*For example: If the value is 3, then the binary value is 011
*and the polynomial is x+1
*@returns {number} this.value
*/
getValue() {
return this.value;
}

/**@param {number} value - an integer, that when converted to binary represents a polynomial
*/
setValue(value) {
this.value = value;
}
/*adds one polynomial in GF(2^n) to another polynomial in GF(2^n)
* rightHandPolynomial is the polynomial to add to this.value
*@param {number} rightHandPolynomial - an integer whose binary form
*represents a polynomial*/
/**@param {number} rightHandPolynomial - an integer whose binary form
*represents a polynomial
*@returns { Polynomial } - polynomial with given value
*/
plus(rightHandPolynomial) {
this.value = this.value ^ rightHandPolynomial;
return this;
}
/*multiplies this.value by a monomial of degree n - for example x^3 or x^2 or x (where ^ represents "to the power of")
* @param n {number}
/**@param {number} n - the degree of monomial to multiply the polonomial by
* @returns {Polynomial} an instance of Polynomial class whose value is this.value << n
*/
timesMonomialOfDegree(n) {
this.value = this.value << n;
return this;
}
/*subtract terms above given degree by bit-wise &
*@param {number} n -degree that we should subtract, if degree is 3, we delete all terms of 1000 and above
*/
/**@param {numer} n - the degree to subtract
*@returns {Polynomial} - polynomial with computed value
*/
subtractTermsAboveDegree(n) {
this.value = this.value & (Math.pow(2, n + 1) - 1);
return this;
}
/*converts a polynomial represented by a bit array
* to integer form. For example,011 would be converted to 3
* @param {string} bitArray - for example "011"
/**@param {string} bitArray - polynomial in the form of a bit array to represent as an integer
* @returns {number} - the integer form of the given bit array
*/
static toIntegerForm(bitArray) {
return parseInt(bitArray, 2);
}
/*converts an integer to binary array representing a polynomial
* for example, 3 would be converted to "011"
*@param {number} integer - for example 3
*@returns {string}
/**@param {number} integer - the integer to convert to a bit array representing a polynomial
* @returns {number} - bit array from given integer
*/
static toPolynomialForm(integer) {
return new Number(integer).toString(2);
}
}

/** represents a characteristic two galois field
*/
export class CharacteristicTwoGaloisField {
/** @class
* @param {integer} numElements - the number of elements in the field
*/
constructor(numElements) {
this.n = Math.log2(numElements);
if (
@@ -60,9 +83,15 @@ export class CharacteristicTwoGaloisField {
this.numberOfElementsInField = numElements;
this.computeLogAndExpTables();
}
/** @param {number} value - is an integer
* @returns {boolean} - whether or not the field contains the given value
*/
fieldContains(value) {
return value < this.numberOfElementsInField;
}
/** @param {number} expOfCurrentDegree - exponent of current degree, used when calculated exponent tables
* @returns {Polynomial} - polynomial with computed value
*/
getExponentOfNextDegree(expOfCurrentDegree) {
let primitivePolynomial = config.primitivepolynomials[this.n];
var polynomial = expOfCurrentDegree.timesMonomialOfDegree(1);
@@ -73,6 +102,8 @@ export class CharacteristicTwoGaloisField {
}
return polynomial;
}
/**computes log and exponent tables for the field GF(2^this.n)
*/
computeLogAndExpTables() {
this.exps = [];
this.logs = [];
@@ -83,6 +114,10 @@ export class CharacteristicTwoGaloisField {
polynomial = this.getExponentOfNextDegree(polynomial);
}
}
/** @param {number} polynomialOne - the left hand assignment
* @param {number} polynomialTwo - the right hand assignment
* @returns {Polynomial} - polynomial with given value
*/
multiply(polynomialOne, polynomialTwo) {
return new Polynomial(
this.exps[
9 changes: 7 additions & 2 deletions lib/randomBitGenerator.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { generateSecureRandom } from '../RNSecureRandom/index';
import { bytesToBits } from './utils';

/**@param {number} numBits - the length of bits the randomly generated sequence of
* bits should be
*/
export async function randomBitGenerator(numBits) {
var numBytes,
str = null;
@@ -13,7 +15,10 @@ export async function randomBitGenerator(numBits) {
str = str.substr(-numBits);
return str;
}

/**@param {number} secretChunk - integer representing one chunk of the secret
* @param {number} numCoefficients - number of coefficients to generate, should correspond to threshold in sssa.generateShares function
* @param {number} coefficientLength - bit length that the coefficients should be, should correspond to coefficient length passed into instance of sssa
*/
export async function generateCoefficients(
secretChunk,
numCoefficients,
164 changes: 112 additions & 52 deletions lib/sssa.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { generateCoefficients, randomBitGenerator } from './randomBitGenerator';
import { generateCoefficients } from './randomBitGenerator';
import { Polynomial, CharacteristicTwoGaloisField } from './galoisField';
var isBase64 = require('is-base64');
import {
@@ -8,29 +8,35 @@ import {
hex2bin,
bin2hex,
bitsToBase64,
convertIntArrayToBits
convertIntArrayToBits,
markPadding,
removePadding
} from './utils.js';
/** Represents an SSSA implementation where coefficients are of length coeffLength
* and are considered in the field GF(2^coeffLength)
*/
export class SSSA {
/**
* Represents an SSSA implementation where coefficients are of length coeffLength
* and are considered in the field GF(2^coeffLength)
* @constructor
*
* @class
* @param {number} coeffLength - bit length of coefficients in polynomial used to construct shamir shares
*/
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
/**@param {number} x - the x coordinate at which to evaluate the polynomial
* @param {Array} coeffs - array of bit sequences, each of which represents a term in the polynomial. The first
* element in the array is the coefficient of the 0th term
* @returns {number} - the y coordinate for the given x coordinate on the polynomial represented by coeffs
*/
horner(x, coeffs) {
var galoisField = this.char2GF;
var fx = 0;
let galoisField = this.char2GF;
let fx = 0;

for (var i = coeffs.length - 1; i >= 0; i--) {
var coefficient = Polynomial.toIntegerForm(coeffs[i]);
for (let i = coeffs.length - 1; i >= 0; i--) {
let coefficient = Polynomial.toIntegerForm(coeffs[i]);
if (fx !== 0) {
fx = galoisField
.multiply(x, fx)
@@ -43,9 +49,13 @@ export class SSSA {

return fx;
}

getPointsOnPolynomialFor(coeffs, numShares, threshold) {
var shares = [],
/**@param {Array} coeffs - array of coefficients representing a polynomial where
* the 0th element in the array represents the 0th term of the polynomial
* @param {number} numShares - the number of shares to generate for the polynomial represented by coeffs
* @returns {Array} - an array of points of the polynomial represented by coeffs
*/
getPointsOnPolynomialFor(coeffs, numShares) {
let shares = [],
i,
len;

@@ -55,12 +65,18 @@ export class SSSA {

return shares;
}
/**@param {Array} shares - the final shares returned by generateShares
* @returns {string} - base64 secret that was input to generateShares
*/
combine(shares) {
let points = this.publicShareToPoints(shares);
let secretChunks = this.getChunksFromPoints(points);
return this.combineSecret(secretChunks);
}

/**@param {Array} x - the x-values of the shares that were generated for one secret chunk
* @param {Array} y - the y-values of the shares that were generated for one secret chunk
* @returns {number} - the 0th term of the polynomial that was used to generate the shares for one secret chunk
*/
lagrange(x, y) {
let sum = 0,
len,
@@ -85,38 +101,51 @@ export class SSSA {
galoisField.logs[0 ^ x[j]] -
galoisField.logs[x[i] ^ x[j]] +
((this.fieldSize - 1) % (this.fieldSize - 1));
// to make sure it's not negative
}
}

// though exps[-1] === undefined and undefined ^ anything = anything in
// chrome, this behavior may not hold everywhere, so do the check
sum = product === -1 ? sum : sum ^ galoisField.exps[product];
}
}

return sum;
}
splitSecret(secret) {
let secretInBits = '1' + base64ToBits(secret); // append a 1 as a marker so that we can preserve the correct number of leading zeros in our secret
let secretChunks = splitBitsToIntArray(secretInBits, this.coeffLength); //uses global coefficient length
/**@param {string} secret - base64 encoded secret
* @param {number} padLength - the number of 0's to pad the secret with, to hide the length
* @returns {Array} - an array of integers generated by starting from the end of the bit sequence, and creating the next int from the
* previous this.coeffLength number of bits
*/
splitSecret(secret, padLength) {
let secretInBits = markPadding(base64ToBits(secret));
let secretChunks = splitBitsToIntArray(
secretInBits,
this.coeffLength,
padLength
);
return secretChunks;
}
/**@param {Array} secretChunks - array of integers, where each element is a chunk of the secret as outputed by splitSecret
* @returns {string} - base64 secret
*/
combineSecret(secretChunks) {
let result = convertIntArrayToBits(secretChunks, this.coeffLength);
result = result.slice(result.indexOf('1') + 1);
return bitsToBase64(result);
}
/**@param {Array} secretChunks - an array of integers, each of which is a chunk of the secret
* @param {number} numShares - the number of shares to generate for each chunk
* @param {number} threshold - the minimum number of points needed to re-generate each secret chunk
*/
async getPointsFromChunks(secretChunks, numShares, threshold) {
let allPoints = [];
for (i = 0, len = secretChunks.length; i < len; i++) {
secretChunk = Polynomial.toPolynomialForm(secretChunks[i]);
for (let i = 0; i < secretChunks.length; i++) {
let secretChunk = Polynomial.toPolynomialForm(secretChunks[i]);
let polynomial = await generateCoefficients(
secretChunk,
threshold,
this.coeffLength
);
subShares = this.getPointsOnPolynomialFor(
let subShares = this.getPointsOnPolynomialFor(
polynomial,
numShares,
threshold
@@ -125,8 +154,11 @@ export class SSSA {
allPoints[i] = allPoints[i] || [];
allPoints[i] = allPoints[i].concat(subShares);
}
return allPoints; //2-d array of integers, elements [i][j] os the jth point of the ith secret chunk
return allPoints;
}
/**@param {Array} points - a 2-d array of points, where the i,jth term is the y-coordinate of point j of the ith secret chunk
* @returns {Array} - an array of integers, each of which is a chunk of the secret
*/
getChunksFromPoints(points) {
let x = [...Array(points[0].length).keys()].map(x => x + 1);
let secretChunks = [];
@@ -135,9 +167,13 @@ export class SSSA {
}
return secretChunks;
}
/**@param {Array} allPoints - a 2-d array representing the y-coordinates of the points for each secret chunk. the [i][j]th element is y-coordinate j for secret chunk i
*@returns {Array} - a 2-d array, which in linear algebra terms is the transpose of the input matrix. In plain english, it is a 2-d array
whose [j][i] term is equal to the [i][j] term of the input
*/
createCrossSection(allPoints) {
let y = [];
numSecretChunks = allPoints.length;
let numSecretChunks = allPoints.length;
for (let i = 0; i < numSecretChunks; i++) {
let subShares = allPoints[i];
for (let j = 0; j < subShares.length; j++) {
@@ -147,14 +183,18 @@ export class SSSA {
}
return y;
}
sharesToBin(sharesMatrix) {
if (!sharesMatrix.length && !sharesMatrix[0].length) {
/**@param {Array} crossSection - a 2-d array, where the [i][j]th term represents the ith y-coordinate of the jth secret
* It should be the output from createCrossSection
* @returns {Array} - a 1-d array, where element i is the concatenation of binary forms for the ith term of each secret chunk
*/
sharesToBin(crossSection) {
if (!crossSection.length && !crossSection[0].length) {
throw new Error(
'input to sharesToBin is expected to be a 2-d arary, representing shamir shares'
);
}
let output = [];
let shares = sharesMatrix;
let shares = crossSection;
for (var i = 0; i < shares.length; i++) {
for (var j = 0; j < shares[i].length; j++) {
output[i] =
@@ -164,36 +204,45 @@ export class SSSA {
}
return output;
}
/**@param {Array} shares - element i is a concatenation of the ith y-coordiantes of all the secret chunks
* @returns {Array} - element i is element i of the input in hex, prepended with the associated x-coordinate in hex
*/
binarySharesToPublicShareString(shares) {
let x = [];
for (let i = 0; i < shares.length; i++) {
x[i] = this.constructPublicShareString(i + 1, bin2hex('1' + shares[i])); //changed to hex so it can be used with RegEx
}
return x;
}
/**@param {Array} shares - an array of shares. Element i is the concatenation of the ith shares of all secret chunks, in hex
* @returns {Array} - a 2-d array of points where term i is the array of points for secretChunk i
*/
publicShareToPoints(shares) {
let x = [],
y = [];
for (let i = 0, len = shares.length; i < len; i++) {
share = this.deconstructPublicShareString(shares[i]);
for (let i = 0; i < shares.length; i++) {
let share = this.deconstructPublicShareString(shares[i]);
if (x.indexOf(share.id) === -1) {
x.push(share.id);
var binData = hex2bin(share.data);
binData = binData.slice(binData.indexOf('1') + 1);
splitShare = splitBitsToIntArray(binData, this.coeffLength);
//each element of y is all the y components of each bit
for (j = 0, len2 = splitShare.length; j < len2; j++) {
let binData = removePadding(hex2bin(share.data));
let splitShare = splitBitsToIntArray(binData, this.coeffLength);
for (let j = 0; j < splitShare.length; j++) {
y[j] = y[j] || [];
y[j][x.length - 1] = splitShare[j];
}
}
}
return y;
}
binarySharesToPublicShareString(shares) {
let x = [];
for (let i = 0; i < shares.length; i++) {
x[i] = this.constructPublicShareString(i + 1, bin2hex('1' + shares[i])); //changed to hex so it can be used with RegEx
}
return x;
}
/**@param {string} secret - the secret to generate shares from
* @param {number} numShares - the number of shares to generate
* @param {number} threshold - the minimum number of shares needed to reconstruct the secret
* @param {number} padLength - the number of 0's to pad the secret with, to hide the length of the original secret
* @returns {Array} - an array of shares, all of which are needed to reconstruct the secret
*/
async generateShares(secret, numShares, threshold, padLength) {
var x = new Array(numShares),
padLength = padLength || 128;
var secretChunks = this.splitSecret(secret);
padLength = padLength || 128;
let secretChunks = this.splitSecret(secret, padLength);
let pointsFromChunks = await this.getPointsFromChunks(
secretChunks,
numShares,
@@ -203,6 +252,10 @@ export class SSSA {
let shares = this.sharesToBin(crossSection);
return this.binarySharesToPublicShareString(shares);
}
/**@param {number} id - the x-coordinate that the param data is associated with
* @param {string} data - a hex string representing a share which is one y-coordinate from each secret chunk, concatenated together
* @returns {string} - the y-coordinates with the associated x-coordinate prepended to it, converted to hex
*/
constructPublicShareString(id, data) {
var idHex, idMax, idPaddingLen, newShareString;

@@ -220,9 +273,11 @@ export class SSSA {

return newShareString;
}

/**@param {string} share - one share in final form
* @returns {Object} - the share, parsed apart into data and associated x-coordinate
*/
deconstructPublicShareString(share) {
var id,
let id,
idLen,
max,
obj = {},
@@ -259,7 +314,12 @@ export class SSSA {

throw new Error('The share data provided is invalid : ' + share);
}

/**@param {string} secret - the original base64 secret
* @param {number} numShares - the number of points to generate for each secret chunk
* @param {number} threshold - the minimum number of points needed to re-generate the entire secret
* @param {number} padLength - the amount of 0's to pad the secret with, to hide its length
* the purpose of this function is to verify the input, and throw and error if anything is wrong
*/
verifyInput(secret, numShares, threshold, padLength) {
if (typeof secret !== 'string') {
throw new Error('Secret must be a string.');
@@ -273,7 +333,7 @@ export class SSSA {
);
}
if (numShares > this.fieldSize - 1) {
neededBits = Math.ceil(Math.log(numShares + 1) / Math.LN2);
let neededBits = Math.ceil(Math.log(numShares + 1) / Math.LN2);
throw new Error(
'Number of shares must be an integer between 2 and (' +
this.fieldSize -
16 changes: 8 additions & 8 deletions lib/sssa.test.js
Original file line number Diff line number Diff line change
@@ -52,7 +52,7 @@ describe('converts 2d array of integer to 1d array of bit sequences', () => {
});
describe('splits up a secret into chunks and puts it back together', () => {
var base64 = 'aaA=';
var chunks = sssa.splitSecret(base64);
var chunks = sssa.splitSecret(base64, 100);
it('should output an integer array', () => {
expect(typeof chunks[0]).toBe('number');
});
@@ -71,12 +71,12 @@ describe('takes publically shared strings and convert back to points', () => {
});
describe('finds a point at a polynomial given x', () => {
it('should output the correct number', () => {
var coeffs = ['001', '010', '010'];
x = [1, 2, 3];
var y1 = sssa.horner(1, coeffs);
var y2 = sssa.horner(2, coeffs);
var y3 = sssa.horner(3, coeffs);
expectedSecret = sssa.lagrange(x, [y1, y2, y2]);
expect(expectedSecret).toBe(1);
let coeffs = ['011', '010', '010'];
let x = [1, 2, 3];
let y1 = sssa.horner(1, coeffs);
let y2 = sssa.horner(2, coeffs);
let y3 = sssa.horner(3, coeffs);
let expectedSecret = sssa.lagrange(x, [y1, y2, y3]);
expect(expectedSecret).toBe(3);
});
});
26 changes: 15 additions & 11 deletions lib/test_utils/base64ToBits.test.js
Original file line number Diff line number Diff line change
@@ -4,7 +4,9 @@ import {
splitBitsToIntArray,
convertIntArrayToBits,
bitsToBytes,
bytesToBits
bytesToBits,
markPadding,
removePadding
} from '../utils';
import { toByteArray, fromByteArray } from 'base64-js';
describe('convert base64 to bits and viceversa', () => {
@@ -21,20 +23,22 @@ describe('convert base64 to bits and viceversa', () => {
);
});
});
describe('compose base64 with splitBitsToInt', () => {
it('should be a perfect inverse', () => {

describe('integration tests between different conversion functions', () => {
it('should output original input', () => {
var secret = 'aaA=';
var secretInBytes = toByteArray(secret);
var secretInBits = bytesToBits(secretInBytes);

//now convert it to an int array of n in field 2^n
var secretInIntArray = splitBitsToIntArray(secretInBits, 3);
var backToBits = convertIntArrayToBits(secretInIntArray, 3);
//now convert it to an int array of n in field GF(2^n)
var secretInIntArray = splitBitsToIntArray(markPadding(secretInBits), 3);
var intArrayToBits = removePadding(
convertIntArrayToBits(secretInIntArray, 3)
);

var reverseToBytes = bitsToBytes(backToBits);
var reverseToSecret = fromByteArray(reverseToBytes);
expect(secretInBytes).toEqual(secretInIntArray);
// expect(secret).toBe(reverseToSecret);
// expect(backToBits).toBe(secretInBits);
var bits_to_bytes = bitsToBytes(intArrayToBits);
var bytesToSecret = fromByteArray(bits_to_bytes);
expect(secret).toBe(bytesToSecret);
expect(intArrayToBits).toBe(secretInBits);
});
});
17 changes: 10 additions & 7 deletions lib/test_utils/bytesToBits.test.js
Original file line number Diff line number Diff line change
@@ -2,18 +2,16 @@ import {
splitBitsToIntArray,
convertIntArrayToBits,
bytesToBits,
bitsToBytes
bitsToBytes,
markPadding,
removePadding
} from '../utils';
describe('splits bit array to array of integers of given bit size', () => {
it('should return an int array of the right size', () => {
var bitArray = '0110100001100001';
var unevenBitArray = bitArray + '1';
let bitArray = '0110100001100001';
expect(bitsToBytes(bitArray)).toEqual([97, 104]);
expect(bitsToBytes(bytesToBits([97, 104]))).toEqual([97, 104]);
expect(bytesToBits(bitsToBytes(bitArray))).toEqual(bitArray);
expect(bitsToBytes(bytesToBits(bitsToBytes(unevenBitArray)))).toEqual(
bitsToBytes(unevenBitArray)
);
});
});
describe('split bits to array of integers', () => {
@@ -27,7 +25,12 @@ describe('split bits to array of integers', () => {
).toEqual([1, 2, 3, 4]);
let inconvenientBitArray = '1111000';
expect(
convertIntArrayToBits(splitBitsToIntArray(inconvenientBitArray, 3), 3)
removePadding(
convertIntArrayToBits(
splitBitsToIntArray(markPadding(inconvenientBitArray), 3),
3
)
)
).toBe(inconvenientBitArray);
});
});
62 changes: 52 additions & 10 deletions lib/utils.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { fromByteArray, toByteArray } from 'base64-js';

/**@param {string} str - bit sequence
* @param {number} intSize - bit size of ints in resulting array
* @param {number} padLength - number of 0's to pad the bit sequence with. if padLength > intSize then there will be 0's at the end of the resultant int array
* before converting it to an array of ints
* @returns {Array} - an array of ints
*/
export function splitBitsToIntArray(str, intSize, padLength) {
let parts = [],
i;
//maybe take off trailing 0's?

str = padLeft(str, padLength || intSize);

for (i = str.length; i > intSize; i -= intSize) {
@@ -13,40 +18,57 @@ export function splitBitsToIntArray(str, intSize, padLength) {
parts.push(parseInt(str.slice(0, i), 2));
return parts;
}
export function convertIntArrayToBits(intArray, intSize) {
/**@param {Array} intArray - the array of integers to convert to bits
* @param {number} numBits - the number of bits to convert each integer to
* @returns {string} - bit sequence
*/
export function convertIntArrayToBits(intArray, numBits) {
let result = '';
for (let i = 0; i < intArray.length; i++) {
result = padLeft(intArray[i].toString(2), intSize) + result;
result = padLeft(intArray[i].toString(2), numBits) + result;
}

return result;
}
export function padLeft(str, endLength) {
/**@param {string} str - the string of bits to pad left with 0's
* @param {number} lengthMultiple - string is padded left until it reaches a multiple of lengthMultiple
* @returns {string} - padded bit sequence
*/
export function padLeft(str, lengthMultiple) {
//default to one byte
if (!endLength) {
endLength = 8;
if (!lengthMultiple) {
lengthMultiple = 8;
}
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);
missing = lengthMultiple - (str.length % lengthMultiple);
}

if (missing !== endLength) {
if (missing !== lengthMultiple) {
return (pregenpadding + str).slice(-(missing + str.length));
}

return str;
}
/**@param {string} base64 - a base64 string
* @returns {string} - a sequence of bits
*/
export function base64ToBits(base64) {
let byteArray = toByteArray(base64).reverse();
return bytesToBits(byteArray);
}
/**@param {string} bits - a string of bits
*@returns {string} - base64 string representing bits
*/
export function bitsToBase64(bits) {
var byteArray = bitsToBytes(bits).reverse();
return fromByteArray(byteArray);
}
/**@param {Array} byteArray - an array of bytes (integers from 0 to 255)
* @returns {string} - sequence of bits
*/
export function bytesToBits(byteArray) {
let i = 0,
len,
@@ -69,9 +91,15 @@ export function bytesToBits(byteArray) {
}
return str;
}
/**@param {string} bits - bit sequence
* @returns {Array} array of integers from 0 to 255
*/
export function bitsToBytes(bits) {
return splitBitsToIntArray(bits, 8);
}
/**@param {string} str - a hex string
* @returns {string} - a binary sequence
*/
export function hex2bin(str) {
let bin = '',
num,
@@ -88,7 +116,9 @@ export function hex2bin(str) {
}
return bin;
}

/**@param {string} str - a binary sequence
* @returns {string} - a hex sequence
*/
export function bin2hex(str) {
let hex = '',
num,
@@ -105,3 +135,15 @@ export function bin2hex(str) {

return hex;
}
/**@param {string} bits - a bit sequence
* @returns {string} - bits with 1 on the front
*/
export function markPadding(bits) {
return '1' + bits;
}
/**@param {string} bits - a bit sequence with padding
* @returns {string} - a bit sequence without the padding
*/
export function removePadding(bits) {
return bits.slice(bits.indexOf('1') + 1);
}
9 changes: 1 addition & 8 deletions lib/utils.test.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
import {
padLeft,
splitBitsToIntArray,
hex2bin,
bin2hex,
bytesToBits,
bitsToBytes
} from './utils';
import { padLeft, hex2bin, bin2hex } from './utils';

describe('pads bits left with specified amount of 0s', () => {
it('should pad left with correct amount of zeros', () => {
32 changes: 32 additions & 0 deletions lib/~
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { generateSecureRandom } from '../RNSecureRandom/index';
import { bytesToBits } from './utils';
/**@param {number} numBits - the length of bits the randomly generated sequence of
* bits should be
*/
export async function randomBitGenerator(numBits) {
var 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;
}
/**@param {number} secretChunk - integer representing one chunk of the secret
* @param {number} numCoefficients - number of coefficients to generate, should correspond to threshold in sssa.generateShares function
* @param {number} coefficientLength - bit length that the coefficients should be, should correspond to coefficient length passed into instance of sssa
*/
export async function generateCoefficients(
secretChunk,
numCoefficients,
coefficientLength
) {
let coeffs = [secretChunk];
for (let i = 1; i < numCoefficients; i++) {
coeffs[i] = await randomBitGenerator(coefficientLength);
}
return coeffs;
}
1 change: 1 addition & 0 deletions lint
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/eslint/bin/eslint.js $1