Skip to content

Commit fc52abf

Browse files
committed
Remove usage of nistSecurityLevel.
1 parent 53cfa7b commit fc52abf

9 files changed

Lines changed: 104 additions & 157 deletions

File tree

lib/constants.js

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,5 @@ export const MULTICODEC_MLDSA44_PUBLIC_KEY_HEADER =
2121
export const MULTICODEC_MLDSA44_SECRET_KEY_HEADER =
2222
new Uint8Array([0x97, 0x26]);
2323

24-
// Supported NIST Security Levels
25-
export const NIST_SECURITY_LEVEL_2 = 2;
26-
27-
// MLDSA hash functions
28-
export const MLDSA_HASH = {
29-
SHA256: 'SHA-256',
30-
SHA384: 'SHA-384',
31-
SHA512: 'SHA-512'
32-
};
24+
// MLDSA hash function for ML-DSA-44
25+
export const MLDSA44_HASH = 'SHA-256';

lib/crypto-browser.js

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
* Copyright (c) 2023-2026 Digital Bazaar, Inc. All rights reserved.
33
*/
44
import {
5+
ALGORITHM_MAP,
56
CryptoKey,
67
mldsaExportKey,
78
mldsaGenerateKey,
89
mldsaImportKey,
910
mldsaSign,
1011
mldsaVerify,
11-
resolveNistSecurityLevel,
1212
} from './crypto-util.js';
1313

1414
// Extends the browser's native subtle crypto with ML-DSA support. Non-ML-DSA
@@ -107,15 +107,10 @@ class BrowserCrypto {
107107
}
108108

