@omnituum/pqc-shared 0.3.1 → 0.4.0

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 (41) hide show
  1. package/dist/crypto/index.cjs +29 -71
  2. package/dist/crypto/index.d.cts +38 -45
  3. package/dist/crypto/index.d.ts +38 -45
  4. package/dist/crypto/index.js +28 -72
  5. package/dist/crypto/safe/index.cjs +556 -0
  6. package/dist/crypto/safe/index.d.cts +341 -0
  7. package/dist/crypto/safe/index.d.ts +341 -0
  8. package/dist/crypto/safe/index.js +524 -0
  9. package/dist/fs/index.cjs +6 -34
  10. package/dist/fs/index.js +6 -34
  11. package/dist/index.cjs +29 -71
  12. package/dist/index.d.cts +4 -4
  13. package/dist/index.d.ts +4 -4
  14. package/dist/index.js +28 -72
  15. package/dist/{integrity-CCYjrap3.d.ts → integrity-BPvNsC50.d.ts} +1 -1
  16. package/dist/{integrity-Dx9jukMH.d.cts → integrity-ByMp24VA.d.cts} +1 -1
  17. package/dist/{types-61c7Q9ri.d.ts → types-Dx_AFqow.d.cts} +54 -2
  18. package/dist/{types-Ch0y-n7K.d.cts → types-Dx_AFqow.d.ts} +54 -2
  19. package/dist/utils/index.d.cts +2 -3
  20. package/dist/utils/index.d.ts +2 -3
  21. package/dist/vault/index.cjs +13 -41
  22. package/dist/vault/index.d.cts +2 -3
  23. package/dist/vault/index.d.ts +2 -3
  24. package/dist/vault/index.js +13 -41
  25. package/package.json +27 -13
  26. package/src/crypto/dilithium.ts +8 -4
  27. package/src/crypto/hybrid.ts +13 -29
  28. package/src/crypto/index.ts +3 -0
  29. package/src/crypto/kyber.ts +63 -121
  30. package/src/crypto/safe/adapters.ts +306 -0
  31. package/src/crypto/safe/dilithium.ts +74 -0
  32. package/src/crypto/safe/index.ts +97 -0
  33. package/src/crypto/safe/kyber.ts +47 -0
  34. package/src/crypto/safe/manifests.ts +192 -0
  35. package/src/crypto/safe/secretbox.ts +24 -0
  36. package/src/crypto/safe/types.ts +88 -0
  37. package/src/crypto/safe/x25519.ts +31 -0
  38. package/src/index.ts +7 -4
  39. package/src/version.ts +9 -4
  40. package/dist/version-BygzPVGs.d.cts +0 -55
  41. package/dist/version-BygzPVGs.d.ts +0 -55
