diff --git a/src/_md.ts b/src/_md.ts index 5818d0f..49ba884 100644 --- a/src/_md.ts +++ b/src/_md.ts @@ -4,7 +4,12 @@ import { Hash, createView, Input, toBytes } from './utils.js'; /** * Polyfill for Safari 14 */ -function setBigUint64(view: DataView, byteOffset: number, value: bigint, isLE: boolean): void { +export function setBigUint64( + view: DataView, + byteOffset: number, + value: bigint, + isLE: boolean +): void { if (typeof view.setBigUint64 === 'function') return view.setBigUint64(byteOffset, value, isLE); const _32n = BigInt(32); const _u32_max = BigInt(0xffffffff); diff --git a/src/blake2s.ts b/src/blake2s.ts index 89499ad..a2fd126 100644 --- a/src/blake2s.ts +++ b/src/blake2s.ts @@ -10,7 +10,7 @@ export const B2S_IV = /* @__PURE__ */ new Uint32Array([ ]); // Mixing function G splitted in two halfs -function G1s(a: number, b: number, c: number, d: number, x: number) { +export function G1s(a: number, b: number, c: number, d: number, x: number) { a = (a + b + x) | 0; d = rotr(d ^ a, 16); c = (c + d) | 0; @@ -18,7 +18,7 @@ function G1s(a: number, b: number, c: number, d: number, x: number) { return { a, b, c, d }; } -function G2s(a: number, b: number, c: number, d: number, x: number) { +export function G2s(a: number, b: number, c: number, d: number, x: number) { a = (a + b + x) | 0; d = rotr(d ^ a, 8); c = (c + d) | 0; diff --git a/test/argon2.test.js b/test/argon2.test.js index 3850d2f..d925ead 100644 --- a/test/argon2.test.js +++ b/test/argon2.test.js @@ -1,4 +1,4 @@ -const { deepStrictEqual } = require('assert'); +const { deepStrictEqual, throws } = require('assert'); const { describe, should } = require('micro-should'); const { argon2i, argon2d, argon2id } = require('../argon2'); const { argon2iAsync, argon2dAsync, argon2idAsync } = require('../argon2'); @@ -9,7 +9,7 @@ const asyncMap = new Map([ [argon2d, argon2dAsync], [argon2id, argon2idAsync], ]); - +const IGNORE_SLOW = false; const VECTORS = [ { fn: argon2i, @@ -342,9 +342,38 @@ const VECTORS = [ salt: 'diffsalt', exp: 'bdf32b05ccc42eb15d58fd19b1f856b113da1e9a5874fdcc544308565aa8141c', }, -].filter((i) => !!i); +].filter((i) => !!i && (!IGNORE_SLOW || i.m <= 256)); describe('Argon2', () => { + should('types', async () => { + const opt = { + t: 2, + m: 256, + p: 1, + }; + argon2id('password', 'diffsalt', opt); + throws(() => argon2id('password', 'diffsalt', { ...opt, p: 0 })); + throws(() => argon2id('password', 'diffsalt', { ...opt, p: true })); + throws(() => argon2id('password', 'diffsalt', { ...opt, t: 0 })); + throws(() => argon2id('password', 'diffsalt', { ...opt, t: true })); + throws(() => argon2id('password', 'diffsalt', { ...opt, onProgress: true })); + throws(() => argon2id('password', 'salt', opt)); + throws(() => argon2id('password', 'diffsalt', { ...opt, maxmem: 1 })); + throws(() => argon2id('password', 'diffsalt', { ...opt, maxmem: true })); + throws(() => argon2id('password', 'diffsalt', { ...opt, dkLen: 0 })); + throws(() => argon2id('password', 'diffsalt', { ...opt, m: true })); + throws(() => argon2id('password', 'diffsalt', { ...opt, m: 1 })); + throws(() => argon2id('password', true, opt)); + throws(() => argon2id(true, 'diffsalt', opt)); + const t = []; + await argon2idAsync('password', 'diffsalt', { + ...opt, + onProgress: (p) => { + t.push(p); + }, + }); + deepStrictEqual(t.length !== 0, true); + }); for (let i = 0; i < VECTORS.length; i++) { const v = VECTORS[i]; const ver = v.version || 0x13; diff --git a/test/blake.test.js b/test/blake.test.js index acb737e..38bad9c 100644 --- a/test/blake.test.js +++ b/test/blake.test.js @@ -44,11 +44,13 @@ describe('blake', () => { should(`BLAKE2s: key`, () => { for (const key of TYPE_TEST.bytes) throws(() => blake2s.fn('data', { key })); throws(() => blake2s.fn('data', { key: new Uint8Array(33) })); + throws(() => blake2s.fn('data', { key: new Uint8Array(0) })); }); should(`BLAKE2b: key`, () => { for (const key of TYPE_TEST.bytes) throws(() => blake2b.fn('data', { key })); throws(() => blake2b.fn('data', { key: new Uint8Array(65) })); + throws(() => blake2b.fn('data', { key: new Uint8Array(0) })); }); should(`BLAKE2s: personalization/salt`, () => { diff --git a/test/eskdf.test.js b/test/eskdf.test.js index 35a6bea..93692a3 100644 --- a/test/eskdf.test.js +++ b/test/eskdf.test.js @@ -1,4 +1,4 @@ -const { equal, rejects, throws } = require('assert'); +const { equal, rejects, throws, deepStrictEqual } = require('assert'); const { describe, should } = require('micro-should'); const { eskdf } = require('../eskdf'); const { bytesToHex: toHex } = require('../utils'); @@ -46,6 +46,40 @@ describe('eskdf', () => { throws(() => e.deriveChildKey('aes', 0, { keyLength: 64, modulus: BigInt(65537) })); }); } + should('types', async () => { + const keyc = await eskdf('test@test.com', 'test2test'); + deepStrictEqual( + toHex(keyc.deriveChildKey('ssh', 'test')), + '3bc39ad06a15d4867aaa53f4025077ecca7cd33b3f5b9da131b50586601726fa' + ); + deepStrictEqual( + toHex(keyc.deriveChildKey('ssh', 0)), + 'd7b14774e815d429e75b5f366b0df4eff32343e94f1b30a5e12eaab682974667' + ); + + throws(() => keyc.deriveChildKey('ssh', 'test', { keyLength: 1, modulus: 1 })); + throws(() => keyc.deriveChildKey('ssh', 'test', {})); + deepStrictEqual( + toHex(keyc.deriveChildKey('ssh', 0, { keyLength: 16 })), + 'd7b14774e815d429e75b5f366b0df4ef' + ); + throws(() => keyc.deriveChildKey('ssh', 'test', { modulus: -1n })); + throws(() => keyc.deriveChildKey('ssh', 'test', { modulus: 1n })); + deepStrictEqual( + toHex(keyc.deriveChildKey('ssh', 0, { modulus: 2n ** 128n - 1n })), + 'e75b5f366b0df4f1a285d2d31f46d8f8' + ); + throws(() => keyc.deriveChildKey('ssh', '')); + throws(() => keyc.deriveChildKey('ssh', '1'.repeat(256))); + throws(() => keyc.deriveChildKey('tmp', 'test')); + throws(() => keyc.deriveChildKey('ssh', true)); + throws(() => keyc.deriveChildKey('ssh', 100n)); + throws(() => keyc.deriveChildKey('ssh', new Uint8Array(10))); + // Expire + keyc.expire(); + throws(() => keyc.deriveChildKey('ssh', 'test')); + throws(() => keyc.deriveChildKey('ssh', 0)); + }); }); // should.run(); diff --git a/test/hashes.test.js b/test/hashes.test.js index ee371d3..6922cb5 100644 --- a/test/hashes.test.js +++ b/test/hashes.test.js @@ -470,6 +470,17 @@ function init() { ); }); + should('clone', () => { + const exp = hash.fn(BUF_768); + const t = hash.obj(); + t.update(BUF_768.subarray(0, 10)); + const t2 = t.clone(); + t2.update(BUF_768.subarray(10)); + deepStrictEqual(t2.digest(), exp); + t.update(BUF_768.subarray(10)); + deepStrictEqual(t.digest(), exp); + }); + should('partial', () => { const fnH = hash.fn(BUF_768); for (let i = 0; i < 256; i++) { diff --git a/test/kdf.test.js b/test/kdf.test.js index 76a3074..275c441 100644 --- a/test/kdf.test.js +++ b/test/kdf.test.js @@ -237,6 +237,14 @@ describe('scrypt', () => { await rejects(() => scryptAsync('pwd', 'salt', { ...opt, N: 0 }), `scrypt(N=0)`); // N==1 -> throws throws(() => scrypt('pwd', 'salt', { ...opt, N: 1 }), `scrypt(N=1)`); + // on progress callback + throws(() => scrypt('pwd', 'salt', { ...opt, onProgress: true })); + // P = 0 + throws(() => scrypt('pwd', 'salt', { ...opt, p: -1 })); + throws(() => scrypt('pwd', 'salt', { ...opt, p: 2 ** 48 })); + // dkLen + throws(() => scrypt('pwd', 'salt', { ...opt, dkLen: -1 })); + throws(() => scrypt('pwd', 'salt', { ...opt, dkLen: 2 ** 48 })); await rejects(() => scryptAsync('pwd', 'salt', { ...opt, N: 1 }), `scrypt(N=1)`); for (const t of TYPE_TEST.int) { for (const k of ['N', 'r', 'p', 'dkLen']) { diff --git a/test/utils.test.js b/test/utils.test.js index 20138f4..c4c7609 100644 --- a/test/utils.test.js +++ b/test/utils.test.js @@ -11,7 +11,13 @@ const { bytesToHex, concatBytes, hexToBytes, + createView, + isBytes, + randomBytes, } = require('../utils'); +const assert = require('../_assert'); +const { sha256 } = require('../sha256'); +const { setBigUint64 } = require('../_md'); describe('utils', () => { const staticHexVectors = [ @@ -148,6 +154,105 @@ describe('utils etc', () => { 20 21 22 23 24 25`) ); }); + should('setBigUint64', () => { + const t = new Uint8Array(20); + const v = createView(t); + const VECTORS = [ + { + n: 123n, + le: false, + hex: '000000000000007b000000000000000000000000', + }, + { + n: 123n, + le: true, + hex: '7b00000000000000000000000000000000000000', + }, + { + n: 2n ** 64n - 1n, + le: true, + hex: 'ffffffffffffffff000000000000000000000000', + }, + { + n: 2n ** 64n - 1n, + le: true, + hex: '000000ffffffffffffffff000000000000000000', + pos: 3, + }, + { + n: 0x123456789abcdef0n, + le: true, + hex: 'f0debc9a78563412000000000000000000000000', + }, + { + n: 0x123456789abcdef0n, + le: false, + hex: '123456789abcdef0000000000000000000000000', + }, + ]; + const createViewMock = (u8) => { + const v = createView(u8); + return { + setUint32: (o, wh, isLE) => v.setUint32(o, wh, isLE), + }; + }; + + for (const cv of [createView, createViewMock]) { + for (const t of VECTORS) { + const b = new Uint8Array(20); + const v = cv(b); + setBigUint64(v, t.pos || 0, t.n, t.le); + deepStrictEqual(bytesToHex(b), t.hex); + } + } + }); + should('randomBytes', () => { + const t = randomBytes(32); + deepStrictEqual(t instanceof Uint8Array, true); + deepStrictEqual(t.length, 32); + const t2 = randomBytes(12); + deepStrictEqual(t2 instanceof Uint8Array, true); + deepStrictEqual(t2.length, 12); + }); + should('isBytes', () => { + deepStrictEqual(isBytes(new Uint8Array(0)), true); + deepStrictEqual(isBytes(Buffer.alloc(10)), true); + deepStrictEqual(isBytes(''), false); + deepStrictEqual(isBytes([1, 2, 3]), false); + }); +}); + +describe('assert', () => { + should('anumber', () => { + deepStrictEqual(assert.anumber(10), undefined); + throws(() => assert.anumber(1.2)); + throws(() => assert.anumber('1')); + throws(() => assert.anumber(true)); + throws(() => assert.anumber(NaN)); + }); + should('abytes', () => { + deepStrictEqual(assert.abytes(new Uint8Array(0)), undefined); + deepStrictEqual(assert.abytes(Buffer.alloc(10)), undefined); + deepStrictEqual(assert.abytes(new Uint8Array(10)), undefined); + assert.abytes(new Uint8Array(11), 11, 12); + assert.abytes(new Uint8Array(12), 12, 12); + throws(() => assert.abytes('test')); + throws(() => assert.abytes(new Uint8Array(10), 11, 12)); + throws(() => assert.abytes(new Uint8Array(10), 11, 12)); + }); + should('ahash', () => { + deepStrictEqual(assert.ahash(sha256), undefined); + throws(() => assert.ahash({})); + throws(() => assert.ahash({ blockLen: 1, outputLen: 1, create: () => {} })); + }); + should('aexists', () => { + deepStrictEqual(assert.aexists({}), undefined); + throws(() => assert.aexists({ destroyed: true })); + }); + should('aoutput', () => { + deepStrictEqual(assert.aoutput(new Uint8Array(10), { outputLen: 5 }), undefined); + throws(() => assert.aoutput(new Uint8Array(1), { outputLen: 5 })); + }); }); if (require.main === module) should.run();