Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PQC: Implement draft RFC for ML-DSA with Ed25519 #13

Draft
wants to merge 63 commits into
base: v6
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
e454faa
CI: setup Dependabot to update playwright and test latest browser ver…
larabr Sep 11, 2024
e80d71b
CI: setup Dependabot to update non-dev dependencies
larabr Sep 11, 2024
ada794c
Throw on (unexpected) low order points in ECDH over Curve25519/448
larabr Sep 12, 2024
5ee8541
CI: update SOP test suite docker image to v1.1.12
larabr Oct 3, 2024
a57bffc
Fix key and signature parsing of `EdDSALegacy` entities with unsuppor…
larabr Oct 14, 2024
e58c02d
Check session key size on SEIPD decryption
larabr Oct 17, 2024
3cdaab7
Check session key size on v3 SKESK and PKESK packet decryption
larabr Oct 17, 2024
05fbc63
Use `WebCrypto.getRandomValues` in Node
larabr Oct 18, 2024
88f2097
Tests: add support for RNG mocking in browser tests
larabr Oct 18, 2024
e924a50
Merge pull request #1799
larabr Oct 22, 2024
4b017f6
Tests: drop karma (deprecated) in favor of web-test-runner
larabr Sep 18, 2024
ae5698c
CI: fix playwright version parsing
larabr Sep 18, 2024
4ddadd4
CI: setup HTTPS in web-test-runner for BrowserStack tests
larabr Oct 9, 2024
59c809c
CI: Browserstack: test only iOS latest and min supported version (iOS…
larabr Oct 10, 2024
013dffc
CI: test latest Webkit on macOS, as a replacement for testing Safari …
larabr Oct 23, 2024
693adb4
CI: run browser tests also on Linux
larabr Oct 24, 2024
d7f5736
Merge pull request #1794
larabr Oct 25, 2024
09095ce
Run npm update
larabr Oct 28, 2024
821f260
Lightweight build: lazy load bzip decompression lib
larabr Oct 28, 2024
0138b69
CI: update Browserstack project id to include target branch
larabr Oct 28, 2024
12274a1
Update README [skip ci]
larabr Oct 28, 2024
d3e75de
`openpgp.encrypt`: use `encryptionKeys` to determine preferred hash a…
larabr Oct 29, 2024
f9a3e54
`openpgp.sign`: add `recipientKeys` option to get the signing prefs from
larabr Oct 29, 2024
fb72ea4
Merge pull request #1802
larabr Oct 30, 2024
42d504a
Switch to SHA512 as default preferred hash algo (`config.preferredHas…
larabr Oct 30, 2024
31a7e26
Merge pull request #1629 from openpgpjs/v6
twiss Nov 4, 2024
0980074
Document required Web Crypto support in README
twiss Nov 4, 2024
a5645e1
Spaces after "RFC" in README
twiss Nov 4, 2024
dd01ee0
6.0.0
larabr Nov 4, 2024
01b6239
Revert "CI: temporarily enable for PRs to v6 branch" [skip ci]
larabr Nov 5, 2024
3f06066
Update hash algorithm preferences order (#1804)
twiss Nov 7, 2024
2d65d1d
TS: `generateKey`: fix `options.type` definitions to accept `'curve25…
larabr Nov 11, 2024
287104a
TS: fix `PrivateKey.getDecryptionKeys()` return type
larabr Nov 11, 2024
ac1bfc0
Fix openpgp.verify/decrypt with expectSigned: true and format: 'binar…
twiss Nov 11, 2024
088d5f3
Merge pull request #1807
larabr Nov 11, 2024
121b478
Tests: drop unused, unnecessary error assertion
larabr Nov 13, 2024
f75447a
Fix ES imports for webpack: declare `exports.browser` entrypoint as h…
larabr Nov 21, 2024
67faffa
6.0.1
larabr Nov 21, 2024
66100b5
Update package.json name and url
twiss Jul 12, 2022
5b6591b
Add support for decrypting autoforwarded messages (#1)
larabr Jun 19, 2020
e04253b
Add symmetric encryption and MAC support
Sep 2, 2021
84a61a8
Update support for decrypting autoforwarded messages (#6)
larabr May 15, 2023
7fd0e2d
Support generating subkeys with 'forwarded communication' flag to dec…
larabr Sep 6, 2023
0283d93
Export Argon2S2K to reuse the WASM module outside of the OpenPGP.js c…
larabr Feb 14, 2024
7735b9c
Add `Argon2S2K.reloadWasmModule()` for manually triggering memory dea…
larabr Apr 12, 2024
486233c
Only add SHA3 preferences to v6 keys
larabr Mar 1, 2024
4f2175f
Temporarily add `config.ignoreSEIPDv2FeatureFlag` for compatibility (…
larabr Apr 19, 2024
1af425f
Use noble-ed25519 over tweetnacl for signature verification (#16)
larabr Jun 26, 2024
7b1c83a
Add ML-KEM (draft) + X25519
larabr Nov 1, 2023
d05c33f
Add PQC key generation
twiss Nov 5, 2023
0c3168c
Add PQC KEM key validation
larabr Mar 15, 2024
8dc88a3
Update multiKeyCombine
larabr Sep 24, 2024
6cef00d
Switch to standardized version of ML-KEM
larabr Sep 13, 2024
3b9450a
Update multiKeyCombine: re-introduce to KMAC
larabr Oct 21, 2024
743f3d3
Switch to seed format for ML-KEM private key, update test vectors (dr…
larabr Oct 21, 2024
59f8c38
Disallow generating and parsing v4 keys of type ML-KEM
larabr Oct 21, 2024
5a796a5
Update multiKeyCombine: switch to LAMPS-aligned-and-NIST-compatible p…
larabr Nov 14, 2024
215b459
Add ML-DSA + Ed25519
larabr Nov 3, 2023
03ef0d9
Add PQC signing key validation
larabr Jul 4, 2024
1020299
CI: test pqc keys in sop interoperability suite
larabr Sep 16, 2024
b801a33
Temp (?): manually encode context in ML-DSA (missing step in noble-po…
larabr Sep 23, 2024
67fc315
Switch to seed format for ML-DSA private key, update test vectors (dr…
larabr Oct 21, 2024
83c2c57
Enforce using SHA3 for ML-DSA signatures
larabr Nov 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/.dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
allow:
- dependency-name: "playwright"
versioning-strategy: increase

- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
allow:
- dependency-name: "@noble*"
- dependency-name: "fflate"
versioning-strategy: increase
groups:
# Any packages matching the pattern @noble* where the highest resolvable
# version is minor or patch will be grouped together.
# Grouping rules apply to version updates only.
noble:
applies-to: version-updates
patterns:
- "@noble*"
update-types:
- "minor"
- "patch"
3 changes: 2 additions & 1 deletion .github/test-suite/config.json.template
Original file line number Diff line number Diff line change
Expand Up @@ -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 } } } } }"
}
},
{
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Performance Regression Test

on:
pull_request:
branches: [main, v6]
branches: [main]

jobs:
benchmark:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
push:
branches: [main]
pull_request:
branches: [main, v6]
branches: [main]

jobs:
lint:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/sop-test-suite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ name: SOP interoperability test suite

on:
pull_request:
branches: [ main, v6 ]
branches: [ main ]

jobs:

test-suite:
name: Run interoperability test suite
runs-on: ubuntu-latest
container:
image: ghcr.io/protonmail/openpgp-interop-test-docker:v1.1.10
image: ghcr.io/protonmail/openpgp-interop-test-docker:v1.1.12
credentials:
username: ${{ github.actor }}
password: ${{ secrets.github_token }}
Expand Down
38 changes: 28 additions & 10 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
push:
branches: [main]
pull_request:
branches: [main, v6]
branches: [main]

jobs:
build: # cache both dist and tests (non-lightweight only), based on commit hash
Expand Down Expand Up @@ -57,8 +57,15 @@ jobs:

test-browsers-latest:
name: Browsers (latest)
runs-on: ubuntu-latest
needs: build
strategy:
fail-fast: false # if tests for one version fail, continue with the rest
matrix:
# run on all main platforms to test platform-specific code, if present
# (e.g. webkit's WebCrypto API implementation is different in macOS vs Linux)
# TODO: windows-latest fails to fetch resources from the wtr server; investigate if the problem is with path declaration or permissions
runner: ['ubuntu-latest', 'macos-latest']
runs-on: ${{ matrix.runner }}

steps:
- uses: actions/checkout@v4
Expand All @@ -79,17 +86,19 @@ jobs:
npm pkg delete scripts.prepare
npm ci

- name: Get Playwright version
- name: Get Playwright version and cache location
id: playwright-version
run: |
PLAYWRIGHT_VERSION=$(npm ls playwright | grep playwright | sed 's/.*@//')
PLAYWRIGHT_VERSION=$(npm ls playwright --depth=0 | grep playwright | sed 's/.*@//')
echo "version=$PLAYWRIGHT_VERSION" >> $GITHUB_OUTPUT
PLAYWRIGHT_CACHE=${{ fromJSON('{"ubuntu-latest": "~/.cache/ms-playwright", "macos-latest": "~/Library/Caches/ms-playwright"}')[matrix.runner] }}
echo "playwright_cache=$PLAYWRIGHT_CACHE" >> $GITHUB_OUTPUT
- name: Check for cached browsers
id: cache-playwright-browsers
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: playwright-browsers-${{ steps.playwright-version.outputs.version }}
path: ${{ steps.playwright-version.outputs.playwright_cache }}
key: playwright-browsers-${{ matrix.runner }}-${{ steps.playwright-version.outputs.version }}
- name: Install browsers
if: steps.cache-playwright-browsers.outputs.cache-hit != 'true'
run: |
Expand All @@ -100,12 +109,12 @@ jobs:
run: npx playwright install --with-deps webkit

- name: Run browser tests
run: npm run test-browser
run: npm run test-browser:ci -- --static-logging

- name: Run browser tests (lightweight) # overwrite test/lib
run: |
npm run build-test --lightweight
npm run test-browser
npm run test-browser:ci -- --static-logging

test-browsers-compatibility:
name: Browsers (older, on Browserstack)
Expand All @@ -119,6 +128,15 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4

- name: Generate self-signed HTTPS certificates for web-test-runner server
uses: kofemann/[email protected]
with:
hostcert: '127.0.0.1.pem'
hostkey: '127.0.0.1-key.pem'
cachain: 'ca-chain.pem'
- name: Adjust HTTPS certificates permissions
run: sudo chown runner:docker *.pem

- name: Install dependencies
run: npm ci --ignore-scripts

Expand All @@ -139,12 +157,12 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Run browserstack tests
run: npm run test-browserstack
run: npm run test-browserstack -- --static-logging

- name: Run browserstack tests (lightweight) # overwrite test/lib
run: |
npm run build-test --lightweight
npm run test-browserstack
npm run test-browserstack -- --static-logging
env:
LIGHTWEIGHT: true

Expand Down
71 changes: 34 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
OpenPGP.js [![BrowserStack Status](https://automate.browserstack.com/badge.svg?badge_key=N1l2eHFOanVBMU9wYWxJM3ZnWERnc1lidkt5UkRqa3BralV3SWVhOGpGTT0tLVljSjE4Z3dzVmdiQjl6RWgxb2c3T2c9PQ==--5864052cd523f751b6b907d547ac9c4c5f88c8a3)](https://automate.browserstack.com/public-build/N1l2eHFOanVBMU9wYWxJM3ZnWERnc1lidkt5UkRqa3BralV3SWVhOGpGTT0tLVljSjE4Z3dzVmdiQjl6RWgxb2c3T2c9PQ==--5864052cd523f751b6b907d547ac9c4c5f88c8a3) [![Join the chat on Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/openpgpjs/openpgpjs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
OpenPGP.js [![Join the chat on Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/openpgpjs/openpgpjs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
==========

[OpenPGP.js](https://openpgpjs.org/) is a JavaScript implementation of the OpenPGP protocol. It implements the [crypto-refresh](https://datatracker.ietf.org/doc/draft-ietf-openpgp-crypto-refresh) (superseding [RFC4880](https://tools.ietf.org/html/rfc4880) and [RFC4880bis](https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10)).
[OpenPGP.js](https://openpgpjs.org/) is a JavaScript implementation of the OpenPGP protocol. It implements [RFC 9580](https://datatracker.ietf.org/doc/rfc9580/) (superseding [RFC 4880](https://tools.ietf.org/html/rfc4880) and [RFC 4880bis](https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10)).

**Table of Contents**

Expand Down Expand Up @@ -35,29 +35,35 @@ OpenPGP.js [![BrowserStack Status](https://automate.browserstack.com/badge.svg?b

* The `dist/openpgp.min.js` (or `.mjs`) bundle works with recent versions of Chrome, Firefox, Edge and Safari 14+.

* The `dist/node/openpgp.min.mjs` (or `.cjs`) bundle works in Node.js v18+: it is used by default when you `import ... from 'openpgp'` (resp. `require('openpgp')`).

* Streaming support: the latest versions of Chrome, Firefox, Edge and Safari implement the
[Streams specification](https://streams.spec.whatwg.org/), including `TransformStream`s.
These are needed if you use the library with streamed inputs.
In previous versions of OpenPGP.js, WebStreams were automatically polyfilled by the library,
but from v6 this task is left up to the library user, due to the more extensive browser support, and the
polyfilling side-effects. If you're working with [older browsers versions which do not implement e.g. TransformStreams](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream), you can manually
load [WebStream polyfill](https://github.com/MattiasBuelens/web-streams-polyfills).
Please note that when you load the polyfills, the global `ReadableStream` property (if it exists) gets overwritten with the polyfill version.
In some edge cases, you might need to use the native
`ReadableStream` (for example when using it to create a `Response`
object), in which case you should store a reference to it before loading
the polyfills. There is also the [web-streams-adapter](https://github.com/MattiasBuelens/web-streams-adapter)
library to convert back and forth between them.
* The `dist/node/openpgp.min.mjs` (or `.cjs`) bundle works in Node.js v18+: it is used by default when you `import ... from 'openpgp'` (or `require('openpgp')`, respectively).

* Support for the [Web Cryptography API](https://w3c.github.io/webcrypto/)'s `SubtleCrypto` is required.
* In browsers, `SubtleCrypto` is only available in [secure contexts](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts).
* In supported versions of Node.js, `SubtleCrypto` is always available.

* Support for the [Web Streams API](https://streams.spec.whatwg.org/) is required.
* In browsers: the latest versions of Chrome, Firefox, Edge and Safari support Streams, including `TransformStream`s.
These are needed if you use the library with stream inputs.
In previous versions of OpenPGP.js, Web Streams were automatically polyfilled by the library,
but from v6 this task is left up to the library user, due to the more extensive browser support, and the
polyfilling side-effects. If you're working with [older browsers versions which do not implement e.g. TransformStreams](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream#browser_compatibility), you can manually
load the [Web Streams polyfill](https://github.com/MattiasBuelens/web-streams-polyfills).
Please note that when you load the polyfills, the global `ReadableStream` property (if it exists) gets overwritten with the polyfill version.
In some edge cases, you might need to use the native
`ReadableStream` (for example when using it to create a `Response`
object), in which case you should store a reference to it before loading
the polyfills. There is also the [web-streams-adapter](https://github.com/MattiasBuelens/web-streams-adapter)
library to convert back and forth between them.
* In Node.js: OpenPGP.js v6 no longer supports native Node `Readable` streams in inputs, and instead expects (and outputs) [Node's Web Streams](https://nodejs.org/api/webstreams.html#class-readablestream). [Node v17+ includes utilities to convert from and to Web Streams](https://nodejs.org/api/stream.html#streamreadabletowebstreamreadable-options).


### Performance

* Version 3.0.0 of the library introduces support for public-key cryptography using [elliptic curves](https://wiki.gnupg.org/ECC). We use native implementations on browsers and Node.js when available. Elliptic curve cryptography provides stronger security per bits of key, which allows for much faster operations. Currently the following curves are supported:
* Version 3.0.0 of the library introduced support for public-key cryptography using [elliptic curves](https://wiki.gnupg.org/ECC). We use native implementations on browsers and Node.js when available. Compared to RSA, elliptic curve cryptography provides stronger security per bits of key, which allows for much faster operations. Currently the following curves are supported:

| Curve | Encryption | Signature | NodeCrypto | WebCrypto | Constant-Time |
|:---------------:|:----------:|:---------:|:----------:|:---------:|:-----------------:|
| curve25519 | ECDH | N/A | No | Yes* | If native** |
| curve25519 | ECDH | N/A | No | No | Algorithmically |
| ed25519 | N/A | EdDSA | No | Yes* | If native** |
| nistP256 | ECDH | ECDSA | Yes* | Yes* | If native** |
| nistP384 | ECDH | ECDSA | Yes* | Yes* | If native** |
Expand All @@ -67,25 +73,22 @@ library to convert back and forth between them.
| brainpoolP512r1 | ECDH | ECDSA | Yes* | No | If native** |
| secp256k1 | ECDH | ECDSA | Yes* | No | If native** |

\* when available
\* when available
\** these curves are only constant-time if the underlying native implementation is available and constant-time

* If the user's browser supports [native WebCrypto](https://caniuse.com/#feat=cryptography) via the `window.crypto.subtle` API, this will be used. Under Node.js the native [crypto module](https://nodejs.org/api/crypto.html#crypto_crypto) is used.
* The platform's [native Web Crypto API](https://w3c.github.io/webcrypto/) is used for performance. On Node.js the native [crypto module](https://nodejs.org/api/crypto.html#crypto_crypto) is also used, in cases where it offers additional functionality.

* The library implements authenticated encryption (AEAD) as per the ["crypto refresh" draft standard](https://datatracker.ietf.org/doc/draft-ietf-openpgp-crypto-refresh) using AES-OCB, EAX, or GCM. This makes symmetric encryption faster on platforms with native implementations. However, since the specification is very recent and other OpenPGP implementations are in the process of adopting it, the feature is currently behind a flag. **Note: activating this setting can break compatibility with other OpenPGP implementations which have yet to implement the feature.** You can enable it by setting `openpgp.config.aeadProtect = true`.
Note that this setting has a different effect from the one in OpenPGP.js v6, which implemented support for a provisional version of AEAD from [RFC4880bis](https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10), which was modified in a later draft of the crypto refresh.
* The library implements authenticated encryption (AEAD) as per [RFC 9580](https://datatracker.ietf.org/doc/rfc9580/) using AES-GCM, OCB, or EAX. This makes symmetric encryption faster on platforms with native implementations. However, since the specification is very recent and other OpenPGP implementations are in the process of adopting it, the feature is currently behind a flag. **Note: activating this setting can break compatibility with other OpenPGP implementations which have yet to implement the feature.** You can enable it by setting `openpgp.config.aeadProtect = true`.
Note that this setting has a different effect from the one in OpenPGP.js v5, which implemented support for a provisional version of AEAD from [RFC 4880bis](https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10), which was modified in RFC 9580.

You can change the AEAD mode by setting one of the following options:

```
openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.ocb; // Default (widest ecosystem support), non-native
openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.gcm; // Native in WebCrypto and Node.js
openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.gcm; // Default, native in WebCrypto and Node.js
openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.ocb; // Non-native, but supported across RFC 9580 implementations
openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.eax; // Native in Node.js
```

* For environments that don't provide native crypto, the library falls back to [asm.js](https://caniuse.com/#feat=asmjs) AES and AEAD implementations.


### Getting started

#### Node.js
Expand Down Expand Up @@ -386,14 +389,8 @@ Where the value can be any of:
})();
```

For more information on using ReadableStreams, see [the MDN Documentation on the
Streams API](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API).

You can also pass a [Node.js `Readable`
stream](https://nodejs.org/api/stream.html#stream_class_stream_readable), in
which case OpenPGP.js will return a Node.js `Readable` stream as well, which you
can `.pipe()` to a `Writable` stream, for example.

For more information on using ReadableStreams (both in browsers and Node.js), see [the MDN Documentation on the
Streams API](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) .

#### Streaming encrypt and decrypt *String* data with PGP keys

Expand Down Expand Up @@ -667,7 +664,7 @@ To create your own build of the library, just run the following command after cl

npm install && npm test

For debugging browser errors, you can run `npm start` and open [`http://localhost:8080/test/unittests.html`](http://localhost:8080/test/unittests.html) in a browser, or run the following command:
For debugging browser errors, run the following command:

npm run browsertest

Expand Down
Loading