Skip to content
Merged
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
50 changes: 25 additions & 25 deletions .docs/implementation-coverage.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ These algorithms provide quantum-resistant cryptography.

# `Crypto`

* Class: `Certificate`
* Static method: `Certificate.exportChallenge(spkac[, encoding])`
* Static method: `Certificate.exportPublicKey(spkac[, encoding])`
* Static method: `Certificate.verifySpkac(spkac[, encoding])`
* Class: `Certificate`
* Static method: `Certificate.exportChallenge(spkac[, encoding])`
* Static method: `Certificate.exportPublicKey(spkac[, encoding])`
* Static method: `Certificate.verifySpkac(spkac[, encoding])`
* ✅ Class: `Cipheriv`
* ✅ `cipher.final([outputEncoding])`
* ✅ `cipher.getAuthTag()`
Expand All @@ -46,7 +46,7 @@ These algorithms provide quantum-resistant cryptography.
* ✅ `diffieHellman.verifyError`
* ✅ Class: `DiffieHellmanGroup`
* ✅ Class: `ECDH`
* static `ECDH.convertKey(key, curve[, inputEncoding[, outputEncoding[, format]]])`
* static `ECDH.convertKey(key, curve[, inputEncoding[, outputEncoding[, format]]])`
* ✅ `ecdh.computeSecret(otherPublicKey[, inputEncoding][, outputEncoding])`
* ✅ `ecdh.generateKeys([encoding[, format]])`
* ✅ `ecdh.getPrivateKey([encoding])`
Expand All @@ -60,14 +60,14 @@ These algorithms provide quantum-resistant cryptography.
* ✅ Class: `Hmac`
* ✅ `hmac.digest([encoding])`
* ✅ `hmac.update(data[, inputEncoding])`
* 🚧 Class: `KeyObject`
* static `KeyObject.from(key)`
* `keyObject.asymmetricKeyDetails`
* Class: `KeyObject`
* static `KeyObject.from(key)`
* `keyObject.asymmetricKeyDetails`
* ✅ `keyObject.asymmetricKeyType`
* ✅ `keyObject.export([options])`
* ✅ `keyObject.equals(otherKeyObject)`
* ✅ `keyObject.symmetricKeySize`
* `keyObject.toCryptoKey(algorithm, extractable, keyUsages)`
* `keyObject.toCryptoKey(algorithm, extractable, keyUsages)`
* ✅ `keyObject.type`
* ✅ Class: `Sign`
* ✅ `sign.sign(privateKey[, outputEncoding])`
Expand Down Expand Up @@ -102,10 +102,10 @@ These algorithms provide quantum-resistant cryptography.
* ❌ `x509.validTo`
* ❌ `x509.verify(publicKey)`
* 🚧 node:crypto module methods and properties
* `crypto.argon2(algorithm, parameters, callback)`
* `crypto.argon2Sync(algorithm, parameters)`
* `crypto.checkPrime(candidate[, options], callback)`
* `crypto.checkPrimeSync(candidate[, options])`
* `crypto.argon2(algorithm, parameters, callback)`
* `crypto.argon2Sync(algorithm, parameters)`
* `crypto.checkPrime(candidate[, options], callback)`
* `crypto.checkPrimeSync(candidate[, options])`
* ✅ `crypto.constants`
* ✅ `crypto.createCipheriv(algorithm, key, iv[, options])`
* ✅ `crypto.createDecipheriv(algorithm, key, iv[, options])`
Expand All @@ -129,11 +129,11 @@ These algorithms provide quantum-resistant cryptography.
* 🚧 `crypto.generateKeyPair(type, options, callback)`
* 🚧 `crypto.generateKeyPairSync(type, options)`
* 🚧 `crypto.generateKeySync(type, options)`
* `crypto.generatePrime(size[, options[, callback]])`
* `crypto.generatePrimeSync(size[, options])`
* `crypto.getCipherInfo(nameOrNid[, options])`
* `crypto.generatePrime(size[, options[, callback]])`
* `crypto.generatePrimeSync(size[, options])`
* `crypto.getCipherInfo(nameOrNid[, options])`
* ✅ `crypto.getCiphers()`
* `crypto.getCurves()`
* `crypto.getCurves()`
* ❌ `crypto.getFips()`
* ✅ `crypto.getHashes()`
* ✅ `crypto.getRandomValues(typedArray)`
Expand All @@ -157,10 +157,10 @@ These algorithms provide quantum-resistant cryptography.
* ❌ `crypto.setEngine(engine[, flags])`
* ❌ `crypto.setFips(bool)`
* ✅ `crypto.sign(algorithm, data, key[, callback])`
* 🚧 `crypto.subtle` (see below)
* `crypto.subtle` (see below)
* ✅ `crypto.timingSafeEqual(a, b)`
* ✅ `crypto.verify(algorithm, data, key, signature[, callback])`
* 🚧 `crypto.webcrypto` (see below)
* `crypto.webcrypto` (see below)

