@unrdf/receipts 26.4.2

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.
@@ -0,0 +1,307 @@
1
+ /**
2
+ * Hybrid Signature Scheme
3
+ * Combines Ed25519 (classical) + Dilithium3 (post-quantum)
4
+ * Defense-in-depth: both signatures must verify
5
+ *
6
+ * @module @unrdf/receipts/hybrid-signature
7
+ */
8
+
9
+ import { ed25519 } from '@noble/curves/ed25519.js';
10
+ import { sha256 } from '@noble/hashes/sha2.js';
11
+ import { z } from 'zod';
12
+ import {
13
+ generateDilithium3KeyPair,
14
+ signDilithium3,
15
+ verifyDilithium3,
16
+ Dilithium3KeyPairSchema,
17
+ Dilithium3SignatureSchema,
18
+ } from './dilithium3.mjs';
19
+
20
+ /**
21
+ * Hybrid Key Pair Schema
22
+ */
23
+ export const HybridKeyPairSchema = z.object({
24
+ classical: z.object({
25
+ publicKey: z.instanceof(Uint8Array),
26
+ privateKey: z.instanceof(Uint8Array),
27
+ algorithm: z.literal('Ed25519'),
28
+ }),
29
+ postQuantum: Dilithium3KeyPairSchema,
30
+ algorithm: z.literal('Hybrid-Ed25519-Dilithium3'),
31
+ });
32
+
33
+ /**
34
+ * Hybrid Signature Schema
35
+ */
36
+ export const HybridSignatureSchema = z.object({
37
+ classical: z.object({
38
+ signature: z.instanceof(Uint8Array),
39
+ publicKey: z.instanceof(Uint8Array),
40
+ algorithm: z.literal('Ed25519'),
41
+ }),
42
+ postQuantum: Dilithium3SignatureSchema,
43
+ algorithm: z.literal('Hybrid-Ed25519-Dilithium3'),
44
+ timestamp: z.bigint(),
45
+ });
46
+
47
+ /**
48
+ * Generate Hybrid Key Pair
49
+ * Creates both classical (Ed25519) and post-quantum (Dilithium3) keys
50
+ *
51
+ * @returns {Promise<Object>} Hybrid key pair
52
+ * @throws {Error} If key generation fails
53
+ *
54
+ * @example
55
+ * const keyPair = await generateHybridKeyPair();
56
+ * console.log('Classical public key:', keyPair.classical.publicKey.length); // 32 bytes
57
+ * console.log('PQ public key:', keyPair.postQuantum.publicKey.length); // 1952 bytes
58
+ */
59
+ export async function generateHybridKeyPair() {
60
+ // Generate Ed25519 key pair
61
+ const classicalPrivateKey = ed25519.utils.randomSecretKey();
62
+ const classicalPublicKey = ed25519.getPublicKey(classicalPrivateKey);
63
+
64
+ // Generate Dilithium3 key pair
65
+ const pqKeyPair = await generateDilithium3KeyPair();
66
+
67
+ const keyPair = {
68
+ classical: {
69
+ publicKey: classicalPublicKey,
70
+ privateKey: classicalPrivateKey,
71
+ algorithm: 'Ed25519',
72
+ },
73
+ postQuantum: pqKeyPair,
74
+ algorithm: 'Hybrid-Ed25519-Dilithium3',
75
+ };
76
+
77
+ // Validate schema
78
+ HybridKeyPairSchema.parse(keyPair);
79
+
80
+ return keyPair;
81
+ }
82
+
83
+ /**
84
+ * Sign with Hybrid Scheme
85
+ * Creates both Ed25519 and Dilithium3 signatures
86
+ *
87
+ * @param {Uint8Array|string} message - Message to sign
88
+ * @param {Object} keyPair - Hybrid key pair
89
+ * @returns {Promise<Object>} Hybrid signature
90
+ * @throws {Error} If signing fails
91
+ *
92
+ * @example
93
+ * const signature = await signHybrid(message, keyPair);
94
+ * console.log('Has classical sig:', signature.classical.signature.length === 64);
95
+ * console.log('Has PQ sig:', signature.postQuantum.signature.length === 3293);
96
+ */
97
+ export async function signHybrid(message, keyPair) {
98
+ // Validate inputs
99
+ if (!message) {
100
+ throw new TypeError('signHybrid: message is required');
101
+ }
102
+
103
+ HybridKeyPairSchema.parse(keyPair);
104
+
105
+ // Convert message to Uint8Array
106
+ const messageBytes = typeof message === 'string'
107
+ ? new TextEncoder().encode(message)
108
+ : message;
109
+
110
+ // Hash message for Ed25519
111
+ const messageHash = sha256(messageBytes);
112
+
113
+ // Sign with Ed25519
114
+ const classicalSignature = ed25519.sign(messageHash, keyPair.classical.privateKey);
115
+
116
+ // Sign with Dilithium3
117
+ const pqSignature = await signDilithium3(messageBytes, keyPair.postQuantum);
118
+
119
+ // Generate timestamp
120
+ const timestamp = typeof process !== 'undefined' && process.hrtime
121
+ ? process.hrtime.bigint()
122
+ : BigInt(Date.now()) * 1_000_000n;
123
+
124
+ const signature = {
125
+ classical: {
126
+ signature: classicalSignature,
127
+ publicKey: keyPair.classical.publicKey,
128
+ algorithm: 'Ed25519',
129
+ },
130
+ postQuantum: pqSignature,
131
+ algorithm: 'Hybrid-Ed25519-Dilithium3',
132
+ timestamp,
133
+ };
134
+
135
+ // Validate schema
136
+ HybridSignatureSchema.parse(signature);
137
+
138
+ return signature;
139
+ }
140
+
141
+ /**
142
+ * Verify Hybrid Signature
143
+ * Verifies BOTH Ed25519 AND Dilithium3 signatures (defense-in-depth)
144
+ * Returns true only if BOTH signatures are valid
145
+ *
146
+ * @param {Uint8Array|string} message - Original message
147
+ * @param {Object} signature - Hybrid signature
148
+ * @returns {Promise<Object>} Verification result with details
149
+ *
150
+ * @example
151
+ * const result = await verifyHybrid(message, signature);
152
+ * console.log('Valid:', result.valid); // true if BOTH valid
153
+ * console.log('Classical valid:', result.classicalValid);
154
+ * console.log('PQ valid:', result.postQuantumValid);
155
+ */
156
+ export async function verifyHybrid(message, signature) {
157
+ try {
158
+ // Validate signature schema
159
+ HybridSignatureSchema.parse(signature);
160
+
161
+ // Convert message to Uint8Array
162
+ const messageBytes = typeof message === 'string'
163
+ ? new TextEncoder().encode(message)
164
+ : message;
165
+
166
+ // Hash message for Ed25519
167
+ const messageHash = sha256(messageBytes);
168
+
169
+ // Verify Ed25519 signature
170
+ const classicalValid = ed25519.verify(
171
+ signature.classical.signature,
172
+ messageHash,
173
+ signature.classical.publicKey
174
+ );
175
+
176
+ // Verify Dilithium3 signature
177
+ const postQuantumValid = await verifyDilithium3(
178
+ messageBytes,
179
+ signature.postQuantum
180
+ );
181
+
182
+ // BOTH must be valid (defense-in-depth)
183
+ const valid = classicalValid && postQuantumValid;
184
+
185
+ return {
186
+ valid,
187
+ classicalValid,
188
+ postQuantumValid,
189
+ algorithm: signature.algorithm,
190
+ timestamp: signature.timestamp,
191
+ securityLevel: {
192
+ classical: '128-bit',
193
+ postQuantum: '128-bit (NIST Level 3)',
194
+ combined: '256-bit equivalent',
195
+ },
196
+ };
197
+ } catch (err) {
198
+ return {
199
+ valid: false,
200
+ classicalValid: false,
201
+ postQuantumValid: false,
202
+ error: err.message,
203
+ };
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Serialize Hybrid Signature
209
+ * Converts hybrid signature to JSON string
210
+ *
211
+ * @param {Object} signature - Hybrid signature
212
+ * @returns {string} JSON string
213
+ *
214
+ * @example
215
+ * const serialized = serializeHybridSignature(signature);
216
+ * const deserialized = deserializeHybridSignature(serialized);
217
+ */
218
+ export function serializeHybridSignature(signature) {
219
+ HybridSignatureSchema.parse(signature);
220
+
221
+ return JSON.stringify({
222
+ classical: {
223
+ signature: Buffer.from(signature.classical.signature).toString('base64'),
224
+ publicKey: Buffer.from(signature.classical.publicKey).toString('base64'),
225
+ algorithm: signature.classical.algorithm,
226
+ },
227
+ postQuantum: {
228
+ signature: Buffer.from(signature.postQuantum.signature).toString('base64'),
229
+ publicKey: Buffer.from(signature.postQuantum.publicKey).toString('base64'),
230
+ algorithm: signature.postQuantum.algorithm,
231
+ },
232
+ algorithm: signature.algorithm,
233
+ timestamp: signature.timestamp.toString(),
234
+ });
235
+ }
236
+
237
+ /**
238
+ * Deserialize Hybrid Signature
239
+ * Parses hybrid signature from JSON
240
+ *
241
+ * @param {string} serialized - Serialized signature
242
+ * @returns {Object} Hybrid signature
243
+ * @throws {Error} If deserialization fails
244
+ *
245
+ * @example
246
+ * const signature = deserializeHybridSignature(serialized);
247
+ */
248
+ export function deserializeHybridSignature(serialized) {
249
+ const parsed = JSON.parse(serialized);
250
+
251
+ const signature = {
252
+ classical: {
253
+ signature: new Uint8Array(Buffer.from(parsed.classical.signature, 'base64')),
254
+ publicKey: new Uint8Array(Buffer.from(parsed.classical.publicKey, 'base64')),
255
+ algorithm: parsed.classical.algorithm,
256
+ },
257
+ postQuantum: {
258
+ signature: new Uint8Array(Buffer.from(parsed.postQuantum.signature, 'base64')),
259
+ publicKey: new Uint8Array(Buffer.from(parsed.postQuantum.publicKey, 'base64')),
260
+ algorithm: parsed.postQuantum.algorithm,
261
+ },
262
+ algorithm: parsed.algorithm,
263
+ timestamp: BigInt(parsed.timestamp),
264
+ };
265
+
266
+ HybridSignatureSchema.parse(signature);
267
+
268
+ return signature;
269
+ }
270
+
271
+ /**
272
+ * Get Hybrid Security Level
273
+ * Returns combined security level information
274
+ *
275
+ * @returns {Object} Security level information
276
+ *
277
+ * @example
278
+ * const level = getHybridSecurityLevel();
279
+ * console.log('Classical:', level.classical); // Ed25519 128-bit
280
+ * console.log('Post-quantum:', level.postQuantum); // Dilithium3 NIST L3
281
+ * console.log('Combined:', level.combined); // 256-bit equivalent
282
+ */
283
+ export function getHybridSecurityLevel() {
284
+ return {
285
+ algorithm: 'Hybrid-Ed25519-Dilithium3',
286
+ classical: {
287
+ algorithm: 'Ed25519',
288
+ securityBits: 128,
289
+ keySize: 32,
290
+ signatureSize: 64,
291
+ },
292
+ postQuantum: {
293
+ algorithm: 'Dilithium3',
294
+ nistLevel: 3,
295
+ classicalBits: 192,
296
+ quantumBits: 128,
297
+ publicKeySize: 1952,
298
+ signatureSize: 3293,
299
+ },
300
+ combined: {
301
+ effectiveBits: 256,
302
+ quantumResistant: true,
303
+ defenseInDepth: true,
304
+ },
305
+ totalSignatureSize: 64 + 3293, // 3357 bytes
306
+ };
307
+ }
package/src/index.mjs ADDED
@@ -0,0 +1,64 @@
1
+ /**
2
+ * KGC Receipts - Main Entry Point
3
+ * Batch receipt generation with Merkle tree verification + Post-Quantum Cryptography
4
+ *
5
+ * @module @unrdf/receipts
6
+ */
7
+
8
+ // Batch Receipt Generator
9
+ export {
10
+ generateBatchReceipt,
11
+ verifyBatchReceipt,
12
+ serializeReceipt,
13
+ deserializeReceipt,
14
+ batchMultipleOperations,
15
+ } from './batch-receipt-generator.mjs';
16
+
17
+ // Merkle Tree Batcher
18
+ export {
19
+ buildMerkleTree,
20
+ generateMerkleProof,
21
+ verifyMerkleProof,
22
+ batchWithMerkleTree,
23
+ verifyOperationInBatch,
24
+ getMerkleRoot,
25
+ calculateTreeDepth,
26
+ getLeafCount,
27
+ } from './merkle-batcher.mjs';
28
+
29
+ // Post-Quantum Cryptography
30
+ export {
31
+ // Dilithium3
32
+ generateDilithium3KeyPair,
33
+ signDilithium3,
34
+ verifyDilithium3,
35
+ serializeDilithium3Signature,
36
+ deserializeDilithium3Signature,
37
+ getDilithium3SecurityLevel,
38
+ } from './dilithium3.mjs';
39
+
40
+ // Hybrid Signatures (Ed25519 + Dilithium3)
41
+ export {
42
+ generateHybridKeyPair,
43
+ signHybrid,
44
+ verifyHybrid,
45
+ serializeHybridSignature,
46
+ deserializeHybridSignature,
47
+ getHybridSecurityLevel,
48
+ } from './hybrid-signature.mjs';
49
+
50
+ // Post-Quantum Receipts
51
+ export {
52
+ createPQReceipt,
53
+ verifyPQReceipt,
54
+ batchSignReceipts,
55
+ getPQCapabilities,
56
+ } from './pq-signer.mjs';
57
+
58
+ // Post-Quantum Merkle Trees (XMSS)
59
+ export {
60
+ buildPQMerkleTree,
61
+ generatePQMerkleProof,
62
+ verifyPQMerkleProof,
63
+ getPQMerkleTreeInfo,
64
+ } from './pq-merkle.mjs';