@omnituum/pqc-shared 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +543 -0
  3. package/dist/crypto/index.cjs +807 -0
  4. package/dist/crypto/index.d.cts +641 -0
  5. package/dist/crypto/index.d.ts +641 -0
  6. package/dist/crypto/index.js +716 -0
  7. package/dist/decrypt-eSHlbh1j.d.cts +321 -0
  8. package/dist/decrypt-eSHlbh1j.d.ts +321 -0
  9. package/dist/fs/index.cjs +1168 -0
  10. package/dist/fs/index.d.cts +400 -0
  11. package/dist/fs/index.d.ts +400 -0
  12. package/dist/fs/index.js +1091 -0
  13. package/dist/index.cjs +2160 -0
  14. package/dist/index.d.cts +282 -0
  15. package/dist/index.d.ts +282 -0
  16. package/dist/index.js +2031 -0
  17. package/dist/integrity-CCYjrap3.d.ts +31 -0
  18. package/dist/integrity-Dx9jukMH.d.cts +31 -0
  19. package/dist/types-61c7Q9ri.d.ts +134 -0
  20. package/dist/types-Ch0y-n7K.d.cts +134 -0
  21. package/dist/utils/index.cjs +129 -0
  22. package/dist/utils/index.d.cts +49 -0
  23. package/dist/utils/index.d.ts +49 -0
  24. package/dist/utils/index.js +114 -0
  25. package/dist/vault/index.cjs +713 -0
  26. package/dist/vault/index.d.cts +237 -0
  27. package/dist/vault/index.d.ts +237 -0
  28. package/dist/vault/index.js +677 -0
  29. package/dist/version-BygzPVGs.d.cts +55 -0
  30. package/dist/version-BygzPVGs.d.ts +55 -0
  31. package/package.json +86 -0
  32. package/src/crypto/dilithium.ts +233 -0
  33. package/src/crypto/hybrid.ts +358 -0
  34. package/src/crypto/index.ts +181 -0
  35. package/src/crypto/kyber.ts +199 -0
  36. package/src/crypto/nacl.ts +204 -0
  37. package/src/crypto/primitives/blake3.ts +141 -0
  38. package/src/crypto/primitives/chacha.ts +211 -0
  39. package/src/crypto/primitives/hkdf.ts +192 -0
  40. package/src/crypto/primitives/index.ts +54 -0
  41. package/src/crypto/primitives.ts +144 -0
  42. package/src/crypto/x25519.ts +134 -0
  43. package/src/fs/aes.ts +343 -0
  44. package/src/fs/argon2.ts +184 -0
  45. package/src/fs/browser.ts +408 -0
  46. package/src/fs/decrypt.ts +320 -0
  47. package/src/fs/encrypt.ts +324 -0
  48. package/src/fs/format.ts +425 -0
  49. package/src/fs/index.ts +144 -0
  50. package/src/fs/types.ts +304 -0
  51. package/src/index.ts +414 -0
  52. package/src/kdf/index.ts +311 -0
  53. package/src/runtime/crypto.ts +16 -0
  54. package/src/security/index.ts +345 -0
  55. package/src/tunnel/index.ts +39 -0
  56. package/src/tunnel/session.ts +229 -0
  57. package/src/tunnel/types.ts +115 -0
  58. package/src/utils/entropy.ts +128 -0
  59. package/src/utils/index.ts +25 -0
  60. package/src/utils/integrity.ts +95 -0
  61. package/src/vault/decrypt.ts +167 -0
  62. package/src/vault/encrypt.ts +207 -0
  63. package/src/vault/index.ts +71 -0
  64. package/src/vault/manager.ts +327 -0
  65. package/src/vault/migrate.ts +190 -0
  66. package/src/vault/types.ts +177 -0
  67. package/src/version.ts +304 -0
