mirror of https://github.com/golang/go.git
x509: Add VerifyOptions.UnknownAlgorithmVerifier
This allows callers to verify certificates using algorithms that Go
does not support (yet).
For instance, here we're verifying the ML-KEM-512 example certificate
from the LAMPS WG signed by a ML-DSA-44 public key.
https://github.com/lamps-wg/dilithium-certificates/blob/main/examples/ML-DSA-44.crt
https://github.com/lamps-wg/kyber-certificates/blob/main/example/ML-KEM-512.crt
package main
import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"errors"
"fmt"
"github.com/cloudflare/circl/sign/schemes"
"os"
)
func loadCert(path string) (*x509.Certificate, error) {
raw, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("ReadFile(%s): %w", path, err)
}
block, _ := pem.Decode(raw)
if block == nil {
return nil, fmt.Errorf("pem.Decode(%s) failed", path)
}
return x509.ParseCertificate(block.Bytes)
}
func main() {
dsaCert, err := loadCert("ML-DSA-44.crt")
if err != nil {
panic(err)
}
kemCert, err := loadCert("ML-KEM-512.crt")
if err != nil {
panic(err)
}
roots := x509.NewCertPool()
roots.AddCert(dsaCert)
_, err = kemCert.Verify(x509.VerifyOptions{
Roots: roots,
UnknownAlgorithmVerifier: func(alg pkix.AlgorithmIdentifier,
signed, signature, pk []byte) error {
if !alg.Algorithm.Equal(asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 17}) {
return errors.New("unsupported scheme")
}
scheme := schemes.ByName("ML-DSA-44")
ppk, err := scheme.UnmarshalBinaryPublicKey(pk)
if err != nil {
return err
}
if !scheme.Verify(ppk, signed, signature, nil) {
return errors.New("invalid signature")
}
return nil
},
})
if err != nil {
panic(err)
}
}
This commit is contained in:
parent
3432c68467
commit
fa2ee19f65
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue