Skip to content

[PM-34825] Add support for ML-DSA keypairs#920

Open
quexten wants to merge 2 commits intomainfrom
km/poc-mldsa-keypair
Open

[PM-34825] Add support for ML-DSA keypairs#920
quexten wants to merge 2 commits intomainfrom
km/poc-mldsa-keypair

Conversation

@quexten
Copy link
Copy Markdown
Contributor

@quexten quexten commented Apr 9, 2026

🎟️ Tracking

https://bitwarden.atlassian.net/browse/PM-34825
bitwarden/server#7435

📔 Objective

Adds post-quantum ML-DSA key pair support for v2 user identity key pairs.

Please note that ML-DSA-44 is FIPS-level-2 security level. It is FIPS compatible under:
https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.204.pdf

The encoding via cose is defined here:
https://datatracker.ietf.org/doc/draft-ietf-cose-dilithium/

The CTX parameter is left empty, since we already do context binding within the signed messages. We support only the randomized signature mode, because it is recommended.

This document also permits a fully deterministic variant of the signing procedure in case the signer has
no access to a fresh source of randomness at signing time. However, the lack of randomness in the
deterministic variant makes the risk of side-channel attacks (particularly fault attacks) more difficult to
mitigate. Therefore, this variant should not be used on platforms where side-channel attacks are a concern
and where they cannot be otherwise mitigated.

ML-DSA are made the default key type for v2 users, so that they don't require a migration of signature keys to get post-quantum ready.

🚨 Breaking Changes

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 9, 2026

Logo
Checkmarx One – Scan Summary & Details6ad71bd6-cd32-4be1-b38b-14b0f4f754da

Great job! No new security vulnerabilities introduced in this pull request

@@ -275,7 +276,7 @@ impl WrappedAccountCryptographicState {
) -> Result<(SymmetricKeyId, Self), AccountCryptographyInitializationError> {
let user_key = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
let private_key = ctx.make_private_key(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
let signing_key = ctx.make_signing_key(SignatureAlgorithm::Ed25519);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: This defaults to ML-DSA for all v2 users!

@quexten quexten changed the title PoC: Add support for ML-DSA keypairs [PM-34825] Add support for ML-DSA keypairs Apr 10, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 10, 2026

🔍 SDK Breaking Change Detection Results

SDK Version: km/poc-mldsa-keypair (92399d0)
Completed: 2026-04-15 10:05:45 UTC
Total Time: 277s

Client Status Details
typescript ✅ No breaking changes detected TypeScript compilation passed with new SDK version - View Details

Breaking change detection completed. View SDK workflow

@quexten quexten requested a review from MGibson1 April 10, 2026 14:53
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 10, 2026

Codecov Report

❌ Patch coverage is 72.51462% with 47 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.45%. Comparing base (45ebecf) to head (92399d0).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
crates/bitwarden-crypto/src/signing/cose.rs 48.64% 19 Missing ⚠️
crates/bitwarden-crypto/src/signing/signing_key.rs 79.34% 19 Missing ⚠️
...ates/bitwarden-crypto/src/signing/verifying_key.rs 65.21% 8 Missing ⚠️
crates/bitwarden-crypto/src/signing/mod.rs 50.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #920      +/-   ##
==========================================
- Coverage   83.50%   83.45%   -0.05%     
==========================================
  Files         379      379              
  Lines       45720    45871     +151     
==========================================
+ Hits        38178    38283     +105     
- Misses       7542     7588      +46     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@quexten quexten marked this pull request as ready for review April 13, 2026 08:57
@quexten quexten requested review from a team as code owners April 13, 2026 08:57
@quexten quexten marked this pull request as draft April 13, 2026 09:02
Value::Bytes(seed.to_vec()),
),
(
coset::Label::Int(AkpKeyParameter::Pub.to_i64()),
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for reviewers; The cose spec says PUB is always required, even in private keys, so the private key (signing key) here contains both pub and private values.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this per https://datatracker.ietf.org/doc/draft-ietf-cose-dilithium/ ?

It's quite odd to me that they chose to make pub required when previous RFCs ecdsa don't. It feels different enough for a comment to me.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's quite odd to me that they chose to make pub required when previous RFCs ecdsa don't.

It was not REQUIRED but RECOMMENDED to add them for eliptic curves. For RSA, it was also REQUIRED https://datatracker.ietf.org/doc/html/rfc8230/#section-4

o For public keys, the fields 'n' and 'e' MUST be present. All
other fields defined in the following table below MUST be absent.
o For private keys with two primes, the fields 'other', 'r_i',
'd_i', and 't_i' MUST be absent; all other fields MUST be present.

@quexten quexten marked this pull request as ready for review April 13, 2026 09:39
@djsmith85 djsmith85 requested review from Hinton and dani-garcia and removed request for djsmith85 April 13, 2026 09:48
@djsmith85
Copy link
Copy Markdown
Contributor

Requesting additional reviews from @dani-garcia as platform-support for Auth and @Hinton due to CXF changes

@quexten
Copy link
Copy Markdown
Contributor Author

quexten commented Apr 13, 2026

Thanks. I'll note that the cxf changes are just tests, that the compiler complained about. (I did not dig into what changed). The same applies to the auth changes.

harr1424
harr1424 previously approved these changes Apr 13, 2026
Copy link
Copy Markdown
Contributor

@harr1424 harr1424 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exporter changes look good

Copy link
Copy Markdown
Member

@MGibson1 MGibson1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me, I am annoyed by the pub key decision so I wouldn't mind a comment there, but nothing else from me.

Value::Bytes(seed.to_vec()),
),
(
coset::Label::Int(AkpKeyParameter::Pub.to_i64()),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this per https://datatracker.ietf.org/doc/draft-ietf-cose-dilithium/ ?

It's quite odd to me that they chose to make pub required when previous RFCs ecdsa don't. It feels different enough for a comment to me.

@quexten quexten force-pushed the km/poc-mldsa-keypair branch from 77b21c2 to e0047f0 Compare April 15, 2026 05:58
@sonarqubecloud
Copy link
Copy Markdown

@quexten quexten enabled auto-merge (squash) April 16, 2026 09:33
@quexten quexten disabled auto-merge April 16, 2026 09:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants