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
10 changes: 10 additions & 0 deletions .claude/rules/code-cpp.xml
Original file line number Diff line number Diff line change
Expand Up @@ -185,4 +185,14 @@
<setting>Link against OpenSSL 3.3+</setting>
</settings>
</rule>

<rule severity="HIGH" enforcement="STRICT">
<name>Code Formatting</name>
<description>Run clang-format on all C++ files before committing</description>
<requirements>
<requirement>Run clang-format -i on all modified .cpp/.hpp/.h files</requirement>
<requirement>Pre-commit hook enforces clang-format compliance</requirement>
</requirements>
<command>clang-format -i path/to/file.cpp</command>
</rule>
</rules>
29 changes: 6 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

A fast implementation of Node's `crypto` module.

> Note: This version `1.x` is undergoing a major refactor, porting to New Architecture, Bridgeless, and [`Nitro Modules`](https://github.com/mrousavy/react-native-nitro) and is incomplete compared to the `0.x` version. Status, as always, will be represented in [implementation-coverage.md](../main/docs/implementation-coverage.md).
> Note: This version `1.x` completed a major refactor, porting to OpenSSL 3.3+, New Architecture, Bridgeless, and [`Nitro Modules`](https://github.com/mrousavy/react-native-nitro). It should be at or above feature-parity compared to the `0.x` version. Status, as always, will be represented in [implementation-coverage.md](../main/docs/implementation-coverage.md).

> Note: Minimum supported version of React Native is `0.75`. If you need to use earlier versions, please use `0.x` versions of this library.

Expand All @@ -33,28 +33,13 @@ QuickCrypto can be used as a drop-in replacement for your Web3/Crypto apps to sp
| `1.x` | new [->](https://github.com/reactwg/react-native-new-architecture/blob/main/docs/enable-apps.md) | Nitro Modules [->](https://github.com/mrousavy/nitro) |
| `0.x` | old, new 🤞 | Bridge & JSI |

## Benchmarks

For example, creating a Wallet using ethers.js uses complex algorithms to generate a private-key/mnemonic-phrase pair:

```ts
const start = performance.now();
const wallet = ethers.Wallet.createRandom();
const end = performance.now();
console.log(`Creating a Wallet took ${end - start} ms.`);
```

**Without** react-native-quick-crypto 🐢:
## Migration

```
Creating a Wallet took 16862 ms
```
Our goal in refactoring to v1.0 was to maintain API compatibility. If you are upgrading to v1.0 from v0.x, and find any discrepancies, please open an issue in this repo.

**With** react-native-quick-crypto ⚡️:
## Benchmarks

```
Creating a Wallet took 289 ms
```
There is a benchmark suite in the Example app in this repo that has benchmarks of algorithms against their pure JS counterparts. This is not meant to disparage the other libraries. On the contrary, they perform amazingly well when used in a server-side Node environment. This library exists because React Native does not have that environment nor the Node Crypto API implementation at hand. So the benchmark suite is there to show you the speedup vs. the alternative of using a pure JS library on React Native.

---

Expand Down Expand Up @@ -154,8 +139,6 @@ const hashed = QuickCrypto.createHash('sha256')

## Limitations

As the library uses JSI for synchronous native methods access, remote debugging (e.g. with Chrome) is no longer possible. Instead, you should use [Flipper](https://fbflipper.com).

Not all cryptographic algorithms are supported yet. See the [implementation coverage](./docs/implementation-coverage.md) document for more details. If you need a specific algorithm, please open a `feature request` issue and we'll see what we can do.

## Community Discord
Expand All @@ -164,7 +147,7 @@ Not all cryptographic algorithms are supported yet. See the [implementation cove

## Adopting at scale

react-native-quick-crypto was built at Margelo, an elite app development agency. For enterprise support or other business inquiries, contact us at <a href="mailto:[email protected]?subject=Adopting react-native-quick-crypto at scale">[email protected]</a>!
`react-native-quick-crypto` was built at Margelo, an elite app development agency. For enterprise support or other business inquiries, contact us at <a href="mailto:[email protected]?subject=Adopting react-native-quick-crypto at scale">[email protected]</a>!

## Contributing

Expand Down
75 changes: 0 additions & 75 deletions docs/VERSION-1.0.0.md

This file was deleted.

10 changes: 5 additions & 5 deletions docs/implementation-coverage.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ This document attempts to describe the implementation status of Crypto APIs/Inte
* ❌ `crypto.hkdfSync(digest, ikm, salt, info, keylen)`
* ✅ `crypto.pbkdf2(password, salt, iterations, keylen, digest, callback)`
* ✅ `crypto.pbkdf2Sync(password, salt, iterations, keylen, digest)`
* `crypto.privateDecrypt(privateKey, buffer)`
* `crypto.privateEncrypt(privateKey, buffer)`
* `crypto.privateDecrypt(privateKey, buffer)`
* `crypto.privateEncrypt(privateKey, buffer)`
* ✅ `crypto.publicDecrypt(key, buffer)`
* ✅ `crypto.publicEncrypt(key, buffer)`
* ✅ `crypto.randomBytes(size[, callback])`
Expand Down Expand Up @@ -185,10 +185,10 @@ This document attempts to describe the implementation status of Crypto APIs/Inte
## `crypto.generateKeyPairSync`
| type | Status |
| --------- | :----: |
| `rsa` | |
| `rsa-pss` | |
| `rsa` | |
| `rsa-pss` | |
| `dsa` | ❌ |
| `ec` | |
| `ec` | |
| `ed25519` | ✅ |
| `ed448` | ✅ |
| `x25519` | ✅ |
Expand Down
147 changes: 134 additions & 13 deletions example/src/tests/keys/generate_keypair.ts
Original file line number Diff line number Diff line change
Expand Up @@ -502,16 +502,137 @@ test(
},
);

test(
SUITE,
'generateKeyPairSync RSA throws (not implemented sync for RSA)',
async () => {
await assertThrowsAsync(async () => {
generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
});
}, '');
},
);
// --- generateKeyPairSync RSA Tests ---

test(SUITE, 'generateKeyPairSync RSA 2048-bit with PEM encoding', () => {
const { privateKey, publicKey } = generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
});

expect(typeof privateKey).to.equal('string');
expect(typeof publicKey).to.equal('string');
expect(privateKey).to.match(/^-----BEGIN PRIVATE KEY-----/);
expect(publicKey).to.match(/^-----BEGIN PUBLIC KEY-----/);
});

