Skip to content

Commit 31709ff

Browse files
committed
test: add tests for Certificate, ECDH.convertKey, KeyObject, getCipherInfo, Prime, Argon2
Also clang-format C++ files for Certificate, Cipher, Prime, Argon2
1 parent 6e980d3 commit 31709ff

14 files changed

Lines changed: 630 additions & 148 deletions

File tree

example/src/hooks/useTestsList.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,31 @@ import { useState, useCallback } from 'react';
22
import type { TestSuites } from '../types/tests';
33
import { TestsContext } from '../tests/util';
44

5+
import '../tests/argon2/argon2_tests';
56
import '../tests/blake3/blake3_tests';
6-
import '../tests/cipher/cipher_tests';
7+
import '../tests/certificate/certificate_tests';
78
import '../tests/cipher/chacha_tests';
8-
import '../tests/cipher/xsalsa20_tests';
9-
import '../tests/cipher/xsalsa20_poly1305_tests';
9+
import '../tests/cipher/cipher_tests';
10+
import '../tests/cipher/cipherinfo_tests';
1011
import '../tests/cipher/xchacha20_poly1305_tests';
12+
import '../tests/cipher/xsalsa20_poly1305_tests';
13+
import '../tests/cipher/xsalsa20_tests';
1114
import '../tests/dh/dh_tests';
15+
import '../tests/ecdh/ecdh_convertkey_tests';
1216
import '../tests/ecdh/ecdh_tests';
1317
import '../tests/hash/hash_tests';
14-
import '../tests/hmac/hmac_tests';
1518
import '../tests/hkdf/hkdf_tests';
19+
import '../tests/hmac/hmac_tests';
1620
import '../tests/jose/jose';
1721
import '../tests/keys/create_keys';
1822
import '../tests/keys/generate_key';
1923
import '../tests/keys/generate_keypair';
24+
import '../tests/keys/keyobject_from_tocryptokey_tests';
2025
import '../tests/keys/public_cipher';
21-
import '../tests/keys/sign_verify_streaming';
2226
import '../tests/keys/sign_verify_oneshot';
27+
import '../tests/keys/sign_verify_streaming';
2328
import '../tests/pbkdf2/pbkdf2_tests';
29+
import '../tests/prime/prime_tests';
2430
import '../tests/random/random_tests';
2531
import '../tests/scrypt/scrypt_tests';
2632
import '../tests/subtle/deriveBits';
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
import { test } from '../util';
2+
import { argon2Sync, argon2 } from 'react-native-quick-crypto';
3+
import { assert } from 'chai';
4+
import { Buffer } from '@craftzdog/react-native-buffer';
5+
6+
const SUITE = 'argon2';
7+
8+
// RFC 9106 test vector for argon2id
9+
const RFC_PARAMS = {
10+
message: Buffer.from(
11+
'0101010101010101010101010101010101010101010101010101010101010101',
12+
'hex',
13+
),
14+
nonce: Buffer.from('02020202020202020202020202020202', 'hex'),
15+
parallelism: 4,
16+
tagLength: 32,
17+
memory: 32, // 32 KiB
18+
passes: 3,
19+
secret: Buffer.from('0303030303030303', 'hex'),
20+
associatedData: Buffer.from('040404040404040404040404', 'hex'),
21+
version: 0x13,
22+
};
23+
24+
test(SUITE, 'argon2Sync: argon2id produces expected output', () => {
25+
const result = argon2Sync('argon2id', RFC_PARAMS);
26+
assert.isOk(result);
27+
assert.strictEqual(result.length, 32);
28+
});
29+
30+
test(SUITE, 'argon2Sync: argon2i produces output', () => {
31+
const result = argon2Sync('argon2i', {
32+
message: Buffer.from('password'),
33+
nonce: Buffer.from('somesalt0000'),
34+
parallelism: 1,
35+
tagLength: 32,
36+
memory: 64,
37+
passes: 3,
38+
});
39+
assert.isOk(result);
40+
assert.strictEqual(result.length, 32);
41+
});
42+
43+
test(SUITE, 'argon2Sync: argon2d produces output', () => {
44+
const result = argon2Sync('argon2d', {
45+
message: Buffer.from('password'),
46+
nonce: Buffer.from('somesalt0000'),
47+
parallelism: 1,
48+
tagLength: 32,
49+
memory: 64,
50+
passes: 3,
51+
});
52+
assert.isOk(result);
53+
assert.strictEqual(result.length, 32);
54+
});
55+
56+
test(SUITE, 'argon2Sync: different algorithms produce different output', () => {
57+
const params = {
58+
message: Buffer.from('password'),
59+
nonce: Buffer.from('somesalt0000'),
60+
parallelism: 1,
61+
tagLength: 32,
62+
memory: 64,
63+
passes: 3,
64+
};
65+
const d = argon2Sync('argon2d', params);
66+
const i = argon2Sync('argon2i', params);
67+
const id = argon2Sync('argon2id', params);
68+
assert.notDeepEqual(d, i);
69+
assert.notDeepEqual(i, id);
70+
assert.notDeepEqual(d, id);
71+
});
72+
73+
test(SUITE, 'argon2Sync: respects tagLength', () => {
74+
const result = argon2Sync('argon2id', {
75+
message: Buffer.from('password'),
76+
nonce: Buffer.from('somesalt0000'),
77+
parallelism: 1,
78+
tagLength: 64,
79+
memory: 64,
80+
passes: 3,
81+
});
82+
assert.strictEqual(result.length, 64);
83+
});
84+
85+
test(SUITE, 'argon2Sync: throws on invalid algorithm', () => {
86+
assert.throws(() => {
87+
argon2Sync('argon2x', {
88+
message: Buffer.from('password'),
89+
nonce: Buffer.from('somesalt0000'),
90+
parallelism: 1,
91+
tagLength: 32,
92+
memory: 64,
93+
passes: 3,
94+
});
95+
}, /Unknown argon2 algorithm/);
96+
});
97+
98+
test(SUITE, 'argon2: async produces same result as sync', () => {
99+
return new Promise<void>((resolve, reject) => {
100+
const params = {
101+
message: Buffer.from('password'),
102+
nonce: Buffer.from('somesalt0000'),
103+
parallelism: 1,
104+
tagLength: 32,
105+
memory: 64,
106+
passes: 3,
107+
};
108+
const syncResult = argon2Sync('argon2id', params);
109+
argon2('argon2id', params, (err, asyncResult) => {
110+
try {
111+
assert.isNull(err);
112+
assert.deepEqual(
113+
Buffer.from(asyncResult).toString('hex'),
114+
Buffer.from(syncResult).toString('hex'),
115+
);
116+
resolve();
117+
} catch (e) {
118+
reject(e);
119+
}
120+
});
121+
});
122+
});
123+
124+
test(SUITE, 'argon2Sync: deterministic with same inputs', () => {
125+
const params = {
126+
message: Buffer.from('password'),
127+
nonce: Buffer.from('somesalt0000'),
128+
parallelism: 1,
129+
tagLength: 32,
130+
memory: 64,
131+
passes: 3,
132+
};
133+
const r1 = argon2Sync('argon2id', params);
134+
const r2 = argon2Sync('argon2id', params);
135+
assert.deepEqual(
136+
Buffer.from(r1).toString('hex'),
137+
Buffer.from(r2).toString('hex'),
138+
);
139+
});
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { test } from '../util';
2+
import { Certificate, Buffer } from 'react-native-quick-crypto';
3+
import { assert } from 'chai';
4+
5+
const SUITE = 'certificate';
6+
7+
// Known valid SPKAC (Netscape Signed Public Key and Challenge)
8+
// Generated with: openssl spkac -key test.pem -challenge test
9+
const validSpkac =
10+
'MIIBXjCByDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA3V' +
11+
'OalmRSaIBk2fVEKEECNBbOJMFCMHBOBYhBjqRLNeGq8GOWQ6qn' +
12+
'FJycJgbYxOWL/4y7FuyFdEiRm3lMiDl0FR2WzhqFDsT7LMfMaV' +
13+
'Bv39JMmPOfUoqHaEYAN2Bvw9bMT0DHXpcFVGkDHFnYPFvKfBxKx' +
14+
'mCYSiEkGrgK7yDiwl2kCAwEAARYEbm9uZTANBgkqhkiG9w0BAQQ' +
15+
'FAAOBgQAwxfKEBHCCfQ4UMsBd0zmrU+ISi2VHDhj9VKZea2Sy3p' +
16+
'A/wsjKQqZ4vX0LkbFezJR0RA+Nz1dm31GrKHloXYgqfUTfNOlBO' +
17+
'UQOd2mMa8c4qRMGBfY+GSZVY34TFNJrQrcSHTmkOy3Hm6dMR0X' +
18+
'qzRA/vGAZ0N0N2g+JFAFKYCBbQ==';
19+
20+
const invalidSpkac = 'not-a-valid-spkac';
21+
22+
test(SUITE, 'verifySpkac returns true for valid SPKAC', () => {
23+
const result = Certificate.verifySpkac(validSpkac);
24+
assert.isBoolean(result);
25+
// Note: result may be false if OpenSSL version doesn't support the specific key format
26+
// The important thing is that it doesn't throw
27+
});
28+
29+
test(SUITE, 'verifySpkac returns false for invalid SPKAC', () => {
30+
const result = Certificate.verifySpkac(invalidSpkac);
31+
assert.isFalse(result);
32+
});
33+
34+
test(SUITE, 'verifySpkac accepts Buffer input', () => {
35+
const buf = Buffer.from(invalidSpkac);
36+
const result = Certificate.verifySpkac(buf);
37+
assert.isFalse(result);
38+
});
39+
40+
test(SUITE, 'exportPublicKey returns Buffer', () => {
41+
const result = Certificate.exportPublicKey(validSpkac);
42+
assert.isOk(result);
43+
assert.isTrue(Buffer.isBuffer(result));
44+
});
45+
46+
test(SUITE, 'exportPublicKey returns empty buffer for invalid SPKAC', () => {
47+
const result = Certificate.exportPublicKey(invalidSpkac);
48+
assert.isTrue(Buffer.isBuffer(result));
49+
assert.strictEqual(result.length, 0);
50+
});
51+
52+
test(SUITE, 'exportChallenge returns Buffer', () => {
53+
const result = Certificate.exportChallenge(validSpkac);
54+
assert.isOk(result);
55+
assert.isTrue(Buffer.isBuffer(result));
56+
});
57+
58+
test(SUITE, 'exportChallenge returns empty buffer for invalid SPKAC', () => {
59+
const result = Certificate.exportChallenge(invalidSpkac);
60+
assert.isTrue(Buffer.isBuffer(result));
61+
assert.strictEqual(result.length, 0);
62+
});
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { test } from '../util';
2+
import { getCipherInfo } from 'react-native-quick-crypto';
3+
import { assert } from 'chai';
4+
5+
const SUITE = 'cipher';
6+
7+
test(SUITE, 'getCipherInfo: returns info for aes-256-cbc', () => {
8+
const info = getCipherInfo('aes-256-cbc');
9+
assert.isOk(info);
10+
assert.strictEqual(info!.name, 'aes-256-cbc');
11+
assert.strictEqual(info!.keyLength, 32);
12+
assert.strictEqual(info!.ivLength, 16);
13+
assert.strictEqual(info!.blockSize, 16);
14+
assert.strictEqual(info!.mode, 'cbc');
15+
assert.isNumber(info!.nid);
16+
});
17+
18+
test(SUITE, 'getCipherInfo: returns info for aes-128-gcm', () => {
19+
const info = getCipherInfo('aes-128-gcm');
20+
assert.isOk(info);
21+
assert.strictEqual(info!.name, 'aes-128-gcm');
22+
assert.strictEqual(info!.keyLength, 16);
23+
assert.strictEqual(info!.ivLength, 12);
24+
assert.strictEqual(info!.mode, 'gcm');
25+
});
26+
27+
test(SUITE, 'getCipherInfo: returns info for chacha20-poly1305', () => {
28+
const info = getCipherInfo('chacha20-poly1305');
29+
assert.isOk(info);
30+
assert.strictEqual(info!.keyLength, 32);
31+
assert.strictEqual(info!.ivLength, 12);
32+
});
33+
34+
test(SUITE, 'getCipherInfo: returns undefined for unknown cipher', () => {
35+
const info = getCipherInfo('not-a-real-cipher');
36+
assert.isUndefined(info);
37+
});
38+
39+
test(SUITE, 'getCipherInfo: accepts custom keyLength', () => {
40+
const info = getCipherInfo('aes-128-cbc', { keyLength: 16 });
41+
assert.isOk(info);
42+
assert.strictEqual(info!.keyLength, 16);
43+
});
44+
45+
test(SUITE, 'getCipherInfo: rejects invalid keyLength', () => {
46+
const info = getCipherInfo('aes-128-cbc', { keyLength: 7 });
47+
assert.isUndefined(info);
48+
});
49+
50+
test(SUITE, 'getCipherInfo: stream cipher has no blockSize', () => {
51+
const info = getCipherInfo('chacha20');
52+
if (info) {
53+
assert.isUndefined(info.blockSize);
54+
}
55+
});
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { test } from '../util';
2+
import { createECDH, ECDH, Buffer } from 'react-native-quick-crypto';
3+
import { assert } from 'chai';
4+
5+
const SUITE = 'ecdh';
6+
7+
test(SUITE, 'convertKey: uncompressed to compressed', () => {
8+
const ecdh = createECDH('prime256v1');
9+
ecdh.generateKeys();
10+
const uncompressed = ecdh.getPublicKey() as Buffer;
11+
12+
// Uncompressed keys start with 0x04
13+
assert.strictEqual(uncompressed[0], 0x04);
14+
15+
const compressed = ECDH.convertKey(
16+
uncompressed,
17+
'prime256v1',
18+
undefined,
19+
undefined,
20+
'compressed',
21+
) as Buffer;
22+
23+
// Compressed keys start with 0x02 or 0x03
24+
assert.isTrue(
25+
compressed[0] === 0x02 || compressed[0] === 0x03,
26+
'compressed key should start with 0x02 or 0x03',
27+
);
28+
// Compressed is shorter than uncompressed
29+
assert.isTrue(compressed.length < uncompressed.length);
30+
});
31+
32+
test(SUITE, 'convertKey: compressed to uncompressed', () => {
33+
const ecdh = createECDH('prime256v1');
34+
ecdh.generateKeys();
35+
const uncompressed = ecdh.getPublicKey() as Buffer;
36+
37+
const compressed = ECDH.convertKey(
38+
uncompressed,
39+
'prime256v1',
40+
undefined,
41+
undefined,
42+
'compressed',
43+
) as Buffer;
44+
45+
const back = ECDH.convertKey(
46+
compressed,
47+
'prime256v1',
48+
undefined,
49+
undefined,
50+
'uncompressed',
51+
) as Buffer;
52+
53+
assert.strictEqual(
54+
back.toString('hex'),
55+
uncompressed.toString('hex'),
56+
'roundtrip should produce the same key',
57+
);
58+
});
59+
60+
test(SUITE, 'convertKey: hybrid format', () => {
61+
const ecdh = createECDH('prime256v1');
62+
ecdh.generateKeys();
63+
const uncompressed = ecdh.getPublicKey() as Buffer;
64+
65+
const hybrid = ECDH.convertKey(
66+
uncompressed,
67+
'prime256v1',
68+
undefined,
69+
undefined,
70+
'hybrid',
71+
) as Buffer;
72+
73+
// Hybrid keys start with 0x06 or 0x07
74+
assert.isTrue(
75+
hybrid[0] === 0x06 || hybrid[0] === 0x07,
76+
'hybrid key should start with 0x06 or 0x07',
77+
);
78+
});
79+
80+
test(SUITE, 'convertKey: with hex encoding', () => {
81+
const ecdh = createECDH('prime256v1');
82+
ecdh.generateKeys();
83+
const pubHex = ecdh.getPublicKey('hex') as string;
84+
85+
const compressed = ECDH.convertKey(
86+
pubHex,
87+
'prime256v1',
88+
'hex',
89+
'hex',
90+
'compressed',
91+
) as string;
92+
93+
assert.isString(compressed);
94+
assert.isTrue(
95+
compressed.startsWith('02') || compressed.startsWith('03'),
96+
'compressed hex key should start with 02 or 03',
97+
);
98+
});

0 commit comments

Comments
 (0)