Skip to content

Commit

Permalink
Separated merkle, added root from proof and single element array merk…
Browse files Browse the repository at this point in the history
…le is sha512
  • Loading branch information
fuzzc0re committed Oct 18, 2022
1 parent 9fc4730 commit 70ae60c
Show file tree
Hide file tree
Showing 18 changed files with 682 additions and 359 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ dist
*valgrind*.txt
.npmrc
deliberative*.tgz
dcryptoMethodsModule.js

# Logs
logs
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ The [symmetric](src/symmetric) directory contains AEAD encryption/decryption wit

The [mnemonic](src/mnemonic) directory contains all the relevant to mnemonic generation functions.

The [hash](src/hash) directory contains a sha512 hashing function, a Merkle root getter function, a Merkle
proof artifacts getter and a verification function.
The [hash](src/hash) directory contains a sha512 hashing function and an Argon2 with optional salt hashing function.

The [merkle](src/merkle) directory contains a Merkle root getter function, a Merkle
proof artifacts getter, a root from proof getter and a proof verification function.

The [shamir](src/shamir) directory contains a WASM implementation of a cryptographic technique called [Shamir's secret
sharing](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing), which allows one to split a secret into random shares that can only recreate it if a threshold of them is combined.
Expand Down
271 changes: 5 additions & 266 deletions __tests__/hash.test.ts
Original file line number Diff line number Diff line change
@@ -1,277 +1,16 @@
import dcrypto from "../src";

