Compare commits

..

16 Commits

Author SHA1 Message Date
larabr
e28b39db5f 6.1.0-patch.0 2025-02-12 13:11:37 +01:00
larabr
61c744e769 Temporary workaround for Babel issue affecting web-app tests
The Babel plugin `@babel/plugin-transform-regenerator` used in the web-app Jest tests
(as part of @babel/preset-env) introduces a bug when transforming this specific minified
lightweight built.
The issue is that the mangled `ArrayStream` class name (`n` in the specific case) gets wrongly
reused and shadowed by a local variable in the `readPacket` function.
As a workaround for the problem, and to not have to amend the babel config for each monorepo workspace,
we disable mangling the specific class name.
2025-02-12 13:07:08 +01:00
larabr
299342946d PQC: switch to latest proposed variant for draft 7 of ML-KEM with X25519
Which also drops ECDH-KEM CCA-conversion.
This variant is yet to be adopted under draft 7, hence official test
vectors aren't available for now.

See https://github.com/openpgp-pqc/draft-openpgp-pqc/pull/161 .
2025-02-05 16:17:41 +01:00
larabr
55de9c0dc4 Palemoon patch: treat 'SyntaxError' as 'NotSupportedError' on EdDSA WebCrypto operations
Fallback to JS code instead of throwing.
2025-02-05 16:17:41 +01:00
larabr
8e497f419b PQC: Implement draft RFC for ML-DSA with Ed25519 (#13)
Implements Draft 6
(https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/06/).

Also, chunk ML-KEM and ML-DSA together in lightweight bundle.
Noble-curves had to be updated to v1.7.0 to ensure the same
version of noble-hashes is used as noble-post-quantum,
making it possible to reuse the sha3 code/chunk across libs.
2025-02-05 16:17:41 +01:00
larabr
f1f7ca972f PQC: Implement draft RFC for ML-KEM with X25519 (#10)
Implements Draft 6 with custom KEM combiner
(https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/06/)

Co-authored-by: Daniel Huigens <d.huigens@protonmail.com>
2025-02-05 14:31:54 +01:00
larabr
0ebf5797a5 Use noble-ed25519 over tweetnacl for signature verification (#16)
Much faster than tweetnacl, and no constant-timeness required.

We are not using v2 for now, despite being smaller, because it relies on
bigint literals, and it requires polyfilling the WebCrypto lib
manually in Node < 19.
2025-02-05 14:22:34 +01:00
larabr
9fe278241a Temporarily add config.ignoreSEIPDv2FeatureFlag for compatibility (#15)
SEIPDv2 is a more secure and faster choice, but it is
not necessarily compatible with other libs and our mobile apps.

Co-authored-by: Daniel Huigens <d.huigens@protonmail.com>
2025-02-05 14:22:34 +01:00
larabr
7cf978092b Only add SHA3 preferences to v6 keys
To ensure compatibility with older mobile clients, which may not
support verifying SHA3 message signatures
2025-02-05 14:22:34 +01:00
larabr
55f8ab2629 Add Argon2S2K.reloadWasmModule() for manually triggering memory deallocation (#14)
Also, make `ARGON2_WASM_MEMORY_THRESHOLD_RELOAD`
a static class property, to be able to change its value.
2025-02-05 14:22:34 +01:00
larabr
bec09a16fa Export Argon2S2K to reuse the WASM module outside of the OpenPGP.js context (#12) 2025-02-05 14:22:34 +01:00
larabr
9cf7a384aa Support generating subkeys with 'forwarded communication' flag to decrypt autoforwarded messages (#8)
These subkeys must not have the standard encryption flags (EtEr) set,
as they are not supposed to be used for direct messages.

Also:
- preserve 'forwarded communication' key flag when reformatting
- fix bug allowing to decrypt forwarded messages by setting
`config.allowInsecureDecryptionWithSigningKeys` instead of
`config.allowForwardedMessages`
- add TS definition for `config.allowForwardedMessages`
2025-02-05 14:22:34 +01:00
larabr
44327e9c95 Update support for decrypting autoforwarded messages (#6)
Update implementation to reflect spec changes to KDF params (v2 -> v255) and
new forwarding-related key flag (0x40).
2025-02-05 14:22:34 +01:00
Dan Ristea
07bedf3392 Add symmetric encryption and MAC support
To enable stored messages to be protected using symmetric key encryption and validated
using message authentication codes, this set of changes adds support for storing
symmetric key material as Secret Key Packets, symmetric key encrypted session keys as
Public Key Encrypted Session Key Packets, and MAC tags as Signature Packets.

Co-authored-by: Konstantinos Andrikopoulos <kandrikopoulos@proton.ch>
Co-authored-by: Daniel Huigens <d.huigens@protonmail.com>
2025-02-05 14:22:31 +01:00
larabr
d8bdf2bed3 Add support for decrypting autoforwarded messages (#1) 2025-02-05 14:13:59 +01:00
Daniel Huigens
cc79face33 Update package.json name and url 2025-02-05 14:13:55 +01:00
132 changed files with 5502 additions and 1418 deletions

View File

@ -1,10 +1,6 @@
version: 2
updates:
- package-ecosystem: "npm"
# The redundant target-branch directive is needed to set two different update schedules for npm,
# working around a dependabot limitation:
# see https://github.com/dependabot/dependabot-core/issues/1778#issuecomment-1988140219 .
target-branch: main
directory: "/"
schedule:
interval: "daily"
@ -30,4 +26,4 @@ updates:
- "@noble*"
update-types:
- "minor"
- "patch"
- "patch"

View File

@ -4,7 +4,8 @@
"id": "sop-openpgpjs-branch",
"path": "__SOP_OPENPGPJS__",
"env": {
"OPENPGPJS_PATH": "__OPENPGPJS_BRANCH__"
"OPENPGPJS_PATH": "__OPENPGPJS_BRANCH__",
"OPENPGPJS_CUSTOM_PROFILES": "{\"generate-key\": { \"post-quantum\": { \"description\": \"generate post-quantum v6 keys (relying on ML-DSA + ML-KEM)\", \"options\": { \"type\": \"pqc\", \"config\": { \"v6Keys\": true } } } } }"
}
},
{

View File

@ -106,7 +106,6 @@ jobs:
npx playwright install --with-deps firefox
- name: Install WebKit # caching not possible, external shared libraries required
if: ${{ matrix.runner == 'macos-latest' }} # do not install on ubuntu, since the X25519 WebCrypto implementation has issues
run: npx playwright install --with-deps webkit
- name: Run browser tests

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

35
openpgp.d.ts vendored
View File

@ -326,8 +326,10 @@ interface Config {
showVersion: boolean;
showComment: boolean;
aeadProtect: boolean;
ignoreSEIPDv2FeatureFlag: boolean;
allowUnauthenticatedMessages: boolean;
allowUnauthenticatedStream: boolean;
allowForwardedMessages: boolean;
minRSABits: number;
passwordCollisionCheck: boolean;
ignoreUnsupportedPackets: boolean;
@ -702,7 +704,7 @@ export type EllipticCurveName = 'ed25519Legacy' | 'curve25519Legacy' | 'nistP256
interface GenerateKeyOptions {
userIDs: MaybeArray<UserID>;
passphrase?: string;
type?: 'ecc' | 'rsa' | 'curve25519' | 'curve448';
type?: 'ecc' | 'rsa' | 'curve25519' | 'curve448' | 'pqc';
curve?: EllipticCurveName;
rsaBits?: number;
keyExpirationTime?: number;
@ -713,8 +715,9 @@ interface GenerateKeyOptions {
}
export type KeyOptions = GenerateKeyOptions;
export interface SubkeyOptions extends Pick<GenerateKeyOptions, 'type' | 'curve' | 'rsaBits' | 'keyExpirationTime' | 'date' | 'config'> {
export interface SubkeyOptions extends Pick<GenerateKeyOptions, 'type' | 'curve' | 'rsaBits' | 'keyExpirationTime' | 'date' | 'config' > {
sign?: boolean;
forwarding?: boolean;
}
export declare class KeyID {
@ -822,7 +825,7 @@ export namespace enums {
aeadEncryptedData = 20
}
export type publicKeyNames = 'rsaEncryptSign' | 'rsaEncrypt' | 'rsaSign' | 'elgamal' | 'dsa' | 'ecdh' | 'ecdsa' | 'eddsaLegacy' | 'aedh' | 'aedsa' | 'ed25519' | 'x25519' | 'ed448' | 'x448';
export type publicKeyNames = 'rsaEncryptSign' | 'rsaEncrypt' | 'rsaSign' | 'elgamal' | 'dsa' | 'ecdh' | 'ecdsa' | 'eddsaLegacy' | 'aedh' | 'aedsa' | 'ed25519' | 'x25519' | 'ed448' | 'x448' | 'pqc_mlkem_x25519' | 'pqc_mldsa_ed25519';
export enum publicKey {
rsaEncryptSign = 1,
rsaEncrypt = 2,
@ -837,7 +840,9 @@ export namespace enums {
x25519 = 25,
x448 = 26,
ed25519 = 27,
ed448 = 28
ed448 = 28,
pqc_mlkem_x25519 = 105,
pqc_mldsa_ed25519 = 107
}
export enum curve {
@ -891,6 +896,7 @@ export namespace enums {
encryptStorage = 8,
splitPrivateKey = 16,
authentication = 32,
forwardedCommunication = 64,
sharedPrivateKey = 128
}
@ -937,3 +943,24 @@ export namespace enums {
gnu = 101
}
}
export declare class Argon2S2K {
static reloadWasmModule(): void;
static ARGON2_WASM_MEMORY_THRESHOLD_RELOAD: number;
constructor(config: Config);
salt: Uint8Array;
/** @throws Argon2OutOfMemoryError */
produceKey(passphrase: string, keySize: number): Promise<Uint8Array>;
}
interface KDFParamsData {
version: number;
hash: enums.hash;
cipher: enums.symmetric;
replacementFingerprint?: Uint8Array;
}
export class KDFParams {
constructor(data: KDFParamsData);
write(forReplacementParams?: boolean): Uint8Array;
}

589
package-lock.json generated
View File

@ -1,17 +1,19 @@
{
"name": "openpgp",
"version": "6.1.0",
"name": "@protontech/openpgp",
"version": "6.1.0-patch.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "openpgp",
"version": "6.1.0",
"name": "@protontech/openpgp",
"version": "6.1.0-patch.0",
"license": "LGPL-3.0+",
"devDependencies": {
"@noble/ciphers": "^1.2.1",
"@noble/curves": "^1.8.1",
"@noble/ciphers": "^1.0.0",
"@noble/curves": "^1.7.0",
"@noble/ed25519": "^1.7.3",
"@noble/hashes": "^1.5.0",
"@noble/post-quantum": "^0.2.1",
"@openpgp/jsdoc": "^3.6.11",
"@openpgp/seek-bzip": "^1.0.5-git",
"@openpgp/tweetnacl": "^1.0.4-1",
@ -45,9 +47,9 @@
"eslint-plugin-chai-friendly": "^0.7.4",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-unicorn": "^48.0.1",
"fflate": "^0.8.2",
"fflate": "^0.7.4",
"mocha": "^10.7.3",
"playwright": "^1.51.1",
"playwright": "^1.48.2",
"rollup": "^4.24.2",
"sinon": "^18.0.1",
"ts-node": "^10.9.2",
@ -929,9 +931,9 @@
}
},
"node_modules/@noble/ciphers": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.2.1.tgz",
"integrity": "sha512-rONPWMC7PeExE077uLE4oqWrZ1IvAfz3oH9LibVAcVCopJiA9R62uavnbEzdkVmJYI6M6Zgkbeb07+tWjlq2XA==",
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.0.0.tgz",
"integrity": "sha512-wH5EHOmLi0rEazphPbecAzmjd12I6/Yv/SiHdkA9LSycsQk7RuuTp7am5/o62qYr0RScE7Pc9icXGBbsr6cesA==",
"dev": true,
"license": "MIT",
"engines": {
@ -942,13 +944,13 @@
}
},
"node_modules/@noble/curves": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.1.tgz",
"integrity": "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==",
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.7.0.tgz",
"integrity": "sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@noble/hashes": "1.7.1"
"@noble/hashes": "1.6.0"
},
"engines": {
"node": "^14.21.3 || >=16"
@ -957,10 +959,22 @@
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@noble/ed25519": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-1.7.3.tgz",
"integrity": "sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
]
},
"node_modules/@noble/hashes": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz",
"integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.6.0.tgz",
"integrity": "sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ==",
"dev": true,
"license": "MIT",
"engines": {
@ -970,6 +984,19 @@
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@noble/post-quantum": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/@noble/post-quantum/-/post-quantum-0.2.1.tgz",
"integrity": "sha512-ImgfMp9notXSEocz464o1AefYfFWEkkszKMGO+ZiTn73yIBFeNyEHKQUMS+SheJwSNymldSts6YyVcQDjcnVVg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@noble/hashes": "1.6.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -2763,85 +2790,6 @@
"node": ">=18.0.0"
}
},
"node_modules/@web/test-runner-chrome/node_modules/@web/test-runner-core": {
"name": "@openpgp/wtr-test-runner-core",
"version": "0.13.4-patch.0",
"resolved": "https://registry.npmjs.org/@openpgp/wtr-test-runner-core/-/wtr-test-runner-core-0.13.4-patch.0.tgz",
"integrity": "sha512-vcQXfDvDyVhQo4IpqHk/ksNayC/hKNjaN7ykXygtrGmOSdsVs1IGopNVzOgjjeISpZUytHnivJNEMtNWNPGgrg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.12.11",
"@types/babel__code-frame": "^7.0.2",
"@types/co-body": "^6.1.0",
"@types/convert-source-map": "^2.0.0",
"@types/debounce": "^1.2.0",
"@types/istanbul-lib-coverage": "^2.0.3",
"@types/istanbul-reports": "^3.0.0",
"@web/browser-logs": "^0.4.0",
"@web/dev-server-core": "^0.7.3",
"chokidar": "^4.0.1",
"cli-cursor": "^3.1.0",
"co-body": "^6.1.0",
"convert-source-map": "^2.0.0",
"debounce": "^1.2.0",
"dependency-graph": "^0.11.0",
"globby": "^11.0.1",
"internal-ip": "^6.2.0",
"istanbul-lib-coverage": "^3.0.0",
"istanbul-lib-report": "^3.0.1",
"istanbul-reports": "^3.0.2",
"log-update": "^4.0.0",
"nanocolors": "^0.2.1",
"nanoid": "^3.1.25",
"open": "^8.0.2",
"picomatch": "^2.2.2",
"source-map": "^0.7.3"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@web/test-runner-chrome/node_modules/chokidar": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dev": true,
"license": "MIT",
"dependencies": {
"readdirp": "^4.0.1"
},
"engines": {
"node": ">= 14.16.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@web/test-runner-chrome/node_modules/readdirp": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.1.tgz",
"integrity": "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14.18.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@web/test-runner-chrome/node_modules/source-map": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
"integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">= 8"
}
},
"node_modules/@web/test-runner-commands": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/@web/test-runner-commands/-/test-runner-commands-0.9.0.tgz",
@ -2856,11 +2804,10 @@
"node": ">=18.0.0"
}
},
"node_modules/@web/test-runner-commands/node_modules/@web/test-runner-core": {
"name": "@openpgp/wtr-test-runner-core",
"version": "0.13.4-patch.0",
"resolved": "https://registry.npmjs.org/@openpgp/wtr-test-runner-core/-/wtr-test-runner-core-0.13.4-patch.0.tgz",
"integrity": "sha512-vcQXfDvDyVhQo4IpqHk/ksNayC/hKNjaN7ykXygtrGmOSdsVs1IGopNVzOgjjeISpZUytHnivJNEMtNWNPGgrg==",
"node_modules/@web/test-runner-core": {
"version": "0.13.3",
"resolved": "https://registry.npmjs.org/@web/test-runner-core/-/test-runner-core-0.13.3.tgz",
"integrity": "sha512-ilDqF/v2sj0sD69FNSIDT7uw4M1yTVedLBt32/lXy3MMi6suCM7m/ZlhsBy8PXhf879WMvzBOl/vhJBpEMB9vA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -2872,8 +2819,8 @@
"@types/istanbul-lib-coverage": "^2.0.3",
"@types/istanbul-reports": "^3.0.0",
"@web/browser-logs": "^0.4.0",
"@web/dev-server-core": "^0.7.3",
"chokidar": "^4.0.1",
"@web/dev-server-core": "^0.7.2",
"chokidar": "^3.4.3",
"cli-cursor": "^3.1.0",
"co-body": "^6.1.0",
"convert-source-map": "^2.0.0",
@ -2895,11 +2842,11 @@
"node": ">=18.0.0"
}
},
"node_modules/@web/test-runner-commands/node_modules/chokidar": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dev": true,
"node_modules/@web/test-runner-core/node_modules/@web/dev-server-core/node_modules/chokidar": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz",
"integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==",
"extraneous": true,
"license": "MIT",
"dependencies": {
"readdirp": "^4.0.1"
@ -2911,21 +2858,7 @@
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@web/test-runner-commands/node_modules/readdirp": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.1.tgz",
"integrity": "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14.18.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@web/test-runner-commands/node_modules/source-map": {
"node_modules/@web/test-runner-core/node_modules/source-map": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
"integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
@ -2952,85 +2885,6 @@
"node": ">=18.0.0"
}
},
"node_modules/@web/test-runner-coverage-v8/node_modules/@web/test-runner-core": {
"name": "@openpgp/wtr-test-runner-core",
"version": "0.13.4-patch.0",
"resolved": "https://registry.npmjs.org/@openpgp/wtr-test-runner-core/-/wtr-test-runner-core-0.13.4-patch.0.tgz",
"integrity": "sha512-vcQXfDvDyVhQo4IpqHk/ksNayC/hKNjaN7ykXygtrGmOSdsVs1IGopNVzOgjjeISpZUytHnivJNEMtNWNPGgrg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.12.11",
"@types/babel__code-frame": "^7.0.2",
"@types/co-body": "^6.1.0",
"@types/convert-source-map": "^2.0.0",
"@types/debounce": "^1.2.0",
"@types/istanbul-lib-coverage": "^2.0.3",
"@types/istanbul-reports": "^3.0.0",
"@web/browser-logs": "^0.4.0",
"@web/dev-server-core": "^0.7.3",
"chokidar": "^4.0.1",
"cli-cursor": "^3.1.0",
"co-body": "^6.1.0",
"convert-source-map": "^2.0.0",
"debounce": "^1.2.0",
"dependency-graph": "^0.11.0",
"globby": "^11.0.1",
"internal-ip": "^6.2.0",
"istanbul-lib-coverage": "^3.0.0",
"istanbul-lib-report": "^3.0.1",
"istanbul-reports": "^3.0.2",
"log-update": "^4.0.0",
"nanocolors": "^0.2.1",
"nanoid": "^3.1.25",
"open": "^8.0.2",
"picomatch": "^2.2.2",
"source-map": "^0.7.3"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@web/test-runner-coverage-v8/node_modules/chokidar": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dev": true,
"license": "MIT",
"dependencies": {
"readdirp": "^4.0.1"
},
"engines": {
"node": ">= 14.16.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@web/test-runner-coverage-v8/node_modules/readdirp": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.1.tgz",
"integrity": "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14.18.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@web/test-runner-coverage-v8/node_modules/source-map": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
"integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">= 8"
}
},
"node_modules/@web/test-runner-mocha": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/@web/test-runner-mocha/-/test-runner-mocha-0.9.0.tgz",
@ -3044,85 +2898,6 @@
"node": ">=18.0.0"
}
},
"node_modules/@web/test-runner-mocha/node_modules/@web/test-runner-core": {
"name": "@openpgp/wtr-test-runner-core",
"version": "0.13.4-patch.0",
"resolved": "https://registry.npmjs.org/@openpgp/wtr-test-runner-core/-/wtr-test-runner-core-0.13.4-patch.0.tgz",
"integrity": "sha512-vcQXfDvDyVhQo4IpqHk/ksNayC/hKNjaN7ykXygtrGmOSdsVs1IGopNVzOgjjeISpZUytHnivJNEMtNWNPGgrg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.12.11",
"@types/babel__code-frame": "^7.0.2",
"@types/co-body": "^6.1.0",
"@types/convert-source-map": "^2.0.0",
"@types/debounce": "^1.2.0",
"@types/istanbul-lib-coverage": "^2.0.3",
"@types/istanbul-reports": "^3.0.0",
"@web/browser-logs": "^0.4.0",
"@web/dev-server-core": "^0.7.3",
"chokidar": "^4.0.1",
"cli-cursor": "^3.1.0",
"co-body": "^6.1.0",
"convert-source-map": "^2.0.0",
"debounce": "^1.2.0",
"dependency-graph": "^0.11.0",
"globby": "^11.0.1",
"internal-ip": "^6.2.0",
"istanbul-lib-coverage": "^3.0.0",
"istanbul-lib-report": "^3.0.1",
"istanbul-reports": "^3.0.2",
"log-update": "^4.0.0",
"nanocolors": "^0.2.1",
"nanoid": "^3.1.25",
"open": "^8.0.2",
"picomatch": "^2.2.2",
"source-map": "^0.7.3"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@web/test-runner-mocha/node_modules/chokidar": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dev": true,
"license": "MIT",
"dependencies": {
"readdirp": "^4.0.1"
},
"engines": {
"node": ">= 14.16.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@web/test-runner-mocha/node_modules/readdirp": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.1.tgz",
"integrity": "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14.18.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@web/test-runner-mocha/node_modules/source-map": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
"integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">= 8"
}
},
"node_modules/@web/test-runner-playwright": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@web/test-runner-playwright/-/test-runner-playwright-0.11.0.tgz",
@ -3138,85 +2913,6 @@
"node": ">=18.0.0"
}
},
"node_modules/@web/test-runner-playwright/node_modules/@web/test-runner-core": {
"name": "@openpgp/wtr-test-runner-core",
"version": "0.13.4-patch.0",
"resolved": "https://registry.npmjs.org/@openpgp/wtr-test-runner-core/-/wtr-test-runner-core-0.13.4-patch.0.tgz",
"integrity": "sha512-vcQXfDvDyVhQo4IpqHk/ksNayC/hKNjaN7ykXygtrGmOSdsVs1IGopNVzOgjjeISpZUytHnivJNEMtNWNPGgrg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.12.11",
"@types/babel__code-frame": "^7.0.2",
"@types/co-body": "^6.1.0",
"@types/convert-source-map": "^2.0.0",
"@types/debounce": "^1.2.0",
"@types/istanbul-lib-coverage": "^2.0.3",
"@types/istanbul-reports": "^3.0.0",
"@web/browser-logs": "^0.4.0",
"@web/dev-server-core": "^0.7.3",
"chokidar": "^4.0.1",
"cli-cursor": "^3.1.0",
"co-body": "^6.1.0",
"convert-source-map": "^2.0.0",
"debounce": "^1.2.0",
"dependency-graph": "^0.11.0",
"globby": "^11.0.1",
"internal-ip": "^6.2.0",
"istanbul-lib-coverage": "^3.0.0",
"istanbul-lib-report": "^3.0.1",
"istanbul-reports": "^3.0.2",
"log-update": "^4.0.0",
"nanocolors": "^0.2.1",
"nanoid": "^3.1.25",
"open": "^8.0.2",
"picomatch": "^2.2.2",
"source-map": "^0.7.3"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@web/test-runner-playwright/node_modules/chokidar": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dev": true,
"license": "MIT",
"dependencies": {
"readdirp": "^4.0.1"
},
"engines": {
"node": ">= 14.16.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@web/test-runner-playwright/node_modules/readdirp": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.1.tgz",
"integrity": "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14.18.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@web/test-runner-playwright/node_modules/source-map": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
"integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">= 8"
}
},
"node_modules/@web/test-runner-webdriver": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@web/test-runner-webdriver/-/test-runner-webdriver-0.8.0.tgz",
@ -3231,154 +2927,6 @@
"node": ">=18.0.0"
}
},
"node_modules/@web/test-runner-webdriver/node_modules/@web/test-runner-core": {
"name": "@openpgp/wtr-test-runner-core",
"version": "0.13.4-patch.0",
"resolved": "https://registry.npmjs.org/@openpgp/wtr-test-runner-core/-/wtr-test-runner-core-0.13.4-patch.0.tgz",
"integrity": "sha512-vcQXfDvDyVhQo4IpqHk/ksNayC/hKNjaN7ykXygtrGmOSdsVs1IGopNVzOgjjeISpZUytHnivJNEMtNWNPGgrg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.12.11",
"@types/babel__code-frame": "^7.0.2",
"@types/co-body": "^6.1.0",
"@types/convert-source-map": "^2.0.0",
"@types/debounce": "^1.2.0",
"@types/istanbul-lib-coverage": "^2.0.3",
"@types/istanbul-reports": "^3.0.0",
"@web/browser-logs": "^0.4.0",
"@web/dev-server-core": "^0.7.3",
"chokidar": "^4.0.1",
"cli-cursor": "^3.1.0",
"co-body": "^6.1.0",
"convert-source-map": "^2.0.0",
"debounce": "^1.2.0",
"dependency-graph": "^0.11.0",
"globby": "^11.0.1",
"internal-ip": "^6.2.0",
"istanbul-lib-coverage": "^3.0.0",
"istanbul-lib-report": "^3.0.1",
"istanbul-reports": "^3.0.2",
"log-update": "^4.0.0",
"nanocolors": "^0.2.1",
"nanoid": "^3.1.25",
"open": "^8.0.2",
"picomatch": "^2.2.2",
"source-map": "^0.7.3"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@web/test-runner-webdriver/node_modules/chokidar": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dev": true,
"license": "MIT",
"dependencies": {
"readdirp": "^4.0.1"
},
"engines": {
"node": ">= 14.16.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@web/test-runner-webdriver/node_modules/readdirp": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.1.tgz",
"integrity": "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14.18.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@web/test-runner-webdriver/node_modules/source-map": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
"integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">= 8"
}
},
"node_modules/@web/test-runner/node_modules/@web/test-runner-core": {
"name": "@openpgp/wtr-test-runner-core",
"version": "0.13.4-patch.0",
"resolved": "https://registry.npmjs.org/@openpgp/wtr-test-runner-core/-/wtr-test-runner-core-0.13.4-patch.0.tgz",
"integrity": "sha512-vcQXfDvDyVhQo4IpqHk/ksNayC/hKNjaN7ykXygtrGmOSdsVs1IGopNVzOgjjeISpZUytHnivJNEMtNWNPGgrg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.12.11",
"@types/babel__code-frame": "^7.0.2",
"@types/co-body": "^6.1.0",
"@types/convert-source-map": "^2.0.0",
"@types/debounce": "^1.2.0",
"@types/istanbul-lib-coverage": "^2.0.3",
"@types/istanbul-reports": "^3.0.0",
"@web/browser-logs": "^0.4.0",
"@web/dev-server-core": "^0.7.3",
"chokidar": "^4.0.1",
"cli-cursor": "^3.1.0",
"co-body": "^6.1.0",
"convert-source-map": "^2.0.0",
"debounce": "^1.2.0",
"dependency-graph": "^0.11.0",
"globby": "^11.0.1",
"internal-ip": "^6.2.0",
"istanbul-lib-coverage": "^3.0.0",
"istanbul-lib-report": "^3.0.1",
"istanbul-reports": "^3.0.2",
"log-update": "^4.0.0",
"nanocolors": "^0.2.1",
"nanoid": "^3.1.25",
"open": "^8.0.2",
"picomatch": "^2.2.2",
"source-map": "^0.7.3"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@web/test-runner/node_modules/chokidar": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dev": true,
"license": "MIT",
"dependencies": {
"readdirp": "^4.0.1"
},
"engines": {
"node": ">= 14.16.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@web/test-runner/node_modules/readdirp": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.1.tgz",
"integrity": "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14.18.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@web/test-runner/node_modules/source-map": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
@ -6554,11 +6102,10 @@
}
},
"node_modules/fflate": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
"integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
"dev": true,
"license": "MIT"
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.4.tgz",
"integrity": "sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==",
"dev": true
},
"node_modules/file-entry-cache": {
"version": "6.0.1",
@ -9995,13 +9542,13 @@
"dev": true
},
"node_modules/playwright": {
"version": "1.51.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.51.1.tgz",
"integrity": "sha512-kkx+MB2KQRkyxjYPc3a0wLZZoDczmppyGJIvQ43l+aZihkaVvmu/21kiyaHeHjiFxjxNNFnUncKmcGIyOojsaw==",
"version": "1.48.2",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.2.tgz",
"integrity": "sha512-NjYvYgp4BPmiwfe31j4gHLa3J7bD2WiBz8Lk2RoSsmX38SVIARZ18VYjxLjAcDsAhA+F4iSEXTSGgjua0rrlgQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.51.1"
"playwright-core": "1.48.2"
},
"bin": {
"playwright": "cli.js"
@ -10014,9 +9561,9 @@
}
},
"node_modules/playwright-core": {
"version": "1.51.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.51.1.tgz",
"integrity": "sha512-/crRMj8+j/Nq5s8QcvegseuyeZPxpQCZb6HNk3Sos3BlZyAknRjoyJPFWkpNn8v0+P3WiwqFF8P+zQo4eqiNuw==",
"version": "1.48.2",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.2.tgz",
"integrity": "sha512-sjjw+qrLFlriJo64du+EK0kJgZzoQPsabGF4lBvsid+3CNIZIYLgnMj9V6JY5VhM2Peh20DJWIVpVljLLnlawA==",
"dev": true,
"license": "Apache-2.0",
"bin": {

View File

@ -1,7 +1,7 @@
{
"name": "openpgp",
"name": "@protontech/openpgp",
"description": "OpenPGP.js is a Javascript implementation of the OpenPGP protocol. This is defined in RFC 4880.",
"version": "6.1.0",
"version": "6.1.0-patch.0",
"license": "LGPL-3.0+",
"homepage": "https://openpgpjs.org/",
"engines": {
@ -59,12 +59,14 @@
"docs": "jsdoc --configure .jsdocrc.cjs --destination docs --recurse README.md src && printf '%s' 'docs.openpgpjs.org' > docs/CNAME",
"preversion": "rm -rf dist docs node_modules && npm ci && npm test",
"version": "npm run docs && git add -A docs",
"postversion": "git push --follow-tags && npm publish"
"postversion": "git push && git push --tags && npm publish"
},
"devDependencies": {
"@noble/ciphers": "^1.2.1",
"@noble/curves": "^1.8.1",
"@noble/ciphers": "^1.0.0",
"@noble/curves": "^1.7.0",
"@noble/ed25519": "^1.7.3",
"@noble/hashes": "^1.5.0",
"@noble/post-quantum": "^0.2.1",
"@openpgp/jsdoc": "^3.6.11",
"@openpgp/seek-bzip": "^1.0.5-git",
"@openpgp/tweetnacl": "^1.0.4-1",
@ -98,9 +100,9 @@
"eslint-plugin-chai-friendly": "^0.7.4",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-unicorn": "^48.0.1",
"fflate": "^0.8.2",
"fflate": "^0.7.4",
"mocha": "^10.7.3",
"playwright": "^1.51.1",
"playwright": "^1.48.2",
"rollup": "^4.24.2",
"sinon": "^18.0.1",
"ts-node": "^10.9.2",
@ -110,11 +112,10 @@
"web-streams-polyfill": "^4.0.0"
},
"overrides": {
"@web/dev-server-core": "npm:@openpgp/wtr-dev-server-core@0.7.3-patch.1",
"@web/test-runner-core": "npm:@openpgp/wtr-test-runner-core@0.13.4-patch.0"
"@web/dev-server-core": "npm:@openpgp/wtr-dev-server-core@0.7.3-patch.1"
},
"repository": {
"type": "git",
"url": "https://github.com/openpgpjs/openpgpjs"
"url": "https://github.com/ProtonMail/openpgpjs"
}
}

View File

@ -51,6 +51,15 @@ const terserOptions = {
compress: {
unsafe: true
},
/**
* The Babel plugin `@babel/plugin-transform-regenerator` used in the web-app Jest tests
* (as part of @babel/preset-env) introduces a bug when transforming the minified lightweight built.
* The issue is that the mangled `ArrayStream` class name (`n` in the specific case) gets wrongly
* reused and shadowed by a local variable in the `readPacket` function (see https://github.com/babel/babel/issues/16334).
* As a workaround for the problem, and to not have to amend the babel config for each monorepo workspace, we disable mangling
* the specific class name.
*/
mangle: { reserved: ['ArrayStream'] },
output: {
comments: '/^(?:!|#__)/',
preserve_annotations: true
@ -61,8 +70,8 @@ const nodeBuild = {
input: 'src/index.js',
external: nodeBuiltinModules.concat(nodeDependencies),
output: [
{ file: 'dist/node/openpgp.cjs', format: 'cjs', name: pkg.name, banner, intro },
{ file: 'dist/node/openpgp.min.cjs', format: 'cjs', name: pkg.name, banner, intro, plugins: [terser(terserOptions)], sourcemap: true },
{ file: 'dist/node/openpgp.cjs', format: 'cjs', name: 'openpgp', banner, intro },
{ file: 'dist/node/openpgp.min.cjs', format: 'cjs', name: 'openpgp', banner, intro, plugins: [terser(terserOptions)], sourcemap: true },
{ file: 'dist/node/openpgp.mjs', format: 'es', banner, intro },
{ file: 'dist/node/openpgp.min.mjs', format: 'es', banner, intro, plugins: [terser(terserOptions)], sourcemap: true }
].map(options => ({ ...options, inlineDynamicImports: true })),
@ -85,8 +94,8 @@ const fullBrowserBuild = {
input: 'src/index.js',
external: nodeBuiltinModules.concat(nodeDependencies),
output: [
{ file: 'dist/openpgp.js', format: 'iife', name: pkg.name, banner, intro },
{ file: 'dist/openpgp.min.js', format: 'iife', name: pkg.name, banner, intro, plugins: [terser(terserOptions)], sourcemap: true },
{ file: 'dist/openpgp.js', format: 'iife', name: 'openpgp', banner, intro },
{ file: 'dist/openpgp.min.js', format: 'iife', name: 'openpgp', banner, intro, plugins: [terser(terserOptions)], sourcemap: true },
{ file: 'dist/openpgp.mjs', format: 'es', banner, intro },
{ file: 'dist/openpgp.min.mjs', format: 'es', banner, intro, plugins: [terser(terserOptions)], sourcemap: true }
].map(options => ({ ...options, inlineDynamicImports: true })),
@ -100,6 +109,13 @@ const fullBrowserBuild = {
commonjs({
ignore: nodeBuiltinModules.concat(nodeDependencies)
}),
replace({
include: 'node_modules/@noble/ed25519/**',
// Rollup ignores the `browser: { crypto: false }` directive in package.json, since `exports` are present,
// hence we need to manually drop it.
"import * as nodeCrypto from 'crypto'": 'const nodeCrypto = null',
delimiters: ['', '']
}),
replace({
'OpenPGP.js VERSION': `OpenPGP.js ${pkg.version}`,
"import { createRequire } from 'module';": 'const createRequire = () => () => {}',
@ -127,6 +143,13 @@ const lightweightBrowserBuild = {
commonjs({
ignore: nodeBuiltinModules.concat(nodeDependencies)
}),
replace({
include: 'node_modules/@noble/ed25519/**',
// Rollup ignores the `browser: { crypto: false }` directive in package.json, since `exports` are present,
// hence we need to manually drop it.
"import * as nodeCrypto from 'crypto'": 'const nodeCrypto = null',
delimiters: ['', '']
}),
replace({
'OpenPGP.js VERSION': `OpenPGP.js ${pkg.version}`,
"import { createRequire } from 'module';": 'const createRequire = () => () => {}',
@ -145,7 +168,7 @@ const testBuild = {
plugins: [
alias({
entries: {
openpgp: `./dist/${process.env.npm_config_lightweight ? 'lightweight/' : ''}openpgp.mjs`
'@protontech/openpgp': `./dist/${process.env.npm_config_lightweight ? 'lightweight/' : ''}openpgp.mjs`
}
}),
resolve({
@ -158,6 +181,13 @@ const testBuild = {
ignore: nodeBuiltinModules.concat(nodeDependencies),
requireReturnsDefault: 'preferred'
}),
replace({
include: 'node_modules/@noble/ed25519/**',
// Rollup ignores the `browser: { crypto: false }` directive in package.json, since `exports` are present,
// hence we need to manually drop it.
"import * as nodeCrypto from 'crypto'": 'const nodeCrypto = null',
delimiters: ['', '']
}),
replace({
"import { createRequire } from 'module';": 'const createRequire = () => () => {}',
delimiters: ['', '']

View File

@ -50,6 +50,14 @@ export default {
* @property {Boolean} aeadProtect
*/
aeadProtect: false,
/**
* Whether to disable encrypton using SEIPDv2 even if the encryption keys include the SEIPDv2 feature flag.
* If true, SEIPDv1 (i.e. no AEAD) packets are always used instead.
* SEIPDv2 is a more secure and faster choice, but it is not necessarily compatible with other libs and our mobile apps.
* @memberof module:config
* @property {Boolean} ignoreSEIPDv2FeatureFlag
*/
ignoreSEIPDv2FeatureFlag: false,
/**
* When reading OpenPGP v4 private keys (e.g. those generated in OpenPGP.js when not setting `config.v5Keys = true`)
* which were encrypted by OpenPGP.js v5 (or older) using `config.aeadProtect = true`,
@ -149,6 +157,13 @@ export default {
* @property {Boolean} allowUnauthenticatedStream
*/
allowUnauthenticatedStream: false,
/**
* Allow decrypting forwarded messages, using keys with 0x40 ('forwarded communication') flag.
* Note: this is related to a **non-standard feature**.
* @memberof module:config
* @property {Boolean} allowForwardedMessages
*/
allowForwardedMessages: false,
/**
* Minimum RSA key size allowed for key generation and message signing, verification and encryption.
* The default is 2047 since due to a bug, previous versions of OpenPGP.js could generate 2047-bit keys instead of 2048-bit ones.

View File

@ -73,8 +73,9 @@ async function OCB(cipher, key) {
// `encipher` and `decipher` cannot be async, since `crypt` shares state across calls,
// hence its execution cannot be broken up.
// As a result, WebCrypto cannot currently be used for `encipher`.
const encipher = block => nobleAesCbc(key, zeroBlock, { disablePadding: true }).encrypt(block);
const decipher = block => nobleAesCbc(key, zeroBlock, { disablePadding: true }).decrypt(block);
const aes = nobleAesCbc(key, zeroBlock, { disablePadding: true });
const encipher = block => aes.encrypt(block);
const decipher = block => aes.decrypt(block);
let mask;
constructKeyVariables(cipher, key);

View File

@ -23,16 +23,21 @@
* @module crypto/crypto
*/
import { rsa, elliptic, elgamal, dsa } from './public_key';
import { rsa, elliptic, elgamal, dsa, hmac, postQuantum } from './public_key';
import { getRandomBytes } from './random';
import { getCipherParams } from './cipher';
import ECDHSymkey from '../type/ecdh_symkey';
import ShortByteString from '../type/short_byte_string';
import { computeDigest, getHashByteLength } from './hash';
import config from '../config';
import KDFParams from '../type/kdf_params';
import { SymAlgoEnum, AEADEnum, HashEnum } from '../type/enum';
import enums from '../enums';
import util from '../util';
import OID from '../type/oid';
import { UnsupportedError } from '../packet/packet';
import ECDHXSymmetricKey from '../type/ecdh_x_symkey';
import { getAEADMode } from './cipherMode';
/**
* Encrypts data using specified algorithm and public key parameters.
@ -40,12 +45,13 @@ import ECDHXSymmetricKey from '../type/ecdh_x_symkey';
* @param {module:enums.publicKey} keyAlgo - Public key algorithm
* @param {module:enums.symmetric|null} symmetricAlgo - Cipher algorithm (v3 only)
* @param {Object} publicParams - Algorithm-specific public key parameters
* @param {Uint8Array} data - Session key data to be encrypted
* @param {Object} privateParams - Algorithm-specific private key parameters
* @param {Uint8Array} data - Data to be encrypted
* @param {Uint8Array} fingerprint - Recipient fingerprint
* @returns {Promise<Object>} Encrypted session key parameters.
* @async
*/
export async function publicKeyEncrypt(keyAlgo, symmetricAlgo, publicParams, data, fingerprint) {
export async function publicKeyEncrypt(keyAlgo, symmetricAlgo, publicParams, privateParams, data, fingerprint) {
switch (keyAlgo) {
case enums.publicKey.rsaEncrypt:
case enums.publicKey.rsaEncryptSign: {
@ -75,6 +81,27 @@ export async function publicKeyEncrypt(keyAlgo, symmetricAlgo, publicParams, dat
const C = ECDHXSymmetricKey.fromObject({ algorithm: symmetricAlgo, wrappedKey });
return { ephemeralPublicKey, C };
}
case enums.publicKey.aead: {
if (!privateParams) {
throw new Error('Cannot encrypt with symmetric key missing private parameters');
}
const { cipher: algo } = publicParams;
const algoValue = algo.getValue();
const { keyMaterial } = privateParams;
const aeadMode = config.preferredAEADAlgorithm;
const mode = getAEADMode(config.preferredAEADAlgorithm);
const { ivLength } = mode;
const iv = getRandomBytes(ivLength);
const modeInstance = await mode(algoValue, keyMaterial);
const c = await modeInstance.encrypt(data, iv, new Uint8Array());
return { aeadMode: new AEADEnum(aeadMode), iv, c: new ShortByteString(c) };
}
case enums.publicKey.pqc_mlkem_x25519: {
const { eccPublicKey, mlkemPublicKey } = publicParams;
const { eccCipherText, mlkemCipherText, wrappedKey } = await postQuantum.kem.encrypt(keyAlgo, eccPublicKey, mlkemPublicKey, data);
const C = ECDHXSymmetricKey.fromObject({ algorithm: symmetricAlgo, wrappedKey });
return { eccCipherText, mlkemCipherText, C };
}
default:
return [];
}
@ -94,8 +121,8 @@ export async function publicKeyEncrypt(keyAlgo, symmetricAlgo, publicParams, dat
* @throws {Error} on sensitive decryption error, unless `randomPayload` is given
* @async
*/
export async function publicKeyDecrypt(algo, publicKeyParams, privateKeyParams, sessionKeyParams, fingerprint, randomPayload) {
switch (algo) {
export async function publicKeyDecrypt(keyAlgo, publicKeyParams, privateKeyParams, sessionKeyParams, fingerprint, randomPayload) {
switch (keyAlgo) {
case enums.publicKey.rsaEncryptSign:
case enums.publicKey.rsaEncrypt: {
const { c } = sessionKeyParams;
@ -125,7 +152,24 @@ export async function publicKeyDecrypt(algo, publicKeyParams, privateKeyParams,
throw new Error('AES session key expected');
}
return elliptic.ecdhX.decrypt(
algo, ephemeralPublicKey, C.wrappedKey, A, k);
keyAlgo, ephemeralPublicKey, C.wrappedKey, A, k);
}
case enums.publicKey.aead: {
const { cipher: algo } = publicKeyParams;
const algoValue = algo.getValue();
const { keyMaterial } = privateKeyParams;
const { aeadMode, iv, c } = sessionKeyParams;
const mode = getAEADMode(aeadMode.getValue());
const modeInstance = await mode(algoValue, keyMaterial);
return modeInstance.decrypt(c.data, iv, new Uint8Array());
}
case enums.publicKey.pqc_mlkem_x25519: {
const { eccSecretKey, mlkemSecretKey } = privateKeyParams;
const { eccPublicKey, mlkemPublicKey } = publicKeyParams;
const { eccCipherText, mlkemCipherText, C } = sessionKeyParams;
return postQuantum.kem.decrypt(keyAlgo, eccCipherText, mlkemCipherText, eccSecretKey, eccPublicKey, mlkemSecretKey, mlkemPublicKey, C.wrappedKey);
}
default:
throw new Error('Unknown public key encryption algorithm.');
@ -191,6 +235,23 @@ export function parsePublicKeyParams(algo, bytes) {
const A = util.readExactSubarray(bytes, read, read + getCurvePayloadSize(algo)); read += A.length;
return { read, publicParams: { A } };
}
case enums.publicKey.hmac:
case enums.publicKey.aead: {
const algo = new SymAlgoEnum(); read += algo.read(bytes);
const digestLength = getHashByteLength(enums.hash.sha256);
const digest = bytes.subarray(read, read + digestLength); read += digestLength;
return { read: read, publicParams: { cipher: algo, digest } };
}
case enums.publicKey.pqc_mlkem_x25519: {
const eccPublicKey = util.readExactSubarray(bytes, read, read + getCurvePayloadSize(enums.publicKey.x25519)); read += eccPublicKey.length;
const mlkemPublicKey = util.readExactSubarray(bytes, read, read + 1184); read += mlkemPublicKey.length;
return { read, publicParams: { eccPublicKey, mlkemPublicKey } };
}
case enums.publicKey.pqc_mldsa_ed25519: {
const eccPublicKey = util.readExactSubarray(bytes, read, read + getCurvePayloadSize(enums.publicKey.ed25519)); read += eccPublicKey.length;
const mldsaPublicKey = util.readExactSubarray(bytes, read, read + 1952); read += mldsaPublicKey.length;
return { read, publicParams: { eccPublicKey, mldsaPublicKey } };
}
default:
throw new UnsupportedError('Unknown public key encryption algorithm.');
}
@ -200,10 +261,10 @@ export function parsePublicKeyParams(algo, bytes) {
* Parse private key material in binary form to get the key parameters
* @param {module:enums.publicKey} algo - The key algorithm
* @param {Uint8Array} bytes - The key material to parse
* @param {Object} publicParams - (ECC only) public params, needed to format some private params
* @param {Object} publicParams - (ECC and symmetric only) public params, needed to format some private params
* @returns {{ read: Number, privateParams: Object }} Number of read bytes plus the key parameters referenced by name.
*/
export function parsePrivateKeyParams(algo, bytes, publicParams) {
export async function parsePrivateKeyParams(algo, bytes, publicParams) {
let read = 0;
switch (algo) {
case enums.publicKey.rsaEncrypt:
@ -248,6 +309,32 @@ export function parsePrivateKeyParams(algo, bytes, publicParams) {
const k = util.readExactSubarray(bytes, read, read + payloadSize); read += k.length;
return { read, privateParams: { k } };
}
case enums.publicKey.hmac: {
const { cipher: algo } = publicParams;
const keySize = getHashByteLength(algo.getValue());
const hashSeed = bytes.subarray(read, read + 32); read += 32;
const keyMaterial = bytes.subarray(read, read + keySize); read += keySize;
return { read, privateParams: { hashSeed, keyMaterial } };
}
case enums.publicKey.aead: {
const { cipher: algo } = publicParams;
const hashSeed = bytes.subarray(read, read + 32); read += 32;
const { keySize } = getCipherParams(algo.getValue());
const keyMaterial = bytes.subarray(read, read + keySize); read += keySize;
return { read, privateParams: { hashSeed, keyMaterial } };
}
case enums.publicKey.pqc_mlkem_x25519: {
const eccSecretKey = util.readExactSubarray(bytes, read, read + getCurvePayloadSize(enums.publicKey.x25519)); read += eccSecretKey.length;
const mlkemSeed = util.readExactSubarray(bytes, read, read + 64); read += mlkemSeed.length;
const { mlkemSecretKey } = await postQuantum.kem.mlkemExpandSecretSeed(algo, mlkemSeed);
return { read, privateParams: { eccSecretKey, mlkemSecretKey, mlkemSeed } };
}
case enums.publicKey.pqc_mldsa_ed25519: {
const eccSecretKey = util.readExactSubarray(bytes, read, read + getCurvePayloadSize(enums.publicKey.ed25519)); read += eccSecretKey.length;
const mldsaSeed = util.readExactSubarray(bytes, read, read + 32); read += mldsaSeed.length;
const { mldsaSecretKey } = await postQuantum.signature.mldsaExpandSecretSeed(algo, mldsaSeed);
return { read, privateParams: { eccSecretKey, mldsaSecretKey, mldsaSeed } };
}
default:
throw new UnsupportedError('Unknown public key encryption algorithm.');
}
@ -297,6 +384,26 @@ export function parseEncSessionKeyParams(algo, bytes) {
const C = new ECDHXSymmetricKey(); C.read(bytes.subarray(read));
return { ephemeralPublicKey, C };
}
// Algorithm-Specific Fields for symmetric AEAD encryption:
// - AEAD algorithm
// - Starting initialization vector
// - Symmetric key encryption of "m" dependent on cipher and AEAD mode prefixed with a one-octet length
// - An authentication tag generated by the AEAD mode.
case enums.publicKey.aead: {
const aeadMode = new AEADEnum(); read += aeadMode.read(bytes.subarray(read));
const { ivLength } = getAEADMode(aeadMode.getValue());
const iv = bytes.subarray(read, read + ivLength); read += ivLength;
const c = new ShortByteString(); read += c.read(bytes.subarray(read));
return { aeadMode, iv, c };
}
case enums.publicKey.pqc_mlkem_x25519: {
const eccCipherText = util.readExactSubarray(bytes, read, read + getCurvePayloadSize(enums.publicKey.x25519)); read += eccCipherText.length;
const mlkemCipherText = util.readExactSubarray(bytes, read, read + 1088); read += mlkemCipherText.length;
const C = new ECDHXSymmetricKey(); C.read(bytes.subarray(read));
return { eccCipherText, mlkemCipherText, C }; // eccCipherText || mlkemCipherText || len(C) || C
}
default:
throw new UnsupportedError('Unknown public key encryption algorithm.');
}
@ -314,9 +421,23 @@ export function serializeParams(algo, params) {
enums.publicKey.ed25519,
enums.publicKey.x25519,
enums.publicKey.ed448,
enums.publicKey.x448
enums.publicKey.x448,
enums.publicKey.aead,
enums.publicKey.hmac,
enums.publicKey.pqc_mlkem_x25519,
enums.publicKey.pqc_mldsa_ed25519
]);
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 => {
if (excludedFields[algo]?.has(name)) {
return new Uint8Array();
}
const param = params[name];
if (!util.isUint8Array(param)) return param.write();
return algosWithNativeRepresentation.has(algo) ? param : util.uint8ArrayToMPI(param);
@ -329,10 +450,11 @@ export function serializeParams(algo, params) {
* @param {module:enums.publicKey} algo - The public key algorithm
* @param {Integer} bits - Bit length for RSA keys
* @param {module:type/oid} oid - Object identifier for ECC keys
* @param {module:enums.symmetric|enums.hash} symmetric - Hash or cipher algorithm for symmetric keys
* @returns {Promise<{ publicParams: {Object}, privateParams: {Object} }>} The parameters referenced by name.
* @async
*/
export function generateParams(algo, bits, oid) {
export async function generateParams(algo, bits, oid, symmetric) {
switch (algo) {
case enums.publicKey.rsaEncrypt:
case enums.publicKey.rsaEncryptSign:
@ -372,6 +494,24 @@ export function generateParams(algo, bits, oid) {
privateParams: { k },
publicParams: { A }
}));
case enums.publicKey.hmac: {
const keyMaterial = await hmac.generate(symmetric);
return createSymmetricParams(keyMaterial, new HashEnum(symmetric));
}
case enums.publicKey.aead: {
const keyMaterial = generateSessionKey(symmetric);
return createSymmetricParams(keyMaterial, new SymAlgoEnum(symmetric));
}
case enums.publicKey.pqc_mlkem_x25519:
return postQuantum.kem.generate(algo).then(({ eccSecretKey, eccPublicKey, mlkemSeed, mlkemSecretKey, mlkemPublicKey }) => ({
privateParams: { eccSecretKey, mlkemSeed, mlkemSecretKey },
publicParams: { eccPublicKey, mlkemPublicKey }
}));
case enums.publicKey.pqc_mldsa_ed25519:
return postQuantum.signature.generate(algo).then(({ eccSecretKey, eccPublicKey, mldsaSeed, mldsaSecretKey, mldsaPublicKey }) => ({
privateParams: { eccSecretKey, mldsaSeed, mldsaSecretKey },
publicParams: { eccPublicKey, mldsaPublicKey }
}));
case enums.publicKey.dsa:
case enums.publicKey.elgamal:
throw new Error('Unsupported algorithm for key generation.');
@ -380,6 +520,21 @@ export function generateParams(algo, bits, oid) {
}
}
async function createSymmetricParams(key, algo) {
const seed = getRandomBytes(32);
const bindingHash = await computeDigest(enums.hash.sha256,seed);
return {
privateParams: {
hashSeed: seed,
keyMaterial: key
},
publicParams: {
cipher: algo,
digest: bindingHash
}
};
}
/**
* Validate algorithm-specific key parameters
* @param {module:enums.publicKey} algo - The public key algorithm
@ -434,6 +589,30 @@ export async function validateParams(algo, publicParams, privateParams) {
const { k } = privateParams;
return elliptic.ecdhX.validateParams(algo, A, k);
}
case enums.publicKey.hmac: {
const { cipher: algo, digest } = publicParams;
const { hashSeed, keyMaterial } = privateParams;
const keySize = getHashByteLength(algo.getValue());
return keySize === keyMaterial.length &&
util.equalsUint8Array(digest, await computeDigest(enums.hash.sha256, hashSeed));
}
case enums.publicKey.aead: {
const { cipher: algo, digest } = publicParams;
const { hashSeed, keyMaterial } = privateParams;
const { keySize } = getCipherParams(algo.getValue());
return keySize === keyMaterial.length &&
util.equalsUint8Array(digest, await computeDigest(enums.hash.sha256, hashSeed));
}
case enums.publicKey.pqc_mlkem_x25519: {
const { eccSecretKey, mlkemSeed } = privateParams;
const { eccPublicKey, mlkemPublicKey } = publicParams;
return postQuantum.kem.validateParams(algo, eccPublicKey, eccSecretKey, mlkemPublicKey, mlkemSeed);
}
case enums.publicKey.pqc_mldsa_ed25519: {
const { eccSecretKey, mldsaSeed } = privateParams;
const { eccPublicKey, mldsaPublicKey } = publicParams;
return postQuantum.signature.validateParams(algo, eccPublicKey, eccSecretKey, mldsaPublicKey, mldsaSeed);
}
default:
throw new Error('Unknown public key algorithm.');
}

View File

@ -50,9 +50,9 @@ function buildEcdhParam(public_algo, oid, kdfParams, fingerprint) {
return util.concatUint8Array([
oid.write(),
new Uint8Array([public_algo]),
kdfParams.write(),
kdfParams.write(true),
util.stringToUint8Array('Anonymous Sender '),
fingerprint
kdfParams.replacementFingerprint || fingerprint
]);
}

View File

@ -11,7 +11,6 @@ import enums from '../../../enums';
import util from '../../../util';
import computeHKDF from '../../hkdf';
import { getCipherParams } from '../../cipher';
import { b64ToUint8Array, uint8ArrayToB64 } from '../../../encoding/base64';
const HKDF_INFO = {
x25519: util.encodeUTF8('OpenPGP X25519'),
@ -25,27 +24,12 @@ const HKDF_INFO = {
*/
export async function generate(algo) {
switch (algo) {
case enums.publicKey.x25519:
try {
const webCrypto = util.getWebCrypto();
const webCryptoKey = await webCrypto.generateKey('X25519', true, ['deriveKey', 'deriveBits']);
const privateKey = await webCrypto.exportKey('jwk', webCryptoKey.privateKey);
const publicKey = await webCrypto.exportKey('jwk', webCryptoKey.publicKey);
return {
A: new Uint8Array(b64ToUint8Array(publicKey.x)),
k: b64ToUint8Array(privateKey.d)
};
} catch (err) {
if (err.name !== 'NotSupportedError') {
throw err;
}
// k stays in little-endian, unlike legacy ECDH over curve25519
const k = getRandomBytes(32);
const { publicKey: A } = x25519.box.keyPair.fromSecretKey(k);
return { A, k };
}
case enums.publicKey.x25519: {
// k stays in little-endian, unlike legacy ECDH over curve25519
const k = getRandomBytes(32);
const { publicKey: A } = x25519.box.keyPair.fromSecretKey(k);
return { A, k };
}
case enums.publicKey.x448: {
const x448 = await util.getNobleCurve(enums.publicKey.x448);
@ -187,32 +171,13 @@ export function getPayloadSize(algo) {
*/
export async function generateEphemeralEncryptionMaterial(algo, recipientA) {
switch (algo) {
case enums.publicKey.x25519:
try {
const webCrypto = util.getWebCrypto();
const jwk = publicKeyToJWK(algo, recipientA);
const ephemeralKeyPair = await webCrypto.generateKey('X25519', true, ['deriveKey', 'deriveBits']);
const recipientPublicKey = await webCrypto.importKey('jwk', jwk, 'X25519', false, []);
const sharedSecretBuffer = await webCrypto.deriveBits(
{ name: 'X25519', public: recipientPublicKey },
ephemeralKeyPair.privateKey,
getPayloadSize(algo) * 8 // in bits
);
const ephemeralPublicKeyJwt = await webCrypto.exportKey('jwk', ephemeralKeyPair.publicKey);
return {
sharedSecret: new Uint8Array(sharedSecretBuffer),
ephemeralPublicKey: new Uint8Array(b64ToUint8Array(ephemeralPublicKeyJwt.x))
};
} catch (err) {
if (err.name !== 'NotSupportedError') {
throw err;
}
const ephemeralSecretKey = getRandomBytes(getPayloadSize(algo));
const sharedSecret = x25519.scalarMult(ephemeralSecretKey, recipientA);
assertNonZeroArray(sharedSecret);
const { publicKey: ephemeralPublicKey } = x25519.box.keyPair.fromSecretKey(ephemeralSecretKey);
return { ephemeralPublicKey, sharedSecret };
}
case enums.publicKey.x25519: {
const ephemeralSecretKey = getRandomBytes(getPayloadSize(algo));
const sharedSecret = x25519.scalarMult(ephemeralSecretKey, recipientA);
assertNonZeroArray(sharedSecret);
const { publicKey: ephemeralPublicKey } = x25519.box.keyPair.fromSecretKey(ephemeralSecretKey);
return { ephemeralPublicKey, sharedSecret };
}
case enums.publicKey.x448: {
const x448 = await util.getNobleCurve(enums.publicKey.x448);
const ephemeralSecretKey = x448.utils.randomPrivateKey();
@ -228,27 +193,11 @@ export async function generateEphemeralEncryptionMaterial(algo, recipientA) {
export async function recomputeSharedSecret(algo, ephemeralPublicKey, A, k) {
switch (algo) {
case enums.publicKey.x25519:
try {
const webCrypto = util.getWebCrypto();
const privateKeyJWK = privateKeyToJWK(algo, A, k);
const ephemeralPublicKeyJWK = publicKeyToJWK(algo, ephemeralPublicKey);
const privateKey = await webCrypto.importKey('jwk', privateKeyJWK, 'X25519', false, ['deriveKey', 'deriveBits']);
const ephemeralPublicKeyReference = await webCrypto.importKey('jwk', ephemeralPublicKeyJWK, 'X25519', false, []);
const sharedSecretBuffer = await webCrypto.deriveBits(
{ name: 'X25519', public: ephemeralPublicKeyReference },
privateKey,
getPayloadSize(algo) * 8 // in bits
);
return new Uint8Array(sharedSecretBuffer);
} catch (err) {
if (err.name !== 'NotSupportedError') {
throw err;
}
const sharedSecret = x25519.scalarMult(k, ephemeralPublicKey);
assertNonZeroArray(sharedSecret);
return sharedSecret;
}
case enums.publicKey.x25519: {
const sharedSecret = x25519.scalarMult(k, ephemeralPublicKey);
assertNonZeroArray(sharedSecret);
return sharedSecret;
}
case enums.publicKey.x448: {
const x448 = await util.getNobleCurve(enums.publicKey.x448);
const sharedSecret = x448.getSharedSecret(k, ephemeralPublicKey);
@ -275,32 +224,3 @@ function assertNonZeroArray(sharedSecret) {
throw new Error('Unexpected low order point');
}
}
function publicKeyToJWK(algo, publicKey) {
switch (algo) {
case enums.publicKey.x25519: {
const jwk = {
kty: 'OKP',
crv: 'X25519',
x: uint8ArrayToB64(publicKey, true),
ext: true
};
return jwk;
}
default:
throw new Error('Unsupported ECDH algorithm');
}
}
function privateKeyToJWK(algo, publicKey, privateKey) {
switch (algo) {
case enums.publicKey.x25519: {
const jwk = publicKeyToJWK(algo, publicKey);
jwk.d = uint8ArrayToB64(privateKey, true);
return jwk;
}
default:
throw new Error('Unsupported ECDH algorithm');
}
}

View File

@ -20,7 +20,9 @@
* @module crypto/public_key/elliptic/eddsa
*/
import ed25519 from '@openpgp/tweetnacl';
import naclEd25519 from '@openpgp/tweetnacl'; // better constant-timeness as it uses Uint8Arrays over BigInts
import { verify as nobleEd25519Verify } from '@noble/ed25519';
import util from '../../../util';
import enums from '../../../enums';
import { getHashByteLength } from '../../hash';
@ -48,11 +50,15 @@ export async function generate(algo) {
seed: b64ToUint8Array(privateKey.d, true)
};
} catch (err) {
if (err.name !== 'NotSupportedError' && err.name !== 'OperationError') { // Temporary (hopefully) fix for WebKit on Linux
if (
err.name !== 'NotSupportedError' &&
err.name !== 'OperationError' && // Temporary (hopefully) fix for WebKit on Linux
err.name !== 'SyntaxError' // Temporary fix for Palemoon throwing 'SyntaxError'
) {
throw err;
}
const seed = getRandomBytes(getPayloadSize(algo));
const { publicKey: A } = ed25519.sign.keyPair.fromSeed(seed);
const { publicKey: A } = naclEd25519.sign.keyPair.fromSeed(seed);
return { A, seed };
}
@ -100,11 +106,11 @@ export async function sign(algo, hashAlgo, message, publicKey, privateKey, hashe
return { RS: signature };
} catch (err) {
if (err.name !== 'NotSupportedError') {
if (err.name !== 'NotSupportedError' && err.name !== 'SyntaxError') { // Temporary fix for Palemoon throwing 'SyntaxError'
throw err;
}
const secretKey = util.concatUint8Array([privateKey, publicKey]);
const signature = ed25519.sign.detached(hashed, secretKey);
const signature = naclEd25519.sign.detached(hashed, secretKey);
return { RS: signature };
}
@ -146,10 +152,10 @@ export async function verify(algo, hashAlgo, { RS }, m, publicKey, hashed) {
const verified = await webCrypto.verify('Ed25519', key, RS, hashed);
return verified;
} catch (err) {
if (err.name !== 'NotSupportedError') {
if (err.name !== 'NotSupportedError' && err.name !== 'SyntaxError') { // Temporary fix for Palemoon throwing 'SyntaxError'
throw err;
}
return ed25519.sign.detached.verify(hashed, RS, publicKey);
return nobleEd25519Verify(RS, hashed, publicKey);
}
case enums.publicKey.ed448: {
@ -177,7 +183,7 @@ export async function validateParams(algo, A, seed) {
* and expect A == A'
* TODO: move to sign-verify using WebCrypto (same as ECDSA) when curve is more widely implemented
*/
const { publicKey } = ed25519.sign.keyPair.fromSeed(seed);
const { publicKey } = naclEd25519.sign.keyPair.fromSeed(seed);
return util.equalsUint8Array(A, publicKey);
}

View File

@ -21,7 +21,7 @@
* @module crypto/public_key/elliptic/eddsa_legacy
*/
import nacl from '@openpgp/tweetnacl';
import naclEd25519 from '@openpgp/tweetnacl'; // better constant-timeness as it uses Uint8Arrays over BigInts
import util from '../../../util';
import enums from '../../../enums';
import { getHashByteLength } from '../../hash';
@ -101,7 +101,7 @@ export async function validateParams(oid, Q, k) {
* Derive public point Q' = dG from private key
* and expect Q == Q'
*/
const { publicKey } = nacl.sign.keyPair.fromSeed(k);
const { publicKey } = naclEd25519.sign.keyPair.fromSeed(k);
const dG = new Uint8Array([0x40, ...publicKey]); // Add public key prefix
return util.equalsUint8Array(Q, dG);

View File

@ -0,0 +1,67 @@
import enums from '../../enums';
import util from '../../util';
const supportedHashAlgos = new Set([enums.hash.sha1, enums.hash.sha256, enums.hash.sha512]);
const webCrypto = util.getWebCrypto();
const nodeCrypto = util.getNodeCrypto();
export async function generate(hashAlgo) {
if (!supportedHashAlgos.has(hashAlgo)) {
throw new Error('Unsupported hash algorithm.');
}
const hashName = enums.read(enums.webHash, hashAlgo);
const crypto = webCrypto || nodeCrypto.webcrypto.subtle;
const key = await crypto.generateKey(
{
name: 'HMAC',
hash: { name: hashName }
},
true,
['sign', 'verify']
);
const exportedKey = await crypto.exportKey('raw', key);
return new Uint8Array(exportedKey);
}
export async function sign(hashAlgo, key, data) {
if (!supportedHashAlgos.has(hashAlgo)) {
throw new Error('Unsupported hash algorithm.');
}
const hashName = enums.read(enums.webHash, hashAlgo);
const crypto = webCrypto || nodeCrypto.webcrypto.subtle;
const importedKey = await crypto.importKey(
'raw',
key,
{
name: 'HMAC',
hash: { name: hashName }
},
false,
['sign']
);
const mac = await crypto.sign('HMAC', importedKey, data);
return new Uint8Array(mac);
}
export async function verify(hashAlgo, key, mac, data) {
if (!supportedHashAlgos.has(hashAlgo)) {
throw new Error('Unsupported hash algorithm.');
}
const hashName = enums.read(enums.webHash, hashAlgo);
const crypto = webCrypto || nodeCrypto.webcrypto.subtle;
const importedKey = await crypto.importKey(
'raw',
key,
{
name: 'HMAC',
hash: { name: hashName }
},
false,
['verify']
);
return crypto.verify('HMAC', importedKey, mac, data);
}

View File

@ -7,3 +7,5 @@ export * as rsa from './rsa';
export * as elgamal from './elgamal';
export * as elliptic from './elliptic';
export * as dsa from './dsa';
export * as hmac from './hmac';
export * as postQuantum from './post_quantum';

View File

@ -0,0 +1,7 @@
import * as kem from './kem/index';
import * as signature from './signature';
export {
kem,
signature
};

View File

@ -0,0 +1,51 @@
import * as ecdhX from '../../elliptic/ecdh_x';
import enums from '../../../../enums';
export async function generate(algo) {
switch (algo) {
case enums.publicKey.pqc_mlkem_x25519: {
const { A, k } = await ecdhX.generate(enums.publicKey.x25519);
return {
eccPublicKey: A,
eccSecretKey: k
};
}
default:
throw new Error('Unsupported KEM algorithm');
}
}
export async function encaps(eccAlgo, eccRecipientPublicKey) {
switch (eccAlgo) {
case enums.publicKey.pqc_mlkem_x25519: {
const { ephemeralPublicKey: eccCipherText, sharedSecret: eccKeyShare } = await ecdhX.generateEphemeralEncryptionMaterial(enums.publicKey.x25519, eccRecipientPublicKey);
return {
eccCipherText,
eccKeyShare
};
}
default:
throw new Error('Unsupported KEM algorithm');
}
}
export async function decaps(eccAlgo, eccCipherText, eccSecretKey, eccPublicKey) {
switch (eccAlgo) {
case enums.publicKey.pqc_mlkem_x25519: {
const eccKeyShare = await ecdhX.recomputeSharedSecret(enums.publicKey.x25519, eccCipherText, eccPublicKey, eccSecretKey);
return eccKeyShare;
}
default:
throw new Error('Unsupported KEM algorithm');
}
}
export async function validateParams(algo, eccPublicKey, eccSecretKey) {
switch (algo) {
case enums.publicKey.pqc_mlkem_x25519:
return ecdhX.validateParams(enums.publicKey.x25519, eccPublicKey, eccSecretKey);
default:
throw new Error('Unsupported KEM algorithm');
}
}

View File

@ -0,0 +1,2 @@
export { generate, encrypt, decrypt, validateParams } from './kem';
export { expandSecretSeed as mlkemExpandSecretSeed } from './ml_kem';

View File

@ -0,0 +1,56 @@
import * as eccKem from './ecc_kem';
import * as mlKem from './ml_kem';
import * as aesKW from '../../../aes_kw';
import util from '../../../../util';
import enums from '../../../../enums';
import { computeDigest } from '../../../hash';
export async function generate(algo) {
const { eccPublicKey, eccSecretKey } = await eccKem.generate(algo);
const { mlkemPublicKey, mlkemSeed, mlkemSecretKey } = await mlKem.generate(algo);
return { eccPublicKey, eccSecretKey, mlkemPublicKey, mlkemSeed, mlkemSecretKey };
}
export async function encrypt(algo, eccPublicKey, mlkemPublicKey, sessioneKeyData) {
const { eccKeyShare, eccCipherText } = await eccKem.encaps(algo, eccPublicKey);
const { mlkemKeyShare, mlkemCipherText } = await mlKem.encaps(algo, mlkemPublicKey);
const kek = await multiKeyCombine(algo, eccKeyShare, eccCipherText, eccPublicKey, mlkemKeyShare, mlkemCipherText, mlkemPublicKey);
const wrappedKey = await aesKW.wrap(enums.symmetric.aes256, kek, sessioneKeyData); // C
return { eccCipherText, mlkemCipherText, wrappedKey };
}
export async function decrypt(algo, eccCipherText, mlkemCipherText, eccSecretKey, eccPublicKey, mlkemSecretKey, mlkemPublicKey, encryptedSessionKeyData) {
const eccKeyShare = await eccKem.decaps(algo, eccCipherText, eccSecretKey, eccPublicKey);
const mlkemKeyShare = await mlKem.decaps(algo, mlkemCipherText, mlkemSecretKey);
const kek = await multiKeyCombine(algo, eccKeyShare, eccCipherText, eccPublicKey, mlkemKeyShare, mlkemCipherText, mlkemPublicKey);
const sessionKey = await aesKW.unwrap(enums.symmetric.aes256, kek, encryptedSessionKeyData);
return sessionKey;
}
async function multiKeyCombine(algo, ecdhKeyShare, ecdhCipherText, ecdhPublicKey, mlkemKeyShare, mlkemCipherText, mlkemPublicKey) {
// LAMPS-aligned and NIST compatible combiner, proposed in: https://mailarchive.ietf.org/arch/msg/openpgp/NMTCy707LICtxIhP3Xt1U5C8MF0/
// 2a. KDF(mlkemSS || tradSS || tradCT || tradPK || Domain)
// where Domain is "Domain" for LAMPS, and "mlkemCT || mlkemPK || algId || const" for OpenPGP
const encData = util.concatUint8Array([
mlkemKeyShare,
ecdhKeyShare,
ecdhCipherText,
ecdhPublicKey,
// domSep
mlkemCipherText,
mlkemPublicKey,
new Uint8Array([algo]),
util.encodeUTF8('OpenPGPCompositeKDFv1')
]);
const kek = await computeDigest(enums.hash.sha3_256, encData);
return kek;
}
export async function validateParams(algo, eccPublicKey, eccSecretKey, mlkemPublicKey, mlkemSeed) {
const eccValidationPromise = eccKem.validateParams(algo, eccPublicKey, eccSecretKey);
const mlkemValidationPromise = mlKem.validateParams(algo, mlkemPublicKey, mlkemSeed);
const valid = await eccValidationPromise && await mlkemValidationPromise;
return valid;
}

View File

@ -0,0 +1,72 @@
import enums from '../../../../enums';
import util from '../../../../util';
import { getRandomBytes } from '../../../random';
export async function generate(algo) {
switch (algo) {
case enums.publicKey.pqc_mlkem_x25519: {
const mlkemSeed = getRandomBytes(64);
const { mlkemSecretKey, mlkemPublicKey } = await expandSecretSeed(algo, mlkemSeed);
return { mlkemSeed, mlkemSecretKey, mlkemPublicKey };
}
default:
throw new Error('Unsupported KEM algorithm');
}
}
/**
* Expand ML-KEM 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<{ mlkemPublicKey: Uint8Array, mlkemSecretKey: Uint8Array }>}
*/
export async function expandSecretSeed(algo, seed) {
switch (algo) {
case enums.publicKey.pqc_mlkem_x25519: {
const { ml_kem768 } = await import('../noble_post_quantum');
const { publicKey: encapsulationKey, secretKey: decapsulationKey } = ml_kem768.keygen(seed);
return { mlkemPublicKey: encapsulationKey, mlkemSecretKey: decapsulationKey };
}
default:
throw new Error('Unsupported KEM algorithm');
}
}
export async function encaps(algo, mlkemRecipientPublicKey) {
switch (algo) {
case enums.publicKey.pqc_mlkem_x25519: {
const { ml_kem768 } = await import('../noble_post_quantum');
const { cipherText: mlkemCipherText, sharedSecret: mlkemKeyShare } = ml_kem768.encapsulate(mlkemRecipientPublicKey);
return { mlkemCipherText, mlkemKeyShare };
}
default:
throw new Error('Unsupported KEM algorithm');
}
}
export async function decaps(algo, mlkemCipherText, mlkemSecretKey) {
switch (algo) {
case enums.publicKey.pqc_mlkem_x25519: {
const { ml_kem768 } = await import('../noble_post_quantum');
const mlkemKeyShare = ml_kem768.decapsulate(mlkemCipherText, mlkemSecretKey);
return mlkemKeyShare;
}
default:
throw new Error('Unsupported KEM algorithm');
}
}
export async function validateParams(algo, mlkemPublicKey, mlkemSeed) {
switch (algo) {
case enums.publicKey.pqc_mlkem_x25519: {
const { mlkemPublicKey: expectedPublicKey } = await expandSecretSeed(algo, mlkemSeed);
return util.equalsUint8Array(mlkemPublicKey, expectedPublicKey);
}
default:
throw new Error('Unsupported KEM algorithm');
}
}

View File

@ -0,0 +1,10 @@
/**
* This file is needed to dynamic import noble-post-quantum libs.
* Separate dynamic imports are not convenient as they result in multiple chunks,
* which ultimately share a lot of code and need to be imported together
* when it comes to Proton's ML-DSA + ML-KEM keys.
*/
export { ml_kem768 } from '@noble/post-quantum/ml-kem';
export { ml_dsa65 } from '@noble/post-quantum/ml-dsa';

View File

@ -0,0 +1,46 @@
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');
}
}

View File

@ -0,0 +1,2 @@
export { generate, sign, verify, validateParams, getRequiredHashAlgo } from './signature';
export { expandSecretSeed as mldsaExpandSecretSeed } from './ml_dsa';

View File

@ -0,0 +1,69 @@
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');
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');
const mldsaSignature = ml_dsa65.sign(mldsaSecretKey, dataDigest);
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');
return ml_dsa65.verify(mldsaPublicKey, dataDigest, 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');
}
}

View File

@ -0,0 +1,70 @@
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) {
if (hashAlgo !== getRequiredHashAlgo(signatureAlgo)) {
// The signature hash algo MUST be set to the specified algorithm, see
// https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-pqc#section-5.2.1.
throw new Error('Unexpected hash algorithm for PQC signature');
}
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 }) {
if (hashAlgo !== getRequiredHashAlgo(signatureAlgo)) {
// The signature hash algo MUST be set to the specified algorithm, see
// https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-pqc#section-5.2.1.
throw new Error('Unexpected hash algorithm for PQC signature');
}
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 function getRequiredHashAlgo(signatureAlgo) {
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-pqc#section-5.2.1.
switch (signatureAlgo) {
case enums.publicKey.pqc_mldsa_ed25519:
return enums.hash.sha3_256;
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;
}

View File

@ -3,9 +3,10 @@
* @module crypto/signature
*/
import { elliptic, rsa, dsa } from './public_key';
import { elliptic, rsa, dsa, hmac, postQuantum } from './public_key';
import enums from '../enums';
import util from '../util';
import ShortByteString from '../type/short_byte_string';
import { UnsupportedError } from '../packet/packet';
/**
@ -65,7 +66,16 @@ export function parseSignatureParams(algo, signature) {
const RS = util.readExactSubarray(signature, read, read + rsSize); read += RS.length;
return { read, signatureParams: { RS } };
}
case enums.publicKey.hmac: {
const mac = new ShortByteString(); read += mac.read(signature.subarray(read));
return { read, signatureParams: { mac } };
}
case enums.publicKey.pqc_mldsa_ed25519: {
const eccSignatureSize = 2 * 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.');
}
@ -80,12 +90,13 @@ export function parseSignatureParams(algo, signature) {
* @param {module:enums.hash} hashAlgo - Hash algorithm
* @param {Object} signature - Named algorithm-specific signature parameters
* @param {Object} publicParams - Algorithm-specific public key parameters
* @param {Object} privateParams - Algorithm-specific private key parameters
* @param {Uint8Array} data - Data for which the signature was created
* @param {Uint8Array} hashed - The hashed data
* @returns {Promise<Boolean>} True if signature is valid.
* @async
*/
export async function verify(algo, hashAlgo, signature, publicParams, data, hashed) {
export async function verify(algo, hashAlgo, signature, publicParams, privateParams, data, hashed) {
switch (algo) {
case enums.publicKey.rsaEncryptSign:
case enums.publicKey.rsaEncrypt:
@ -121,6 +132,18 @@ export async function verify(algo, hashAlgo, signature, publicParams, data, hash
const { A } = publicParams;
return elliptic.eddsa.verify(algo, hashAlgo, signature, data, A, hashed);
}
case enums.publicKey.hmac: {
if (!privateParams) {
throw new Error('Cannot verify HMAC signature with symmetric key missing private parameters');
}
const { cipher: algo } = publicParams;
const { keyMaterial } = privateParams;
return hmac.verify(algo.getValue(), keyMaterial, signature.mac.data, hashed);
}
case enums.publicKey.pqc_mldsa_ed25519: {
const { eccPublicKey, mldsaPublicKey } = publicParams;
return postQuantum.signature.verify(algo, hashAlgo, eccPublicKey, mldsaPublicKey, hashed, signature);
}
default:
throw new Error('Unknown signature algorithm.');
}
@ -176,6 +199,17 @@ export async function sign(algo, hashAlgo, publicKeyParams, privateKeyParams, da
const { seed } = privateKeyParams;
return elliptic.eddsa.sign(algo, hashAlgo, data, A, seed, hashed);
}
case enums.publicKey.hmac: {
const { cipher: algo } = publicKeyParams;
const { keyMaterial } = privateKeyParams;
const mac = await 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 postQuantum.signature.sign(algo, hashAlgo, eccSecretKey, eccPublicKey, mldsaSecretKey, hashed);
}
default:
throw new Error('Unknown signature algorithm.');
}

View File

@ -50,6 +50,18 @@ export default {
'brainpoolP512r1': 'brainpoolP512r1'
},
/** KDF parameters flags
* Non-standard extensions (for now) to allow email forwarding
* @enum {Integer}
* @readonly
*/
kdfFlags: {
/** Specify fingerprint to use instead of the recipient's */
replace_fingerprint: 0x01,
/** Specify custom parameters to use in the KDF digest computation */
replace_kdf_params: 0x02
},
/** A string to key specifier type
* @enum {Integer}
* @readonly
@ -95,7 +107,16 @@ export default {
/** Ed25519 (Sign only) */
ed25519: 27,
/** Ed448 (Sign only) */
ed448: 28
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 */
hmac: 101
},
/** {@link https://tools.ietf.org/html/rfc4880#section-9.2|RFC4880, section 9.2}
@ -382,6 +403,8 @@ export default {
splitPrivateKey: 16,
/** 0x20 - This key may be used for authentication. */
authentication: 32,
/** This key may be used for forwarded communications */
forwardedCommunication: 64,
/** 0x80 - The private component of this key may be in the
* possession of more than one person. */
sharedPrivateKey: 128

Some files were not shown because too many files have changed in this diff Show More