Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -187,4 +187,4 @@ tsconfig.tsbuildinfo
# development stuffs
*scratch*

.claude/settings.local.json
.agent/
3 changes: 3 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Add bun to PATH
export PATH="$HOME/.bun/bin:$PATH"

# Run linting and formatting on staged files
bun lint-staged

Expand Down
21 changes: 4 additions & 17 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 21 additions & 17 deletions docs/implementation-coverage.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Implementation Coverage - NodeJS
This document attempts to describe the implementation status of Crypto APIs/Interfaces from Node.js in the `react-native-quick-crypto` library.

> Note: This is the status for version 1.x and higher. For version `0.x` see [this document](https://github.com/margelo/react-native-quick-crypto/blob/0.x/docs/implementation-coverage.md) and the [0.x branch](https://github.com/margelo/react-native-quick-crypto/tree/0.x).

* ` ` - not implemented in Node
* ❌ - implemented in Node, not RNQC
* ✅ - implemented in Node and RNQC
Expand All @@ -13,6 +15,7 @@ This document attempts to describe the implementation status of Crypto APIs/Inte

These algorithms provide quantum-resistant cryptography.


# `Crypto`

* ❌ Class: `Certificate`
Expand Down Expand Up @@ -260,9 +263,9 @@ These algorithms provide quantum-resistant cryptography.
* ❌ `subtle.getPublicKey(key, keyUsages)`
* 🚧 `subtle.importKey(format, keyData, algorithm, extractable, keyUsages)`
* ✅ `subtle.sign(algorithm, key, data)`
* `subtle.unwrapKey(format, wrappedKey, unwrappingKey, unwrapAlgo, unwrappedKeyAlgo, extractable, keyUsages)`
* `subtle.verify(algorithm, key, signature, data)`
* `subtle.wrapKey(format, key, wrappingKey, wrapAlgo)`
* `subtle.unwrapKey(format, wrappedKey, unwrappingKey, unwrapAlgo, unwrappedKeyAlgo, extractable, keyUsages)`
* 🚧 `subtle.verify(algorithm, key, signature, data)`
* `subtle.wrapKey(format, key, wrappingKey, wrapAlgo)`

## `subtle.decrypt`
| Algorithm | Status |
Expand All @@ -271,13 +274,14 @@ These algorithms provide quantum-resistant cryptography.
| `AES-CTR` | ✅ |
| `AES-CBC` | ✅ |
| `AES-GCM` | ✅ |
| `ChaCha20-Poly1305` | ✅ |

## `subtle.deriveBits`
| Algorithm | Status |
| --------- | :----: |
| `ECDH` | ❌ |
| `X25519` | |
| `X448` | |
| `X25519` | |
| `X448` | |
| `HKDF` | ❌ |
| `PBKDF2` | ✅ |

Expand All @@ -286,9 +290,9 @@ These algorithms provide quantum-resistant cryptography.
| --------- | :----: |
| `ECDH` | ❌ |
| `HKDF` | ❌ |
| `PBKDF2` | |
| `X25519` | |
| `X448` | |
| `PBKDF2` | |
| `X25519` | |
| `X448` | |

## `subtle.digest`
| Algorithm | Status |
Expand All @@ -310,7 +314,7 @@ These algorithms provide quantum-resistant cryptography.
| `AES-CBC` | ✅ |
| `AES-GCM` | ✅ |
| `AES-OCB` | ❌ |
| `ChaCha20-Poly1305` | |
| `ChaCha20-Poly1305` | |
| `RSA-OAEP` | ✅ |

## `subtle.exportKey`
Expand Down Expand Up @@ -398,8 +402,8 @@ These algorithms provide quantum-resistant cryptography.
| `RSA-OAEP` | ✅ | ✅ | ✅ | | | | |
| `RSA-PSS` | ✅ | ✅ | ✅ | | | | |
| `RSASSA-PKCS1-v1_5` | ✅ | ✅ | ✅ | | | | |
| `X25519` | | | | | | | |
| `X448` | | | | | | | |
| `X25519` | | | | | | | |
| `X448` | | | | | | | |

## `subtle.sign`
| Algorithm | Status |
Expand All @@ -421,10 +425,10 @@ These algorithms provide quantum-resistant cryptography.
| ------------------- | :----: |
| `AES-CBC` | ❌ |
| `AES-CTR` | ❌ |
| `AES-GCM` | |
| `AES-KW` | |
| `AES-GCM` | |
| `AES-KW` | |
| `AES-OCB` | ❌ |
| `ChaCha20-Poly1305` | |
| `ChaCha20-Poly1305` | |
| `RSA-OAEP` | ❌ |

### unwrapped key algorithms
Expand Down Expand Up @@ -473,8 +477,8 @@ These algorithms provide quantum-resistant cryptography.
| ------------------- | :----: |
| `AES-CBC` | ❌ |
| `AES-CTR` | ❌ |
| `AES-GCM` | |
| `AES-KW` | |
| `AES-GCM` | |
| `AES-KW` | |
| `AES-OCB` | ❌ |
| `ChaCha20-Poly1305` | |
| `ChaCha20-Poly1305` | |
| `RSA-OAEP` | ❌ |
5 changes: 2 additions & 3 deletions example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,9 @@
"react": "19.1.0",
"react-native": "0.81.1",
"react-native-bouncy-checkbox": "4.1.2",
"react-native-fast-encoder": "^0.3.1",
"react-native-nitro-modules": "0.29.1",
"react-native-quick-base64": "2.2.2",
"react-native-quick-crypto": "1.0.1",
"react-native-quick-crypto": "workspace:*",
"react-native-safe-area-context": "^5.2.2",
"react-native-screens": "4.18.0",
"react-native-vector-icons": "^10.3.0",
Expand Down Expand Up @@ -76,4 +75,4 @@
"engines": {
"node": ">=18"
}
}
}
3 changes: 3 additions & 0 deletions example/src/hooks/useTestsList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@ import '../tests/keys/sign_verify_streaming';
import '../tests/pbkdf2/pbkdf2_tests';
import '../tests/random/random_tests';
import '../tests/subtle/deriveBits';
import '../tests/subtle/derive_key';
import '../tests/subtle/digest';
import '../tests/subtle/encrypt_decrypt';
import '../tests/subtle/generateKey';
import '../tests/subtle/import_export';
import '../tests/subtle/jwk_rfc7517_tests';
import '../tests/subtle/sign_verify';
import '../tests/subtle/wrap_unwrap';
import '../tests/subtle/x25519_x448';

