Conversation
Certificates signed with or holding ML-DSA keys (RFC 9881) were parsed
with an unknown signature and public key algorithm, so their JSON
reported "signature_algorithm": {"name": "0", "oid": ""} and
"key_algorithm": {"name": "unknown_algorithm"}, and self-signed ML-DSA
roots were reported as not self-signed. This is what step certificate
inspect --format json shows for ML-DSA certificates.
Add the ML-DSA-44, ML-DSA-65 and ML-DSA-87 signature algorithms and an
ML-DSA public key algorithm, named as crypto/x509 names them. They are
identified on every toolchain. With Go 1.27 or later, public keys are
parsed into *mldsa.PublicKey and signatures are verified with
crypto/mldsa, rejecting a signature whose parameter set differs from the
key's. Older toolchains parse the certificate but report its signatures
as unsupported, following the stub pattern in go.step.sm/crypto/mldsa.
Key lengths are checked on every toolchain so a certificate parses the
same way regardless of Go version. When crypto/mldsa is unavailable at
runtime, as with the FIPS 140-3 Go Cryptographic Module v1.0.0, the
certificate still parses with a nil public key instead of failing.
The test certificates are generated with OpenSSL by
x509/testdata/mldsa/gen.sh, so they come from an ML-DSA implementation
independent of Go's.
|
|
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.
Name of feature:
ML-DSA (FIPS 204, RFC 9881) signature and public key algorithms in
x509.Pain or issue this feature alleviates:
zcrypto parsed ML-DSA certificates with an unknown signature and public key algorithm.
step certificate inspect --format json, which uses zcrypto's JSON encoding, therefore reports every ML-DSA certificate like this:The text output (certinfo) and OpenSSL identify the same certificates correctly. step CLI v0.30.7 can now create ML-DSA certificates (smallstep/cli#1698), so this shows up for anyone who uses them.
With this change, built into step with Go 1.27.1 (ECDSA output and all text output unchanged):
"0",unknown_algorithm,self_signed: falseML-DSA-65/2.16.840.1.101.3.4.3.18,ML-DSA,self_signed: true"0",unknown_algorithm,self_signed: falseML-DSA-65/…3.18,ML-DSA,self_signed: falseWhy is this important to the project (if not answered above):
Post-quantum certificates are starting to be used, and JSON output is what automation consumes.
What changed:
MLDSA44,MLDSA65,MLDSA87signature algorithms and anMLDSApublic key algorithm, named exactly as Go 1.27'scrypto/x509names them (ML-DSA-44… andML-DSA). A test asserts the names match the standard library. The new constants are appended to the enums, so existing values do not change.mldsa_go127.go): keys parse to*mldsa.PublicKey, and signatures are verified as pure ML-DSA with an empty context, as RFC 9881 requires. A signature whose parameter set differs from the key's is rejected, which followscrypto/x509.mldsa_stub.go): certificates still parse and report their algorithms, but signatures returnErrUnsupportedAlgorithm. This is the samego1.27build-tag and stub split thatgo.step.sm/crypto/mldsauses. The stub can be deleted once the minimum Go is 1.27.Two deliberate differences from
crypto/x509:crypto/mldsais unavailable there. The standard library reportsUnknownPublicKeyAlgorithm. This change instead still names the algorithm, parses the certificate with a nil public key, and leaves signatures unverifiable. That matters for FIPS builds such as step-ca-pro's-fipsimage.Is there documentation on how to use this feature? If so, where?
No API to learn:
ParseCertificateand the JSON encoding pick it up automatically.In what environments or workflows is this feature supported?
SelfSigned,CheckSignature, andCheckSignatureFrom.In what environments or workflows is this feature explicitly NOT supported (if any)?
ErrUnsupportedAlgorithm, not a verification failure.CreateCertificatewith ML-DSA keys (marshalPublicKeyand signing). It is out of scope for this PR, which only covers parsing.Testing
The test certificates are generated by
x509/testdata/mldsa/gen.shwith OpenSSL 3.6, an ML-DSA implementation independent of Go's. OpenSSL verifies all of them except the deliberately tampered one. The set is:The tests cover:
mldsa_test.go(all toolchains): the algorithm enums, the name functions, JSONsignature_algorithmname and OID, JSONkey_algorithm, and rejection of each key length ±1.mldsa_go127_test.go:SelfSignedis true for the roots and false for the tampered root;CheckSignatureFrom;crypto/x509.mldsa_stub_test.go(Go 1.26): the certificate parses, has a nil key,SelfSignedis false, and signature checks returnErrUnsupportedAlgorithm.How I ran it:
go vetpasses on both, andgolangci-lintwith the shared smallstep config reports 0 new issues on both.go test ./...has the same 11 failures asmasteron both toolchains, with no new ones. Those failures predate this PR: the test fixtures use 512-bit RSA keys, which Go 1.24+ refuses, plusTestImportsand sometlshandshake tests. CI does not catch them becauseci.ymlsetsrun-test: falseandrun-lint: false. That also means CI will not run the tests in this PR, so please run them locally.go.work, where the JSON output matches the table above, and step's fullgo test -short ./...passes against zcryptomasterplus this change.Supporting links/other PRs/issues:
v0.0.0-20221001003018-1ab2364d2a91, 227 commits behindmaster, so it needs a zcrypto bump to pick this up. I ran step's tests againstmasterplus this change and they pass, so the bump looks straightforward.