jest.setTimeout(10000);
describe("Sha512 and Merkle root test suite.", () => {
describe("Sha512 and Argon2 test suite.", () => {
test("Public key SHA512 hash works.", async () => {
const mnemonic = await dcrypto.generateMnemonic();
const keypair = await dcrypto.keyPairFromMnemonic(mnemonic);
const hash = await dcrypto.sha512(keypair.publicKey);
expect(hash.length).toBe(64);
});

test("Merkle root calculation works.", async () => {
const tree: Uint8Array[] = [];
for (let i = 0; i < 201; i++) {
const rand = await dcrypto.randomBytes(128);
tree.push(rand);
}

const root = await dcrypto.getMerkleRoot(tree);
const root2 = await dcrypto.getMerkleRoot(tree);

expect(root.length).toBe(64);
expect(root[0]).toBe(root2[0]);
expect(root[1]).toBe(root2[1]);
expect(root[63]).toBe(root2[63]);
});

test("Merkle proof verification works for odd number of elements.", async () => {
const tree: Uint8Array[] = [];
const element = new Uint8Array(128);
const elements = 201;
const elementIndex = 99;
for (let i = 0; i < elements; i++) {
const rand = await dcrypto.randomBytes(128);

if (i === elementIndex) element.set([...rand]);

tree.push(rand);
}

const root = await dcrypto.getMerkleRoot(tree);

const proof = await dcrypto.getMerkleProof(tree, tree[elementIndex]);

const elementHash = await dcrypto.sha512(element);

const verification = await dcrypto.verifyMerkleProof(
elementHash,
root,
proof,
);

expect(verification).toBe(true);
});

test("Merkle proof verification works for even number of elements.", async () => {
const tree: Uint8Array[] = [];
const element = new Uint8Array(128);
const elements = 200;
const elementIndex = 99;
for (let i = 0; i < elements; i++) {
const rand = await dcrypto.randomBytes(128);

if (i === elementIndex) element.set([...rand]);

tree.push(rand);
}

const root = await dcrypto.getMerkleRoot(tree);

const proof = await dcrypto.getMerkleProof(tree, tree[elementIndex]);

const elementHash = await dcrypto.sha512(element);

const verification = await dcrypto.verifyMerkleProof(
elementHash,
root,
proof,
);

expect(verification).toBe(true);
});

it("Should throw an error when faced with false data.", async () => {
const tree: Uint8Array[] = [];
const element = new Uint8Array(128);
const elements = 201;
const elementIndex = 99;
for (let i = 0; i < elements; i++) {
const rand = await dcrypto.randomBytes(128);

if (i === elementIndex) element.set([...rand]);

tree.push(rand);
}

const root = await dcrypto.getMerkleRoot(tree);
const proof = await dcrypto.getMerkleProof(tree, tree[elementIndex]);
const elementHash = await dcrypto.sha512(element);

await expect(
dcrypto.verifyMerkleProof(
elementHash,
root,
proof.slice(0, proof.length - 1),
),
).rejects.toThrow("Proof length not multiple of 65.");

const proofWrongPosition = Uint8Array.from([...proof]);
proofWrongPosition[dcrypto.constants.crypto_hash_sha512_BYTES] = 2;
await expect(
dcrypto.verifyMerkleProof(elementHash, root, proofWrongPosition),
).rejects.toThrow("Proof artifact position is neither left nor right.");

const proofWrongByte = Uint8Array.from([...proof]);
proofWrongByte[1] = proof[1] === 255 ? 254 : proof[1] + 1;

const verification = await dcrypto.verifyMerkleProof(
elementHash,
root,
proofWrongByte,
);

expect(verification).toBe(false);
});

const len = 64;
const arr1 = new Uint8Array(len);
const arr2 = new Uint8Array(len);
arr1.fill(1);
arr2.fill(2);

const arr3 = new Uint8Array(len);
arr3.fill(1);

const arr4 = new Uint8Array(len);
arr4.fill(4);

const arrayOfArrays1: Uint8Array[] = [];
arrayOfArrays1.push(arr2);
arrayOfArrays1.push(arr4);
arrayOfArrays1.push(arr2);

interface SomeRandomInterface {
val1: string;
val2: string;
val3: string;
}

const arr5: SomeRandomInterface = {
val1: "1",
val2: "2",
val3: "3",
};

const arr6: SomeRandomInterface = {
val1: "5",
val2: "6",
val3: "7",
};

const arr7: SomeRandomInterface = {
val1: "10",
val2: "20",
val3: "30",
};

const arrayOfArrays3: SomeRandomInterface[] = [arr5, arr6, arr7];

const numberToUint8Array = (n: number): Uint8Array => {
return Uint8Array.of(
(n & 0xff000000) >> 24,
(n & 0x00ff0000) >> 16,
(n & 0x0000ff00) >> 8,
(n & 0x000000ff) >> 0,
);
};

const someRandomInterfaceSerializer = (item: SomeRandomInterface) => {
const uint8 = new Uint8Array(4 * 3 * Uint8Array.BYTES_PER_ELEMENT);

uint8.set(numberToUint8Array(Number(item.val1)));
uint8.set(
numberToUint8Array(Number(item.val2)),
4 * Uint8Array.BYTES_PER_ELEMENT,
);
uint8.set(
numberToUint8Array(Number(item.val3)),
8 * Uint8Array.BYTES_PER_ELEMENT,
);

return uint8;
};

it("Should be possible to get Merkle root and proof from non-Uint8 data.", async () => {
const root = await dcrypto.getMerkleRoot(
arrayOfArrays3,
someRandomInterfaceSerializer,
);

const arr6Serialized = someRandomInterfaceSerializer(arr6);
const proof1 = await dcrypto.getMerkleProof(
arrayOfArrays3,
arr6Serialized,
someRandomInterfaceSerializer,
);

const arrayOfArrays3Serialized: Uint8Array[] = [];
for (let i = 0; i < arrayOfArrays3.length; i++) {
arrayOfArrays3Serialized.push(
someRandomInterfaceSerializer(arrayOfArrays3[i]),
);
}
const proof2 = await dcrypto.getMerkleProof(
arrayOfArrays3Serialized,
arr6,
someRandomInterfaceSerializer,
);

const elementHash = await dcrypto.sha512(arr6Serialized);
const verification1 = await dcrypto.verifyMerkleProof(
elementHash,
root,
proof1,
);

const verification2 = await dcrypto.verifyMerkleProof(
elementHash,
root,
proof2,
);

expect(verification1).toBe(true);
expect(verification2).toBe(true);

const root1 = await dcrypto.getMerkleRoot(
[arr6],
someRandomInterfaceSerializer,
);
expect(root1.length).toBe(64);

const root2 = await dcrypto.getMerkleRoot([arr6Serialized]);
expect(root2.length).toBe(64);
});

it("Should throw errors when trying to get merkle root with wrong data.", async () => {
await expect(
dcrypto.getMerkleRoot([], someRandomInterfaceSerializer),
).rejects.toThrow("Cannot calculate Merkle root of tree with no leaves.");

await expect(dcrypto.getMerkleRoot([arr6])).rejects.toThrow(
"Tree leaf not Uint8Array, needs serializer.",
);

await expect(dcrypto.getMerkleRoot(arrayOfArrays3)).rejects.toThrow(
"Tree leaf not Uint8Array, needs serializer.",
);
});

it("Should throw errors when trying to get merkle proof with wrong data.", async () => {
await expect(
dcrypto.getMerkleProof([], arr6, someRandomInterfaceSerializer),
).rejects.toThrow(
"Cannot calculate Merkle proof of element of empty tree.",
);

await expect(
dcrypto.getMerkleProof([arr6], arr6, someRandomInterfaceSerializer),
).rejects.toThrow(
"No point in calculating proof of a tree with single leaf.",
);

await expect(dcrypto.getMerkleProof(arrayOfArrays3, arr6)).rejects.toThrow(
"It is mandatory to provide a serializer for non-Uint8Array items",
);
test("Mnemonic Argon2 hash works.", async () => {
const mnemonic = await dcrypto.generateMnemonic();
const hash = await dcrypto.argon2(mnemonic);
expect(hash.length).toBe(32);
});
});
Loading

0 comments on commit 70ae60c

Please sign in to comment.