@@ -0,0 +1,358 @@
1
+ /**
2
+ * Omnituum PQC Shared - Hybrid Encryption
3
+ *
4
+ * Post-quantum secure hybrid encryption combining:
5
+ * - X25519 ECDH (classical, proven security)
6
+ * - Kyber ML-KEM-768 (post-quantum, NIST Level 3)
7
+ *
8
+ * Both key exchanges must succeed for decryption, providing
9
+ * security against both classical and quantum attacks.
10
+ */
11
+
12
+ import nacl from 'tweetnacl';
13
+ import {
14
+ rand32,
15
+ rand24,
16
+ b64,
17
+ ub64,
18
+ toHex,
19
+ fromHex,
20
+ hkdfSha256,
21
+ textEncoder,
22
+ textDecoder,
23
+ u8,
24
+ } from './primitives';
25
+ import { generateX25519Keypair } from './x25519';
26
+ import {
27
+ generateKyberKeypair,
28
+ kyberEncapsulate,
29
+ kyberDecapsulate,
30
+ isKyberAvailable,
31
+ } from './kyber';
32
+ import {
33
+ ENVELOPE_VERSION,
34
+ ENVELOPE_SUITE,
35
+ ENVELOPE_AEAD,
36
+ assertEnvelopeVersion,
37
+ } from '../version';
38
+
39
+ // ═══════════════════════════════════════════════════════════════════════════
40
+ // TYPES
41
+ // ═══════════════════════════════════════════════════════════════════════════
42
+
43
+ export interface HybridIdentity {
44
+ /** Unique identifier */
45
+ id: string;
46
+ /** Display name */
47
+ name: string;
48
+ /** X25519 public key (hex) */
49
+ x25519PubHex: string;
50
+ /** X25519 secret key (hex) - keep private! */
51
+ x25519SecHex: string;
52
+ /** Kyber public key (base64) */
53
+ kyberPubB64: string;
54
+ /** Kyber secret key (base64) - keep private! */
55
+ kyberSecB64: string;
56
+ /** Creation timestamp */
57
+ createdAt: string;
58
+ /** Last rotation timestamp */
59
+ lastRotatedAt?: string;
60
+ /** Key rotation count */
61
+ rotationCount: number;
62
+ }
63
+
64
+ export interface HybridPublicKeys {
65
+ /** X25519 public key (hex) */
66
+ x25519PubHex: string;
67
+ /** Kyber public key (base64) */
68
+ kyberPubB64: string;
69
+ }
70
+
71
+ export interface HybridSecretKeys {
72
+ /** X25519 secret key (hex) */
73
+ x25519SecHex: string;
74
+ /** Kyber secret key (base64) */
75
+ kyberSecB64: string;
76
+ }
77
+
78
+ export interface HybridEnvelope {
79
+ /** Version identifier (FROZEN - see pqc-docs/specs/envelope.v1.md) */
80
+ v: typeof ENVELOPE_VERSION;
81
+ /** Algorithm suite */
82
+ suite: typeof ENVELOPE_SUITE;
83
+ /** AEAD algorithm */
84
+ aead: typeof ENVELOPE_AEAD;
85
+ /** X25519 ephemeral public key (hex) */
86
+ x25519Epk: string;
87
+ /** X25519 wrapped content key */
88
+ x25519Wrap: {
89
+ nonce: string;
90
+ wrapped: string;
91
+ };
92
+ /** Kyber KEM ciphertext (base64) */
93
+ kyberKemCt: string;
94
+ /** Kyber wrapped content key */
95
+ kyberWrap: {
96
+ nonce: string;
97
+ wrapped: string;
98
+ };
99
+ /** Content encryption nonce (base64) */
100
+ contentNonce: string;
101
+ /** Encrypted content (base64) */
102
+ ciphertext: string;
103
+ /** Metadata */
104
+ meta: {
105
+ createdAt: string;
106
+ senderName?: string;
107
+ senderId?: string;
108
+ };
109
+ }
110
+
111
+ // ═══════════════════════════════════════════════════════════════════════════
112
+ // HELPERS
113
+ // ═══════════════════════════════════════════════════════════════════════════
114
+
115
+ function hkdfFlex(ikm: Uint8Array, salt: string, info: string): Uint8Array {
116
+ return hkdfSha256(ikm, { salt: u8(salt), info: u8(info), length: 32 });
117
+ }
118
+
119
+ function generateId(): string {
120
+ const bytes = globalThis.crypto.getRandomValues(new Uint8Array(16));
121
+ return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
122
+ }
123
+
124
+ // ═══════════════════════════════════════════════════════════════════════════
125
+ // IDENTITY GENERATION
126
+ // ═══════════════════════════════════════════════════════════════════════════
127
+
128
+ /**
129
+ * Generate a new hybrid identity with both X25519 and Kyber keys.
130
+ */
131
+ export async function generateHybridIdentity(name: string): Promise<HybridIdentity | null> {
132
+ // Generate X25519 keypair
133
+ const x25519 = generateX25519Keypair();
134
+
135
+ // Generate Kyber keypair
136
+ const kyber = await generateKyberKeypair();
137
+ if (!kyber) {
138
+ console.error('Kyber key generation failed - library not available');
139
+ return null;
140
+ }
141
+
142
+ return {
143
+ id: generateId(),
144
+ name,
145
+ x25519PubHex: x25519.publicHex,
146
+ x25519SecHex: x25519.secretHex,
147
+ kyberPubB64: kyber.publicB64,
148
+ kyberSecB64: kyber.secretB64,
149
+ createdAt: new Date().toISOString(),
150
+ rotationCount: 0,
151
+ };
152
+ }
153
+
154
+ /**
155
+ * Rotate keys for an existing identity.
156
+ */
157
+ export async function rotateHybridIdentity(identity: HybridIdentity): Promise<HybridIdentity | null> {
158
+ // Generate new X25519 keypair
159
+ const x25519 = generateX25519Keypair();
160
+
161
+ // Generate new Kyber keypair
162
+ const kyber = await generateKyberKeypair();
163
+ if (!kyber) {
164
+ return null;
165
+ }
166
+
167
+ return {
168
+ ...identity,
169
+ x25519PubHex: x25519.publicHex,
170
+ x25519SecHex: x25519.secretHex,
171
+ kyberPubB64: kyber.publicB64,
172
+ kyberSecB64: kyber.secretB64,
173
+ lastRotatedAt: new Date().toISOString(),
174
+ rotationCount: identity.rotationCount + 1,
175
+ };
176
+ }
177
+
178
+ /**
179
+ * Extract public keys from identity.
180
+ */
181
+ export function getPublicKeys(identity: HybridIdentity): HybridPublicKeys {
182
+ return {
183
+ x25519PubHex: identity.x25519PubHex,
184
+ kyberPubB64: identity.kyberPubB64,
185
+ };
186
+ }
187
+
188
+ /**
189
+ * Extract secret keys from identity.
190
+ */
191
+ export function getSecretKeys(identity: HybridIdentity): HybridSecretKeys {
192
+ return {
193
+ x25519SecHex: identity.x25519SecHex,
194
+ kyberSecB64: identity.kyberSecB64,
195
+ };
196
+ }
197
+
198
+ // ═══════════════════════════════════════════════════════════════════════════
199
+ // ENCRYPTION
200
+ // ═══════════════════════════════════════════════════════════════════════════
201
+
202
+ /**
203
+ * Encrypt a message using hybrid X25519 + Kyber encryption.
204
+ *
205
+ * @param plaintext - Message to encrypt (string or bytes)
206
+ * @param recipientPublicKeys - Recipient's public keys
207
+ * @param sender - Optional sender identity for metadata
208
+ */
209
+ export async function hybridEncrypt(
210
+ plaintext: string | Uint8Array,
211
+ recipientPublicKeys: HybridPublicKeys,
212
+ sender?: { name?: string; id?: string }
213
+ ): Promise<HybridEnvelope> {
214
+ const pt = typeof plaintext === 'string' ? textEncoder.encode(plaintext) : plaintext;
215
+
216
+ // 1. Generate random content key (32 bytes)
217
+ const CK = rand32();
218
+
219
+ // 2. Encrypt content with content key
220
+ const contentNonce = rand24();
221
+ const ciphertext = nacl.secretbox(pt, contentNonce, CK);
222
+
223
+ // 3. Wrap content key with X25519 ECDH
224
+ const x25519EphKp = nacl.box.keyPair();
225
+ const recipientX25519Pk = fromHex(recipientPublicKeys.x25519PubHex);
226
+
227
+ const x25519Shared = nacl.scalarMult(x25519EphKp.secretKey, recipientX25519Pk);
228
+ const x25519Kek = hkdfFlex(x25519Shared, 'omnituum/x25519', 'wrap-ck');
229
+ const x25519WrapNonce = rand24();
230
+ const x25519Wrapped = nacl.secretbox(CK, x25519WrapNonce, x25519Kek);
231
+
232
+ // 4. Wrap content key with Kyber KEM
233
+ const kyberResult = await kyberEncapsulate(recipientPublicKeys.kyberPubB64);
234
+ const kyberKek = hkdfFlex(kyberResult.sharedSecret, 'omnituum/kyber', 'wrap-ck');
235
+ const kyberWrapNonce = rand24();
236
+ const kyberWrapped = nacl.secretbox(CK, kyberWrapNonce, kyberKek);
237
+
238
+ return {
239
+ v: ENVELOPE_VERSION,
240
+ suite: ENVELOPE_SUITE,
241
+ aead: ENVELOPE_AEAD,
242
+ x25519Epk: toHex(x25519EphKp.publicKey),
243
+ x25519Wrap: {
244
+ nonce: b64(x25519WrapNonce),
245
+ wrapped: b64(x25519Wrapped),
246
+ },
247
+ kyberKemCt: b64(kyberResult.ciphertext),
248
+ kyberWrap: {
249
+ nonce: b64(kyberWrapNonce),
250
+ wrapped: b64(kyberWrapped),
251
+ },
252
+ contentNonce: b64(contentNonce),
253
+ ciphertext: b64(ciphertext),
254
+ meta: {
255
+ createdAt: new Date().toISOString(),
256
+ senderName: sender?.name,
257
+ senderId: sender?.id,
258
+ },
259
+ };
260
+ }
261
+
262
+ // ═══════════════════════════════════════════════════════════════════════════
263
+ // DECRYPTION
264
+ // ═══════════════════════════════════════════════════════════════════════════
265
+
266
+ /**
267
+ * Decrypt a hybrid envelope.
268
+ *
269
+ * Tries Kyber first (post-quantum), falls back to X25519 (classical).
270
+ * Both must be valid for the envelope to be considered secure.
271
+ *
272
+ * @param envelope - Encrypted envelope
273
+ * @param secretKeys - Recipient's secret keys
274
+ * @returns Decrypted plaintext as bytes
275
+ */
276
+ export async function hybridDecrypt(
277
+ envelope: HybridEnvelope,
278
+ secretKeys: HybridSecretKeys
279
+ ): Promise<Uint8Array> {
280
+ // Validate envelope version (throws VersionMismatchError if unsupported)
281
+ const version = envelope.v as string;
282
+ assertEnvelopeVersion(version);
283
+
284
+ let CK: Uint8Array | null = null;
285
+
286
+ // Determine HKDF salt based on envelope version
287
+ const saltPrefix = version === 'pqc-demo.hybrid.v1' ? 'pqc-demo' : 'omnituum';
288
+
289
+ // Try Kyber decapsulation first (post-quantum)
290
+ try {
291
+ const kyberShared = await kyberDecapsulate(envelope.kyberKemCt, secretKeys.kyberSecB64);
292
+ const kyberKek = hkdfFlex(kyberShared, `${saltPrefix}/kyber`, 'wrap-ck');
293
+ CK = nacl.secretbox.open(
294
+ ub64(envelope.kyberWrap.wrapped),
295
+ ub64(envelope.kyberWrap.nonce),
296
+ kyberKek
297
+ );
298
+ if (CK) {
299
+ console.log('[Hybrid] Decrypted using Kyber (post-quantum)');
300
+ }
301
+ } catch (e) {
302
+ console.warn('[Hybrid] Kyber decapsulation failed:', e);
303
+ }
304
+
305
+ // Try X25519 if Kyber failed
306
+ if (!CK) {
307
+ try {
308
+ const ephPk = fromHex(envelope.x25519Epk);
309
+ const sk = fromHex(secretKeys.x25519SecHex);
310
+ const x25519Shared = nacl.scalarMult(sk, ephPk);
311
+ const x25519Kek = hkdfFlex(x25519Shared, `${saltPrefix}/x25519`, 'wrap-ck');
312
+ CK = nacl.secretbox.open(
313
+ ub64(envelope.x25519Wrap.wrapped),
314
+ ub64(envelope.x25519Wrap.nonce),
315
+ x25519Kek
316
+ );
317
+ if (CK) {
318
+ console.log('[Hybrid] Decrypted using X25519 (classical)');
319
+ }
320
+ } catch (e) {
321
+ console.warn('[Hybrid] X25519 decryption failed:', e);
322
+ }
323
+ }
324
+
325
+ if (!CK) {
326
+ throw new Error('Could not unwrap content key with either algorithm');
327
+ }
328
+
329
+ // Decrypt content
330
+ const pt = nacl.secretbox.open(
331
+ ub64(envelope.ciphertext),
332
+ ub64(envelope.contentNonce),
333
+ CK
334
+ );
335
+
336
+ if (!pt) {
337
+ throw new Error('Content authentication failed');
338
+ }
339
+
340
+ return pt;
341
+ }
342
+
343
+ /**
344
+ * Decrypt and decode as UTF-8 string.
345
+ */
346
+ export async function hybridDecryptToString(
347
+ envelope: HybridEnvelope,
348
+ secretKeys: HybridSecretKeys
349
+ ): Promise<string> {
350
+ const pt = await hybridDecrypt(envelope, secretKeys);
351
+ return textDecoder.decode(pt);
352
+ }
353
+
354
+ // ═══════════════════════════════════════════════════════════════════════════
355
+ // UTILITY EXPORTS
356
+ // ═══════════════════════════════════════════════════════════════════════════
357
+
358
+ export { isKyberAvailable };
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Omnituum PQC Shared - Crypto Exports
3
+ *
4
+ * Unified cryptographic primitives for both Loggie and Omnituum.
5
+ * Browser-compatible, no Node.js dependencies.
6
+ */
7
+
8
+ // ═══════════════════════════════════════════════════════════════════════════
9
+ // PRIMITIVES
10
+ // ═══════════════════════════════════════════════════════════════════════════
11
+
12
+ export {
13
+ textEncoder,
14
+ textDecoder,
15
+ toB64,
16
+ fromB64,
17
+ toHex,
18
+ fromHex,
19
+ b64,
20
+ ub64,
21
+ assertLen,
22
+ rand32,
23
+ rand24,
24
+ rand12,
25
+ randN,
26
+ sha256,
27
+ sha256String,
28
+ hkdfSha256,
29
+ u8,
30
+ } from './primitives';
31
+
32
+ // ═══════════════════════════════════════════════════════════════════════════
33
+ // NaCl SECRETBOX / BOX
34
+ // ═══════════════════════════════════════════════════════════════════════════
35
+
36
+ export type { SecretboxPayload, BoxPayload } from './nacl';
37
+
38
+ export {
39
+ secretboxEncrypt,
40
+ secretboxEncryptString,
41
+ secretboxDecrypt,
42
+ secretboxDecryptString,
43
+ secretboxRaw,
44
+ secretboxOpenRaw,
45
+ boxEncrypt,
46
+ boxDecrypt,
47
+ SECRETBOX_KEY_SIZE,
48
+ SECRETBOX_NONCE_SIZE,
49
+ SECRETBOX_OVERHEAD,
50
+ BOX_KEY_SIZE,
51
+ BOX_NONCE_SIZE,
52
+ } from './nacl';
53
+
54
+ // ═══════════════════════════════════════════════════════════════════════════
55
+ // X25519 (Classical ECDH)
56
+ // ═══════════════════════════════════════════════════════════════════════════
57
+
58
+ export type {
59
+ X25519Keypair,
60
+ X25519KeypairHex,
61
+ ClassicalWrap,
62
+ } from './x25519';
63
+
64
+ export {
65
+ generateX25519Keypair,
66
+ generateX25519KeypairFromSeed,
67
+ boxWrapWithX25519,
68
+ boxUnwrapWithX25519,
69
+ x25519SharedSecret,
70
+ deriveKeyFromShared,
71
+ } from './x25519';
72
+
73
+ // ═══════════════════════════════════════════════════════════════════════════
74
+ // KYBER ML-KEM-768 (Post-Quantum KEM)
75
+ // ═══════════════════════════════════════════════════════════════════════════
76
+
77
+ export type {
78
+ KyberKeypair,
79
+ KyberKeypairB64,
80
+ KyberEncapsulation,
81
+ } from './kyber';
82
+
83
+ export {
84
+ isKyberAvailable,
85
+ generateKyberKeypair,
86
+ kyberEncapsulate,
87
+ kyberDecapsulate,
88
+ kyberWrapKey,
89
+ kyberUnwrapKey,
90
+ } from './kyber';
91
+
92
+ // ═══════════════════════════════════════════════════════════════════════════
93
+ // DILITHIUM ML-DSA-65 (Post-Quantum Signatures)
94
+ // ═══════════════════════════════════════════════════════════════════════════
95
+
96
+ export type {
97
+ DilithiumKeypair,
98
+ DilithiumKeypairB64,
99
+ DilithiumSignature,
100
+ } from './dilithium';
101
+
102
+ export {
103
+ isDilithiumAvailable,
104
+ generateDilithiumKeypair,
105
+ generateDilithiumKeypairFromSeed,
106
+ dilithiumSign,
107
+ dilithiumSignRaw,
108
+ dilithiumVerify,
109
+ dilithiumVerifyRaw,
110
+ DILITHIUM_PUBLIC_KEY_SIZE,
111
+ DILITHIUM_SECRET_KEY_SIZE,
112
+ DILITHIUM_SIGNATURE_SIZE,
113
+ DILITHIUM_ALGORITHM,
114
+ } from './dilithium';
115
+
116
+ // ═══════════════════════════════════════════════════════════════════════════
117
+ // HYBRID ENCRYPTION (X25519 + Kyber768)
118
+ // ═══════════════════════════════════════════════════════════════════════════
119
+
120
+ export type {
121
+ HybridIdentity,
122
+ HybridPublicKeys,
123
+ HybridSecretKeys,
124
+ HybridEnvelope,
125
+ } from './hybrid';
126
+
127
+ export {
128
+ generateHybridIdentity,
129
+ rotateHybridIdentity,
130
+ getPublicKeys,
131
+ getSecretKeys,
132
+ hybridEncrypt,
133
+ hybridDecrypt,
134
+ hybridDecryptToString,
135
+ } from './hybrid';
136
+
137
+ // ═══════════════════════════════════════════════════════════════════════════
138
+ // PRIMITIVES (for protocol implementations like noise-kyber)
139
+ // ═══════════════════════════════════════════════════════════════════════════
140
+
141
+ // BLAKE3 hash function
142
+ export {
143
+ blake3,
144
+ blake3Hex,
145
+ blake3Mac,
146
+ blake3DeriveKey,
147
+ BLAKE3_OUTPUT_LENGTH,
148
+ BLAKE3_KEY_LENGTH,
149
+ BLAKE3_BLOCK_SIZE,
150
+ type Blake3Options,
151
+ } from './primitives/blake3';
152
+
153
+ // ChaCha20-Poly1305 AEAD
154
+ export {
155
+ chaCha20Poly1305Encrypt,
156
+ chaCha20Poly1305Decrypt,
157
+ xChaCha20Poly1305Encrypt,
158
+ xChaCha20Poly1305Decrypt,
159
+ createChaCha20Poly1305,
160
+ createXChaCha20Poly1305,
161
+ CHACHA20_KEY_SIZE,
162
+ CHACHA20_NONCE_SIZE,
163
+ XCHACHA20_NONCE_SIZE,
164
+ POLY1305_TAG_SIZE,
165
+ type ChaChaPayload,
166
+ } from './primitives/chacha';
167
+
168
+ // HKDF key derivation
169
+ export {
170
+ hkdfDerive,
171
+ hkdfExtract,
172
+ hkdfExpand,
173
+ hkdfSplitForNoise,
174
+ hkdfTripleSplitForNoise,
175
+ HKDF_SHA256_OUTPUT_SIZE,
176
+ HKDF_SHA512_OUTPUT_SIZE,
177
+ HKDF_SHA256_MAX_OUTPUT,
178
+ HKDF_SHA512_MAX_OUTPUT,
179
+ type HkdfHash,
180
+ type HkdfOptions,
181
+ } from './primitives/hkdf';