mirror of
https://github.com/openpgpjs/openpgpjs.git
synced 2025-07-05 04:22:31 +00:00
WIP: Add ML-DSA
This commit is contained in:
parent
b9b5667a3f
commit
0468a41f98
5
package-lock.json
generated
5
package-lock.json
generated
@ -3698,7 +3698,6 @@
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.1.13"
|
||||
@ -3709,7 +3708,6 @@
|
||||
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz",
|
||||
"integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
@ -7028,8 +7026,7 @@
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "BSD-3-Clause"
|
||||
]
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.3.2",
|
||||
|
@ -331,8 +331,9 @@ export async function parsePrivateKeyParams(algo, bytes, publicParams) {
|
||||
}
|
||||
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||
const eccSecretKey = util.readExactSubarray(bytes, read, read + getCurvePayloadSize(enums.publicKey.ed25519)); read += eccSecretKey.length;
|
||||
const mldsaSecretKey = util.readExactSubarray(bytes, read, read + 4032); read += mldsaSecretKey.length;
|
||||
return { read, privateParams: { eccSecretKey, mldsaSecretKey } };
|
||||
const mldsaSeed = util.readExactSubarray(bytes, read, read + 32); read += mldsaSeed.length;
|
||||
const { mldsaSecretKey } = await publicKey.postQuantum.signature.mldsaExpandSecretSeed(algo, mldsaSeed);
|
||||
return { read, privateParams: { eccSecretKey, mldsaSecretKey, mldsaSeed } };
|
||||
}
|
||||
default:
|
||||
throw new UnsupportedError('Unknown public key encryption algorithm.');
|
||||
@ -429,6 +430,7 @@ export function serializeParams(algo, params) {
|
||||
|
||||
const excludedFields = {
|
||||
[enums.publicKey.pqc_mlkem_x25519]: new Set(['mlkemSecretKey']), // only `mlkemSeed` is serialized
|
||||
[enums.publicKey.pqc_mldsa_ed25519]: new Set(['mldsaSecretKey']) // only `mldsaSeed` is serialized
|
||||
};
|
||||
|
||||
const orderedParams = Object.keys(params).map(name => {
|
||||
@ -506,8 +508,8 @@ export async function generateParams(algo, bits, oid, symmetric) {
|
||||
publicParams: { eccPublicKey, mlkemPublicKey }
|
||||
}));
|
||||
case enums.publicKey.pqc_mldsa_ed25519:
|
||||
return publicKey.postQuantum.signature.generate(algo).then(({ eccSecretKey, eccPublicKey, mldsaSecretKey, mldsaPublicKey }) => ({
|
||||
privateParams: { eccSecretKey, mldsaSecretKey },
|
||||
return publicKey.postQuantum.signature.generate(algo).then(({ eccSecretKey, eccPublicKey, mldsaSeed, mldsaSecretKey, mldsaPublicKey }) => ({
|
||||
privateParams: { eccSecretKey, mldsaSeed, mldsaSecretKey },
|
||||
publicParams: { eccPublicKey, mldsaPublicKey }
|
||||
}));
|
||||
case enums.publicKey.dsa:
|
||||
@ -607,9 +609,9 @@ export async function validateParams(algo, publicParams, privateParams) {
|
||||
return publicKey.postQuantum.kem.validateParams(algo, eccPublicKey, eccSecretKey, mlkemPublicKey, mlkemSeed);
|
||||
}
|
||||
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||
const { eccSecretKey, mldsaSecretKey } = privateParams;
|
||||
const { eccSecretKey, mldsaSeed } = privateParams;
|
||||
const { eccPublicKey, mldsaPublicKey } = publicParams;
|
||||
return publicKey.postQuantum.signature.validateParams(algo, eccPublicKey, eccSecretKey, mldsaPublicKey, mldsaSecretKey);
|
||||
return publicKey.postQuantum.signature.validateParams(algo, eccPublicKey, eccSecretKey, mldsaPublicKey, mldsaSeed);
|
||||
}
|
||||
default:
|
||||
throw new Error('Unknown public key algorithm.');
|
||||
|
@ -1,5 +1,7 @@
|
||||
import * as kem from './kem/index';
|
||||
import * as signature from './signature';
|
||||
|
||||
export {
|
||||
kem
|
||||
kem,
|
||||
signature
|
||||
};
|
||||
|
49
src/crypto/public_key/post_quantum/signature/ecc_dsa.js
Normal file
49
src/crypto/public_key/post_quantum/signature/ecc_dsa.js
Normal file
@ -0,0 +1,49 @@
|
||||
// TODOOOOO is this file needed? vs inlining calls in signature.js?
|
||||
|
||||
|
||||
import * as eddsa from '../../elliptic/eddsa';
|
||||
import enums from '../../../../enums';
|
||||
|
||||
export async function generate(algo) {
|
||||
switch (algo) {
|
||||
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||
const { A, seed } = await eddsa.generate(enums.publicKey.ed25519);
|
||||
return {
|
||||
eccPublicKey: A,
|
||||
eccSecretKey: seed
|
||||
};
|
||||
}
|
||||
default:
|
||||
throw new Error('Unsupported signature algorithm');
|
||||
}
|
||||
}
|
||||
|
||||
export async function sign(signatureAlgo, hashAlgo, eccSecretKey, eccPublicKey, dataDigest) {
|
||||
switch (signatureAlgo) {
|
||||
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||
const { RS: eccSignature } = await eddsa.sign(enums.publicKey.ed25519, hashAlgo, null, eccPublicKey, eccSecretKey, dataDigest);
|
||||
|
||||
return { eccSignature };
|
||||
}
|
||||
default:
|
||||
throw new Error('Unsupported signature algorithm');
|
||||
}
|
||||
}
|
||||
|
||||
export async function verify(signatureAlgo, hashAlgo, eccPublicKey, dataDigest, eccSignature) {
|
||||
switch (signatureAlgo) {
|
||||
case enums.publicKey.pqc_mldsa_ed25519:
|
||||
return eddsa.verify(enums.publicKey.ed25519, hashAlgo, { RS: eccSignature }, null, eccPublicKey, dataDigest);
|
||||
default:
|
||||
throw new Error('Unsupported signature algorithm');
|
||||
}
|
||||
}
|
||||
|
||||
export async function validateParams(algo, eccPublicKey, eccSecretKey) {
|
||||
switch (algo) {
|
||||
case enums.publicKey.pqc_mldsa_ed25519:
|
||||
return eddsa.validateParams(enums.publicKey.ed25519, eccPublicKey, eccSecretKey);
|
||||
default:
|
||||
throw new Error('Unsupported signature algorithm');
|
||||
}
|
||||
}
|
2
src/crypto/public_key/post_quantum/signature/index.js
Normal file
2
src/crypto/public_key/post_quantum/signature/index.js
Normal file
@ -0,0 +1,2 @@
|
||||
export { generate, sign, verify, validateParams } from './signature';
|
||||
export { expandSecretSeed as mldsaExpandSecretSeed } from './ml_dsa';
|
71
src/crypto/public_key/post_quantum/signature/ml_dsa.js
Normal file
71
src/crypto/public_key/post_quantum/signature/ml_dsa.js
Normal file
@ -0,0 +1,71 @@
|
||||
import enums from '../../../../enums';
|
||||
import util from '../../../../util';
|
||||
import { getRandomBytes } from '../../../random';
|
||||
|
||||
export async function generate(algo) {
|
||||
switch (algo) {
|
||||
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||
const mldsaSeed = getRandomBytes(32);
|
||||
const { mldsaSecretKey, mldsaPublicKey } = await expandSecretSeed(algo, mldsaSeed);
|
||||
|
||||
return { mldsaSeed, mldsaSecretKey, mldsaPublicKey };
|
||||
}
|
||||
default:
|
||||
throw new Error('Unsupported signature algorithm');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand ML-DSA secret seed and retrieve the secret and public key material
|
||||
* @param {module:enums.publicKey} algo - Public key algorithm
|
||||
* @param {Uint8Array} seed - secret seed to expand
|
||||
* @returns {Promise<{ mldsaPublicKey: Uint8Array, mldsaSecretKey: Uint8Array }>}
|
||||
*/
|
||||
export async function expandSecretSeed(algo, seed) {
|
||||
switch (algo) {
|
||||
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||
const { ml_dsa65 } = await import('@noble/post-quantum/ml-dsa');
|
||||
const { secretKey: mldsaSecretKey, publicKey: mldsaPublicKey } = ml_dsa65.keygen(seed);
|
||||
|
||||
return { mldsaSecretKey, mldsaPublicKey };
|
||||
}
|
||||
default:
|
||||
throw new Error('Unsupported signature algorithm');
|
||||
}
|
||||
}
|
||||
|
||||
export async function sign(algo, mldsaSecretKey, dataDigest) {
|
||||
switch (algo) {
|
||||
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||
const { ml_dsa65 } = await import('@noble/post-quantum/ml-dsa');
|
||||
const dataDigestWithContext = util.concatUint8Array([new Uint8Array([0, 0]), dataDigest]);
|
||||
const mldsaSignature = ml_dsa65.sign(mldsaSecretKey, dataDigestWithContext);
|
||||
return { mldsaSignature };
|
||||
}
|
||||
default:
|
||||
throw new Error('Unsupported signature algorithm');
|
||||
}
|
||||
}
|
||||
|
||||
export async function verify(algo, mldsaPublicKey, dataDigest, mldsaSignature) {
|
||||
switch (algo) {
|
||||
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||
const { ml_dsa65 } = await import('@noble/post-quantum/ml-dsa');
|
||||
const dataDigestWithContext = util.concatUint8Array([new Uint8Array([0, 0]), dataDigest]);
|
||||
return ml_dsa65.verify(mldsaPublicKey, dataDigestWithContext, mldsaSignature);
|
||||
}
|
||||
default:
|
||||
throw new Error('Unsupported signature algorithm');
|
||||
}
|
||||
}
|
||||
|
||||
export async function validateParams(algo, mldsaPublicKey, mldsaSeed) {
|
||||
switch (algo) {
|
||||
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||
const { mldsaPublicKey: expectedPublicKey } = await expandSecretSeed(algo, mldsaSeed);
|
||||
return util.equalsUint8Array(mldsaPublicKey, expectedPublicKey);
|
||||
}
|
||||
default:
|
||||
throw new Error('Unsupported signature algorithm');
|
||||
}
|
||||
}
|
48
src/crypto/public_key/post_quantum/signature/signature.js
Normal file
48
src/crypto/public_key/post_quantum/signature/signature.js
Normal file
@ -0,0 +1,48 @@
|
||||
import enums from '../../../../enums';
|
||||
import * as mldsa from './ml_dsa';
|
||||
import * as eccdsa from './ecc_dsa';
|
||||
|
||||
export async function generate(algo) {
|
||||
switch (algo) {
|
||||
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||
const { eccSecretKey, eccPublicKey } = await eccdsa.generate(algo);
|
||||
const { mldsaSeed, mldsaSecretKey, mldsaPublicKey } = await mldsa.generate(algo);
|
||||
return { eccSecretKey, eccPublicKey, mldsaSeed, mldsaSecretKey, mldsaPublicKey };
|
||||
}
|
||||
default:
|
||||
throw new Error('Unsupported signature algorithm');
|
||||
}
|
||||
}
|
||||
|
||||
export async function sign(signatureAlgo, hashAlgo, eccSecretKey, eccPublicKey, mldsaSecretKey, dataDigest) {
|
||||
switch (signatureAlgo) {
|
||||
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||
const { eccSignature } = await eccdsa.sign(signatureAlgo, hashAlgo, eccSecretKey, eccPublicKey, dataDigest);
|
||||
const { mldsaSignature } = await mldsa.sign(signatureAlgo, mldsaSecretKey, dataDigest);
|
||||
|
||||
return { eccSignature, mldsaSignature };
|
||||
}
|
||||
default:
|
||||
throw new Error('Unsupported signature algorithm');
|
||||
}
|
||||
}
|
||||
|
||||
export async function verify(signatureAlgo, hashAlgo, eccPublicKey, mldsaPublicKey, dataDigest, { eccSignature, mldsaSignature }) {
|
||||
switch (signatureAlgo) {
|
||||
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||
const eccVerifiedPromise = eccdsa.verify(signatureAlgo, hashAlgo, eccPublicKey, dataDigest, eccSignature);
|
||||
const mldsaVerifiedPromise = mldsa.verify(signatureAlgo, mldsaPublicKey, dataDigest, mldsaSignature);
|
||||
const verified = await eccVerifiedPromise && await mldsaVerifiedPromise;
|
||||
return verified;
|
||||
}
|
||||
default:
|
||||
throw new Error('Unsupported signature algorithm');
|
||||
}
|
||||
}
|
||||
|
||||
export async function validateParams(algo, eccPublicKey, eccSecretKey, mldsaPublicKey, mldsaSeed) {
|
||||
const eccValidationPromise = eccdsa.validateParams(algo, eccPublicKey, eccSecretKey);
|
||||
const mldsaValidationPromise = mldsa.validateParams(algo, mldsaPublicKey, mldsaSeed);
|
||||
const valid = await eccValidationPromise && await mldsaValidationPromise;
|
||||
return valid;
|
||||
}
|
@ -70,6 +70,12 @@ export function parseSignatureParams(algo, signature) {
|
||||
const mac = new ShortByteString(); read += mac.read(signature.subarray(read));
|
||||
return { read, signatureParams: { mac } };
|
||||
}
|
||||
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||
const eccSignatureSize = 2 * publicKey.elliptic.eddsa.getPayloadSize(enums.publicKey.ed25519);
|
||||
const eccSignature = util.readExactSubarray(signature, read, read + eccSignatureSize); read += eccSignature.length;
|
||||
const mldsaSignature = util.readExactSubarray(signature, read, read + 3309); read += mldsaSignature.length;
|
||||
return { read, signatureParams: { eccSignature, mldsaSignature } };
|
||||
}
|
||||
default:
|
||||
throw new UnsupportedError('Unknown signature algorithm.');
|
||||
}
|
||||
@ -134,6 +140,10 @@ export async function verify(algo, hashAlgo, signature, publicParams, privatePar
|
||||
const { keyMaterial } = privateParams;
|
||||
return publicKey.hmac.verify(algo.getValue(), keyMaterial, signature.mac.data, hashed);
|
||||
}
|
||||
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||
const { eccPublicKey, mldsaPublicKey } = publicParams;
|
||||
return publicKey.postQuantum.signature.verify(algo, hashAlgo, eccPublicKey, mldsaPublicKey, hashed, signature);
|
||||
}
|
||||
default:
|
||||
throw new Error('Unknown signature algorithm.');
|
||||
}
|
||||
@ -195,6 +205,11 @@ export async function sign(algo, hashAlgo, publicKeyParams, privateKeyParams, da
|
||||
const mac = await publicKey.hmac.sign(algo.getValue(), keyMaterial, hashed);
|
||||
return { mac: new ShortByteString(mac) };
|
||||
}
|
||||
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||
const { eccPublicKey } = publicKeyParams;
|
||||
const { eccSecretKey, mldsaSecretKey } = privateKeyParams;
|
||||
return publicKey.postQuantum.signature.sign(algo, hashAlgo, eccSecretKey, eccPublicKey, mldsaSecretKey, hashed);
|
||||
}
|
||||
default:
|
||||
throw new Error('Unknown signature algorithm.');
|
||||
}
|
||||
|
@ -98,6 +98,9 @@ export default {
|
||||
ed448: 28,
|
||||
/** Post-quantum ML-KEM-768 + X25519 (Encrypt only) */
|
||||
pqc_mlkem_x25519: 105,
|
||||
/** Post-quantum ML-DSA-64 + Ed25519 (Sign only) */
|
||||
pqc_mldsa_ed25519: 107,
|
||||
|
||||
/** Persistent symmetric keys: encryption algorithm */
|
||||
aead: 100,
|
||||
/** Persistent symmetric keys: authentication algorithm */
|
||||
|
@ -399,7 +399,7 @@ export function sanitizeKeyOptions(options, subkeyDefaults = {}) {
|
||||
switch (options.type) {
|
||||
case 'pqc':
|
||||
if (options.sign) {
|
||||
throw new Error('Post-quantum signing algorithms are not yet supported.');
|
||||
options.algorithm = enums.publicKey.pqc_mldsa_ed25519;
|
||||
} else {
|
||||
options.algorithm = enums.publicKey.pqc_mlkem_x25519;
|
||||
}
|
||||
@ -462,6 +462,7 @@ export function validateSigningKeyPacket(keyPacket, signature, config) {
|
||||
case enums.publicKey.ed25519:
|
||||
case enums.publicKey.ed448:
|
||||
case enums.publicKey.hmac:
|
||||
case enums.publicKey.pqc_mldsa_ed25519:
|
||||
if (!signature.keyFlags && !config.allowMissingKeyFlags) {
|
||||
throw new Error('None of the key flags is set: consider passing `config.allowMissingKeyFlags`');
|
||||
}
|
||||
|
@ -138,9 +138,13 @@ class PublicKeyPacket {
|
||||
) {
|
||||
throw new Error('Legacy curve25519 cannot be used with v6 keys');
|
||||
}
|
||||
// The composite ML-DSA + EdDSA schemes MUST be used only with v6 keys.
|
||||
// The composite ML-KEM + ECDH schemes MUST be used only with v6 keys.
|
||||
if (this.version !== 6 && this.algorithm === enums.publicKey.pqc_mlkem_x25519) {
|
||||
throw new Error('Unexpected key version: ML-KEM algorithms can only be used with v6 keys');
|
||||
if (this.version !== 6 && (
|
||||
this.algorithm === enums.publicKey.pqc_mldsa_ed25519 ||
|
||||
this.algorithm === enums.publicKey.pqc_mlkem_x25519
|
||||
)) {
|
||||
throw new Error('Unexpected key version: ML-DSA and ML-KEM algorithms can only be used with v6 keys');
|
||||
}
|
||||
this.publicParams = publicParams;
|
||||
pos += read;
|
||||
|
@ -532,7 +532,10 @@ class SecretKeyPacket extends PublicKeyPacket {
|
||||
)) {
|
||||
throw new Error(`Cannot generate v6 keys of type 'ecc' with curve ${curve}. Generate a key of type 'curve25519' instead`);
|
||||
}
|
||||
if (this.version !== 6 && this.algorithm === enums.publicKey.pqc_mlkem_x25519) {
|
||||
if (this.version !== 6 && (
|
||||
this.algorithm === enums.publicKey.pqc_mldsa_ed25519 ||
|
||||
this.algorithm === enums.publicKey.pqc_mlkem_x25519
|
||||
)) {
|
||||
throw new Error(`Cannot generate v${this.version} keys of type 'pqc'. Generate a v6 key instead`);
|
||||
}
|
||||
const { privateParams, publicParams } = await crypto.generateParams(this.algorithm, bits, curve, symmetric);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -315,31 +315,41 @@ export default () => {
|
||||
});
|
||||
|
||||
describe('PQC parameter validation', function() {
|
||||
let pqcSigningKey;
|
||||
let pqcEncryptionSubkey;
|
||||
before(async () => {
|
||||
const key = await generatePrivateKeyObject({ type: 'symmetric', subkeys: [{ type: 'pqc', config: { v6Keys: true } }] });
|
||||
pqcEncryptionSubkey = key.subkeys[0];
|
||||
pqcSigningKey = await generatePrivateKeyObject({ type: 'pqc', config: { v6Keys: true } });
|
||||
pqcEncryptionSubkey = pqcSigningKey.subkeys[0];
|
||||
});
|
||||
|
||||
async function cloneSubeyPacket(subkey) {
|
||||
const subkeyPacket = new openpgp.SecretSubkeyPacket();
|
||||
await subkeyPacket.read(subkey.keyPacket.write());
|
||||
return subkeyPacket;
|
||||
}
|
||||
|
||||
it('generated params are valid', async function() {
|
||||
await expect(pqcSigningKey.keyPacket.validate()).to.not.be.rejected;
|
||||
await expect(pqcEncryptionSubkey.keyPacket.validate()).to.not.be.rejected;
|
||||
});
|
||||
|
||||
it('detect invalid ML-KEM public key part', async function() {
|
||||
const keyPacket = await cloneSubeyPacket(pqcEncryptionSubkey);
|
||||
const keyPacket = await cloneKeyPacket(pqcEncryptionSubkey);
|
||||
const { mlkemPublicKey } = keyPacket.publicParams;
|
||||
mlkemPublicKey[0]++;
|
||||
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||
});
|
||||
|
||||
it('detect invalid ECC-KEM key part', async function() {
|
||||
const keyPacket = await cloneSubeyPacket(pqcEncryptionSubkey);
|
||||
const keyPacket = await cloneKeyPacket(pqcEncryptionSubkey);
|
||||
const { eccPublicKey } = keyPacket.publicParams;
|
||||
eccPublicKey[0]++;
|
||||
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||
});
|
||||
|
||||
it('detect invalid ML-DSA public key part', async function() {
|
||||
const keyPacket = await cloneKeyPacket(pqcSigningKey);
|
||||
const { mldsaPublicKey } = keyPacket.publicParams;
|
||||
mldsaPublicKey[0]++;
|
||||
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||
});
|
||||
|
||||
it('detect invalid ECC part', async function() {
|
||||
const keyPacket = await cloneKeyPacket(pqcSigningKey);
|
||||
const { eccPublicKey } = keyPacket.publicParams;
|
||||
eccPublicKey[0]++;
|
||||
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||
|
@ -4607,15 +4607,19 @@ I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg==
|
||||
expect(v6Key.subkeys).to.have.length(1);
|
||||
});
|
||||
|
||||
it('should throw when trying to add a ML-KEM PQC key to a v4 key', async function() {
|
||||
it('should throw when trying to add a ML-KEM or ML-DSA PQC key to a v4 key', async function() {
|
||||
const v4Key = await openpgp.decryptKey({
|
||||
privateKey: await openpgp.readKey({ armoredKey: priv_key_rsa }),
|
||||
passphrase: 'hello world'
|
||||
});
|
||||
expect(v4Key.keyPacket.version).to.equal(4);
|
||||
expect(v4Key.subkeys).to.have.length(1);
|
||||
// try adding an ML-KEM subkey
|
||||
await expect(v4Key.addSubkey({ type: 'pqc', sign: false })).to.be.rejectedWith(/Cannot generate v4 keys of type 'pqc'/);
|
||||
expect(v4Key.subkeys).to.have.length(1);
|
||||
// try adding an ML-DSA subkey
|
||||
await expect(v4Key.addSubkey({ type: 'pqc', sign: true })).to.be.rejectedWith(/Cannot generate v4 keys of type 'pqc'/);
|
||||
expect(v4Key.subkeys).to.have.length(1);
|
||||
});
|
||||
|
||||
it('should throw when trying to encrypt a subkey separately from key', async function() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user