Skip to content

proposal: crypto/x509: add VerifyOptions.UnknownAlgorithmVerifier #74024

Closed as not planned
@bwesterb

Description

@bwesterb

Proposal Details

Context

It's unclear when and which post-quantum signatures Go should support, cf. #64537. There are use cases where migration is more urgent, and generally it's good to be able to start testing early. Presently the only way to support new algorithms in certificates in Go is to either fork Go, or to fork the x509 package. It'd be convenient if upstream Go allows verifying to it unknown algorithms using a callback.

API

Add a new field to VerifyOptions:

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

When during Certificate.Verify an unknown AlgorithmIdentifier is encountered and the UnknownAlgorithmVerifier field is set, Certificate.Verify will call UnknownAlgorithmVerifier to verify the signature.

Notes:

  1. We need access to the public key. Presently Certificate.PublicKey is not set when the AlgorithmIdentifier is unknown during parsing. Instead, we can set Certificate.PublicKey to &publicKeyInfo{...}. This might trip up users that assume PublicKey is nil in case of an unknown algorithm. (Are there any?)
  2. Technically, the public key is an asn1.BitString. I do not know of any post-quantum signature scheme whose public keys aren't simply byte strings, and I do not expect them in the future. For simplicity, I'd say we'd only support byte strings.
  3. Similarly, all post-quantum signature schemes so far do not use parameters, but only an OID. We could simplify matters a bit more by passing an asn1.ObjectIdentifier instead of a pkix.AlgorithmIdentifier.

Example usage:

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    LibraryProposalIssues describing a requested change to the Go standard library or x/ libraries, but not to a toolProposalProposal-CryptoProposal related to crypto packages or other security issues

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions