Repository navigation
Fix BIT STRING unused-bits byte in PKCS#8 v2 publicKey (RFC 5958) - #11674
Open
MarkAtwood wants to merge 1 commit into
Open
MarkAtwood wants to merge 1 commit into
MarkAtwood wants to merge 1 commit into
Conversation
MarkAtwood
requested review from
kaleb-himes
and
a balanced review from Copilot
October 6, 2026 23:35
MarkAtwood
force-pushed
the
fix/asymkey-pubkey-bitstring
branch
from
October 6, 2026 23:38
18887cb to
7f64587
Compare
This was referenced Oct 6, 2026
Contributor
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The newly added X448 decoding branch lacks direct test coverage.
Review effort: Balanced
Findings: 1
Open (1)
What changed in this PR
Corrects RFC 5958 PKCS#8 v2 public-key BIT STRING framing while retaining legacy wolfSSL decoding compatibility.
Changes:
- Adds and validates the unused-bits byte during encoding and decoding.
- Updates affected DER size constants and tests.
- Documents compatibility and migration implications.
| File | Description |
|---|---|
wolfssl/wolfcrypt/wc_mldsa.h |
Increases ML-DSA DER size constants. |
wolfssl/wolfcrypt/curve25519.h |
Increases Curve25519 DER buffer size. |
wolfcrypt/src/asn.c |
Implements RFC-compliant encoding and compatible decoding. |
tests/api/test_mldsa.c |
Updates expected ML-DSA sizes. |
tests/api/test_asn.h |
Registers the new ASN test. |
tests/api/test_asn.c |
Adds interoperability and malformed-input tests. |
doc/dox_comments/header_files/wc_mldsa.h |
Documents ML-DSA behavior. |
doc/dox_comments/header_files/falcon.h |
Documents Falcon behavior. |
doc/dox_comments/header_files/asn_public.h |
Documents Curve25519 and EdDSA behavior. |
doc/dox_comments/header_files-ja/wc_mldsa.h |
Updates Japanese ML-DSA documentation. |
doc/dox_comments/header_files-ja/asn_public.h |
Updates Japanese Curve25519 documentation. |
ChangeLog.md |
Records compatibility-breaking behavior. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+35181
to
+35183
| #ifdef HAVE_CURVE448 | ||
| case X448k: | ||
| return CURVE448_PUB_KEY_SIZE; |
|
This was referenced Oct 6, 2026
MarkAtwood
force-pushed
the
fix/asymkey-pubkey-bitstring
branch
3 times, most recently
from
October 7, 2026 17:38
2d10b62 to
6c6acfb
Compare
MarkAtwood
force-pushed
the
fix/asymkey-pubkey-bitstring
branch
from
October 7, 2026 17:39
6c6acfb to
360cd0d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Fixes #10019
The
publicKeyfield of a PKCS#8 v2 private key (RFC 5958OneAsymmetricKey,[1] IMPLICIT BIT STRING) was written and read without its BIT STRING unused-bits byte. wolfSSL wrote81 20 <32>for Ed25519 where ring, BoringSSL and OpenSSL write81 21 00 <32>, and wolfSSL rejected keys from those implementations.Thanks to @cpsource, whose patch in #10019 (April) took the same approach for the Ed25519, Ed448, X25519 and X448 cases.
Changes
SetAsymKeyDerwrites the0x00unused-bits byte before the key, in both the template and original ASN code paths.DecodeAsymKey_Assignaccepts two forms for fixed-size keys (Ed25519, Ed448, X25519, X448, ML-DSA, Falcon): the RFC form (keySz + 1bytes, leading0x00) and the legacy wolfSSL form (keySzbytes). Any other non-empty form returnsASN_PARSE_E; an empty field is treated as absent, as before. Before this, a leading0x40or0x04reached the Ed25519/Ed448 importers, which accepted it as an OpenPGP or uncompressed prefix.CURVE25519_MAX_KEY_TO_DER_SZandWC_MLDSA_44/65/87_BOTH_KEY_DER_SIZEgrow by one byte.Compatibility
Two effects maintainers should weigh, also stated in ChangeLog.md under Behavioral Changes:
wc_Ed25519PrivateKeyToDerand the other*PrivateKeyToDerfunctions omitpublicKey, and every version reads their output. If maintainers want more, I can add a build macro that restores the legacy write; I did not add one.BUFFER_E/BAD_FUNC_ARGfrom*KeyToDerafter a drop-in library upgrade. It fails closed, with no overflow, but those applications must be recompiled. TheWC_MLDSA_*change may need a decision on the v7 boundary.X25519 PKCS#8 keys still do not interoperate after this PR: wolfSSL imports and exports X25519 key values in reversed byte order (#11673), so an RFC 8410 key from OpenSSL loads as a different key. This PR fixes only the
[1]framing for X25519.SE050:
se050_ed25519_sign_msgimports a software key by passingwc_Ed25519KeyToDeroutput to the NXP middleware, which already strips a leading0x00/0x01from[1]. Under the old layout a private-only key (all-zero public key) failed that parse by accident, whiched25519_asn_testexpected asWC_HW_E. With the RFC layout it parsed and signed with a zero public key. The port now rejects a software key without a public key withBAD_FUNC_ARG, the same check as the host path, and the test expectsBAD_FUNC_ARGon all builds. Verified with the SE050 simulator (default andWOLFSSL_SE050_ONLY_KEY_ID): wolfCrypt test passes.Follow-ups
Two pre-existing bugs found during this work have fixes stacked on this PR as drafts: #11675 for #11670 (
[0]attributes rejected) and #11676 for #11671 ([1]publicKey not checked against the private key for ML-DSA and Falcon; X25519 waits on #11673). They will be rebased onto master after this merges.Tests
All vectors come from outside wolfSSL:
[1]framing (see the X25519 note above). OpenSSL independently derives each public key from its private key.0x01and0x40, a mismatched public key (PUBLIC_KEY_E), akeySz + 2field, and an empty field.test_SetAsymKeyDerandtest_mldsa_derexpected sizes are updated for the extra byte.Built from e61f90d with
--enable-opensslall --enable-crl --enable-certgen --enable-ed25519 --enable-curve25519 --enable-ed448 --enable-curve448 --enable-mldsa --enable-falcon --enable-experimental, runningtests/unit.test --api:--disable-keygen--enable-asn=originalThe 10
--enable-asn=originalfailures (ML-DSA PUBKEY/PEM/X509 tests) are already on master. Against the earlier lenient decoder (strip0x00when present, pass anything else through), the new test fails: the0x01case returnsBAD_FUNC_ARGinstead ofASN_PARSE_E, and with that case removed the0x40key decodes successfully.