Skip to content

Commit 750767b

Browse files
authored
docs: add missing API pages, llms.txt endpoints, and review fixes (#956)
1 parent df27591 commit 750767b

25 files changed

Lines changed: 1830 additions & 58 deletions

docs/app/llms-full.txt/route.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,7 @@ export async function GET() {
66
const scan = source.getPages().map(getLLMText);
77
const scanned = await Promise.all(scan);
88

9-
return new Response(scanned.join('\n\n'));
9+
return new Response(scanned.join('\n\n'), {
10+
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
11+
});
1012
}

docs/app/llms.txt/route.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { basePath } from '@/lib/basePath';
2+
import { getLLMSummary, source } from '@/lib/source';
3+
4+
export const revalidate = false;
5+
6+
const origin = 'https://margelo.github.io';
7+
8+
export async function GET() {
9+
const pages = source.getPages().map(getLLMSummary);
10+
11+
const lines = [
12+
'# React Native Quick Crypto',
13+
'',
14+
'> Drop-in replacement for Node.js crypto on React Native, powered by OpenSSL 3.6+ and Nitro Modules.',
15+
'',
16+
'Documentation for the react-native-quick-crypto library.',
17+
'',
18+
'## Docs',
19+
'',
20+
...pages.map(
21+
p => `- [${p.title}](${origin}${basePath}${p.url}): ${p.description}`,
22+
),
23+
];
24+
25+
return new Response(lines.join('\n'), {
26+
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
27+
});
28+
}

docs/components/SiteUrl.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
'use client';
2+
3+
import { basePath } from '@/lib/basePath';
4+
import { useEffect, useState } from 'react';
5+
6+
export function SiteUrl({ path }: { path: string }) {
7+
const [origin, setOrigin] = useState('https://margelo.github.io');
8+
9+
useEffect(() => {
10+
setOrigin(window.location.origin);
11+
}, []);
12+
13+
const url = `${origin}${basePath}${path}`;
14+
15+
return <code>{url}</code>;
16+
}

docs/content/docs/api/argon2.mdx

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
---
2+
title: Argon2
3+
description: Memory-hard password hashing (PHC winner)
4+
---
5+
6+
import { Callout } from 'fumadocs-ui/components/callout';
7+
import { TypeTable } from 'fumadocs-ui/components/type-table';
8+
9+
**Argon2** is the winner of the Password Hashing Competition (PHC) and the recommended algorithm for password hashing and key derivation. It is designed to resist GPU, ASIC, and side-channel attacks.
10+
11+
<Callout type="info" title="When to use Argon2 vs PBKDF2/Scrypt">
12+
**Argon2** is the modern choice for password hashing. Use **PBKDF2** only for FIPS compliance, and **scrypt** when Argon2 is unavailable. Argon2 offers the best resistance to hardware-accelerated attacks.
13+
</Callout>
14+
15+
## Table of Contents
16+
17+
- [Theory](#theory)
18+
- [Variants](#variants)
19+
- [Module Methods](#module-methods)
20+
- [WebCrypto API](#webcrypto-api)
21+
- [Real-World Examples](#real-world-examples)
22+
23+
## Theory
24+
25+
Argon2 fills a large block of memory with pseudorandom data derived from the password and salt, then performs multiple passes over it. This makes brute-force attacks expensive on both time and memory dimensions.
26+
27+
Key parameters:
28+
29+
| Parameter | Description | Typical Value |
30+
|:----------|:-----------|:-------------|
31+
| `memoryCost` | Memory in KiB | 65536 (64 MB) |
32+
| `timeCost` | Number of passes | 3 |
33+
| `parallelism` | Parallel threads | 4 |
34+
35+
## Variants
36+
37+
| Variant | Best For | Properties |
38+
|:--------|:---------|:-----------|
39+
| `argon2d` | Cryptocurrency mining | Fastest, data-dependent memory access (vulnerable to side-channel) |
40+
| `argon2i` | Password hashing | Data-independent memory access (side-channel resistant) |
41+
| `argon2id` | **Recommended default** | Hybrid of argon2d and argon2i |
42+
43+
---
44+
45+
## Module Methods
46+
47+
### argon2(algorithm, parameters, callback)
48+
49+
Asynchronous Argon2 hashing.
50+
51+
**Parameters:**
52+
53+
<TypeTable
54+
type={{
55+
algorithm: { description: '"argon2d", "argon2i", or "argon2id".', type: 'string' },
56+
parameters: { description: 'Configuration object.', type: 'Object' },
57+
'parameters.pass': { description: 'Password to hash.', type: 'Buffer | string' },
58+
'parameters.salt': { description: 'Salt (16+ bytes recommended).', type: 'Buffer' },
59+
'parameters.memoryCost': { description: 'Memory in KiB.', type: 'number' },
60+
'parameters.timeCost': { description: 'Number of iterations.', type: 'number' },
61+
'parameters.parallelism': { description: 'Degree of parallelism.', type: 'number' },
62+
'parameters.hashLength': { description: 'Output length in bytes (default: 32).', type: 'number' },
63+
callback: { description: '`(err, hash)`.', type: 'Function' }
64+
}}
65+
/>
66+
67+
**Example:**
68+
69+
```ts
70+
import { argon2, randomBytes } from 'react-native-quick-crypto';
71+
72+
const password = 'user-password';
73+
const salt = randomBytes(16);
74+
75+
argon2('argon2id', {
76+
pass: password,
77+
salt,
78+
memoryCost: 65536, // 64 MB
79+
timeCost: 3,
80+
parallelism: 4,
81+
hashLength: 32
82+
}, (err, hash) => {
83+
if (err) throw err;
84+
console.log(hash.toString('hex'));
85+
});
86+
```
87+
88+
### argon2Sync(algorithm, parameters)
89+
90+
Synchronous version. Returns `Buffer`.
91+
92+
<Callout type="warn">
93+
The synchronous version blocks the JS thread. Use the async version for UI-facing operations.
94+
</Callout>
95+
96+
```ts
97+
import { argon2Sync, randomBytes } from 'react-native-quick-crypto';
98+
99+
const hash = argon2Sync('argon2id', {
100+
pass: 'password',
101+
salt: randomBytes(16),
102+
memoryCost: 65536,
103+
timeCost: 3,
104+
parallelism: 4,
105+
hashLength: 32
106+
});
107+
```
108+
109+
---
110+
111+
## WebCrypto API
112+
113+
Argon2 is available through `subtle.deriveBits()` and `subtle.deriveKey()`.
114+
115+
### Import a Password Key
116+
117+
```ts
118+
import { subtle } from 'react-native-quick-crypto';
119+
120+
const passwordKey = await subtle.importKey(
121+
'raw-secret',
122+
new TextEncoder().encode('user-password'),
123+
{ name: 'Argon2id' },
124+
false,
125+
['deriveBits', 'deriveKey']
126+
);
127+
```
128+
129+
### deriveBits
130+
131+
```ts
132+
const salt = crypto.getRandomValues(new Uint8Array(16));
133+
134+
const bits = await subtle.deriveBits(
135+
{
136+
name: 'Argon2id',
137+
salt,
138+
memoryCost: 65536,
139+
timeCost: 3,
140+
parallelism: 4
141+
},
142+
passwordKey,
143+
256 // bits
144+
);
145+
```
146+
147+
### deriveKey
148+
149+
Derive an AES key directly from a password:
150+
151+
```ts
152+
const aesKey = await subtle.deriveKey(
153+
{
154+
name: 'Argon2id',
155+
salt: crypto.getRandomValues(new Uint8Array(16)),
156+
memoryCost: 65536,
157+
timeCost: 3,
158+
parallelism: 4
159+
},
160+
passwordKey,
161+
{ name: 'AES-GCM', length: 256 },
162+
true,
163+
['encrypt', 'decrypt']
164+
);
165+
```
166+
167+
---
168+
169+
## Real-World Examples
170+
171+
### Secure Password Storage
172+
173+
```ts
174+
import { argon2, randomBytes, timingSafeEqual } from 'react-native-quick-crypto';
175+
176+
interface StoredPassword {
177+
hash: string;
178+
salt: string;
179+
algorithm: string;
180+
memoryCost: number;
181+
timeCost: number;
182+
parallelism: number;
183+
}
184+
185+
function hashPassword(password: string): Promise<StoredPassword> {
186+
return new Promise((resolve, reject) => {
187+
const salt = randomBytes(16);
188+
const params = {
189+
pass: password,
190+
salt,
191+
memoryCost: 65536,
192+
timeCost: 3,
193+
parallelism: 4,
194+
hashLength: 32
195+
};
196+
197+
argon2('argon2id', params, (err, hash) => {
198+
if (err) return reject(err);
199+
resolve({
200+
hash: hash.toString('hex'),
201+
salt: salt.toString('hex'),
202+
algorithm: 'argon2id',
203+
memoryCost: 65536,
204+
timeCost: 3,
205+
parallelism: 4
206+
});
207+
});
208+
});
209+
}
210+
211+
function verifyPassword(password: string, stored: StoredPassword): Promise<boolean> {
212+
return new Promise((resolve, reject) => {
213+
argon2(stored.algorithm, {
214+
pass: password,
215+
salt: Buffer.from(stored.salt, 'hex'),
216+
memoryCost: stored.memoryCost,
217+
timeCost: stored.timeCost,
218+
parallelism: stored.parallelism,
219+
hashLength: 32
220+
}, (err, hash) => {
221+
if (err) return reject(err);
222+
const expected = Buffer.from(stored.hash, 'hex');
223+
resolve(timingSafeEqual(hash, expected));
224+
});
225+
});
226+
}
227+
```
228+
229+
### Deriving an Encryption Key from a Password
230+
231+
```ts
232+
import {
233+
argon2Sync,
234+
randomBytes,
235+
createCipheriv,
236+
createDecipheriv
237+
} from 'react-native-quick-crypto';
238+
239+
function encryptWithPassword(plaintext: string, password: string) {
240+
const salt = randomBytes(16);
241+
const iv = randomBytes(12);
242+
243+
const key = argon2Sync('argon2id', {
244+
pass: password,
245+
salt,
246+
memoryCost: 65536,
247+
timeCost: 3,
248+
parallelism: 4,
249+
hashLength: 32
250+
});
251+
252+
const cipher = createCipheriv('aes-256-gcm', key, iv);
253+
let encrypted = cipher.update(plaintext, 'utf8', 'base64');
254+
encrypted += cipher.final('base64');
255+
const tag = cipher.getAuthTag();
256+
257+
return {
258+
encrypted,
259+
salt: salt.toString('base64'),
260+
iv: iv.toString('base64'),
261+
tag: tag.toString('base64')
262+
};
263+
}
264+
```

0 commit comments

Comments
 (0)