## `crypto.diffieHellman`
| type | Status |
Expand Down Expand Up @@ -242,10 +242,10 @@ These ciphers are **not available in Node.js** but are provided by RNQC via libs

# `WebCrypto`

* Class: `Crypto`
* `crypto.subtle`
* `crypto.getRandomValues(typedArray)`
* `crypto.randomUUID()`
* Class: `Crypto`
* `crypto.subtle`
* `crypto.getRandomValues(typedArray)`
* `crypto.randomUUID()`
* ✅ Class: `CryptoKey`
* ✅ `cryptoKey.algorithm`
* ✅ `cryptoKey.extractable`
Expand All @@ -254,12 +254,12 @@ These ciphers are **not available in Node.js** but are provided by RNQC via libs
* ✅ Class: `CryptoKeyPair`
* ✅ `cryptoKeyPair.privateKey`
* ✅ `cryptoKeyPair.publicKey`
* Class: `CryptoSubtle`
* 🚧 Class: `CryptoSubtle`
* (see below)

# `SubtleCrypto`

* Class: `SubtleCrypto`
* 🚧 Class: `SubtleCrypto`
* ❌ static `supports(operation, algorithm[, lengthOrAdditionalAlgorithm])`
* ❌ `subtle.decapsulateBits(decapsulationAlgorithm, decapsulationKey, ciphertext)`
* ❌ `subtle.decapsulateKey(decapsulationAlgorithm, decapsulationKey, ciphertext, sharedKeyAlgorithm, extractable, usages)`
Expand Down
31 changes: 19 additions & 12 deletions docs/data/coverage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ export const COVERAGE_DATA: CoverageCategory[] = [
{
name: 'Certificate',
subItems: [
{ name: 'exportChallenge', status: 'missing' },
{ name: 'exportPublicKey', status: 'missing' },
{ name: 'verifySpkac', status: 'missing' },
{ name: 'exportChallenge', status: 'implemented' },
{ name: 'exportPublicKey', status: 'implemented' },
{ name: 'verifySpkac', status: 'implemented' },
],
},
{
Expand Down Expand Up @@ -76,8 +76,15 @@ export const COVERAGE_DATA: CoverageCategory[] = [
},
{
name: 'ECDH',
status: 'implemented',
note: 'Use simple ECDH methods instead',
subItems: [
{ name: 'convertKey', status: 'implemented', note: 'static' },
{ name: 'computeSecret', status: 'implemented' },
{ name: 'generateKeys', status: 'implemented' },
{ name: 'getPrivateKey', status: 'implemented' },
{ name: 'getPublicKey', status: 'implemented' },
{ name: 'setPrivateKey', status: 'implemented' },
{ name: 'setPublicKey', status: 'implemented' },
],
},
{
name: 'Hash',
Expand Down Expand Up @@ -114,11 +121,11 @@ export const COVERAGE_DATA: CoverageCategory[] = [
{ name: 'asymmetricKeyType', status: 'implemented' },
{ name: 'export', status: 'implemented' },
{ name: 'type', status: 'implemented' },
{ name: 'asymmetricKeyDetails', status: 'missing' },
{ name: 'asymmetricKeyDetails', status: 'implemented' },
{ name: 'equals', status: 'implemented' },
{ name: 'symmetricKeySize', status: 'implemented' },
{ name: 'toCryptoKey', status: 'missing' },
{ name: 'from', status: 'missing', note: 'static' },
{ name: 'toCryptoKey', status: 'implemented' },
{ name: 'from', status: 'implemented', note: 'static' },
],
},
{
Expand All @@ -130,8 +137,8 @@ export const COVERAGE_DATA: CoverageCategory[] = [
{
title: 'Crypto Methods',
items: [
{ name: 'argon2', status: 'missing' },
{ name: 'checkPrime', status: 'missing' },
{ name: 'argon2', status: 'implemented' },
{ name: 'checkPrime', status: 'implemented' },
{ name: 'constants', status: 'implemented' },
{ name: 'createCipheriv', status: 'implemented' },
{ name: 'createDecipheriv', status: 'implemented' },
Expand Down Expand Up @@ -201,8 +208,8 @@ export const COVERAGE_DATA: CoverageCategory[] = [
{ name: 'hmac', status: 'implemented' },
],
},
{ name: 'generatePrime', status: 'missing' },
{ name: 'getCipherInfo', status: 'missing' },
{ name: 'generatePrime', status: 'implemented' },
{ name: 'getCipherInfo', status: 'implemented' },
{ name: 'getCiphers', status: 'implemented' },
{ name: 'getCurves', status: 'implemented' },
{ name: 'getDiffieHellman', status: 'implemented' },
Expand Down
2 changes: 1 addition & 1 deletion example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2811,7 +2811,7 @@ SPEC CHECKSUMS:
MMKVCore: f2dd4c9befea04277a55e84e7812f930537993df
NitroMmkv: afbc5b2fbf963be567c6c545aa1efcf6a9cec68e
NitroModules: 11bba9d065af151eae51e38a6425e04c3b223ff3
QuickCrypto: 91cda93ba3146b0cb92039d1058fbbaec100edd1
QuickCrypto: aad2c3dbe94e5eb4322d20e1bdccfa66464e2ae0
RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669
RCTDeprecation: c4b9e2fd0ab200e3af72b013ed6113187c607077
RCTRequired: e97dd5dafc1db8094e63bc5031e0371f092ae92a
Expand Down
4 changes: 2 additions & 2 deletions example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"lint:fix": "eslint \"src/**/*.{js,ts,tsx}\" --fix",
"format": "prettier --check \"**/*.{js,ts,tsx}\"",
"format:fix": "prettier --write \"**/*.{js,ts,tsx}\"",
"start": "react-native start",
"start": "react-native start --client-logs",
"dev": "sh -c 'react-native start --client-logs \"$@\" 2>&1 | tee /tmp/rnqc-metro.log' --",
"pods": "RCT_USE_RN_DEP=1 RCT_USE_PREBUILT_RNCORE=1 bundle install && bundle exec pod install --project-directory=ios",
"build:android": "cd android && ./gradlew assembleDebug --no-daemon --console=plain -PreactNativeArchitectures=arm64-v8a",
Expand All @@ -40,7 +40,7 @@
"react-native-mmkv": "4.0.1",
"react-native-nitro-modules": "0.33.2",
"react-native-quick-base64": "2.2.2",
"react-native-quick-crypto": "1.0.10",
"react-native-quick-crypto": "workspace:*",
"react-native-safe-area-context": "5.6.2",
"react-native-screens": "4.18.0",
"react-native-vector-icons": "10.3.0",
Expand Down
16 changes: 11 additions & 5 deletions example/src/hooks/useTestsList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,31 @@ import { useState, useCallback } from 'react';
import type { TestSuites } from '../types/tests';
import { TestsContext } from '../tests/util';

import '../tests/argon2/argon2_tests';
import '../tests/blake3/blake3_tests';
import '../tests/cipher/cipher_tests';
import '../tests/certificate/certificate_tests';
import '../tests/cipher/chacha_tests';
import '../tests/cipher/xsalsa20_tests';
import '../tests/cipher/xsalsa20_poly1305_tests';
import '../tests/cipher/cipher_tests';
import '../tests/cipher/cipherinfo_tests';
import '../tests/cipher/xchacha20_poly1305_tests';
import '../tests/cipher/xsalsa20_poly1305_tests';
import '../tests/cipher/xsalsa20_tests';
import '../tests/dh/dh_tests';
import '../tests/ecdh/ecdh_convertkey_tests';
import '../tests/ecdh/ecdh_tests';
import '../tests/hash/hash_tests';
import '../tests/hmac/hmac_tests';
import '../tests/hkdf/hkdf_tests';
import '../tests/hmac/hmac_tests';
import '../tests/jose/jose';
import '../tests/keys/create_keys';
import '../tests/keys/generate_key';
import '../tests/keys/generate_keypair';
import '../tests/keys/keyobject_from_tocryptokey_tests';
import '../tests/keys/public_cipher';
import '../tests/keys/sign_verify_streaming';
import '../tests/keys/sign_verify_oneshot';
import '../tests/keys/sign_verify_streaming';
import '../tests/pbkdf2/pbkdf2_tests';
import '../tests/prime/prime_tests';
import '../tests/random/random_tests';
import '../tests/scrypt/scrypt_tests';
import '../tests/subtle/deriveBits';
Expand Down
138 changes: 138 additions & 0 deletions example/src/tests/argon2/argon2_tests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import { test } from '../util';
import { argon2Sync, argon2, Buffer } from 'react-native-quick-crypto';
import { assert } from 'chai';

const SUITE = 'argon2';

// RFC 9106 test vector for argon2id
const RFC_PARAMS = {
message: Buffer.from(
'0101010101010101010101010101010101010101010101010101010101010101',
'hex',
),
nonce: Buffer.from('02020202020202020202020202020202', 'hex'),
parallelism: 4,
tagLength: 32,
memory: 32, // 32 KiB
passes: 3,
secret: Buffer.from('0303030303030303', 'hex'),
associatedData: Buffer.from('040404040404040404040404', 'hex'),
version: 0x13,
};

test(SUITE, 'argon2Sync: argon2id produces expected output', () => {
const result = argon2Sync('argon2id', RFC_PARAMS);
assert.isOk(result);
assert.strictEqual(result.length, 32);
});

test(SUITE, 'argon2Sync: argon2i produces output', () => {
const result = argon2Sync('argon2i', {
message: Buffer.from('password'),
nonce: Buffer.from('somesalt0000'),
parallelism: 1,
tagLength: 32,
memory: 64,
passes: 3,
});
assert.isOk(result);
assert.strictEqual(result.length, 32);
});

test(SUITE, 'argon2Sync: argon2d produces output', () => {
const result = argon2Sync('argon2d', {
message: Buffer.from('password'),
nonce: Buffer.from('somesalt0000'),
parallelism: 1,
tagLength: 32,
memory: 64,
passes: 3,
});
assert.isOk(result);
assert.strictEqual(result.length, 32);
});

test(SUITE, 'argon2Sync: different algorithms produce different output', () => {
const params = {
message: Buffer.from('password'),
nonce: Buffer.from('somesalt0000'),
parallelism: 1,
tagLength: 32,
memory: 64,
passes: 3,
};
const d = argon2Sync('argon2d', params);
const i = argon2Sync('argon2i', params);
const id = argon2Sync('argon2id', params);
assert.notDeepEqual(d, i);
assert.notDeepEqual(i, id);
assert.notDeepEqual(d, id);
});

test(SUITE, 'argon2Sync: respects tagLength', () => {
const result = argon2Sync('argon2id', {
message: Buffer.from('password'),
nonce: Buffer.from('somesalt0000'),
parallelism: 1,
tagLength: 64,
memory: 64,
passes: 3,
});
assert.strictEqual(result.length, 64);
});

test(SUITE, 'argon2Sync: throws on invalid algorithm', () => {
assert.throws(() => {
argon2Sync('argon2x', {
message: Buffer.from('password'),
nonce: Buffer.from('somesalt0000'),
parallelism: 1,
tagLength: 32,
memory: 64,
passes: 3,
});
}, /Unknown argon2 algorithm/);
});

test(SUITE, 'argon2: async produces same result as sync', () => {
return new Promise<void>((resolve, reject) => {
const params = {
message: Buffer.from('password'),
nonce: Buffer.from('somesalt0000'),
parallelism: 1,
tagLength: 32,
memory: 64,
passes: 3,
};
const syncResult = argon2Sync('argon2id', params);
argon2('argon2id', params, (err, asyncResult) => {
try {
assert.isNull(err);
assert.deepEqual(
Buffer.from(asyncResult).toString('hex'),
Buffer.from(syncResult).toString('hex'),
);
resolve();
} catch (e) {
reject(e);
}
});
});
});

test(SUITE, 'argon2Sync: deterministic with same inputs', () => {
const params = {
message: Buffer.from('password'),
nonce: Buffer.from('somesalt0000'),
parallelism: 1,
tagLength: 32,
memory: 64,
passes: 3,
};
const r1 = argon2Sync('argon2id', params);
const r2 = argon2Sync('argon2id', params);
assert.deepEqual(
Buffer.from(r1).toString('hex'),
Buffer.from(r2).toString('hex'),
);
});
Loading
Loading