kxco-post-quantum 1.7.2 → 1.7.5
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/CHANGELOG.md +38 -0
- package/CRYPTO-INVENTORY.md +227 -0
- package/HNDL.md +84 -0
- package/PQCMM.md +164 -0
- package/README.md +149 -87
- package/package.json +42 -16
- package/src/_native.node.js +110 -9
- package/src/_native.stub.js +4 -0
- package/src/backend.d.ts +25 -0
- package/src/backend.js +47 -12
- package/src/index.js +1 -1
- package/src/ml-dsa-87.js +8 -3
- package/src/ml-dsa.js +12 -6
- package/src/slh-dsa.js +8 -3
package/src/_native.node.js
CHANGED
|
@@ -28,11 +28,73 @@
|
|
|
28
28
|
|
|
29
29
|
import crypto from 'node:crypto'
|
|
30
30
|
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
31
|
+
// Which implementation this process must use, chosen by the operator.
|
|
32
|
+
//
|
|
33
|
+
// Until now the only control was requireNativeBackend(), which asserts OpenSSL
|
|
34
|
+
// is present. That was enough while only one implementation was going to carry
|
|
35
|
+
// a certificate. Both are now being taken to validation, so a deployment under
|
|
36
|
+
// a validated-module control has to be able to pin EITHER, and the one it must
|
|
37
|
+
// pin is whichever its certificate names.
|
|
38
|
+
//
|
|
39
|
+
// Set from the environment rather than from application code, for the same
|
|
40
|
+
// reason the assertion is: the team under the control is usually not the team
|
|
41
|
+
// calling this library.
|
|
42
|
+
//
|
|
43
|
+
// KXCO_PQ_BACKEND=javascript never use OpenSSL, even where it is present
|
|
44
|
+
// KXCO_PQ_BACKEND=openssl prefer OpenSSL, which is the default anyway;
|
|
45
|
+
// pair with requireBackend to make it binding
|
|
46
|
+
//
|
|
47
|
+
// A value that is neither throws at import. A misspelled pin that silently did
|
|
48
|
+
// nothing would leave the operator believing a control was in force when it
|
|
49
|
+
// was not, which is the exact failure this exists to remove.
|
|
50
|
+
//
|
|
51
|
+
// Nothing here changes what a signature looks like. The two implementations
|
|
52
|
+
// produce identical wire bytes, proved for every parameter set in both
|
|
53
|
+
// directions by the interoperability matrix. What it changes is which one
|
|
54
|
+
// computed them, and backend() always reports that truthfully, including when
|
|
55
|
+
// the answer is the result of this pin.
|
|
56
|
+
const PIN = (() => {
|
|
57
|
+
try {
|
|
58
|
+
const v = globalThis.process?.env?.KXCO_PQ_BACKEND
|
|
59
|
+
if (v === undefined || v === null || v === '') return null
|
|
60
|
+
const k = String(v).trim().toLowerCase()
|
|
61
|
+
if (k !== 'openssl' && k !== 'javascript') {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`KXCO_PQ_BACKEND must be 'openssl' or 'javascript', got '${v}'`,
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
return k
|
|
67
|
+
} catch (err) {
|
|
68
|
+
if (err instanceof Error && err.message.startsWith('KXCO_PQ_BACKEND')) throw err
|
|
69
|
+
return null
|
|
70
|
+
}
|
|
71
|
+
})()
|
|
72
|
+
|
|
73
|
+
/** The operator's pin, or null. Reported by backend() so evidence records it. */
|
|
74
|
+
export const pinned = PIN
|
|
75
|
+
|
|
76
|
+
// FIPS 204 section 5.2 and FIPS 205 context strings. Node's sign and verify
|
|
77
|
+
// took no context argument when this module was written, so a call that used
|
|
78
|
+
// one fell back to the JavaScript backend: signing without the caller's context
|
|
79
|
+
// produces a signature that verifies against nothing, which would look like a
|
|
35
80
|
// cross-implementation disagreement rather than a missing feature.
|
|
81
|
+
//
|
|
82
|
+
// Newer Node builds do take one, and honour it. That is worth having rather
|
|
83
|
+
// than assuming either way, because the fallback quietly moved every
|
|
84
|
+
// context-using call onto the implementation the operator did not ask for, and
|
|
85
|
+
// requireNativeBackend() cannot see it happen: it asserts the backend is
|
|
86
|
+
// present, not that a particular call reached it.
|
|
87
|
+
//
|
|
88
|
+
// So the capability is probed rather than inferred from a version number, and
|
|
89
|
+
// the probe is the real thing: sign under one context, then require that the
|
|
90
|
+
// same context verifies and a different one does not. A build that accepted
|
|
91
|
+
// the argument and ignored it would pass the first check and fail the second,
|
|
92
|
+
// and ignoring it is the dangerous outcome, not rejecting it.
|
|
93
|
+
//
|
|
94
|
+
// Verified before this was enabled: with a context string, OpenSSL and
|
|
95
|
+
// @noble/post-quantum accept each other's signatures in both directions, and
|
|
96
|
+
// both reject a wrong context, across ML-DSA-44/65/87 and the three SLH-DSA
|
|
97
|
+
// sets this build exposes.
|
|
36
98
|
|
|
37
99
|
const DER_SEQUENCE = 0x30
|
|
38
100
|
const DER_OCTET_STRING = 0x04
|
|
@@ -109,6 +171,34 @@ function probe() {
|
|
|
109
171
|
|
|
110
172
|
const SUPPORTED = probe()
|
|
111
173
|
|
|
174
|
+
// Whether this runtime's sign and verify honour a context string. Probed once,
|
|
175
|
+
// on first use rather than at import, because most callers never pass one and
|
|
176
|
+
// the probe costs a keygen.
|
|
177
|
+
//
|
|
178
|
+
// `null` means not yet probed. Any throw is read as "no", so a build that
|
|
179
|
+
// rejects the argument outright falls back exactly as before.
|
|
180
|
+
let contextHonoured = null
|
|
181
|
+
function probeContext() {
|
|
182
|
+
if (contextHonoured !== null) return contextHonoured
|
|
183
|
+
contextHonoured = false
|
|
184
|
+
try {
|
|
185
|
+
// ML-DSA-44 is the cheapest set to key and sign. If the build has no
|
|
186
|
+
// ML-DSA at all there is nothing to probe with and the answer stays no.
|
|
187
|
+
const spec = SUPPORTED.get('ML-DSA-44')
|
|
188
|
+
if (!spec) return contextHonoured
|
|
189
|
+
const { privateKey, publicKey } = crypto.generateKeyPairSync(spec.nodeName)
|
|
190
|
+
const message = Buffer.from('kxco-pq context probe')
|
|
191
|
+
const a = Buffer.from('a'), b = Buffer.from('b')
|
|
192
|
+
const sig = crypto.sign(null, message, { key: privateKey, context: a })
|
|
193
|
+
contextHonoured =
|
|
194
|
+
crypto.verify(null, message, { key: publicKey, context: a }, sig) === true &&
|
|
195
|
+
crypto.verify(null, message, { key: publicKey, context: b }, sig) === false
|
|
196
|
+
} catch {
|
|
197
|
+
contextHonoured = false
|
|
198
|
+
}
|
|
199
|
+
return contextHonoured
|
|
200
|
+
}
|
|
201
|
+
|
|
112
202
|
function privateKeyObject(spec, secretKey, publicKey) {
|
|
113
203
|
if (spec.privateForm === 'jwk') {
|
|
114
204
|
// FIPS 205 lays the private key out as SK.seed || SK.prf || PK.seed ||
|
|
@@ -142,7 +232,7 @@ function publicKeyObject(spec, publicKey) {
|
|
|
142
232
|
})
|
|
143
233
|
}
|
|
144
234
|
|
|
145
|
-
export const native = SUPPORTED.size === 0 ? null : {
|
|
235
|
+
export const native = (PIN === 'javascript' || SUPPORTED.size === 0) ? null : {
|
|
146
236
|
/** Parameter sets this build can do. Anything else falls through to JS.
|
|
147
237
|
*
|
|
148
238
|
* The Buffer check is not defensive padding. This package's browser-mode
|
|
@@ -161,22 +251,33 @@ export const native = SUPPORTED.size === 0 ? null : {
|
|
|
161
251
|
return [...SUPPORTED.keys()].sort()
|
|
162
252
|
},
|
|
163
253
|
|
|
254
|
+
/** Whether a signature carrying a context string can stay on this backend. */
|
|
255
|
+
supportsContext() {
|
|
256
|
+
return probeContext()
|
|
257
|
+
},
|
|
258
|
+
|
|
164
259
|
openssl: process.versions.openssl,
|
|
165
260
|
|
|
166
|
-
sign(alg, secretKey, message, publicKey) {
|
|
261
|
+
sign(alg, secretKey, message, publicKey, context) {
|
|
167
262
|
const spec = SUPPORTED.get(alg)
|
|
168
263
|
if (!spec) return null
|
|
169
|
-
|
|
264
|
+
const key = privateKeyObject(spec, secretKey, publicKey)
|
|
265
|
+
return crypto.sign(
|
|
266
|
+
null,
|
|
267
|
+
Buffer.from(message),
|
|
268
|
+
context === undefined ? key : { key, context: Buffer.from(context) },
|
|
269
|
+
)
|
|
170
270
|
},
|
|
171
271
|
|
|
172
|
-
verify(alg, publicKey, message, signature) {
|
|
272
|
+
verify(alg, publicKey, message, signature, context) {
|
|
173
273
|
const spec = SUPPORTED.get(alg)
|
|
174
274
|
if (!spec) return null
|
|
175
275
|
try {
|
|
276
|
+
const key = publicKeyObject(spec, publicKey)
|
|
176
277
|
return crypto.verify(
|
|
177
278
|
null,
|
|
178
279
|
Buffer.from(message),
|
|
179
|
-
|
|
280
|
+
context === undefined ? key : { key, context: Buffer.from(context) },
|
|
180
281
|
Buffer.from(signature)
|
|
181
282
|
)
|
|
182
283
|
} catch {
|
package/src/_native.stub.js
CHANGED
package/src/backend.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export interface BackendReport {
|
|
|
7
7
|
parameterSets?: string[]
|
|
8
8
|
/** Why the native backend is unavailable, on the JavaScript backend only. */
|
|
9
9
|
reason?: string
|
|
10
|
+
/** The operator's KXCO_PQ_BACKEND pin, when one is set. */
|
|
11
|
+
pinned?: 'openssl' | 'javascript'
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
/** Describe the backend doing the maths in this process. Reports; never switches. */
|
|
@@ -32,3 +34,26 @@ export function isNative(alg: string): boolean
|
|
|
32
34
|
* @throws Error with `code: 'ERR_KXCO_PQ_BACKEND'` when the requirement fails.
|
|
33
35
|
*/
|
|
34
36
|
export function requireNativeBackend(algorithms?: string[]): BackendReport
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Refuse to run unless the cryptography is executing in a named implementation.
|
|
40
|
+
*
|
|
41
|
+
* The general form of requireNativeBackend. Both implementations are being
|
|
42
|
+
* taken to algorithm validation, so a deployment under a control that names a
|
|
43
|
+
* certificate must be able to pin the one its certificate covers, and for some
|
|
44
|
+
* that is the JavaScript one.
|
|
45
|
+
*
|
|
46
|
+
* Asserting 'javascript' where OpenSSL is present fails unless the operator
|
|
47
|
+
* also set `KXCO_PQ_BACKEND=javascript`. This function reports; the
|
|
48
|
+
* environment decides. Keeping those apart is what stops application code
|
|
49
|
+
* quietly changing which implementation a customer's evidence is about.
|
|
50
|
+
*
|
|
51
|
+
* @param kind Which implementation must be running.
|
|
52
|
+
* @param algorithms Parameter sets that must run in it. Only meaningful for
|
|
53
|
+
* 'openssl'; the JavaScript backend covers every set.
|
|
54
|
+
* @throws Error with `code: 'ERR_KXCO_PQ_BACKEND'` when the requirement fails.
|
|
55
|
+
*/
|
|
56
|
+
export function requireBackend(
|
|
57
|
+
kind: 'openssl' | 'javascript',
|
|
58
|
+
algorithms?: string[],
|
|
59
|
+
): BackendReport
|
package/src/backend.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// wrong one, which is a different thing, and the thing a deployment under a
|
|
17
17
|
// validated-module control actually needs.
|
|
18
18
|
|
|
19
|
-
import { native } from '#native'
|
|
19
|
+
import { native, pinned } from '#native'
|
|
20
20
|
|
|
21
21
|
// An operator control. A deployment under a validated-module requirement is
|
|
22
22
|
// usually not the same team as the one calling this library, so the
|
|
@@ -46,13 +46,20 @@ export function backend() {
|
|
|
46
46
|
return {
|
|
47
47
|
kind: 'javascript',
|
|
48
48
|
library: '@noble/post-quantum',
|
|
49
|
-
|
|
49
|
+
// Two different facts wear the same result, and an evidence bundle that
|
|
50
|
+
// conflated them would be wrong. Absent means the runtime cannot do it.
|
|
51
|
+
// Pinned means it can and the operator said not to.
|
|
52
|
+
reason: pinned === 'javascript'
|
|
53
|
+
? 'KXCO_PQ_BACKEND=javascript pins this process to the JavaScript backend'
|
|
54
|
+
: 'the runtime does not provide the FIPS 203/204/205 primitives',
|
|
55
|
+
...(pinned ? { pinned } : {}),
|
|
50
56
|
}
|
|
51
57
|
}
|
|
52
58
|
return {
|
|
53
59
|
kind: 'openssl',
|
|
54
60
|
library: 'node:crypto',
|
|
55
61
|
openssl: native.openssl,
|
|
62
|
+
...(pinned ? { pinned } : {}),
|
|
56
63
|
// Only the sets OpenSSL can express. Anything absent here still works, on
|
|
57
64
|
// the JavaScript backend, which is why this is a list rather than a flag.
|
|
58
65
|
parameterSets: native.algorithms(),
|
|
@@ -93,20 +100,48 @@ export function isNative(alg) {
|
|
|
93
100
|
* @throws {Error} with `code: 'ERR_KXCO_PQ_BACKEND'` when the requirement fails.
|
|
94
101
|
*/
|
|
95
102
|
export function requireNativeBackend(algorithms) {
|
|
103
|
+
return requireBackend('openssl', algorithms)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Refuse to run unless the cryptography is executing in a named implementation.
|
|
108
|
+
*
|
|
109
|
+
* The general form of requireNativeBackend, and the reason it exists is that
|
|
110
|
+
* both implementations are being taken to algorithm validation. A deployment
|
|
111
|
+
* under a control that names a certificate has to be able to pin the one its
|
|
112
|
+
* certificate covers, and for some of them that is the JavaScript one.
|
|
113
|
+
*
|
|
114
|
+
* Asserting 'javascript' on a runtime that has OpenSSL will fail unless the
|
|
115
|
+
* operator also set KXCO_PQ_BACKEND=javascript, and that is deliberate. This
|
|
116
|
+
* function reports; the environment decides. Keeping the two apart is what
|
|
117
|
+
* stops application code quietly changing which implementation a customer's
|
|
118
|
+
* evidence is about.
|
|
119
|
+
*
|
|
120
|
+
* @param {'openssl'|'javascript'} kind
|
|
121
|
+
* @param {string[]} [algorithms] — parameter sets that must run in it. Only
|
|
122
|
+
* meaningful for 'openssl'; the JavaScript backend covers every set.
|
|
123
|
+
* @throws {Error} with `code: 'ERR_KXCO_PQ_BACKEND'` when the requirement fails.
|
|
124
|
+
*/
|
|
125
|
+
export function requireBackend(kind, algorithms) {
|
|
126
|
+
if (kind !== 'openssl' && kind !== 'javascript') {
|
|
127
|
+
throw backendError(`backend must be 'openssl' or 'javascript', got '${kind}'`, { required: kind })
|
|
128
|
+
}
|
|
96
129
|
const b = backend()
|
|
97
|
-
if (b.kind !==
|
|
130
|
+
if (b.kind !== kind) {
|
|
98
131
|
throw backendError(
|
|
99
|
-
`the
|
|
100
|
-
{ required:
|
|
132
|
+
`the ${kind} backend is required and is not the one running: ${b.reason ?? `this process is on ${b.kind}`}`,
|
|
133
|
+
{ required: kind, actual: b.kind, reason: b.reason, pinned: b.pinned },
|
|
101
134
|
)
|
|
102
135
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
`
|
|
108
|
-
|
|
109
|
-
|
|
136
|
+
if (kind === 'openssl') {
|
|
137
|
+
const missing = (algorithms ?? []).filter((a) => !isNative(a))
|
|
138
|
+
if (missing.length) {
|
|
139
|
+
throw backendError(
|
|
140
|
+
`the native backend is required for ${missing.join(', ')}, ` +
|
|
141
|
+
`and this OpenSSL does not provide ${missing.length > 1 ? 'them' : 'it'}`,
|
|
142
|
+
{ required: 'openssl', actual: b.kind, missing, available: b.parameterSets },
|
|
143
|
+
)
|
|
144
|
+
}
|
|
110
145
|
}
|
|
111
146
|
return b
|
|
112
147
|
}
|
package/src/index.js
CHANGED
|
@@ -37,4 +37,4 @@ export * as jws from './jws.js'
|
|
|
37
37
|
|
|
38
38
|
// Reports which backend is doing the maths in this process, for evidence
|
|
39
39
|
// bundles and support. It reports; it never switches.
|
|
40
|
-
export { backend, isNative, requireNativeBackend } from './backend.js'
|
|
40
|
+
export { backend, isNative, requireNativeBackend, requireBackend } from './backend.js'
|
package/src/ml-dsa-87.js
CHANGED
|
@@ -64,8 +64,13 @@ function wrap(bytes) {
|
|
|
64
64
|
// changes. The two backends are checked against each other for this parameter
|
|
65
65
|
// set in both directions by the interoperability matrix.
|
|
66
66
|
const NATIVE_ALG = 'ML-DSA-87'
|
|
67
|
+
// A context string stays on the native backend only where the runtime honours
|
|
68
|
+
// it; see the probe in _native.node.js. Where it does not, this falls back to
|
|
69
|
+
// the JavaScript backend exactly as it always did.
|
|
67
70
|
const usesNative = (context) =>
|
|
68
|
-
|
|
71
|
+
native !== null &&
|
|
72
|
+
native.supports(NATIVE_ALG) &&
|
|
73
|
+
(context === undefined || native.supportsContext())
|
|
69
74
|
|
|
70
75
|
/**
|
|
71
76
|
* Generate an ML-DSA-87 keypair from a master + domain-separation info.
|
|
@@ -104,7 +109,7 @@ export function keypairFromMaster(master, info = 'ml-dsa-87-v1') {
|
|
|
104
109
|
export function sign(secretKey, message, opts) {
|
|
105
110
|
const context = normalizeContext(opts)
|
|
106
111
|
if (usesNative(context)) {
|
|
107
|
-
return bytesToHex(native.sign(NATIVE_ALG, secretKey, toBytes(message)))
|
|
112
|
+
return bytesToHex(native.sign(NATIVE_ALG, secretKey, toBytes(message), undefined, context))
|
|
108
113
|
}
|
|
109
114
|
const sig = context === undefined
|
|
110
115
|
? ml_dsa87.sign(toBytes(message), secretKey)
|
|
@@ -135,7 +140,7 @@ export function verify(publicKey, message, sigHex, opts) {
|
|
|
135
140
|
const context = normalizeContext(opts)
|
|
136
141
|
try {
|
|
137
142
|
if (usesNative(context)) {
|
|
138
|
-
return native.verify(NATIVE_ALG, publicKey, toBytes(message), hexToBytes(sigHex))
|
|
143
|
+
return native.verify(NATIVE_ALG, publicKey, toBytes(message), hexToBytes(sigHex), context)
|
|
139
144
|
}
|
|
140
145
|
return context === undefined
|
|
141
146
|
? ml_dsa87.verify(hexToBytes(sigHex), toBytes(message), publicKey)
|
package/src/ml-dsa.js
CHANGED
|
@@ -20,12 +20,18 @@ export { MAX_CONTEXT_BYTES }
|
|
|
20
20
|
// set in both directions by the interoperability matrix, so this is a swap
|
|
21
21
|
// between two implementations known to agree, not an assumption that they do.
|
|
22
22
|
//
|
|
23
|
-
// A context string
|
|
24
|
-
//
|
|
25
|
-
//
|
|
23
|
+
// A context string used to force the JavaScript path unconditionally, because
|
|
24
|
+
// Node's sign and verify took no context argument. Newer builds take one and
|
|
25
|
+
// honour it, so the capability is probed and the call stays native where it
|
|
26
|
+
// holds. The fallback is unchanged everywhere else.
|
|
26
27
|
const NATIVE_ALG = 'ML-DSA-65'
|
|
28
|
+
// A context string stays on the native backend only where the runtime honours
|
|
29
|
+
// it; see the probe in _native.node.js. Where it does not, this falls back to
|
|
30
|
+
// the JavaScript backend exactly as it always did.
|
|
27
31
|
const usesNative = (context) =>
|
|
28
|
-
|
|
32
|
+
native !== null &&
|
|
33
|
+
native.supports(NATIVE_ALG) &&
|
|
34
|
+
(context === undefined || native.supportsContext())
|
|
29
35
|
|
|
30
36
|
const HAS_BUFFER = typeof Buffer !== 'undefined'
|
|
31
37
|
const enc = new TextEncoder()
|
|
@@ -85,7 +91,7 @@ export function keypairFromMaster(master, info = 'ml-dsa-65-v1') {
|
|
|
85
91
|
export function sign(secretKey, message, opts) {
|
|
86
92
|
const context = normalizeContext(opts)
|
|
87
93
|
if (usesNative(context)) {
|
|
88
|
-
return bytesToHex(native.sign(NATIVE_ALG, secretKey, toBytes(message)))
|
|
94
|
+
return bytesToHex(native.sign(NATIVE_ALG, secretKey, toBytes(message), undefined, context))
|
|
89
95
|
}
|
|
90
96
|
const sig = context === undefined
|
|
91
97
|
? ml_dsa65.sign(toBytes(message), secretKey)
|
|
@@ -115,7 +121,7 @@ export function verify(publicKey, message, sigHex, opts) {
|
|
|
115
121
|
const context = normalizeContext(opts)
|
|
116
122
|
try {
|
|
117
123
|
if (usesNative(context)) {
|
|
118
|
-
return native.verify(NATIVE_ALG, publicKey, toBytes(message), hexToBytes(sigHex))
|
|
124
|
+
return native.verify(NATIVE_ALG, publicKey, toBytes(message), hexToBytes(sigHex), context)
|
|
119
125
|
}
|
|
120
126
|
return context === undefined
|
|
121
127
|
? ml_dsa65.verify(hexToBytes(sigHex), toBytes(message), publicKey)
|
package/src/slh-dsa.js
CHANGED
|
@@ -52,8 +52,13 @@ const SEED_BYTES = slh_dsa_sha2_192s.lengths.seed
|
|
|
52
52
|
// changes. The two backends are checked against each other for this parameter
|
|
53
53
|
// set in both directions by the interoperability matrix.
|
|
54
54
|
const NATIVE_ALG = 'SLH-DSA-SHA2-192s'
|
|
55
|
+
// A context string stays on the native backend only where the runtime honours
|
|
56
|
+
// it; see the probe in _native.node.js. Where it does not, this falls back to
|
|
57
|
+
// the JavaScript backend exactly as it always did.
|
|
55
58
|
const usesNative = (context) =>
|
|
56
|
-
|
|
59
|
+
native !== null &&
|
|
60
|
+
native.supports(NATIVE_ALG) &&
|
|
61
|
+
(context === undefined || native.supportsContext())
|
|
57
62
|
|
|
58
63
|
/**
|
|
59
64
|
* Generate an SLH-DSA-SHA2-192s keypair from a master + domain-separation info.
|
|
@@ -85,7 +90,7 @@ export function keypairFromMaster(master, info = 'slh-dsa-sha2-192s-v1') {
|
|
|
85
90
|
export function sign(secretKey, message, opts) {
|
|
86
91
|
const context = normalizeContext(opts)
|
|
87
92
|
if (usesNative(context)) {
|
|
88
|
-
return bytesToHex(native.sign(NATIVE_ALG, secretKey, toBytes(message)))
|
|
93
|
+
return bytesToHex(native.sign(NATIVE_ALG, secretKey, toBytes(message), undefined, context))
|
|
89
94
|
}
|
|
90
95
|
const sig = context === undefined
|
|
91
96
|
? slh_dsa_sha2_192s.sign(toBytes(message), secretKey)
|
|
@@ -109,7 +114,7 @@ export function verify(publicKey, message, sigHex, opts) {
|
|
|
109
114
|
const context = normalizeContext(opts)
|
|
110
115
|
try {
|
|
111
116
|
if (usesNative(context)) {
|
|
112
|
-
return native.verify(NATIVE_ALG, publicKey, toBytes(message), hexToBytes(sigHex))
|
|
117
|
+
return native.verify(NATIVE_ALG, publicKey, toBytes(message), hexToBytes(sigHex), context)
|
|
113
118
|
}
|
|
114
119
|
return context === undefined
|
|
115
120
|
? slh_dsa_sha2_192s.verify(hexToBytes(sigHex), toBytes(message), publicKey)
|