Skip to content

crypto/x509: Add VerifyOptions.UnknownAlgorithmVerifier #74023

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 8 additions & 5 deletions src/crypto/x509/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -1038,11 +1038,14 @@ func parseCertificate(der []byte) (*Certificate, error) {
if !spki.ReadASN1BitString(&spk) {
return nil, errors.New("x509: malformed subjectPublicKey")
}
if cert.PublicKeyAlgorithm != UnknownPublicKeyAlgorithm {
cert.PublicKey, err = parsePublicKey(&publicKeyInfo{
Algorithm: pkAI,
PublicKey: spk,
})
pki := &publicKeyInfo{
Algorithm: pkAI,
PublicKey: spk,
}
if cert.PublicKeyAlgorithm == UnknownPublicKeyAlgorithm {
cert.PublicKey = pki
} else {
cert.PublicKey, err = parsePublicKey(pki)
if err != nil {
return nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions src/crypto/x509/root_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,8 @@ func verifyChain(c *Certificate, chainCtx *syscall.CertChainContext, opts *Verif
if parent.PublicKeyAlgorithm != ECDSA {
continue
}
if err := parent.CheckSignature(chain[i].SignatureAlgorithm,
chain[i].RawTBSCertificate, chain[i].Signature); err != nil {
if err := checkSignature(chain[i].SignatureAlgorithm,
chain[i].RawTBSCertificate, chain[i].Signature, parent.PublicKey, true, opts); err != nil {
return nil, err
}
}
Expand Down
6 changes: 5 additions & 1 deletion src/crypto/x509/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,10 @@ type VerifyOptions struct {
// field implies any valid policy is acceptable.
CertificatePolicies []OID

// UnknownAlgorithmVerifier specifies a callback to use to verify
// a signature with an unknown AlgorithmIdentifier.
UnknownAlgorithmVerifier func(alg pkix.AlgorithmIdentifier, signed, signature, pk []byte) error

// The following policy fields are unexported, because we do not expect
// users to actually need to use them, but are useful for testing the
// policy validation code.
Expand Down Expand Up @@ -975,7 +979,7 @@ func (c *Certificate) buildChains(currentChain []*Certificate, sigChecks *int, o
return
}

if err := c.CheckSignatureFrom(candidate.cert); err != nil {
if err := c.checkSignatureFrom(candidate.cert, opts); err != nil {
if hintErr == nil {
hintErr = err
hintCert = candidate.cert
Expand Down
43 changes: 34 additions & 9 deletions src/crypto/x509/x509.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,17 @@ type publicKeyInfo struct {
PublicKey asn1.BitString
}

func (pki *publicKeyInfo) Equal(other crypto.PublicKey) bool {
pki2, ok := other.(*publicKeyInfo)
if !ok {
return false
}
return (pki.Algorithm.Algorithm.Equal(pki2.Algorithm.Algorithm) &&
bytes.Equal(pki.Algorithm.Parameters.FullBytes, pki2.Algorithm.Parameters.FullBytes) &&
pki.PublicKey.BitLength == pki2.PublicKey.BitLength &&
bytes.Equal(pki.PublicKey.Bytes, pki2.PublicKey.Bytes))
}

// RFC 5280, 4.2.1.1
type authKeyId struct {
Id []byte `asn1:"optional,tag:0"`
Expand Down Expand Up @@ -909,6 +920,10 @@ func (c *Certificate) hasSANExtension() bool {
// This is a low-level API that performs very limited checks, and not a full
// path verifier. Most users should use [Certificate.Verify] instead.
func (c *Certificate) CheckSignatureFrom(parent *Certificate) error {
return c.checkSignatureFrom(parent, nil)
}

func (c *Certificate) checkSignatureFrom(parent *Certificate, opts *VerifyOptions) error {
// RFC 5280, 4.2.1.9:
// "If the basic constraints extension is not present in a version 3
// certificate, or the extension is present but the cA boolean is not
Expand All @@ -923,11 +938,7 @@ func (c *Certificate) CheckSignatureFrom(parent *Certificate) error {
return ConstraintViolationError{}
}

if parent.PublicKeyAlgorithm == UnknownPublicKeyAlgorithm {
return ErrUnsupportedAlgorithm
}

return checkSignature(c.SignatureAlgorithm, c.RawTBSCertificate, c.Signature, parent.PublicKey, false)
return checkSignature(c.SignatureAlgorithm, c.RawTBSCertificate, c.Signature, parent.PublicKey, false, opts)
}

// CheckSignature verifies that signature is a valid signature over signed from
Expand All @@ -938,7 +949,7 @@ func (c *Certificate) CheckSignatureFrom(parent *Certificate) error {
// [MD5WithRSA] signatures are rejected, while [SHA1WithRSA] and [ECDSAWithSHA1]
// signatures are currently accepted.
func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature []byte) error {
return checkSignature(algo, signed, signature, c.PublicKey, true)
return checkSignature(algo, signed, signature, c.PublicKey, true, nil)
}

func (c *Certificate) hasNameConstraints() bool {
Expand All @@ -960,10 +971,24 @@ func signaturePublicKeyAlgoMismatchError(expectedPubKeyAlgo PublicKeyAlgorithm,

// checkSignature verifies that signature is a valid signature over signed from
// a crypto.PublicKey.
func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey crypto.PublicKey, allowSHA1 bool) (err error) {
func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey crypto.PublicKey, allowSHA1 bool, opts *VerifyOptions) (err error) {
var hashType crypto.Hash
var pubKeyAlgo PublicKeyAlgorithm

if algo == UnknownSignatureAlgorithm {
pki, ok := publicKey.(*publicKeyInfo)
if !ok || opts == nil || opts.UnknownAlgorithmVerifier == nil {
return ErrUnsupportedAlgorithm
}

return opts.UnknownAlgorithmVerifier(
pki.Algorithm,
signed,
signature,
pki.PublicKey.Bytes,
)
}

for _, details := range signatureAlgorithmDetails {
if details.algo == algo {
hashType = details.hash
Expand Down Expand Up @@ -1585,7 +1610,7 @@ func signTBS(tbs []byte, key crypto.Signer, sigAlg SignatureAlgorithm, rand io.R
}

// Check the signature to ensure the crypto.Signer behaved correctly.
if err := checkSignature(sigAlg, tbs, signature, key.Public(), true); err != nil {
if err := checkSignature(sigAlg, tbs, signature, key.Public(), true, nil); err != nil {
return nil, fmt.Errorf("x509: signature returned by signer is invalid: %w", err)
}

Expand Down Expand Up @@ -2259,7 +2284,7 @@ func parseCertificateRequest(in *certificateRequest) (*CertificateRequest, error

// CheckSignature reports whether the signature on c is valid.
func (c *CertificateRequest) CheckSignature() error {
return checkSignature(c.SignatureAlgorithm, c.RawTBSCertificateRequest, c.Signature, c.PublicKey, true)
return checkSignature(c.SignatureAlgorithm, c.RawTBSCertificateRequest, c.Signature, c.PublicKey, true, nil)
}

// RevocationListEntry represents an entry in the revokedCertificates
Expand Down