Go version
go1.26.0 darwin/arm64
What did you do?
I called the public ML-DSA signing API with an uninitialized zero-value mldsa.PrivateKey.
Minimal reproducer:
package main
import (
"crypto/mldsa"
)
func main() {
var sk mldsa.PrivateKey
_, _ = sk.Sign(nil, []byte("message"), nil)
}
This is the same invalid-zero-key family as the public-key issue in crypto/mldsa, but on the private-key signing path.
What did you see happen?
The zero-value PrivateKey is not rejected up front. The public (*PrivateKey).Sign / SignDeterministic path forwards &sk.k into the internal signer, which derives ML-DSA parameters from zero-value internal state instead of returning an error.
In current source, that reaches signInternal, where p.γ2 is still zero for the zero-value key state, and the signer computes:
γ2 := (q - 1) / uint32(p.γ2)
That is a divide-by-zero panic rather than a clean error return.
What did you expect to see?
I expected the public signing API to reject an uninitialized mldsa.PrivateKey with an error, not panic.
For example, this should fail cleanly:
var sk mldsa.PrivateKey
if _, err := sk.Sign(nil, []byte("message"), nil); err == nil {
panic("Sign accepted zero-value PrivateKey")
}
More generally, zero-value or otherwise invalid ML-DSA key state should be handled consistently across the public API surface. A caller should not be able to trigger an internal panic by passing an uninitialized mldsa.PrivateKey.
Go version
go1.26.0 darwin/arm64
What did you do?
I called the public ML-DSA signing API with an uninitialized zero-value
mldsa.PrivateKey.Minimal reproducer:
This is the same invalid-zero-key family as the public-key issue in crypto/mldsa, but on the private-key signing path.
What did you see happen?
The zero-value PrivateKey is not rejected up front. The public
(*PrivateKey).Sign/SignDeterministicpath forwards&sk.kinto the internal signer, which derives ML-DSA parameters from zero-value internal state instead of returning an error.In current source, that reaches signInternal, where p.γ2 is still zero for the zero-value key state, and the signer computes:
That is a divide-by-zero panic rather than a clean error return.
What did you expect to see?
I expected the public signing API to reject an uninitialized
mldsa.PrivateKeywith an error, not panic.For example, this should fail cleanly:
More generally, zero-value or otherwise invalid ML-DSA key state should be handled consistently across the public API surface. A caller should not be able to trigger an internal panic by passing an uninitialized
mldsa.PrivateKey.