@@ -0,0 +1,306 @@
1
+ /**
2
+ * High-assurance adapter factories.
3
+ *
4
+ * Single-boundary, typed, invariant-checked, manifest-verified.
5
+ *
6
+ * Each factory wraps a low-level positional API into a safe object-parameter API.
7
+ * - Branded types prevent argument confusion at compile time.
8
+ * - Lazy invariant checks catch API drift at runtime.
9
+ * - Manifest checks verify primitive identity (key/signature sizes) to catch
10
+ * supply-chain substitution or silent dependency upgrades.
11
+ */
12
+
13
+ import type { SignatureManifest, KemManifest, DhManifest, SecretboxManifest } from './manifests'
14
+
15
+ // ---- Generic brand utility ----
16
+
17
+ type Brand<T, B extends string> = T & { readonly __brand: B }
18
+
19
+ // ---- Signature adapter ----
20
+
21
+ export type SigMessage = Brand<Uint8Array, 'SigMessage'>
22
+ export type SigPublicKey<Alg extends string> = Brand<Uint8Array, `${Alg}PublicKey`>
23
+ export type SigSecretKey<Alg extends string> = Brand<Uint8Array, `${Alg}SecretKey`>
24
+ export type SigSignature<Alg extends string> = Brand<Uint8Array, `${Alg}Signature`>
25
+
26
+ export type NobleSigLike = {
27
+ keygen(seed?: Uint8Array): { publicKey: Uint8Array; secretKey: Uint8Array }
28
+ sign(message: Uint8Array, secretKey: Uint8Array): Uint8Array
29
+ verify(signature: Uint8Array, message: Uint8Array, publicKey: Uint8Array): boolean
30
+ }
31
+
32
+ export function makeSignatureAdapter<Alg extends string>(
33
+ noble: NobleSigLike,
34
+ manifest: SignatureManifest,
35
+ ) {
36
+ let checked = false
37
+ const alg = manifest.algorithm
38
+
39
+ function ensureInvariant() {
40
+ if (checked) return
41
+ const msg = new Uint8Array([0xde, 0xad, 0xbe, 0xef])
42
+ const { publicKey, secretKey } = noble.keygen()
43
+ const sig = noble.sign(msg, secretKey)
44
+
45
+ // Manifest size checks — verify primitive identity
46
+ if (publicKey.length !== manifest.publicKeyBytes) {
47
+ throw new Error(`${alg}: publicKey ${publicKey.length}B, expected ${manifest.publicKeyBytes}B`)
48
+ }
49
+ if (secretKey.length !== manifest.secretKeyBytes) {
50
+ throw new Error(`${alg}: secretKey ${secretKey.length}B, expected ${manifest.secretKeyBytes}B`)
51
+ }
52
+ if (sig.length !== manifest.signatureBytes) {
53
+ throw new Error(`${alg}: signature ${sig.length}B, expected ${manifest.signatureBytes}B`)
54
+ }
55
+
56
+ // Behavioral roundtrip
57
+ if (!noble.verify(sig, msg, publicKey)) {
58
+ throw new Error(`${alg}: sign/verify roundtrip failed`)
59
+ }
60
+
61
+ checked = true
62
+ }
63
+
64
+ return {
65
+ keygen(): { publicKey: SigPublicKey<Alg>; secretKey: SigSecretKey<Alg> } {
66
+ ensureInvariant()
67
+ const kp = noble.keygen()
68
+ return {
69
+ publicKey: kp.publicKey as SigPublicKey<Alg>,
70
+ secretKey: kp.secretKey as SigSecretKey<Alg>,
71
+ }
72
+ },
73
+
74
+ sign(params: {
75
+ message: SigMessage
76
+ secretKey: SigSecretKey<Alg>
77
+ }): SigSignature<Alg> {
78
+ ensureInvariant()
79
+ return noble.sign(params.message, params.secretKey) as SigSignature<Alg>
80
+ },
81
+
82
+ verify(params: {
83
+ signature: SigSignature<Alg>
84
+ message: SigMessage
85
+ publicKey: SigPublicKey<Alg>
86
+ }): boolean {
87
+ ensureInvariant()
88
+ return noble.verify(params.signature, params.message, params.publicKey)
89
+ },
90
+ }
91
+ }
92
+
93
+ export function asSigMessage(v: Uint8Array): SigMessage {
94
+ return v as SigMessage
95
+ }
96
+
97
+ // ---- KEM adapter ----
98
+
99
+ export type KemEncapsKey<Alg extends string> = Brand<Uint8Array, `${Alg}EncapsKey`>
100
+ export type KemDecapsKey<Alg extends string> = Brand<Uint8Array, `${Alg}DecapsKey`>
101
+ export type KemCiphertext<Alg extends string> = Brand<Uint8Array, `${Alg}Ciphertext`>
102
+ export type KemSharedSecret = Brand<Uint8Array, 'KemSharedSecret'>
103
+
104
+ export type NobleKemLike = {
105
+ keygen(): { publicKey: Uint8Array; secretKey: Uint8Array }
106
+ encapsulate(publicKey: Uint8Array): { ciphertext: Uint8Array; sharedSecret: Uint8Array }
107
+ decapsulate(ciphertext: Uint8Array, secretKey: Uint8Array): Uint8Array
108
+ }
109
+
110
+ export function makeKemAdapter<Alg extends string>(
111
+ noble: NobleKemLike,
112
+ manifest: KemManifest,
113
+ ) {
114
+ let checked = false
115
+ const alg = manifest.algorithm
116
+
117
+ function ensureInvariant() {
118
+ if (checked) return
119
+ const { publicKey, secretKey } = noble.keygen()
120
+ const { ciphertext, sharedSecret: ss1 } = noble.encapsulate(publicKey)
121
+ const ss2 = noble.decapsulate(ciphertext, secretKey)
122
+
123
+ // Manifest size checks
124
+ if (publicKey.length !== manifest.encapsulationKeyBytes) {
125
+ throw new Error(`${alg}: encapsKey ${publicKey.length}B, expected ${manifest.encapsulationKeyBytes}B`)
126
+ }
127
+ if (secretKey.length !== manifest.decapsulationKeyBytes) {
128
+ throw new Error(`${alg}: decapsKey ${secretKey.length}B, expected ${manifest.decapsulationKeyBytes}B`)
129
+ }
130
+ if (ciphertext.length !== manifest.ciphertextBytes) {
131
+ throw new Error(`${alg}: ciphertext ${ciphertext.length}B, expected ${manifest.ciphertextBytes}B`)
132
+ }
133
+ if (ss1.length !== manifest.sharedSecretBytes) {
134
+ throw new Error(`${alg}: sharedSecret ${ss1.length}B, expected ${manifest.sharedSecretBytes}B`)
135
+ }
136
+
137
+ // Behavioral roundtrip
138
+ if (ss2.length !== ss1.length) {
139
+ throw new Error(`${alg}: encap/decap shared secret length mismatch`)
140
+ }
141
+ for (let i = 0; i < ss1.length; i++) {
142
+ if (ss1[i] !== ss2[i]) throw new Error(`${alg}: encap/decap shared secrets differ`)
143
+ }
144
+
145
+ checked = true
146
+ }
147
+
148
+ return {
149
+ keygen(): { encapsulationKey: KemEncapsKey<Alg>; decapsulationKey: KemDecapsKey<Alg> } {
150
+ ensureInvariant()
151
+ const kp = noble.keygen()
152
+ return {
153
+ encapsulationKey: kp.publicKey as KemEncapsKey<Alg>,
154
+ decapsulationKey: kp.secretKey as KemDecapsKey<Alg>,
155
+ }
156
+ },
157
+
158
+ encapsulate(params: {
159
+ encapsulationKey: KemEncapsKey<Alg>
160
+ }): { ciphertext: KemCiphertext<Alg>; sharedSecret: KemSharedSecret } {
161
+ ensureInvariant()
162
+ const result = noble.encapsulate(params.encapsulationKey)
163
+ return {
164
+ ciphertext: result.ciphertext as KemCiphertext<Alg>,
165
+ sharedSecret: result.sharedSecret as KemSharedSecret,
166
+ }
167
+ },
168
+
169
+ decapsulate(params: {
170
+ ciphertext: KemCiphertext<Alg>
171
+ decapsulationKey: KemDecapsKey<Alg>
172
+ }): KemSharedSecret {
173
+ ensureInvariant()
174
+ return noble.decapsulate(params.ciphertext, params.decapsulationKey) as KemSharedSecret
175
+ },
176
+ }
177
+ }
178
+
179
+ // ---- DH adapter ----
180
+
181
+ export type DhPublicKey<Alg extends string> = Brand<Uint8Array, `${Alg}PublicKey`>
182
+ export type DhSecretKey<Alg extends string> = Brand<Uint8Array, `${Alg}SecretKey`>
183
+ export type DhSharedSecret = Brand<Uint8Array, 'DhSharedSecret'>
184
+
185
+ export type NobleDhLike = {
186
+ getPublicKey(secretKey: Uint8Array): Uint8Array
187
+ getSharedSecret(secretKey: Uint8Array, publicKey: Uint8Array): Uint8Array
188
+ utils: { randomPrivateKey(): Uint8Array }
189
+ }
190
+
191
+ export function makeDhAdapter<Alg extends string>(
192
+ noble: NobleDhLike,
193
+ manifest: DhManifest,
194
+ ) {
195
+ let checked = false
196
+ const alg = manifest.algorithm
197
+
198
+ function ensureInvariant() {
199
+ if (checked) return
200
+ const sk = noble.utils.randomPrivateKey()
201
+ const pk = noble.getPublicKey(sk)
202
+
203
+ if (pk.length !== manifest.publicKeyBytes) {
204
+ throw new Error(`${alg}: publicKey ${pk.length}B, expected ${manifest.publicKeyBytes}B`)
205
+ }
206
+ if (sk.length !== manifest.secretKeyBytes) {
207
+ throw new Error(`${alg}: secretKey ${sk.length}B, expected ${manifest.secretKeyBytes}B`)
208
+ }
209
+
210
+ // DH commutativity check
211
+ const sk2 = noble.utils.randomPrivateKey()
212
+ const pk2 = noble.getPublicKey(sk2)
213
+ const ss1 = noble.getSharedSecret(sk, pk2)
214
+ const ss2 = noble.getSharedSecret(sk2, pk)
215
+
216
+ if (ss1.length !== manifest.sharedSecretBytes) {
217
+ throw new Error(`${alg}: sharedSecret ${ss1.length}B, expected ${manifest.sharedSecretBytes}B`)
218
+ }
219
+ for (let i = 0; i < ss1.length; i++) {
220
+ if (ss1[i] !== ss2[i]) throw new Error(`${alg}: DH commutativity failed`)
221
+ }
222
+
223
+ checked = true
224
+ }
225
+
226
+ return {
227
+ keygen(): { publicKey: DhPublicKey<Alg>; secretKey: DhSecretKey<Alg> } {
228
+ ensureInvariant()
229
+ const secretKey = noble.utils.randomPrivateKey()
230
+ const publicKey = noble.getPublicKey(secretKey)
231
+ return {
232
+ publicKey: publicKey as DhPublicKey<Alg>,
233
+ secretKey: secretKey as DhSecretKey<Alg>,
234
+ }
235
+ },
236
+
237
+ sharedSecret(params: {
238
+ ourSecretKey: DhSecretKey<Alg>
239
+ theirPublicKey: DhPublicKey<Alg>
240
+ }): DhSharedSecret {
241
+ ensureInvariant()
242
+ return noble.getSharedSecret(params.ourSecretKey, params.theirPublicKey) as DhSharedSecret
243
+ },
244
+ }
245
+ }
246
+
247
+ // ---- Secretbox adapter ----
248
+
249
+ export type BoxKey = Brand<Uint8Array, 'BoxKey'>
250
+ export type BoxNonce = Brand<Uint8Array, 'BoxNonce'>
251
+
252
+ export function makeSecretboxAdapter(
253
+ impl: {
254
+ seal: (key: Uint8Array, plaintext: Uint8Array, nonce: Uint8Array) => Uint8Array
255
+ open: (key: Uint8Array, ciphertext: Uint8Array, nonce: Uint8Array) => Uint8Array | null
256
+ },
257
+ manifest: SecretboxManifest,
258
+ ) {
259
+ let checked = false
260
+ const alg = manifest.algorithm
261
+
262
+ function ensureInvariant() {
263
+ if (checked) return
264
+ const key = new Uint8Array(manifest.keyBytes)
265
+ crypto.getRandomValues(key)
266
+ const nonce = new Uint8Array(manifest.nonceBytes)
267
+ crypto.getRandomValues(nonce)
268
+ const plaintext = new Uint8Array([0xca, 0xfe, 0xba, 0xbe])
269
+
270
+ const ct = impl.seal(key, plaintext, nonce)
271
+ const overhead = ct.length - plaintext.length
272
+ if (overhead !== manifest.tagBytes) {
273
+ throw new Error(`${alg}: overhead ${overhead}B, expected ${manifest.tagBytes}B tag`)
274
+ }
275
+
276
+ const pt = impl.open(key, ct, nonce)
277
+ if (!pt || pt.length !== plaintext.length) {
278
+ throw new Error(`${alg}: seal/open roundtrip failed`)
279
+ }
280
+ for (let i = 0; i < plaintext.length; i++) {
281
+ if (pt[i] !== plaintext[i]) throw new Error(`${alg}: seal/open roundtrip mismatch`)
282
+ }
283
+
284
+ checked = true
285
+ }
286
+
287
+ return {
288
+ seal(params: { key: BoxKey; plaintext: Uint8Array; nonce: BoxNonce }): Uint8Array {
289
+ ensureInvariant()
290
+ return impl.seal(params.key, params.plaintext, params.nonce)
291
+ },
292
+
293
+ open(params: { key: BoxKey; ciphertext: Uint8Array; nonce: BoxNonce }): Uint8Array | null {
294
+ ensureInvariant()
295
+ return impl.open(params.key, params.ciphertext, params.nonce)
296
+ },
297
+ }
298
+ }
299
+
300
+ export function asBoxKey(v: Uint8Array): BoxKey {
301
+ return v as BoxKey
302
+ }
303
+
304
+ export function asBoxNonce(v: Uint8Array): BoxNonce {
305
+ return v as BoxNonce
306
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Safe Dilithium ML-DSA-65 adapter.
3
+ *
4
+ * Object parameters + branded types + manifest verification.
5
+ * - Argument inversion is a compile-time error.
6
+ * - Primitive identity verified on first use (key/sig sizes match manifest).
7
+ * - Behavioral roundtrip confirmed before any real operation.
8
+ */
9
+
10
+ import type {
11
+ DilithiumKeyPair,
12
+ DilithiumSecretKey,
13
+ DilithiumPublicKey,
14
+ DilithiumSignature,
15
+ Message,
16
+ } from './types'
17
+ import { ML_DSA_65, assertSignaturePrimitive } from './manifests'
18
+
19
+ let _mod: any = null
20
+ let _invariantChecked = false
21
+
22
+ async function load(): Promise<any> {
23
+ if (!_mod) {
24
+ const { ml_dsa65 } = await import('@noble/post-quantum/ml-dsa.js')
25
+ _mod = ml_dsa65
26
+ }
27
+ return _mod
28
+ }
29
+
30
+ /**
31
+ * Run once on first use. Verifies:
32
+ * 1. Key and signature sizes match ML-DSA-65 manifest
33
+ * 2. sign/verify roundtrip produces correct result
34
+ * Catches both argument order bugs and supply-chain primitive substitution.
35
+ */
36
+ async function checkInvariant(): Promise<void> {
37
+ if (_invariantChecked) return
38
+ const mod = await load()
39
+ assertSignaturePrimitive(mod, ML_DSA_65)
40
+ _invariantChecked = true
41
+ }
42
+
43
+ export async function keygen(): Promise<DilithiumKeyPair> {
44
+ const mod = await load()
45
+ await checkInvariant()
46
+ const kp = mod.keygen()
47
+ return {
48
+ publicKey: kp.publicKey as DilithiumPublicKey,
49
+ secretKey: kp.secretKey as DilithiumSecretKey,
50
+ }
51
+ }
52
+
53
+ export async function sign(params: {
54
+ message: Message
55
+ secretKey: DilithiumSecretKey
56
+ }): Promise<DilithiumSignature> {
57
+ const mod = await load()
58
+ await checkInvariant()
59
+ return mod.sign(params.message, params.secretKey) as DilithiumSignature
60
+ }
61
+
62
+ export async function verify(params: {
63
+ signature: DilithiumSignature
64
+ message: Message
65
+ publicKey: DilithiumPublicKey
66
+ }): Promise<boolean> {
67
+ const mod = await load()
68
+ await checkInvariant()
69
+ try {
70
+ return mod.verify(params.signature, params.message, params.publicKey)
71
+ } catch {
72
+ return false
73
+ }
74
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * @omnituum/pqc-shared/safe — Type-safe crypto adapter layer.
3
+ *
4
+ * All functions use branded types and object parameters.
5
+ * Argument inversion is a compile-time error.
6
+ *
7
+ * Usage:
8
+ * import { dilithium, kyber, x25519, secretbox } from '@omnituum/pqc-shared/safe'
9
+ *
10
+ * const kp = await dilithium.keygen()
11
+ * const sig = await dilithium.sign({ message: msg, secretKey: kp.secretKey })
12
+ * const ok = await dilithium.verify({ signature: sig, message: msg, publicKey: kp.publicKey })
13
+ */
14
+
15
+ export * as dilithium from './dilithium'
16
+ export * as kyber from './kyber'
17
+ export * as x25519 from './x25519'
18
+ export * as secretbox from './secretbox'
19
+
20
+ export type {
21
+ DilithiumSecretKey,
22
+ DilithiumPublicKey,
23
+ DilithiumSignature,
24
+ DilithiumKeyPair,
25
+ KyberEncapsulationKey,
26
+ KyberDecapsulationKey,
27
+ KyberCiphertext,
28
+ KyberKeyPair,
29
+ KyberEncapsulation,
30
+ SharedSecret,
31
+ X25519PublicKey,
32
+ X25519SecretKey,
33
+ X25519KeyPair,
34
+ SymmetricKey,
35
+ Nonce,
36
+ Message,
37
+ } from './types'
38
+
39
+ export {
40
+ asMessage,
41
+ asDilithiumSecretKey,
42
+ asDilithiumPublicKey,
43
+ asKyberEncapsulationKey,
44
+ asKyberDecapsulationKey,
45
+ asX25519PublicKey,
46
+ asX25519SecretKey,
47
+ asSymmetricKey,
48
+ asNonce,
49
+ } from './types'
50
+
51
+ // Adapter factories — for building new type-safe wrappers around any noble-like API
52
+ export {
53
+ makeSignatureAdapter,
54
+ makeKemAdapter,
55
+ makeDhAdapter,
56
+ makeSecretboxAdapter,
57
+ asSigMessage,
58
+ asBoxKey,
59
+ asBoxNonce,
60
+ } from './adapters'
61
+
62
+ export type {
63
+ NobleSigLike,
64
+ NobleKemLike,
65
+ NobleDhLike,
66
+ SigMessage,
67
+ SigPublicKey,
68
+ SigSecretKey,
69
+ SigSignature,
70
+ KemEncapsKey,
71
+ KemDecapsKey,
72
+ KemCiphertext,
73
+ KemSharedSecret,
74
+ DhPublicKey,
75
+ DhSecretKey,
76
+ DhSharedSecret,
77
+ BoxKey,
78
+ BoxNonce,
79
+ } from './adapters'
80
+
81
+ // Manifests — primitive identity declarations for supply-chain verification
82
+ export {
83
+ ML_DSA_65,
84
+ ML_KEM_768,
85
+ X25519,
86
+ XSALSA20_POLY1305,
87
+ assertSignaturePrimitive,
88
+ assertKemPrimitive,
89
+ assertDhPrimitive,
90
+ } from './manifests'
91
+
92
+ export type {
93
+ SignatureManifest,
94
+ KemManifest,
95
+ DhManifest,
96
+ SecretboxManifest,
97
+ } from './manifests'
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Safe Kyber ML-KEM-768 adapter.
3
+ *
4
+ * Object parameters + branded types.
5
+ */
6
+
7
+ import { toB64, fromB64 } from '../primitives'
8
+ import {
9
+ kyberEncapsulate as rawEncapsulate,
10
+ kyberDecapsulate as rawDecapsulate,
11
+ generateKyberKeypair as rawKeygen,
12
+ } from '../kyber'
13
+ import type {
14
+ KyberKeyPair,
15
+ KyberEncapsulationKey,
16
+ KyberDecapsulationKey,
17
+ KyberCiphertext,
18
+ KyberEncapsulation,
19
+ SharedSecret,
20
+ } from './types'
21
+
22
+ export async function keygen(): Promise<KyberKeyPair | null> {
23
+ const kp = await rawKeygen()
24
+ if (!kp) return null
25
+ return {
26
+ encapsulationKey: fromB64(kp.publicB64) as KyberEncapsulationKey,
27
+ decapsulationKey: fromB64(kp.secretB64) as KyberDecapsulationKey,
28
+ }
29
+ }
30
+
31
+ export async function encapsulate(params: {
32
+ encapsulationKey: KyberEncapsulationKey
33
+ }): Promise<KyberEncapsulation> {
34
+ const result = await rawEncapsulate(toB64(params.encapsulationKey))
35
+ return {
36
+ ciphertext: result.ciphertext as KyberCiphertext,
37
+ sharedSecret: result.sharedSecret as SharedSecret,
38
+ }
39
+ }
40
+
41
+ export async function decapsulate(params: {
42
+ ciphertext: KyberCiphertext
43
+ decapsulationKey: KyberDecapsulationKey
44
+ }): Promise<SharedSecret> {
45
+ const ss = await rawDecapsulate(toB64(params.ciphertext), toB64(params.decapsulationKey))
46
+ return ss as SharedSecret
47
+ }