test(SUITE, 'generateKeyPairSync RSA with DER encoding', () => {
const { privateKey, publicKey } = generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: { type: 'spki', format: 'der' },
privateKeyEncoding: { type: 'pkcs8', format: 'der' },
});

expect(privateKey instanceof ArrayBuffer).to.equal(true);
expect(publicKey instanceof ArrayBuffer).to.equal(true);
expect((privateKey as ArrayBuffer).byteLength).to.be.greaterThan(0);
expect((publicKey as ArrayBuffer).byteLength).to.be.greaterThan(0);
});

test(SUITE, 'generateKeyPairSync RSA keys work for signing', () => {
const { privateKey, publicKey } = generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
});

const testData = 'Test data for sync RSA signing';
const signature = createSign('SHA256')
.update(testData)
.sign(privateKey as string);
const isValid = createVerify('SHA256')
.update(testData)
.verify(publicKey as string, signature);

expect(isValid).to.equal(true);
});

test(SUITE, 'generateKeyPairSync RSA-PSS', () => {
const { privateKey, publicKey } = generateKeyPairSync('rsa-pss', {
modulusLength: 2048,
hashAlgorithm: 'SHA-256',
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
});

expect(typeof privateKey).to.equal('string');
expect(typeof publicKey).to.equal('string');
expect(privateKey).to.match(/^-----BEGIN PRIVATE KEY-----/);
expect(publicKey).to.match(/^-----BEGIN PUBLIC KEY-----/);
});

// --- generateKeyPairSync EC Tests ---

test(SUITE, 'generateKeyPairSync EC P-256', () => {
const { privateKey, publicKey } = generateKeyPairSync('ec', {
namedCurve: 'P-256',
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
});

expect(typeof privateKey).to.equal('string');
expect(typeof publicKey).to.equal('string');
expect(privateKey).to.match(/^-----BEGIN PRIVATE KEY-----/);
expect(publicKey).to.match(/^-----BEGIN PUBLIC KEY-----/);

const key = createPrivateKey(privateKey as string);
expect(key.asymmetricKeyType).to.equal('ec');
});

test(SUITE, 'generateKeyPairSync EC P-384', () => {
const { privateKey, publicKey } = generateKeyPairSync('ec', {
namedCurve: 'P-384',
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
});

expect(typeof privateKey).to.equal('string');
expect(typeof publicKey).to.equal('string');
expect(privateKey).to.match(/^-----BEGIN PRIVATE KEY-----/);
expect(publicKey).to.match(/^-----BEGIN PUBLIC KEY-----/);
});

test(SUITE, 'generateKeyPairSync EC P-521', () => {
const { privateKey, publicKey } = generateKeyPairSync('ec', {
namedCurve: 'P-521',
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
});

expect(typeof privateKey).to.equal('string');
expect(typeof publicKey).to.equal('string');
expect(privateKey).to.match(/^-----BEGIN PRIVATE KEY-----/);
expect(publicKey).to.match(/^-----BEGIN PUBLIC KEY-----/);
});

test(SUITE, 'generateKeyPairSync EC keys work for signing', () => {
const { privateKey, publicKey } = generateKeyPairSync('ec', {
namedCurve: 'P-256',
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
});

const testData = 'Test data for sync ECDSA signing';
const signature = createSign('SHA256')
.update(testData)
.sign(privateKey as string);
const isValid = createVerify('SHA256')
.update(testData)
.verify(publicKey as string, signature);

expect(isValid).to.equal(true);
});

test(SUITE, 'generateKeyPairSync EC with DER encoding', () => {
const { privateKey, publicKey } = generateKeyPairSync('ec', {
namedCurve: 'P-256',
publicKeyEncoding: { type: 'spki', format: 'der' },
privateKeyEncoding: { type: 'pkcs8', format: 'der' },
});

expect(privateKey instanceof ArrayBuffer).to.equal(true);
expect(publicKey instanceof ArrayBuffer).to.equal(true);
expect((privateKey as ArrayBuffer).byteLength).to.be.greaterThan(0);
expect((publicKey as ArrayBuffer).byteLength).to.be.greaterThan(0);
});
Loading