@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.
- package/LICENSE +22 -0
- package/README.md +543 -0
- package/dist/crypto/index.cjs +807 -0
- package/dist/crypto/index.d.cts +641 -0
- package/dist/crypto/index.d.ts +641 -0
- package/dist/crypto/index.js +716 -0
- package/dist/decrypt-eSHlbh1j.d.cts +321 -0
- package/dist/decrypt-eSHlbh1j.d.ts +321 -0
- package/dist/fs/index.cjs +1168 -0
- package/dist/fs/index.d.cts +400 -0
- package/dist/fs/index.d.ts +400 -0
- package/dist/fs/index.js +1091 -0
- package/dist/index.cjs +2160 -0
- package/dist/index.d.cts +282 -0
- package/dist/index.d.ts +282 -0
- package/dist/index.js +2031 -0
- package/dist/integrity-CCYjrap3.d.ts +31 -0
- package/dist/integrity-Dx9jukMH.d.cts +31 -0
- package/dist/types-61c7Q9ri.d.ts +134 -0
- package/dist/types-Ch0y-n7K.d.cts +134 -0
- package/dist/utils/index.cjs +129 -0
- package/dist/utils/index.d.cts +49 -0
- package/dist/utils/index.d.ts +49 -0
- package/dist/utils/index.js +114 -0
- package/dist/vault/index.cjs +713 -0
- package/dist/vault/index.d.cts +237 -0
- package/dist/vault/index.d.ts +237 -0
- package/dist/vault/index.js +677 -0
- package/dist/version-BygzPVGs.d.cts +55 -0
- package/dist/version-BygzPVGs.d.ts +55 -0
- package/package.json +86 -0
- package/src/crypto/dilithium.ts +233 -0
- package/src/crypto/hybrid.ts +358 -0
- package/src/crypto/index.ts +181 -0
- package/src/crypto/kyber.ts +199 -0
- package/src/crypto/nacl.ts +204 -0
- package/src/crypto/primitives/blake3.ts +141 -0
- package/src/crypto/primitives/chacha.ts +211 -0
- package/src/crypto/primitives/hkdf.ts +192 -0
- package/src/crypto/primitives/index.ts +54 -0
- package/src/crypto/primitives.ts +144 -0
- package/src/crypto/x25519.ts +134 -0
- package/src/fs/aes.ts +343 -0
- package/src/fs/argon2.ts +184 -0
- package/src/fs/browser.ts +408 -0
- package/src/fs/decrypt.ts +320 -0
- package/src/fs/encrypt.ts +324 -0
- package/src/fs/format.ts +425 -0
- package/src/fs/index.ts +144 -0
- package/src/fs/types.ts +304 -0
- package/src/index.ts +414 -0
- package/src/kdf/index.ts +311 -0
- package/src/runtime/crypto.ts +16 -0
- package/src/security/index.ts +345 -0
- package/src/tunnel/index.ts +39 -0
- package/src/tunnel/session.ts +229 -0
- package/src/tunnel/types.ts +115 -0
- package/src/utils/entropy.ts +128 -0
- package/src/utils/index.ts +25 -0
- package/src/utils/integrity.ts +95 -0
- package/src/vault/decrypt.ts +167 -0
- package/src/vault/encrypt.ts +207 -0
- package/src/vault/index.ts +71 -0
- package/src/vault/manager.ts +327 -0
- package/src/vault/migrate.ts +190 -0
- package/src/vault/types.ts +177 -0
- package/src/version.ts +304 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Omnituum PQC Shared - Kyber ML-KEM-768
|
|
3
|
+
*
|
|
4
|
+
* Browser-compatible Kyber operations using kyber-crystals.
|
|
5
|
+
* NIST Level 3 security - quantum-resistant.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { b64, ub64, sha256 } from './primitives';
|
|
9
|
+
import nacl from 'tweetnacl';
|
|
10
|
+
|
|
11
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
12
|
+
// TYPES
|
|
13
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
14
|
+
|
|
15
|
+
export interface KyberKeypair {
|
|
16
|
+
publicKey: Uint8Array;
|
|
17
|
+
secretKey: Uint8Array;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface KyberKeypairB64 {
|
|
21
|
+
publicB64: string;
|
|
22
|
+
secretB64: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface KyberEncapsulation {
|
|
26
|
+
ciphertext: Uint8Array;
|
|
27
|
+
sharedSecret: Uint8Array;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
31
|
+
// KYBER LIBRARY LOADING
|
|
32
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
33
|
+
|
|
34
|
+
let kyberModule: any = null;
|
|
35
|
+
|
|
36
|
+
async function loadKyber(): Promise<any> {
|
|
37
|
+
if (kyberModule) return kyberModule;
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const m = await import('kyber-crystals');
|
|
41
|
+
const k = (m as any).default ?? m;
|
|
42
|
+
kyberModule = k.kyber ?? k;
|
|
43
|
+
return kyberModule;
|
|
44
|
+
} catch (e) {
|
|
45
|
+
console.warn('[Kyber] Failed to load kyber-crystals:', e);
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
51
|
+
// AVAILABILITY CHECK
|
|
52
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
53
|
+
|
|
54
|
+
export async function isKyberAvailable(): Promise<boolean> {
|
|
55
|
+
const mod = await loadKyber();
|
|
56
|
+
return mod !== null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
60
|
+
// KEY GENERATION
|
|
61
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Generate a Kyber ML-KEM-768 keypair.
|
|
65
|
+
*/
|
|
66
|
+
export async function generateKyberKeypair(): Promise<KyberKeypairB64 | null> {
|
|
67
|
+
try {
|
|
68
|
+
const mod = await loadKyber();
|
|
69
|
+
if (!mod) return null;
|
|
70
|
+
|
|
71
|
+
if (mod?.ready?.then) {
|
|
72
|
+
await mod.ready;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const fn = mod?.keypair ?? mod?.keyPair ?? mod?.generateKeyPair ?? null;
|
|
76
|
+
if (typeof fn !== 'function') {
|
|
77
|
+
console.warn('[Kyber] No keypair function found');
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const kp = await fn.call(mod);
|
|
82
|
+
const pub = kp?.publicKey ?? kp?.public ?? kp?.pk;
|
|
83
|
+
const priv = kp?.privateKey ?? kp?.secretKey ?? kp?.secret ?? kp?.sk;
|
|
84
|
+
|
|
85
|
+
if (!pub || !priv) {
|
|
86
|
+
console.warn('[Kyber] Invalid keypair result');
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
publicB64: b64(new Uint8Array(pub)),
|
|
92
|
+
secretB64: b64(new Uint8Array(priv)),
|
|
93
|
+
};
|
|
94
|
+
} catch (e) {
|
|
95
|
+
console.warn('[Kyber] Key generation failed:', e);
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
101
|
+
// ENCAPSULATION / DECAPSULATION
|
|
102
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Encapsulate a shared secret using Kyber ML-KEM-768.
|
|
106
|
+
*/
|
|
107
|
+
export async function kyberEncapsulate(pubKeyB64: string): Promise<KyberEncapsulation> {
|
|
108
|
+
const kyber = await loadKyber();
|
|
109
|
+
if (!kyber?.encrypt) {
|
|
110
|
+
throw new Error('Kyber encrypt not available');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const pk = ub64(pubKeyB64);
|
|
114
|
+
const r: any = await kyber.encrypt(pk);
|
|
115
|
+
|
|
116
|
+
// Normalize ciphertext
|
|
117
|
+
const ctRaw =
|
|
118
|
+
r?.ciphertext ?? r?.cyphertext ?? r?.ct ??
|
|
119
|
+
r?.bytes?.ciphertext ?? r?.bytes?.cyphertext ?? r?.bytes?.ct ??
|
|
120
|
+
(Array.isArray(r) ? r[0] : undefined);
|
|
121
|
+
|
|
122
|
+
// Normalize shared secret
|
|
123
|
+
const ssRaw =
|
|
124
|
+
r?.key ?? r?.sharedSecret ?? r?.secret ??
|
|
125
|
+
r?.bytes?.key ?? r?.bytes?.sharedSecret ??
|
|
126
|
+
(Array.isArray(r) ? r[1] : undefined);
|
|
127
|
+
|
|
128
|
+
if (!ctRaw || !ssRaw) {
|
|
129
|
+
throw new Error('Kyber encapsulate failed: missing ciphertext or shared secret');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
ciphertext: new Uint8Array(ctRaw),
|
|
134
|
+
sharedSecret: new Uint8Array(ssRaw),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Decapsulate a shared secret using Kyber ML-KEM-768.
|
|
140
|
+
*/
|
|
141
|
+
export async function kyberDecapsulate(
|
|
142
|
+
kemCiphertextB64: string,
|
|
143
|
+
secretKeyB64: string
|
|
144
|
+
): Promise<Uint8Array> {
|
|
145
|
+
const kyber = await loadKyber();
|
|
146
|
+
if (!kyber?.decrypt && !kyber?.decapsulate) {
|
|
147
|
+
throw new Error('Kyber decrypt/decapsulate not available');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const ct = ub64(kemCiphertextB64);
|
|
151
|
+
const sk = ub64(secretKeyB64);
|
|
152
|
+
|
|
153
|
+
const r: any = kyber.decrypt
|
|
154
|
+
? await kyber.decrypt(ct, sk)
|
|
155
|
+
: await kyber.decapsulate(ct, sk);
|
|
156
|
+
|
|
157
|
+
const key = (r && (r.key ?? r.sharedSecret)) ? (r.key ?? r.sharedSecret) : r;
|
|
158
|
+
return new Uint8Array(key);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
162
|
+
// KEY WRAPPING HELPERS
|
|
163
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Wrap a message key using Kyber shared secret.
|
|
167
|
+
*/
|
|
168
|
+
export function kyberWrapKey(sharedSecret: Uint8Array, msgKey32: Uint8Array): {
|
|
169
|
+
nonce: string;
|
|
170
|
+
wrapped: string;
|
|
171
|
+
} {
|
|
172
|
+
if (msgKey32.length !== 32) {
|
|
173
|
+
throw new Error('Message key must be 32 bytes');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const kek = sha256(sharedSecret); // Derive KEK from shared secret
|
|
177
|
+
const nonce = globalThis.crypto.getRandomValues(new Uint8Array(24));
|
|
178
|
+
const wrapped = nacl.secretbox(msgKey32, nonce, kek);
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
nonce: b64(nonce),
|
|
182
|
+
wrapped: b64(wrapped),
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Unwrap a message key using Kyber shared secret.
|
|
188
|
+
*/
|
|
189
|
+
export function kyberUnwrapKey(
|
|
190
|
+
sharedSecret: Uint8Array,
|
|
191
|
+
nonceB64: string,
|
|
192
|
+
wrappedB64: string
|
|
193
|
+
): Uint8Array | null {
|
|
194
|
+
const kek = sha256(sharedSecret);
|
|
195
|
+
const nonce = ub64(nonceB64);
|
|
196
|
+
const wrapped = ub64(wrappedB64);
|
|
197
|
+
|
|
198
|
+
return nacl.secretbox.open(wrapped, nonce, kek) || null;
|
|
199
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Omnituum PQC Shared - NaCl Secretbox Operations
|
|
3
|
+
*
|
|
4
|
+
* Symmetric encryption using XSalsa20-Poly1305 (NaCl secretbox).
|
|
5
|
+
* 32-byte key, 24-byte nonce, authenticated encryption.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import nacl from 'tweetnacl';
|
|
9
|
+
import { rand24, toB64, fromB64, assertLen } from './primitives';
|
|
10
|
+
|
|
11
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
12
|
+
// TYPES
|
|
13
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
14
|
+
|
|
15
|
+
export interface SecretboxPayload {
|
|
16
|
+
/** Scheme identifier for format detection */
|
|
17
|
+
scheme: 'nacl.secretbox';
|
|
18
|
+
/** Nonce (base64, 24 bytes) */
|
|
19
|
+
nonce: string;
|
|
20
|
+
/** Ciphertext (base64, includes auth tag) */
|
|
21
|
+
ciphertext: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
25
|
+
// ENCRYPTION
|
|
26
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Encrypt data using NaCl secretbox (XSalsa20-Poly1305).
|
|
30
|
+
*
|
|
31
|
+
* @param key - 32-byte symmetric key
|
|
32
|
+
* @param plaintext - Data to encrypt
|
|
33
|
+
* @param nonce - Optional 24-byte nonce (generated if not provided)
|
|
34
|
+
* @returns Encrypted payload with nonce and ciphertext
|
|
35
|
+
*/
|
|
36
|
+
export function secretboxEncrypt(
|
|
37
|
+
key: Uint8Array,
|
|
38
|
+
plaintext: Uint8Array,
|
|
39
|
+
nonce?: Uint8Array
|
|
40
|
+
): SecretboxPayload {
|
|
41
|
+
assertLen('secretbox key', key, 32);
|
|
42
|
+
|
|
43
|
+
const n = nonce ?? rand24();
|
|
44
|
+
assertLen('nonce', n, 24);
|
|
45
|
+
|
|
46
|
+
const ciphertext = nacl.secretbox(plaintext, n, key);
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
scheme: 'nacl.secretbox' as const,
|
|
50
|
+
nonce: toB64(n),
|
|
51
|
+
ciphertext: toB64(ciphertext),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Encrypt a string using NaCl secretbox.
|
|
57
|
+
*/
|
|
58
|
+
export function secretboxEncryptString(
|
|
59
|
+
key: Uint8Array,
|
|
60
|
+
plaintext: string,
|
|
61
|
+
nonce?: Uint8Array
|
|
62
|
+
): SecretboxPayload {
|
|
63
|
+
return secretboxEncrypt(key, new TextEncoder().encode(plaintext), nonce);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
67
|
+
// DECRYPTION
|
|
68
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Decrypt data using NaCl secretbox.
|
|
72
|
+
*
|
|
73
|
+
* @param key - 32-byte symmetric key
|
|
74
|
+
* @param payload - Encrypted payload from secretboxEncrypt
|
|
75
|
+
* @returns Decrypted data, or null if authentication fails
|
|
76
|
+
*/
|
|
77
|
+
export function secretboxDecrypt(
|
|
78
|
+
key: Uint8Array,
|
|
79
|
+
payload: SecretboxPayload
|
|
80
|
+
): Uint8Array | null {
|
|
81
|
+
if (payload.scheme !== 'nacl.secretbox') {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
assertLen('secretbox key', key, 32);
|
|
85
|
+
|
|
86
|
+
const nonce = fromB64(payload.nonce);
|
|
87
|
+
const ciphertext = fromB64(payload.ciphertext);
|
|
88
|
+
|
|
89
|
+
return nacl.secretbox.open(ciphertext, nonce, key) || null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Decrypt to a string using NaCl secretbox.
|
|
94
|
+
*/
|
|
95
|
+
export function secretboxDecryptString(
|
|
96
|
+
key: Uint8Array,
|
|
97
|
+
payload: SecretboxPayload
|
|
98
|
+
): string | null {
|
|
99
|
+
const result = secretboxDecrypt(key, payload);
|
|
100
|
+
if (!result) return null;
|
|
101
|
+
return new TextDecoder().decode(result);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
105
|
+
// RAW OPERATIONS (for low-level use)
|
|
106
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Raw secretbox encryption (returns raw bytes).
|
|
110
|
+
*/
|
|
111
|
+
export function secretboxRaw(
|
|
112
|
+
key: Uint8Array,
|
|
113
|
+
plaintext: Uint8Array,
|
|
114
|
+
nonce: Uint8Array
|
|
115
|
+
): Uint8Array {
|
|
116
|
+
assertLen('secretbox key', key, 32);
|
|
117
|
+
assertLen('nonce', nonce, 24);
|
|
118
|
+
return nacl.secretbox(plaintext, nonce, key);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Raw secretbox decryption (returns raw bytes).
|
|
123
|
+
*/
|
|
124
|
+
export function secretboxOpenRaw(
|
|
125
|
+
key: Uint8Array,
|
|
126
|
+
ciphertext: Uint8Array,
|
|
127
|
+
nonce: Uint8Array
|
|
128
|
+
): Uint8Array | null {
|
|
129
|
+
assertLen('secretbox key', key, 32);
|
|
130
|
+
assertLen('nonce', nonce, 24);
|
|
131
|
+
return nacl.secretbox.open(ciphertext, nonce, key) || null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
135
|
+
// BOX OPERATIONS (authenticated public-key encryption)
|
|
136
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
137
|
+
|
|
138
|
+
export interface BoxPayload {
|
|
139
|
+
/** Nonce (base64, 24 bytes) */
|
|
140
|
+
nonce: string;
|
|
141
|
+
/** Ciphertext (base64) */
|
|
142
|
+
ciphertext: string;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Encrypt data using NaCl box (X25519 + XSalsa20-Poly1305).
|
|
147
|
+
*
|
|
148
|
+
* @param plaintext - Data to encrypt
|
|
149
|
+
* @param nonce - 24-byte nonce
|
|
150
|
+
* @param recipientPubKey - Recipient's X25519 public key (32 bytes)
|
|
151
|
+
* @param senderSecKey - Sender's X25519 secret key (32 bytes)
|
|
152
|
+
* @returns Encrypted ciphertext
|
|
153
|
+
*/
|
|
154
|
+
export function boxEncrypt(
|
|
155
|
+
plaintext: Uint8Array,
|
|
156
|
+
nonce: Uint8Array,
|
|
157
|
+
recipientPubKey: Uint8Array,
|
|
158
|
+
senderSecKey: Uint8Array
|
|
159
|
+
): Uint8Array {
|
|
160
|
+
assertLen('nonce', nonce, 24);
|
|
161
|
+
assertLen('recipient pubKey', recipientPubKey, 32);
|
|
162
|
+
assertLen('sender secKey', senderSecKey, 32);
|
|
163
|
+
return nacl.box(plaintext, nonce, recipientPubKey, senderSecKey);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Decrypt data using NaCl box.
|
|
168
|
+
*
|
|
169
|
+
* @param ciphertext - Encrypted data
|
|
170
|
+
* @param nonce - 24-byte nonce
|
|
171
|
+
* @param senderPubKey - Sender's X25519 public key (32 bytes)
|
|
172
|
+
* @param recipientSecKey - Recipient's X25519 secret key (32 bytes)
|
|
173
|
+
* @returns Decrypted data, or null if authentication fails
|
|
174
|
+
*/
|
|
175
|
+
export function boxDecrypt(
|
|
176
|
+
ciphertext: Uint8Array,
|
|
177
|
+
nonce: Uint8Array,
|
|
178
|
+
senderPubKey: Uint8Array,
|
|
179
|
+
recipientSecKey: Uint8Array
|
|
180
|
+
): Uint8Array | null {
|
|
181
|
+
assertLen('nonce', nonce, 24);
|
|
182
|
+
assertLen('sender pubKey', senderPubKey, 32);
|
|
183
|
+
assertLen('recipient secKey', recipientSecKey, 32);
|
|
184
|
+
return nacl.box.open(ciphertext, nonce, senderPubKey, recipientSecKey) || null;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
188
|
+
// CONSTANTS
|
|
189
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
190
|
+
|
|
191
|
+
/** NaCl secretbox key size (32 bytes) */
|
|
192
|
+
export const SECRETBOX_KEY_SIZE = nacl.secretbox.keyLength;
|
|
193
|
+
|
|
194
|
+
/** NaCl secretbox nonce size (24 bytes) */
|
|
195
|
+
export const SECRETBOX_NONCE_SIZE = nacl.secretbox.nonceLength;
|
|
196
|
+
|
|
197
|
+
/** NaCl secretbox overhead (16 bytes auth tag) */
|
|
198
|
+
export const SECRETBOX_OVERHEAD = nacl.secretbox.overheadLength;
|
|
199
|
+
|
|
200
|
+
/** NaCl box key size (32 bytes) */
|
|
201
|
+
export const BOX_KEY_SIZE = nacl.box.publicKeyLength;
|
|
202
|
+
|
|
203
|
+
/** NaCl box nonce size (24 bytes) */
|
|
204
|
+
export const BOX_NONCE_SIZE = nacl.box.nonceLength;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Omnituum PQC Shared - BLAKE3 Hash Primitive
|
|
3
|
+
*
|
|
4
|
+
* BLAKE3 is a cryptographic hash function that provides:
|
|
5
|
+
* - Fast performance (faster than SHA-256, SHA-3)
|
|
6
|
+
* - 256-bit security (collision resistance)
|
|
7
|
+
* - Variable output length (default 32 bytes)
|
|
8
|
+
*
|
|
9
|
+
* Used in Noise protocols for transcript hashing.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { blake3 as nobleBlake3 } from '@noble/hashes/blake3';
|
|
13
|
+
|
|
14
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
15
|
+
// TYPES
|
|
16
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
17
|
+
|
|
18
|
+
export interface Blake3Options {
|
|
19
|
+
/** Output length in bytes (default: 32) */
|
|
20
|
+
outputLength?: number;
|
|
21
|
+
/** Optional key for keyed hashing (32 bytes) */
|
|
22
|
+
key?: Uint8Array;
|
|
23
|
+
/** Optional context string for domain separation */
|
|
24
|
+
context?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
28
|
+
// BLAKE3 HASH
|
|
29
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Compute BLAKE3 hash of input data.
|
|
33
|
+
*
|
|
34
|
+
* @param data - Data to hash (Uint8Array or string)
|
|
35
|
+
* @param options - Optional configuration
|
|
36
|
+
* @returns BLAKE3 hash (default 32 bytes)
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* // Simple hash
|
|
41
|
+
* const hash = blake3(new Uint8Array([1, 2, 3]));
|
|
42
|
+
*
|
|
43
|
+
* // Hash with context (domain separation)
|
|
44
|
+
* const hash = blake3(data, { context: 'MyApp v1.0 signing' });
|
|
45
|
+
*
|
|
46
|
+
* // Keyed hash (requires 32-byte key)
|
|
47
|
+
* const hash = blake3(data, { key: keyBytes });
|
|
48
|
+
*
|
|
49
|
+
* // Variable output length
|
|
50
|
+
* const hash = blake3(data, { outputLength: 64 });
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export function blake3(
|
|
54
|
+
data: Uint8Array | string,
|
|
55
|
+
options?: Blake3Options
|
|
56
|
+
): Uint8Array {
|
|
57
|
+
const input = typeof data === 'string'
|
|
58
|
+
? new TextEncoder().encode(data)
|
|
59
|
+
: data;
|
|
60
|
+
|
|
61
|
+
// @noble/hashes blake3 supports key and context options
|
|
62
|
+
const opts: { dkLen?: number; key?: Uint8Array; context?: Uint8Array } = {};
|
|
63
|
+
|
|
64
|
+
if (options?.outputLength) {
|
|
65
|
+
opts.dkLen = options.outputLength;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (options?.key) {
|
|
69
|
+
if (options.key.length !== 32) {
|
|
70
|
+
throw new Error('BLAKE3 key must be exactly 32 bytes');
|
|
71
|
+
}
|
|
72
|
+
opts.key = options.key;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (options?.context) {
|
|
76
|
+
opts.context = new TextEncoder().encode(options.context);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return nobleBlake3(input, opts);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Compute BLAKE3 hash and return as hex string.
|
|
84
|
+
*/
|
|
85
|
+
export function blake3Hex(
|
|
86
|
+
data: Uint8Array | string,
|
|
87
|
+
options?: Blake3Options
|
|
88
|
+
): string {
|
|
89
|
+
const hash = blake3(data, options);
|
|
90
|
+
return Array.from(hash)
|
|
91
|
+
.map(b => b.toString(16).padStart(2, '0'))
|
|
92
|
+
.join('');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Create a keyed BLAKE3 hash (MAC).
|
|
97
|
+
*
|
|
98
|
+
* @param key - 32-byte key
|
|
99
|
+
* @param data - Data to hash
|
|
100
|
+
* @returns BLAKE3-MAC (32 bytes by default)
|
|
101
|
+
*/
|
|
102
|
+
export function blake3Mac(
|
|
103
|
+
key: Uint8Array,
|
|
104
|
+
data: Uint8Array,
|
|
105
|
+
outputLength = 32
|
|
106
|
+
): Uint8Array {
|
|
107
|
+
if (key.length !== 32) {
|
|
108
|
+
throw new Error('BLAKE3 MAC key must be exactly 32 bytes');
|
|
109
|
+
}
|
|
110
|
+
return blake3(data, { key, outputLength });
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Derive a key from a context and key material using BLAKE3.
|
|
115
|
+
* This uses BLAKE3's built-in KDF mode.
|
|
116
|
+
*
|
|
117
|
+
* @param context - Domain separation string
|
|
118
|
+
* @param keyMaterial - Input key material
|
|
119
|
+
* @param outputLength - Desired output length (default 32)
|
|
120
|
+
* @returns Derived key
|
|
121
|
+
*/
|
|
122
|
+
export function blake3DeriveKey(
|
|
123
|
+
context: string,
|
|
124
|
+
keyMaterial: Uint8Array,
|
|
125
|
+
outputLength = 32
|
|
126
|
+
): Uint8Array {
|
|
127
|
+
return blake3(keyMaterial, { context, outputLength });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
131
|
+
// CONSTANTS
|
|
132
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
133
|
+
|
|
134
|
+
/** Default BLAKE3 output length (32 bytes = 256 bits) */
|
|
135
|
+
export const BLAKE3_OUTPUT_LENGTH = 32;
|
|
136
|
+
|
|
137
|
+
/** BLAKE3 key length for keyed mode (32 bytes) */
|
|
138
|
+
export const BLAKE3_KEY_LENGTH = 32;
|
|
139
|
+
|
|
140
|
+
/** BLAKE3 block size (64 bytes) */
|
|
141
|
+
export const BLAKE3_BLOCK_SIZE = 64;
|