@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.
- package/package.json +69 -0
- package/src/batch-receipt-generator.mjs +308 -0
- package/src/dilithium3.mjs +274 -0
- package/src/hybrid-signature.mjs +307 -0
- package/src/index.mjs +64 -0
- package/src/merkle-batcher.mjs +395 -0
- package/src/pq-merkle.mjs +438 -0
- package/src/pq-signer.mjs +381 -0
|
@@ -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';
|