certspotter/cttypes/certs.go

122 lines
3.2 KiB
Go

// Copyright (C) 2025 Opsmate, Inc.
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License, v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// This software is distributed WITHOUT A WARRANTY OF ANY KIND.
// See the Mozilla Public License for details.
package cttypes
import (
"fmt"
"golang.org/x/crypto/cryptobyte"
)
type TBSCertificate []byte
type ASN1Cert []byte
type ASN1CertChain []ASN1Cert
// Corresponds to the PreCert structure in RFC 6962. PreCert is a misnomer because this is really a TBSCertificate, not a precertificate.
type PreCert struct {
IssuerKeyHash [32]byte
TBSCertificate TBSCertificate
}
type PrecertChainEntry struct {
PreCertificate ASN1Cert
PrecertificateChain ASN1CertChain
}
func (v *TBSCertificate) Unmarshal(s *cryptobyte.String) bool {
return s.ReadUint24LengthPrefixed((*cryptobyte.String)(v))
}
func (v TBSCertificate) Marshal(b *cryptobyte.Builder) error {
b.AddUint24LengthPrefixed(addBytesFunc(v))
return nil
}
func (v *ASN1Cert) Unmarshal(s *cryptobyte.String) bool {
return s.ReadUint24LengthPrefixed((*cryptobyte.String)(v))
}
func (v ASN1Cert) Marshal(b *cryptobyte.Builder) error {
b.AddUint24LengthPrefixed(addBytesFunc(v))
return nil
}
func (v *ASN1CertChain) Unmarshal(s *cryptobyte.String) bool {
chainBytes := new(cryptobyte.String)
if !s.ReadUint24LengthPrefixed(chainBytes) {
return false
}
*v = []ASN1Cert{}
for !chainBytes.Empty() {
var cert ASN1Cert
if !cert.Unmarshal(chainBytes) {
return false
}
*v = append(*v, cert)
}
return true
}
func (v ASN1CertChain) Marshal(b *cryptobyte.Builder) error {
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
for _, cert := range v {
b.AddValue(cert)
}
})
return nil
}
func (precert *PreCert) Unmarshal(s *cryptobyte.String) error {
if !s.CopyBytes(precert.IssuerKeyHash[:]) {
return fmt.Errorf("error reading PreCert issuer_key_hash")
}
if !precert.TBSCertificate.Unmarshal(s) {
return fmt.Errorf("error reading PreCert tbs_certificate")
}
return nil
}
func (v *PreCert) Marshal(b *cryptobyte.Builder) error {
b.AddBytes(v.IssuerKeyHash[:])
b.AddValue(v.TBSCertificate)
return nil
}
func (entry *PrecertChainEntry) Unmarshal(s *cryptobyte.String) error {
if !entry.PreCertificate.Unmarshal(s) {
return fmt.Errorf("error reading PrecertChainEntry pre_certificate")
}
if !entry.PrecertificateChain.Unmarshal(s) {
return fmt.Errorf("error reading PrecertChainEntry preeertificate_chain")
}
return nil
}
func ParseExtraDataForX509Entry(extraData []byte) (ASN1CertChain, error) {
str := cryptobyte.String(extraData)
var chain ASN1CertChain
if !chain.Unmarshal(&str) {
return nil, fmt.Errorf("error reading ASN.1Cert chain")
}
if !str.Empty() {
return nil, fmt.Errorf("trailing garbage after ASN.1Cert chain")
}
return chain, nil
}
func ParseExtraDataForPrecertEntry(extraData []byte) (*PrecertChainEntry, error) {
str := cryptobyte.String(extraData)
entry := new(PrecertChainEntry)
if err := entry.Unmarshal(&str); err != nil {
return nil, err
}
if !str.Empty() {
return nil, fmt.Errorf("trailing garbage after PrecertChainEntry")
}
return entry, nil
}