This commit is contained in:
Bas Westerbaan 2025-06-20 15:32:00 -04:00 committed by GitHub
commit 357571e787
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 49 additions and 17 deletions

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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.
@ -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

View File

@ -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"`
@ -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
@ -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
@ -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 {
@ -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
@ -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)
}
@ -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