Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
17 changes: 17 additions & 0 deletions lib/WASMInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,11 @@ export async function WASMInterface(binary: IEmbeddedWasm, hashLength: number) {
wasmInstance.exports.Hash_Init(bits);
};

const init_derive_key = (context: string) => {
initialized = true;
wasmInstance.exports.Hash_Init_Derive_Key(context.length);
};

const updateUInt8Array = (data: Uint8Array): void => {
let read = 0;
while (read < data.length) {
Expand Down Expand Up @@ -290,6 +295,17 @@ export async function WASMInterface(binary: IEmbeddedWasm, hashLength: number) {
return getDigestHex(digestChars, memoryView, hashLength);
};

const derive = (
context: string,
key: IDataType,
outputType: "hex" | "binary" = "hex",
digestParam = null,
): Uint8Array | string => {
init_derive_key(context);
update(key);
return digest(outputType, digestParam);
};

await setupInterface();

return {
Expand All @@ -303,6 +319,7 @@ export async function WASMInterface(binary: IEmbeddedWasm, hashLength: number) {
save,
load,
calculate,
derive,
hashLength,
};
}
Expand Down
42 changes: 42 additions & 0 deletions lib/blake3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,48 @@ export function blake3(
}
}

/**
* Derives new keys from the key material (NOT password!) using BLAKE3. For passwords use Argon2.
* @param context Context string of any length.
* Should be hardcoded, globally unique, and application-specific.
* Example: `"[application] [commit timestamp] [purpose]"`
* @param key Key material (not a password) of any length. (string, Buffer or TypedArray).
* @param bits Number of output bits, which has to be a number
* divisible by 8. Defaults to 256.
* @param outputType Type of the output ("hex" | "binary"): HEX string or binary. Default is HEX.
* @returns Computed a derived key as a hexadecimal string
*/
export function deriveKeyBLAKE3(
context: string,
key: IDataType,
bits = 256,
outputType: "hex" | "binary" = "hex",
): Promise<Uint8Array | string> {
if (validateBits(bits)) {
return Promise.reject(validateBits(bits));
}
const hashLength = bits / 8;
const digestParam = hashLength;

const contextBuffer = getUInt8Buffer(context);

if (wasmCache === null || wasmCache.hashLength !== hashLength) {
return lockedCreate(mutex, wasmJson, hashLength).then((wasm) => {
wasmCache = wasm;
wasmCache.writeMemory(contextBuffer);
return wasmCache.derive(context, key, outputType, digestParam);
});
}

try {
wasmCache.writeMemory(contextBuffer);
const hash = wasmCache.derive(context, key, outputType, digestParam);
return Promise.resolve(hash);
} catch (err) {
return Promise.reject(err);
}
}

/**
* Creates a new BLAKE3 hash instance
* @param bits Number of output bits, which has to be a number
Expand Down
16 changes: 16 additions & 0 deletions src/blake3.c
Original file line number Diff line number Diff line change
Expand Up @@ -829,6 +829,17 @@ void blake3_hasher_finalize(const blake3_hasher *self, uint8_t *out, size_t out_
blake3_hasher_finalize_seek(self, 0, out, out_len);
}

void blake3_hasher_init_derive_key_raw(blake3_hasher *self, const void *context, size_t context_len) {
blake3_hasher context_hasher;
hasher_init_base(&context_hasher, IV, DERIVE_KEY_CONTEXT);
blake3_hasher_update(&context_hasher, context, context_len);
uint8_t context_key[BLAKE3_KEY_LEN];
blake3_hasher_finalize(&context_hasher, context_key, BLAKE3_KEY_LEN);
uint32_t context_key_words[8];
load_key_words(context_key, context_key_words);
hasher_init_base(self, context_key_words, DERIVE_KEY_MATERIAL);
}

blake3_hasher hasher;

WASM_EXPORT
Expand All @@ -840,6 +851,11 @@ void Hash_Init(uint32_t keyLen) {
}
}

WASM_EXPORT
void Hash_Init_Derive_Key(size_t context_len) {
blake3_hasher_init_derive_key_raw(&hasher, main_buffer, context_len);
}

WASM_EXPORT
void Hash_Update(uint32_t len) {
blake3_hasher_update(&hasher, main_buffer, len);
Expand Down
248 changes: 248 additions & 0 deletions test/blake3-derive.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
import { deriveKeyBLAKE3 } from "../lib";
describe("Test deriveKeyBLAKE3", () => {
const testCases = [
{
input_len: 0,
derive_key:
"2cc39783c223154fea8dfb7c1b1660f2ac2dcbd1c1de8277b0b0dd39b7e50d7d905630c8be290dfcf3e6842f13bddd573c098c3f17361f1f206b8cad9d088aa4a3f746752c6b0ce6a83b0da81d59649257cdf8eb3e9f7d4998e41021fac119deefb896224ac99f860011f73609e6e0e4540f93b273e56547dfd3aa1a035ba6689d89a0",
},
{
input_len: 1,
derive_key:
"b3e2e340a117a499c6cf2398a19ee0d29cca2bb7404c73063382693bf66cb06c5827b91bf889b6b97c5477f535361caefca0b5d8c4746441c57617111933158950670f9aa8a05d791daae10ac683cbef8faf897c84e6114a59d2173c3f417023a35d6983f2c7dfa57e7fc559ad751dbfb9ffab39c2ef8c4aafebc9ae973a64f0c76551",
},
{
input_len: 2,
derive_key:
"1f166565a7df0098ee65922d7fea425fb18b9943f19d6161e2d17939356168e6daa59cae19892b2d54f6fc9f475d26031fd1c22ae0a3e8ef7bdb23f452a15e0027629d2e867b1bb1e6ab21c71297377750826c404dfccc2406bd57a83775f89e0b075e59a7732326715ef912078e213944f490ad68037557518b79c0086de6d6f6cdd2",
},
{
input_len: 3,
derive_key:
"440aba35cb006b61fc17c0529255de438efc06a8c9ebf3f2ddac3b5a86705797f27e2e914574f4d87ec04c379e12789eccbfbc15892626042707802dbe4e97c3ff59dca80c1e54246b6d055154f7348a39b7d098b2b4824ebe90e104e763b2a447512132cede16243484a55a4e40a85790038bb0dcf762e8c053cabae41bbe22a5bff7",
},
{
input_len: 4,
derive_key:
"f46085c8190d69022369ce1a18880e9b369c135eb93f3c63550d3e7630e91060fbd7d8f4258bec9da4e05044f88b91944f7cab317a2f0c18279629a3867fad0662c9ad4d42c6f27e5b124da17c8c4f3a94a025ba5d1b623686c6099d202a7317a82e3d95dae46a87de0555d727a5df55de44dab799a20dffe239594d6e99ed17950910",
},
{
input_len: 5,
derive_key:
"1f24eda69dbcb752847ec3ebb5dd42836d86e58500c7c98d906ecd82ed9ae47f6f48a3f67e4e43329c9a89b1ca526b9b35cbf7d25c1e353baffb590fd79be58ddb6c711f1a6b60e98620b851c688670412fcb0435657ba6b638d21f0f2a04f2f6b0bd8834837b10e438d5f4c7c2c71299cf7586ea9144ed09253d51f8f54dd6bff719d",
},
{
input_len: 6,
derive_key:
"be96b30b37919fe4379dfbe752ae77b4f7e2ab92f7ff27435f76f2f065f6a5f435ae01a1d14bd5a6b3b69d8cbd35f0b01ef2173ff6f9b640ca0bd4748efa398bf9a9c0acd6a66d9332fdc9b47ffe28ba7ab6090c26747b85f4fab22f936b71eb3f64613d8bd9dfabe9bb68da19de78321b481e5297df9e40ec8a3d662f3e1479c65de0",
},
{
input_len: 7,
derive_key:
"dc3b6485f9d94935329442916b0d059685ba815a1fa2a14107217453a7fc9f0e66266db2ea7c96843f9d8208e600a73f7f45b2f55b9e6d6a7ccf05daae63a3fdd10b25ac0bd2e224ce8291f88c05976d575df998477db86fb2cfbbf91725d62cb57acfeb3c2d973b89b503c2b60dde85a7802b69dc1ac2007d5623cbea8cbfb6b181f5",
},
{
input_len: 8,
derive_key:
"2b166978cef14d9d438046c720519d8b1cad707e199746f1562d0c87fbd32940f0e2545a96693a66654225ebbaac76d093bfa9cd8f525a53acb92a861a98c42e7d1c4ae82e68ab691d510012edd2a728f98cd4794ef757e94d6546961b4f280a51aac339cc95b64a92b83cc3f26d8af8dfb4c091c240acdb4d47728d23e7148720ef04",
},
{
input_len: 63,
derive_key:
"b6451e30b953c206e34644c6803724e9d2725e0893039cfc49584f991f451af3b89e8ff572d3da4f4022199b9563b9d70ebb616efff0763e9abec71b550f1371e233319c4c4e74da936ba8e5bbb29a598e007a0bbfa929c99738ca2cc098d59134d11ff300c39f82e2fce9f7f0fa266459503f64ab9913befc65fddc474f6dc1c67669",
},
{
input_len: 64,
derive_key:
"a5c4a7053fa86b64746d4bb688d06ad1f02a18fce9afd3e818fefaa7126bf73e9b9493a9befebe0bf0c9509fb3105cfa0e262cde141aa8e3f2c2f77890bb64a4cca96922a21ead111f6338ad5244f2c15c44cb595443ac2ac294231e31be4a4307d0a91e874d36fc9852aeb1265c09b6e0cda7c37ef686fbbcab97e8ff66718be048bb",
},
{
input_len: 65,
derive_key:
"51fd05c3c1cfbc8ed67d139ad76f5cf8236cd2acd26627a30c104dfd9d3ff8a82b02e8bd36d8498a75ad8c8e9b15eb386970283d6dd42c8ae7911cc592887fdbe26a0a5f0bf821cd92986c60b2502c9be3f98a9c133a7e8045ea867e0828c7252e739321f7c2d65daee4468eb4429efae469a42763f1f94977435d10dccae3e3dce88d",
},
{
input_len: 127,
derive_key:
"c91c090ceee3a3ac81902da31838012625bbcd73fcb92e7d7e56f78deba4f0c3feeb3974306966ccb3e3c69c337ef8a45660ad02526306fd685c88542ad00f759af6dd1adc2e50c2b8aac9f0c5221ff481565cf6455b772515a69463223202e5c371743e35210bbbbabd89651684107fd9fe493c937be16e39cfa7084a36207c99bea3",
},
{
input_len: 128,
derive_key:
"81720f34452f58a0120a58b6b4608384b5c51d11f39ce97161a0c0e442ca022550e7cd651e312f0b4c6afb3c348ae5dd17d2b29fab3b894d9a0034c7b04fd9190cbd90043ff65d1657bbc05bfdecf2897dd894c7a1b54656d59a50b51190a9da44db426266ad6ce7c173a8c0bbe091b75e734b4dadb59b2861cd2518b4e7591e4b83c9",
},
{
input_len: 129,
derive_key:
"938d2d4435be30eafdbb2b7031f7857c98b04881227391dc40db3c7b21f41fc18d72d0f9c1de5760e1941aebf3100b51d64644cb459eb5d20258e233892805eb98b07570ef2a1787cd48e117c8d6a63a68fd8fc8e59e79dbe63129e88352865721c8d5f0cf183f85e0609860472b0d6087cefdd186d984b21542c1c780684ed6832d8d",
},
{
input_len: 1023,
derive_key:
"74a16c1c3d44368a86e1ca6df64be6a2f64cce8f09220787450722d85725dea59c413264404661e9e4d955409dfe4ad3aa487871bcd454ed12abfe2c2b1eb7757588cf6cb18d2eccad49e018c0d0fec323bec82bf1644c6325717d13ea712e6840d3e6e730d35553f59eff5377a9c350bcc1556694b924b858f329c44ee64b884ef00d",
},
{
input_len: 1024,
derive_key:
"7356cd7720d5b66b6d0697eb3177d9f8d73a4a5c5e968896eb6a6896843027066c23b601d3ddfb391e90d5c8eccdef4ae2a264bce9e612ba15e2bc9d654af1481b2e75dbabe615974f1070bba84d56853265a34330b4766f8e75edd1f4a1650476c10802f22b64bd3919d246ba20a17558bc51c199efdec67e80a227251808d8ce5bad",
},
{
input_len: 1025,
derive_key:
"effaa245f065fbf82ac186839a249707c3bddf6d3fdda22d1b95a3c970379bcb5d31013a167509e9066273ab6e2123bc835b408b067d88f96addb550d96b6852dad38e320b9d940f86db74d398c770f462118b35d2724efa13da97194491d96dd37c3c09cbef665953f2ee85ec83d88b88d11547a6f911c8217cca46defa2751e7f3ad",
},
{
input_len: 2048,
derive_key:
"7b2945cb4fef70885cc5d78a87bf6f6207dd901ff239201351ffac04e1088a23e2c11a1ebffcea4d80447867b61badb1383d842d4e79645d48dd82ccba290769caa7af8eaa1bd78a2a5e6e94fbdab78d9c7b74e894879f6a515257ccf6f95056f4e25390f24f6b35ffbb74b766202569b1d797f2d4bd9d17524c720107f985f4ddc583",
},
{
input_len: 2049,
derive_key:
"2ea477c5515cc3dd606512ee72bb3e0e758cfae7232826f35fb98ca1bcbdf27316d8e9e79081a80b046b60f6a263616f33ca464bd78d79fa18200d06c7fc9bffd808cc4755277a7d5e09da0f29ed150f6537ea9bed946227ff184cc66a72a5f8c1e4bd8b04e81cf40fe6dc4427ad5678311a61f4ffc39d195589bdbc670f63ae70f4b6",
},
{
input_len: 3072,
derive_key:
"050df97f8c2ead654d9bb3ab8c9178edcd902a32f8495949feadcc1e0480c46b3604131bbd6e3ba573b6dd682fa0a63e5b165d39fc43a625d00207607a2bfeb65ff1d29292152e26b298868e3b87be95d6458f6f2ce6118437b632415abe6ad522874bcd79e4030a5e7bad2efa90a7a7c67e93f0a18fb28369d0a9329ab5c24134ccb0",
},
{
input_len: 3073,
derive_key:
"72613c9ec9ff7e40f8f5c173784c532ad852e827dba2bf85b2ab4b76f7079081576288e552647a9d86481c2cae75c2dd4e7c5195fb9ada1ef50e9c5098c249d743929191441301c69e1f48505a4305ec1778450ee48b8e69dc23a25960fe33070ea549119599760a8a2d28aeca06b8c5e9ba58bc19e11fe57b6ee98aa44b2a8e6b14a5",
},
{
input_len: 4096,
derive_key:
"1e0d7f3db8c414c97c6307cbda6cd27ac3b030949da8e23be1a1a924ad2f25b9d78038f7b198596c6cc4a9ccf93223c08722d684f240ff6569075ed81591fd93f9fff1110b3a75bc67e426012e5588959cc5a4c192173a03c00731cf84544f65a2fb9378989f72e9694a6a394a8a30997c2e67f95a504e631cd2c5f55246024761b245",
},
{
input_len: 4097,
derive_key:
"aca51029626b55fda7117b42a7c211f8c6e9ba4fe5b7a8ca922f34299500ead8a897f66a400fed9198fd61dd2d58d382458e64e100128075fc54b860934e8de2e84170734b06e1d212a117100820dbc48292d148afa50567b8b84b1ec336ae10d40c8c975a624996e12de31abbe135d9d159375739c333798a80c64ae895e51e22f3ad",
},
{
input_len: 5120,
derive_key:
"7a7acac8a02adcf3038d74cdd1d34527de8a0fcc0ee3399d1262397ce5817f6055d0cefd84d9d57fe792d65a278fd20384ac6c30fdb340092f1a74a92ace99c482b28f0fc0ef3b923e56ade20c6dba47e49227166251337d80a037e987ad3a7f728b5ab6dfafd6e2ab1bd583a95d9c895ba9c2422c24ea0f62961f0dca45cad47bfa0d",
},
{
input_len: 5121,
derive_key:
"b07f01e518e702f7ccb44a267e9e112d403a7b3f4883a47ffbed4b48339b3c341a0add0ac032ab5aaea1e4e5b004707ec5681ae0fcbe3796974c0b1cf31a194740c14519273eedaabec832e8a784b6e7cfc2c5952677e6c3f2c3914454082d7eb1ce1766ac7d75a4d3001fc89544dd46b5147382240d689bbbaefc359fb6ae30263165",
},
{
input_len: 6144,
derive_key:
"2a95beae63ddce523762355cf4b9c1d8f131465780a391286a5d01abb5683a1597099e3c6488aab6c48f3c15dbe1942d21dbcdc12115d19a8b8465fb54e9053323a9178e4275647f1a9927f6439e52b7031a0b465c861a3fc531527f7758b2b888cf2f20582e9e2c593709c0a44f9c6e0f8b963994882ea4168827823eef1f64169fef",
},
{
input_len: 6145,
derive_key:
"379bcc61d0051dd489f686c13de00d5b14c505245103dc040d9e4dd1facab8e5114493d029bdbd295aaa744a59e31f35c7f52dba9c3642f773dd0b4262a9980a2aef811697e1305d37ba9d8b6d850ef07fe41108993180cf779aeece363704c76483458603bbeeb693cffbbe5588d1f3535dcad888893e53d977424bb707201569a8d2",
},
{
input_len: 7168,
derive_key:
"11c37a112765370c94a51415d0d651190c288566e295d505defdad895dae223730d5a5175a38841693020669c7638f40b9bc1f9f39cf98bda7a5b54ae24218a800a2116b34665aa95d846d97ea988bfcb53dd9c055d588fa21ba78996776ea6c40bc428b53c62b5f3ccf200f647a5aae8067f0ea1976391fcc72af1945100e2a6dcb88",
},
{
input_len: 7169,
derive_key:
"554b0a5efea9ef183f2f9b931b7497995d9eb26f5c5c6dad2b97d62fc5ac31d99b20652c016d88ba2a611bbd761668d5eda3e568e940faae24b0d9991c3bd25a65f770b89fdcadabcb3d1a9c1cb63e69721cacf1ae69fefdcef1e3ef41bc5312ccc17222199e47a26552c6adc460cf47a72319cb5039369d0060eaea59d6c65130f1dd",
},
{
input_len: 8192,
derive_key:
"ad01d7ae4ad059b0d33baa3c01319dcf8088094d0359e5fd45d6aeaa8b2d0c3d4c9e58958553513b67f84f8eac653aeeb02ae1d5672dcecf91cd9985a0e67f4501910ecba25555395427ccc7241d70dc21c190e2aadee875e5aae6bf1912837e53411dabf7a56cbf8e4fb780432b0d7fe6cec45024a0788cf5874616407757e9e6bef7",
},
{
input_len: 8193,
derive_key:
"af1e0346e389b17c23200270a64aa4e1ead98c61695d917de7d5b00491c9b0f12f20a01d6d622edf3de026a4db4e4526225debb93c1237934d71c7340bb5916158cbdafe9ac3225476b6ab57a12357db3abbad7a26c6e66290e44034fb08a20a8d0ec264f309994d2810c49cfba6989d7abb095897459f5425adb48aba07c5fb3c83c0",
},
{
input_len: 16384,
derive_key:
"160e18b5878cd0df1c3af85eb25a0db5344d43a6fbd7a8ef4ed98d0714c3f7e160dc0b1f09caa35f2f417b9ef309dfe5ebd67f4c9507995a531374d099cf8ae317542e885ec6f589378864d3ea98716b3bbb65ef4ab5e0ab5bb298a501f19a41ec19af84a5e6b428ecd813b1a47ed91c9657c3fba11c406bc316768b58f6802c9e9b57",
},
{
input_len: 31744,
derive_key:
"39772aef80e0ebe60596361e45b061e8f417429d529171b6764468c22928e28e9759adeb797a3fbf771b1bcea30150a020e317982bf0d6e7d14dd9f064bc11025c25f31e81bd78a921db0174f03dd481d30e93fd8e90f8b2fee209f849f2d2a52f31719a490fb0ba7aea1e09814ee912eba111a9fde9d5c274185f7bae8ba85d300a2b",
},
{
input_len: 102400,
derive_key:
"4652cff7a3f385a6103b5c260fc1593e13c778dbe608efb092fe7ee69df6e9c6d83a3e041bc3a48df2879f4a0a3ed40e7c961c73eff740f3117a0504c2dff4786d44fb17f1549eb0ba585e40ec29bf7732f0b7e286ff8acddc4cb1e23b87ff5d824a986458dcc6a04ac83969b80637562953df51ed1a7e90a7926924d2763778be8560",
},
];

function createTestInput(size) {
const result = new Uint8Array(size);
for (let i = 0; i < size; i++) {
result[i] = i % 251;
}
return result;
}

const context = "BLAKE3 2019-12-27 16:29:52 test vectors context";
for (const { input_len, derive_key } of testCases) {
test(`blake3 test vector with input_len= ${input_len}`, async () => {
await expect(
deriveKeyBLAKE3(context, createTestInput(input_len), 1048),
).resolves.toBe(derive_key);
});
}

test("invalid parameters", async () => {
const invalidLength = [-1, "a", 223, 0, 127, 257, null];

for (const len of invalidLength) {
await expect(deriveKeyBLAKE3(context, "", len as any)).rejects.toThrow();
}

const invalidKey = [0, 1, Number(1), {}, [], null, undefined];

for (const key of invalidKey) {
await expect(deriveKeyBLAKE3(context, key as any)).rejects.toThrow();
}
});

test("default value for key length", async () => {
const key = await deriveKeyBLAKE3(context, "", 256);
const default_key = await deriveKeyBLAKE3(context, "");
expect(key).toBe(default_key);
});

test("different context gives different derived keys", async () => {
const diff_context =
"BLAKE3 2019-12-27 16:29:52 test vectors context different";
const key1 = await deriveKeyBLAKE3(context, "");
const key2 = await deriveKeyBLAKE3(diff_context, "");
expect(key1).not.toBe(key2);
});

test("different key material gives different derived keys", async () => {
const key1 = await deriveKeyBLAKE3(context, "");
const key2 = await deriveKeyBLAKE3(context, "different key material");
expect(key1).not.toBe(key2);
});

test("same key material and same context gives same derived keys", async () => {
const key_material = createTestInput(256);
const len = 128;
const key1 = await deriveKeyBLAKE3(context, key_material, len);
const key2 = await deriveKeyBLAKE3(context, key_material, len);
expect(key1).toBe(key2);
});

test("works for different output types", async () => {
const key1 = await deriveKeyBLAKE3(context, "", 128, "binary");
const key2 = await deriveKeyBLAKE3(context, "", 128, "hex");
expect(key1).toBeInstanceOf(Uint8Array);
expect(typeof key2).toBe("string");
const key2Array = new Uint8Array(Buffer.from(key2 as string, "hex"));
expect(key1).toStrictEqual(key2Array);
});
});