109109
function _isMldsaAlgorithm(algorithm) {
110-
if(!algorithm) {
111-
return false;
112-
}
113-
try {
114-
resolveNistSecurityLevel(algorithm);
115-
return true;
116-
} catch(e) {
110+
if(!algorithm?.name) {
117111
return false;
118112
}
113+
return ALGORITHM_MAP.has(algorithm.name);
119114
}
120115

121116
const _nativeCrypto = self.crypto ?? window.crypto;

lib/crypto-util.js

Lines changed: 29 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
*/
44
import * as base64url from 'base64url-universal';
55
import {ml_dsa44} from '@noble/post-quantum/ml-dsa.js';
6-
import {ALGORITHM, NIST_SECURITY_LEVEL_2} from './constants.js';
6+
import {ALGORITHM} from './constants.js';
77

8-
// Maps NIST security level to noble implementation and algorithm name
9-
export const NIST_LEVEL_MAP = new Map([
10-
[2, {impl: ml_dsa44, name: ALGORITHM.MLDSA44}],
8+
// Maps algorithm name to noble implementation
9+
export const ALGORITHM_MAP = new Map([
10+
[ALGORITHM.MLDSA44, {impl: ml_dsa44}],
1111
]);
1212

1313
export class CryptoKey {
@@ -34,7 +34,7 @@ export class CryptoKey {
3434
return this._extractable;
3535
}
3636

37-
// algorithm: KeyAlgorithm object (e.g. {name: "MLDSA", nistSecurityLevel: 2})
37+
// algorithm: KeyAlgorithm object (e.g. {name: "ML-DSA-44"})
3838
get algorithm() {
3939
return this._algorithm;
4040
}
@@ -48,7 +48,7 @@ export class CryptoKey {
4848
// Signs data using ML-DSA.
4949
export async function mldsaSign(key, data) {
5050
assertMldsaKey(key, 'private', 'sign');
51-
const {impl} = getImpl(key.algorithm.nistSecurityLevel);
51+
const {impl} = getImpl(key.algorithm.name);
5252
const msg = toUint8Array(data);
5353
const sig = impl.sign(msg, key._keyBytes);
5454
return sig.buffer.slice(sig.byteOffset, sig.byteOffset + sig.byteLength);
@@ -57,7 +57,7 @@ export async function mldsaSign(key, data) {
5757
// Verifies a signature over data using ML-DSA.
5858
export async function mldsaVerify(key, signature, data) {
5959
assertMldsaKey(key, 'public', 'verify');
60-
const {impl} = getImpl(key.algorithm.nistSecurityLevel);
60+
const {impl} = getImpl(key.algorithm.name);
6161
const msg = toUint8Array(data);
6262
const sig = toUint8Array(signature);
6363
try {
@@ -69,12 +69,9 @@ export async function mldsaVerify(key, signature, data) {
6969

7070
// Generates a new ML-DSA key pair from a 32-byte seed.
7171
export function mldsaGenerateKey(algorithm, extractable, keyUsages, seed) {
72-
const nistSecurityLevel = resolveNistSecurityLevel(algorithm);
73-
const {impl} = getImpl(nistSecurityLevel);
74-
const keyAlgorithm = {
75-
name: NIST_LEVEL_MAP.get(nistSecurityLevel).name,
76-
nistSecurityLevel
77-
};
72+
const algorithmName = resolveAlgorithmName(algorithm);
73+
const {impl} = getImpl(algorithmName);
74+
const keyAlgorithm = {name: algorithmName};
7875

7976
const {publicKey: pubBytes, secretKey: secBytes} = impl.keygen(seed);
8077

@@ -101,12 +98,9 @@ export function mldsaGenerateKey(algorithm, extractable, keyUsages, seed) {
10198
// Imports an ML-DSA key from various formats.
10299
export function mldsaImportKey(format, keyData, algorithm, extractable,
103100
keyUsages) {
104-
const nistSecurityLevel = resolveNistSecurityLevel(algorithm);
105-
const {impl} = getImpl(nistSecurityLevel);
106-
const keyAlgorithm = {
107-
name: NIST_LEVEL_MAP.get(nistSecurityLevel).name,
108-
nistSecurityLevel
109-
};
101+
const algorithmName = resolveAlgorithmName(algorithm);
102+
const {impl} = getImpl(algorithmName);
103+
const keyAlgorithm = {name: algorithmName};
110104

111105
if(format === 'spki' || format === 'pkcs8') {
112106
throw new DOMException(
@@ -152,7 +146,7 @@ export function mldsaImportKey(format, keyData, algorithm, extractable,
152146
}
153147

154148
if(format === 'jwk') {
155-
return importJwk({keyData, keyAlgorithm, nistSecurityLevel, impl,
149+
return importJwk({keyData, keyAlgorithm, algorithmName, impl,
156150
extractable, keyUsages});
157151
}
158152

@@ -165,8 +159,7 @@ export function mldsaExportKey(format, key) {
165159
if(!key.extractable) {
166160
throw new DOMException('Key is not extractable.', 'InvalidAccessError');
167161
}
168-
const {nistSecurityLevel} = key.algorithm;
169-
const {name} = getImpl(nistSecurityLevel);
162+
const {name} = key.algorithm;
170163

171164
if(format === 'spki' || format === 'pkcs8') {
172165
throw new DOMException(
@@ -233,27 +226,26 @@ export function mldsaExportKey(format, key) {
233226
'NotSupportedError');
234227
}
235228

236-
// Resolves nistSecurityLevel from an algorithm object or integer.
237-
export function resolveNistSecurityLevel(algorithm) {
238-
if(algorithm?.nistSecurityLevel) {
239-
return algorithm.nistSecurityLevel;
229+
// Resolves algorithm name from an algorithm object or string.
230+
export function resolveAlgorithmName(algorithm) {
231+
if(typeof algorithm === 'string') {
232+
return algorithm;
240233
}
241-
if(algorithm.name === ALGORITHM.MLDSA44) {
242-
return NIST_SECURITY_LEVEL_2;
234+
if(algorithm?.name) {
235+
return algorithm.name;
243236
}
244237
throw new DOMException(
245-
`Unknown NIST security level for given algorithm "${algorithm}". ` +
246-
'Only ML-DSA-44 (level 2) is supported.',
238+
`Unknown algorithm "${algorithm}". Only ML-DSA-44 is supported.`,
247239
'NotSupportedError');
248240
}
249241

250-
// Returns the noble implementation and name for a given NIST security level.
251-
export function getImpl(nistSecurityLevel) {
252-
const entry = NIST_LEVEL_MAP.get(nistSecurityLevel);
242+
// Returns the noble implementation for a given algorithm name.
243+
export function getImpl(algorithmName) {
244+
const entry = ALGORITHM_MAP.get(algorithmName);
253245
if(!entry) {
254246
throw new DOMException(
255-
`Unsupported NIST security level "${nistSecurityLevel}". ` +
256-
'Only ML-DSA-44 (level 2) is supported.',
247+
`Unsupported algorithm "${algorithmName}". ` +
248+
'Only ML-DSA-44 is supported.',
257249
'NotSupportedError');
258250
}
259251
return entry;
@@ -287,8 +279,8 @@ export function toUint8Array(data) {
287279

288280
// Imports a JWK-formatted key per webcryptomldsa spec Section 7.4.4.
289281
function importJwk({
290-
keyData, keyAlgorithm, nistSecurityLevel, impl, extractable, keyUsages}) {
291-
const {name} = getImpl(nistSecurityLevel);
282+
keyData, keyAlgorithm, algorithmName, impl, extractable, keyUsages}) {
283+
const {name} = keyAlgorithm;
292284
const jwk = keyData;
293285

294286
if(jwk.kty !== 'AKP') {

lib/factory.js

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,18 @@
11
/*!
22
* Copyright (c) 2023-2026 Digital Bazaar, Inc. All rights reserved.
33
*/
4-
import {ALGORITHM, NIST_SECURITY_LEVEL_2, MLDSA_HASH} from './constants.js';
4+
import {MLDSA44_HASH} from './constants.js';
55
import {webcrypto} from './crypto.js';
66

7-
// returns algorithm name for a given NIST security level
8-
function _getAlgorithmName(nistSecurityLevel) {
9-
if(nistSecurityLevel === NIST_SECURITY_LEVEL_2) {
10-
return ALGORITHM.MLDSA44;
11-
}
12-
return 'ML-DSA-UNKNOWN';
13-
}
14-
157
// exposes sign method
168
export function createSigner({id, secretKey}) {
179
if(!secretKey) {
1810
throw new Error('"secretKey" is required for signing.');
1911
}
20-
const {nistSecurityLevel} = secretKey.algorithm;
21-
const name = _getAlgorithmName(nistSecurityLevel);
12+
const {name} = secretKey.algorithm;
2213
const algorithm = {
2314
name,
24-
hash: {name: _getMldsaHash({nistSecurityLevel})}};
15+
hash: {name: _getMldsaHash(name)}};
2516
return {
2617
algorithm,
2718
id,
@@ -37,11 +28,10 @@ export function createVerifier({id, publicKey}) {
3728
if(!publicKey) {
3829
throw new Error('"publicKey" is required for verifying.');
3930
}
40-
const {nistSecurityLevel} = publicKey.algorithm;
41-
const name = _getAlgorithmName(nistSecurityLevel);
31+
const {name} = publicKey.algorithm;
4232
const algorithm = {
4333
name,
44-
hash: {name: _getMldsaHash({nistSecurityLevel})}
34+
hash: {name: _getMldsaHash(name)}
4535
};
4636
return {
4737
algorithm,
@@ -53,10 +43,10 @@ export function createVerifier({id, publicKey}) {
5343
}
5444

5545
// retrieves name of appropriate ML-DSA hash function
56-
function _getMldsaHash({nistSecurityLevel}) {
57-
if(nistSecurityLevel === NIST_SECURITY_LEVEL_2) {
58-
return MLDSA_HASH.SHA256;
46+
function _getMldsaHash(algorithmName) {
47+
if(algorithmName === 'ML-DSA-44') {
48+
return MLDSA44_HASH;
5949
}
6050
throw new TypeError(
61-
`Unsupported NIST Security Level "${nistSecurityLevel}".`);
51+
`Unsupported algorithm "${algorithmName}".`);
6252
}

lib/helpers.js

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,49 +5,49 @@ import {sha256} from '@noble/hashes/sha2.js';
55
import * as base58 from 'base58-universal';
66
import * as base64url from 'base64url-universal';
77
import {
8+
ALGORITHM,
89
MULTIBASE_BASE64URL_HEADER,
910
MULTICODEC_MLDSA44_PUBLIC_KEY_HEADER,
1011
MULTICODEC_MLDSA44_SECRET_KEY_HEADER,
11-
NIST_SECURITY_LEVEL_2,
1212
} from './constants.js';
1313

14-
// retrieves name of appropriate NIST security level from public Multikey
15-
export function getNistSecurityLevelFromPublicMultikey({publicMultikey}) {
14+
// retrieves algorithm name from public Multikey header bytes
15+
export function getAlgorithmFromPublicMultikey({publicMultikey}) {
1616
if(publicMultikey[0] === MULTICODEC_MLDSA44_PUBLIC_KEY_HEADER[0] &&
1717
publicMultikey[1] === MULTICODEC_MLDSA44_PUBLIC_KEY_HEADER[1]) {
18-
return NIST_SECURITY_LEVEL_2;
18+
return ALGORITHM.MLDSA44;
1919
}
2020

2121
throw new TypeError('Unsupported public multikey header.');
2222
}
2323

24-
// retrieves name of appropriate NIST security level from secret Multikey
25-
export function getNistSecurityLevelFromSecretMultikey({secretMultikey}) {
24+
// retrieves algorithm name from secret Multikey header bytes
25+
export function getAlgorithmFromSecretMultikey({secretMultikey}) {
2626
if(secretMultikey[0] === MULTICODEC_MLDSA44_SECRET_KEY_HEADER[0] &&
2727
secretMultikey[1] === MULTICODEC_MLDSA44_SECRET_KEY_HEADER[1]) {
28-
return NIST_SECURITY_LEVEL_2;
28+
return ALGORITHM.MLDSA44;
2929
}
3030

3131
throw new TypeError('Unsupported secret multikey header.');
3232
}
3333

3434
// retrieves byte size of secret key
35-
export function getSecretKeySize({nistSecurityLevel}) {
36-
if(nistSecurityLevel === NIST_SECURITY_LEVEL_2) {
35+
export function getSecretKeySize({algorithm}) {
36+
if(algorithm === ALGORITHM.MLDSA44) {
3737
return 2560;
3838
}
3939

40-
throw new TypeError(`Unsupported nistSecurityLevel "${nistSecurityLevel}".`);
40+
throw new TypeError(`Unsupported algorithm "${algorithm}".`);
4141
}
4242

4343
// sets secret key header bytes on key pair
44-
export function setSecretKeyHeader({nistSecurityLevel, buffer}) {
45-
if(nistSecurityLevel === NIST_SECURITY_LEVEL_2) {
44+
export function setSecretKeyHeader({algorithm, buffer}) {
45+
if(algorithm === ALGORITHM.MLDSA44) {
4646
buffer[0] = MULTICODEC_MLDSA44_SECRET_KEY_HEADER[0];
4747
buffer[1] = MULTICODEC_MLDSA44_SECRET_KEY_HEADER[1];
4848
} else {
4949
throw new TypeError(
50-
`Unsupported NIST security level "${nistSecurityLevel}".`);
50+
`Unsupported algorithm "${algorithm}".`);
5151
}
5252
}
5353

@@ -77,12 +77,12 @@ export function publicKeyBytesToKeyId({publicKeyBytes}) {
7777
}
7878

7979
// sets public key header bytes on key pair
80-
export function setPublicKeyHeader({nistSecurityLevel, buffer}) {
81-
if(nistSecurityLevel === NIST_SECURITY_LEVEL_2) {
80+
export function setPublicKeyHeader({algorithm, buffer}) {
81+
if(algorithm === ALGORITHM.MLDSA44) {
8282
buffer[0] = MULTICODEC_MLDSA44_PUBLIC_KEY_HEADER[0];
8383
buffer[1] = MULTICODEC_MLDSA44_PUBLIC_KEY_HEADER[1];
8484
} else {
8585
throw new TypeError(
86-
`Unsupported NIST security level "${nistSecurityLevel}".`);
86+
`Unsupported algorithm "${algorithm}".`);
8787
}
8888
}

lib/index.js

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
import {
55
ALGORITHM,
66
EXTRACTABLE,
7-
MULTIKEY_CONTEXT_V1_URL,
8-
NIST_SECURITY_LEVEL_2
7+
MULTIKEY_CONTEXT_V1_URL
98
} from './constants.js';
109
import {CryptoKey, webcrypto} from './crypto.js';
1110
import {createSigner, createVerifier} from './factory.js';
@@ -22,11 +21,10 @@ import {
2221
import {toMultikey} from './translators.js';
2322

2423
// generates ML-DSA key pair
25-
export async function generate({id, controller, nistSecurityLevel = 2} = {}) {
26-
const algorithm = {nistSecurityLevel};
27-
if(nistSecurityLevel === 2) {
28-
algorithm.name = ALGORITHM.MLDSA44;
29-
}
24+
export async function generate({
25+
id, controller, algorithm: algorithmName = ALGORITHM.MLDSA44
26+
} = {}) {
27+
const algorithm = {name: algorithmName};
3028
const keyPair = await webcrypto.subtle.generateKey(
3129
algorithm, EXTRACTABLE, ['sign', 'verify']);
3230
keyPair.secretKey = keyPair.privateKey;
@@ -111,15 +109,15 @@ export async function toJwk({keyPair, secretKey = false} = {}) {
111109

112110
// raw import from secretKey and/or publicKey bytes
113111
export async function fromRaw({
114-
nistSecurityLevel = NIST_SECURITY_LEVEL_2, secretKey, publicKey
112+
algorithm: algorithmName = ALGORITHM.MLDSA44, secretKey, publicKey
115113
} = {}) {
116114
if(secretKey && !(secretKey instanceof Uint8Array)) {
117115
throw new TypeError('"secretKey" must be a Uint8Array.');
118116
}
119117
if(!(publicKey instanceof Uint8Array)) {
120118
throw new TypeError('"publicKey" must be a Uint8Array.');
121119
}
122-
const cryptoKey = await cryptoKeyfromRaw({nistSecurityLevel,
120+
const cryptoKey = await cryptoKeyfromRaw({algorithm: algorithmName,
123121
secretKey, publicKey});
124122
const jwk = await webcrypto.subtle.exportKey('jwk', cryptoKey);
125123
return fromJwk({jwk, secretKey: !!secretKey});

0 commit comments

Comments
 (0)