export const useTestsList = (): [
TestSuites,
Expand Down
100 changes: 100 additions & 0 deletions example/src/tests/subtle/derive_key.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { test } from '../util';
import { expect } from 'chai';
import { subtle, getRandomValues } from 'react-native-quick-crypto';
import { CryptoKey } from 'react-native-quick-crypto';
import type { CryptoKeyPair } from 'react-native-quick-crypto';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const subtleAny = subtle as any;

const SUITE = 'subtle.deriveKey';

// Test 1: PBKDF2 deriveKey
test(SUITE, 'PBKDF2 deriveKey to AES-GCM', async () => {
const password = new TextEncoder().encode('my-password');
const salt = getRandomValues(new Uint8Array(16));

const baseKey = await subtle.importKey(
'raw',
password,
{ name: 'PBKDF2' },
false,
['deriveKey'],
);

const derivedKey = await subtleAny.deriveKey(
{
name: 'PBKDF2',
salt,
iterations: 100000,
hash: 'SHA-256',
},
baseKey as CryptoKey,
{ name: 'AES-GCM', length: 256 },
true,
['encrypt', 'decrypt'],
);

// Verify key can encrypt/decrypt
const plaintext = new Uint8Array([1, 2, 3, 4]);
const iv = getRandomValues(new Uint8Array(12));

const ciphertext = await subtle.encrypt(
{ name: 'AES-GCM', iv },
derivedKey as CryptoKey,
plaintext,
);

const decrypted = await subtle.decrypt(
{ name: 'AES-GCM', iv },
derivedKey as CryptoKey,
ciphertext,
);

expect(Buffer.from(decrypted).toString('hex')).to.equal(
Buffer.from(plaintext).toString('hex'),
);
});

// Test 2: X25519 deriveKey
test(SUITE, 'X25519 deriveKey to AES-GCM', async () => {
const aliceKeyPair = await subtle.generateKey({ name: 'X25519' }, false, [
'deriveKey',
'deriveBits',
]);

const bobKeyPair = await subtle.generateKey({ name: 'X25519' }, false, [
'deriveKey',
'deriveBits',
]);

const aliceDerivedKey = await subtleAny.deriveKey(
{
name: 'X25519',
public: (bobKeyPair as CryptoKeyPair).publicKey,
},
(aliceKeyPair as CryptoKeyPair).privateKey,
{ name: 'AES-GCM', length: 256 },
true,
['encrypt', 'decrypt'],
);

const bobDerivedKey = await subtleAny.deriveKey(
{
name: 'X25519',
public: (aliceKeyPair as CryptoKeyPair).publicKey,
},
(bobKeyPair as CryptoKeyPair).privateKey,
{ name: 'AES-GCM', length: 256 },
true,
['encrypt', 'decrypt'],
);

// Both should derive the same key
const aliceRaw = await subtle.exportKey('raw', aliceDerivedKey as CryptoKey);
const bobRaw = await subtle.exportKey('raw', bobDerivedKey as CryptoKey);

expect(Buffer.from(aliceRaw as ArrayBuffer).toString('hex')).to.equal(
Buffer.from(bobRaw as ArrayBuffer).toString('hex'),
);
});
Loading
Loading