CMS reader: its own certificate profile, identifiers by their bytes

Fixes of the review of the session of 1 and 2 October in internal/cms and
internal/der:

- Object identifiers are compared by the bytes of their DER: an arc of 2^31
  or more no longer makes an attribute that decides nothing fail the
  signature (F1), and an algorithm with one is outside the table (F5, S1),
  as spec v0.11 says.
- A SET OF may repeat an element, as X.690 allows: a TSA that sends its
  certificate twice no longer gives S2. Two copies of a certificate are one.
- Certificates are read with a profile of their own instead of
  encoding/asn1 and crypto/x509, field by field, so that a second
  implementation can read them the same: version 3, the fields in order,
  names of non-empty SETs, times of validity in DER without a fraction,
  extensions without repetition, and the text of a name only from
  UTF8String, PrintableString, IA5String, TeletexString in ASCII and
  BMPString without surrogates, nothing removed from it. A certificate that
  breaks the profile decides nothing unless a SignerInfo names it. The
  holder is givenName and surname before commonName, which in the
  certificates of the FNMT carries the NIF; the issuer is its commonName or
  its organizationName.
- The key: RSA with NULL parameters, exactly a modulus and an exponent, the
  modulus odd; EC only uncompressed on P-256, P-384 and P-521.
- A key of another scheme than its algorithm is invalid (F2) and not
  outside the table (F5), as step 3 of 29.10 says; a messageImprint of
  another length is S3; the crls of a token decide nothing.
- DER: UTCTime and GeneralizedTime in their forms of X.690, a date that
  exists, and the millis and micros of accuracy as minimal INTEGERs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.12
dev 6 days ago
parent fee531b768
commit 839e1173e0

@ -3,136 +3,284 @@ package cms
import ( import (
"bytes" "bytes"
"crypto/sha256" "crypto/sha256"
"crypto/x509/pkix"
"encoding/asn1"
"errors" "errors"
"fmt" "fmt"
"math/big"
"strings"
"time" "time"
"unicode/utf8"
"g.activething.com/go/DateKeys/internal/der"
) )
// Cert is an X.509 certificate read for what spec v0.11 §29.10 uses of it: // Cert is an X.509 certificate read with the profile of spec §29.10 for what
// who it names, when it is valid and its key. DateKeys does not check who // DateKeys uses of it: who it names, when it is valid and its key. DateKeys
// issued it. It is read with encoding/asn1 and not with crypto/x509, which // does not check who issued it. It is read with the DER of this module, not
// refuses a certificate with a key of a curve that Go does not have, such as // with crypto/x509 or encoding/asn1, so that two implementations read it the
// brainpool: that certificate is still one that a signer names, and its // same: a certificate with a key of a curve that Go does not have, such as
// signature is "not verifiable", not malformed. // brainpool, is still one that a signer names, and its signature is "not
// verifiable", not malformed.
type Cert struct { type Cert struct {
// Raw is the DER of the certificate and Hash its SHA-256. // Raw is the DER of the certificate and Hash its SHA-256.
Raw []byte Raw []byte
Hash [32]byte Hash [32]byte
// Serial is the content of serialNumber; RawIssuer and RawSubject are the
Serial *big.Int // DER of the two names, and SKI the keyIdentifier of the extension
// subjectKeyIdentifier, nil without it.
Serial []byte
RawIssuer, RawSubject []byte RawIssuer, RawSubject []byte
SKI []byte SKI []byte
NotBefore, NotAfter time.Time NotBefore, NotAfter time.Time
// SPKI is the DER of the SubjectPublicKeyInfo. // SPKI is the DER of the SubjectPublicKeyInfo.
SPKI []byte SPKI []byte
Subject pkix.RDNSequence subject, issuer []attribute
Issuer pkix.RDNSequence
} }
type tbsCertificate struct { // attribute is an AttributeTypeAndValue of a name: the content of its object
Raw asn1.RawContent // identifier and the DER of its value.
Version int `asn1:"optional,explicit,default:0,tag:0"` type attribute struct{ oid, value []byte }
Serial *big.Int
SigAlg asn1.RawValue
Issuer asn1.RawValue
Validity validity
Subject asn1.RawValue
PublicKey asn1.RawValue
IssuerUID asn1.BitString `asn1:"optional,tag:1"`
SubjectUID asn1.BitString `asn1:"optional,tag:2"`
Extensions []pkix.Extension `asn1:"optional,explicit,tag:3"`
}
type validity struct{ NotBefore, NotAfter time.Time } var (
oidSKI = oid("2.5.29.14")
oidCommonName = oid("2.5.4.3")
oidSurname = oid("2.5.4.4")
oidOrgName = oid("2.5.4.10")
oidGivenName = oid("2.5.4.42")
)
type certificate struct { // ParseCert reads the DER of a certificate, which its caller checked, with
TBS tbsCertificate // the profile of spec §29.10:
SigAlg asn1.RawValue //
Sig asn1.BitString // - a SEQUENCE of tbsCertificate, signatureAlgorithm and a BIT STRING;
// - tbsCertificate: version [0], which is 2 (version 3), serialNumber,
// signature, issuer, validity, subject and subjectPublicKeyInfo, then
// [1], [2] and [3] when present, in that order and nothing after;
// - a name: a SEQUENCE of non-empty SETs of AttributeTypeAndValue, each an
// object identifier and one value, in any order;
// - validity: two times, UTCTime or GeneralizedTime without a fraction;
// - extensions: [3] holds a SEQUENCE of one or more Extension, each an
// object identifier, an optional BOOLEAN and an OCTET STRING, none twice;
// subjectKeyIdentifier holds a non-empty OCTET STRING.
//
// What the profile does not read decides nothing: the signature of the
// certificate, its algorithm and the other extensions.
func ParseCert(raw []byte) (*Cert, error) {
bad := func(what string) (*Cert, error) { return nil, fmt.Errorf("certificate: %s", what) }
id, top, err := der.Split(raw)
if err != nil || id != 0x30 || len(top) != 3 || top[0][0] != 0x30 || top[1][0] != 0x30 || top[2][0] != 0x03 {
return bad("not a SEQUENCE of tbsCertificate, signatureAlgorithm and signature")
}
_, f, err := der.Split(top[0])
if err != nil || len(f) < 7 || f[0][0] != 0xa0 {
return bad("a tbsCertificate without version, or with fields missing")
}
if _, v, err := der.Split(f[0]); err != nil || len(v) != 1 || !bytes.Equal(v[0], []byte{0x02, 0x01, 0x02}) {
return bad("the version is not 3")
}
f = f[1:]
if f[0][0] != 0x02 || f[1][0] != 0x30 || f[2][0] != 0x30 || f[3][0] != 0x30 || f[4][0] != 0x30 || f[5][0] != 0x30 {
return bad("the fields of tbsCertificate")
}
c := &Cert{Raw: raw, Hash: sha256.Sum256(raw), RawIssuer: f[2], RawSubject: f[4], SPKI: f[5]}
if c.Serial, err = der.Content(f[0]); err != nil {
return bad("serialNumber")
}
if c.issuer, err = parseName(f[2]); err != nil {
return bad("issuer: " + err.Error())
}
if c.subject, err = parseName(f[4]); err != nil {
return bad("subject: " + err.Error())
}
if _, v, err := der.Split(f[3]); err != nil || len(v) != 2 {
return bad("validity")
} else if c.NotBefore, err = certTime(v[0]); err != nil {
return bad("notBefore: " + err.Error())
} else if c.NotAfter, err = certTime(v[1]); err != nil {
return bad("notAfter: " + err.Error())
}
rest := f[6:]
for _, tag := range []byte{0x81, 0x82} { // issuerUniqueID, subjectUniqueID
if len(rest) > 0 && rest[0][0] == tag {
rest = rest[1:]
}
}
if len(rest) > 0 && rest[0][0] == 0xa3 {
if c.SKI, err = parseExtensions(rest[0]); err != nil {
return bad(err.Error())
}
rest = rest[1:]
}
if len(rest) != 0 {
return bad("a field out of its place, or after the last")
}
return c, nil
} }
var oidSKI = asn1.ObjectIdentifier{2, 5, 29, 14} // certTime reads a time of validity: a UTCTime or a GeneralizedTime in DER,
// without a fraction of seconds (RFC 5280 4.1.2.5).
func certTime(b []byte) (time.Time, error) {
t, fraction, err := der.ParseTime(b)
if err == nil && fraction {
err = errors.New("a GeneralizedTime with a fraction of seconds")
}
return t, err
}
// ParseCert reads the DER of a certificate. // parseName reads a Name: a SEQUENCE of RelativeDistinguishedName, each a
func ParseCert(raw []byte) (*Cert, error) { // non-empty SET of AttributeTypeAndValue. The order of a SET is not checked:
var c certificate // a name only gives the text that a reader shows.
rest, err := asn1.Unmarshal(raw, &c) func parseName(b []byte) ([]attribute, error) {
_, rdns, err := der.Split(b)
if err != nil { if err != nil {
return nil, fmt.Errorf("certificate: %w", err) return nil, errors.New("not a SEQUENCE")
} }
if len(rest) != 0 { var out []attribute
return nil, errors.New("certificate: bytes after it") for _, rdn := range rdns {
id, atvs, err := der.Split(rdn)
if err != nil || id != 0x31 || len(atvs) == 0 {
return nil, errors.New("a RelativeDistinguishedName that is not a non-empty SET")
}
for _, atv := range atvs {
id, p, err := der.Split(atv)
if err != nil || id != 0x30 || len(p) != 2 || p[0][0] != 0x06 {
return nil, errors.New("an AttributeTypeAndValue that is not an object identifier and a value")
} }
t := &c.TBS o, _ := der.Content(p[0])
if t.Serial == nil || t.Issuer.Tag != asn1.TagSequence || t.Subject.Tag != asn1.TagSequence || t.PublicKey.Tag != asn1.TagSequence { out = append(out, attribute{o, p[1]})
return nil, errors.New("certificate: malformed")
} }
out := &Cert{
Raw: raw, Hash: sha256.Sum256(raw), Serial: t.Serial,
RawIssuer: t.Issuer.FullBytes, RawSubject: t.Subject.FullBytes,
NotBefore: t.Validity.NotBefore, NotAfter: t.Validity.NotAfter, SPKI: t.PublicKey.FullBytes,
} }
if _, err := asn1.Unmarshal(out.RawSubject, &out.Subject); err != nil { return out, nil
return nil, fmt.Errorf("certificate: subject: %w", err) }
// parseExtensions reads [3] of tbsCertificate and returns the keyIdentifier
// of subjectKeyIdentifier, nil without it.
func parseExtensions(b []byte) ([]byte, error) {
_, in, err := der.Split(b)
if err != nil || len(in) != 1 || in[0][0] != 0x30 {
return nil, errors.New("extensions")
} }
if _, err := asn1.Unmarshal(out.RawIssuer, &out.Issuer); err != nil { _, exts, err := der.Split(in[0])
return nil, fmt.Errorf("certificate: issuer: %w", err) if err != nil || len(exts) == 0 {
return nil, errors.New("extensions without an Extension")
} }
for _, e := range t.Extensions { seen := map[string]bool{}
if e.Id.Equal(oidSKI) {
var ski []byte var ski []byte
if rest, err := asn1.Unmarshal(e.Value, &ski); err != nil || len(rest) != 0 { for _, e := range exts {
return nil, errors.New("certificate: subjectKeyIdentifier malformed") id, p, err := der.Split(e)
if err != nil || id != 0x30 || len(p) < 2 || len(p) > 3 || p[0][0] != 0x06 || p[len(p)-1][0] != 0x04 || len(p) == 3 && p[1][0] != 0x01 {
return nil, errors.New("an Extension that is not an object identifier, an optional BOOLEAN and an OCTET STRING")
} }
out.SKI = ski o, _ := der.Content(p[0])
if seen[string(o)] {
return nil, errors.New("an extension twice")
} }
seen[string(o)] = true
if bytes.Equal(o, oidSKI) {
v, _ := der.Content(p[len(p)-1])
if der.Check(v) != nil || v[0] != 0x04 {
return nil, errors.New("a subjectKeyIdentifier that is not an OCTET STRING")
} }
return out, nil if ski, _ = der.Content(v); len(ski) == 0 {
return nil, errors.New("an empty subjectKeyIdentifier")
}
}
}
return ski, nil
} }
var ( // text returns the text of the value of an attribute of a name, and false
oidCommonName = asn1.ObjectIdentifier{2, 5, 4, 3} // when it is not of a string type that the profile reads, or when its bytes
oidSurname = asn1.ObjectIdentifier{2, 5, 4, 4} // break its type (spec §29.10): UTF8String in valid UTF-8, PrintableString
oidGivenName = asn1.ObjectIdentifier{2, 5, 4, 42} // in its alphabet, IA5String and TeletexString in ASCII, and BMPString in
) // UTF-16BE without surrogates. Nothing is removed from the text: a byte order
// mark or a terminator stays, and the rules of §29.6 decide.
func text(v []byte) (string, bool) {
c, err := der.Content(v)
if err != nil {
return "", false
}
switch v[0] {
case 0x0c: // UTF8String
return string(c), utf8.Valid(c)
case 0x13: // PrintableString
for _, b := range c {
if !printable(b) {
return "", false
}
}
case 0x14, 0x16: // TeletexString, IA5String
for _, b := range c {
if b > 0x7f {
return "", false
}
}
case 0x1e: // BMPString
if len(c)%2 != 0 {
return "", false
}
r := make([]rune, 0, len(c)/2)
for i := 0; i < len(c); i += 2 {
u := rune(c[i])<<8 | rune(c[i+1])
if u >= 0xd800 && u <= 0xdfff {
return "", false
}
r = append(r, u)
}
return string(r), true
default:
return "", false
}
return string(c), true
}
func rdnString(rdn pkix.RDNSequence, oid asn1.ObjectIdentifier) string { // printable reports whether b is in the alphabet of PrintableString (X.680).
for _, set := range rdn { func printable(b byte) bool {
for _, a := range set { return b >= 'a' && b <= 'z' || b >= 'A' && b <= 'Z' || b >= '0' && b <= '9' || bytes.IndexByte([]byte(" '()+,-./:=?"), b) >= 0
if s, ok := a.Value.(string); ok && a.Type.Equal(oid) { }
return s
// nameText returns the text of the only attribute of type o among attrs, and
// false when there is none, when there are several, or when its value is not
// text.
func nameText(attrs []attribute, o []byte) (string, bool) {
var v []byte
n := 0
for _, a := range attrs {
if bytes.Equal(a.oid, o) {
v, n = a.value, n+1
} }
} }
if n != 1 {
return "", false
} }
return "" return text(v)
} }
// Holder returns the name of the subject, taken from its commonName or from // Holder returns the name of the subject as spec §29.7 takes it: its
// its givenName and surname, and "" when it has neither. It does not apply the // givenName and its surname, when it has one of each, and its commonName
// rules of text of spec §29.6 to it: the caller does, and shows the hash of // otherwise; "" when it has neither. The commonName comes second because it
// may carry the national identifier of the person, as the one of the FNMT
// does. The caller applies the rules of text of §29.6, and shows the hash of
// the certificate when they fail. // the certificate when they fail.
func (c *Cert) Holder() string { func (c *Cert) Holder() string {
if n := rdnString(c.Subject, oidCommonName); n != "" { given, ok1 := nameText(c.subject, oidGivenName)
return n sur, ok2 := nameText(c.subject, oidSurname)
} if ok1 && ok2 && given != "" && sur != "" {
given, sur := rdnString(c.Subject, oidGivenName), rdnString(c.Subject, oidSurname)
if given != "" && sur != "" {
return given + " " + sur return given + " " + sur
} }
if cn, ok := nameText(c.subject, oidCommonName); ok {
return cn
}
return "" return ""
} }
// IssuerName returns the issuer as the certificate names it, as a text for // IssuerName returns the issuer that the certificate names: its commonName,
// the person to read: commonName, or all of the attributes it has. // or its organizationName, or "" when it has neither (spec §29.7).
func (c *Cert) IssuerName() string { func (c *Cert) IssuerName() string {
if n := rdnString(c.Issuer, oidCommonName); n != "" { if cn, ok := nameText(c.issuer, oidCommonName); ok {
return n return cn
}
if o, ok := nameText(c.issuer, oidOrgName); ok {
return o
} }
return strings.TrimSpace(c.Issuer.String()) return ""
} }
// ValidAt reports whether t is in the validity period of the certificate. // ValidAt reports whether t is in the validity period of the certificate.
@ -140,6 +288,6 @@ func (c *Cert) ValidAt(t time.Time) bool {
return !t.Before(c.NotBefore) && !t.After(c.NotAfter) return !t.Before(c.NotBefore) && !t.After(c.NotAfter)
} }
func (c *Cert) hasSID(issuer []byte, serial *big.Int) bool { func (c *Cert) hasSID(issuer, serial []byte) bool {
return bytes.Equal(c.RawIssuer, issuer) && c.Serial.Cmp(serial) == 0 return bytes.Equal(c.RawIssuer, issuer) && bytes.Equal(c.Serial, serial)
} }

@ -1,11 +1,13 @@
// Package cms reads the CMS signatures (RFC 5652) and the RFC 3161 time-stamp // Package cms reads the CMS signatures (RFC 5652) and the RFC 3161 time-stamp
// tokens that spec v0.11 §29.10 and §29.11 define, with the CAdES profile // tokens that spec §29.10 and §29.11 define, with the CAdES profile that
// that AutoFirma and other signing applications produce, and checks them with // AutoFirma and other signing applications produce, and checks them with a
// a closed table of algorithms. It uses the standard library only. // closed table of algorithms. It uses the standard library only.
// //
// It checks the signature and the dates, never who issued a certificate or // It checks the signature and the dates, never who issued a certificate or
// whether it was revoked: a validator of the country that corresponds does // whether it was revoked: a validator of the country that corresponds does
// that (spec §29.10). // that (spec §29.10). Object identifiers are compared by the bytes of their
// DER, so that an arc of any size is only an identifier that the table does
// not have.
package cms package cms
import ( import (
@ -13,11 +15,11 @@ import (
"crypto/sha1" "crypto/sha1"
"crypto/sha256" "crypto/sha256"
"crypto/sha512" "crypto/sha512"
"encoding/asn1"
"errors" "errors"
"fmt" "fmt"
"hash" "hash"
"math/big" "strconv"
"strings"
"g.activething.com/go/DateKeys/internal/der" "g.activething.com/go/DateKeys/internal/der"
) )
@ -34,26 +36,66 @@ func formErr(format string, args ...any) error {
return fmt.Errorf("%w: %s", ErrForm, fmt.Sprintf(format, args...)) return fmt.Errorf("%w: %s", ErrForm, fmt.Sprintf(format, args...))
} }
// oid returns the content of the DER of the object identifier s, written
// with dots: the bytes that this package compares.
func oid(s string) []byte {
parts := strings.Split(s, ".")
arcs := make([]uint64, len(parts))
for i, p := range parts {
n, err := strconv.ParseUint(p, 10, 64)
if err != nil || len(parts) < 2 {
panic("cms: a bad object identifier " + s)
}
arcs[i] = n
}
out := base128(nil, arcs[0]*40+arcs[1])
for _, a := range arcs[2:] {
out = base128(out, a)
}
return out
}
func base128(out []byte, v uint64) []byte {
var tmp [10]byte
i := len(tmp) - 1
tmp[i] = byte(v & 0x7f)
for v >>= 7; v > 0; v >>= 7 {
i--
tmp[i] = byte(v&0x7f) | 0x80
}
return append(out, tmp[i:]...)
}
// oidOf returns the content of the object identifier element b.
func oidOf(b []byte) ([]byte, bool) {
if len(b) == 0 || b[0] != 0x06 {
return nil, false
}
c, err := der.Content(b)
return c, err == nil
}
// The object identifiers of the profile. // The object identifiers of the profile.
var ( var (
oidData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 1} oidData = oid("1.2.840.113549.1.7.1")
oidSignedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 2} oidSignedData = oid("1.2.840.113549.1.7.2")
oidContentType = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 3} oidContentType = oid("1.2.840.113549.1.9.3")
oidMessageDig = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 4} oidMessageDig = oid("1.2.840.113549.1.9.4")
oidSigCertV1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 2, 12} oidSigCertV1 = oid("1.2.840.113549.1.9.16.2.12")
oidSigCertV2 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 2, 47} oidSigCertV2 = oid("1.2.840.113549.1.9.16.2.47")
oidSigTimeStamp = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 2, 14} oidSigTimeStamp = oid("1.2.840.113549.1.9.16.2.14")
oidTSTInfo = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 1, 4} oidTSTInfo = oid("1.2.840.113549.1.9.16.1.4")
oidRIOCSP = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 16, 2} oidRIOCSP = oid("1.3.6.1.5.5.7.16.2")
oidSHA1 = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26} oidSHA256 = oid("2.16.840.1.101.3.4.2.1")
oidSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1} oidSHA384 = oid("2.16.840.1.101.3.4.2.2")
oidSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2} oidSHA512 = oid("2.16.840.1.101.3.4.2.3")
oidSHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3}
) )
// SignedData is the part of a CMS SignedData that the profile uses. // SignedData is the part of a CMS SignedData that the profile uses.
type SignedData struct { type SignedData struct {
// Certs are the certificates that meet the profile of §29.10, each once:
// another one decides nothing, unless a SignerInfo names it.
Certs []*Cert Certs []*Cert
// OCSP are the OCSP responses of crls. // OCSP are the OCSP responses of crls.
OCSP [][]byte OCSP [][]byte
@ -79,18 +121,19 @@ type SignerInfo struct {
} }
type algID struct { type algID struct {
OID asn1.ObjectIdentifier OID []byte // the content of the object identifier
Params []byte // the DER of the parameters, nil when absent Params []byte // the DER of the parameters, nil when absent
} }
func parseAlgID(b []byte) (algID, error) { func parseAlgID(b []byte) (algID, error) {
id, kids, err := der.Split(b) id, kids, err := der.Split(b)
if err != nil || id != 0x30 || len(kids) < 1 || len(kids) > 2 || kids[0][0] != 0x06 { if err != nil || id != 0x30 || len(kids) < 1 || len(kids) > 2 {
return algID{}, formErr("an AlgorithmIdentifier") return algID{}, formErr("an AlgorithmIdentifier")
} }
var a algID var a algID
if _, err := asn1.Unmarshal(kids[0], &a.OID); err != nil { var ok bool
return algID{}, formErr("an AlgorithmIdentifier: %v", err) if a.OID, ok = oidOf(kids[0]); !ok {
return algID{}, formErr("an AlgorithmIdentifier without an object identifier")
} }
if len(kids) == 2 { if len(kids) == 2 {
a.Params = kids[1] a.Params = kids[1]
@ -105,11 +148,11 @@ func (a algID) hashOf() (func() hash.Hash, bool) {
return nil, false return nil, false
} }
switch { switch {
case a.OID.Equal(oidSHA256): case bytes.Equal(a.OID, oidSHA256):
return sha256.New, true return sha256.New, true
case a.OID.Equal(oidSHA384): case bytes.Equal(a.OID, oidSHA384):
return sha512.New384, true return sha512.New384, true
case a.OID.Equal(oidSHA512): case bytes.Equal(a.OID, oidSHA512):
return sha512.New, true return sha512.New, true
} }
return nil, false return nil, false
@ -126,11 +169,10 @@ func parse(b []byte, token bool) (*SignedData, error) {
return nil, formErr("%v", err) return nil, formErr("%v", err)
} }
id, ci, err := der.Split(b) id, ci, err := der.Split(b)
if err != nil || id != 0x30 || len(ci) != 2 || ci[0][0] != 0x06 || ci[1][0] != 0xa0 { if err != nil || id != 0x30 || len(ci) != 2 || ci[1][0] != 0xa0 {
return nil, formErr("a ContentInfo") return nil, formErr("a ContentInfo")
} }
var oid asn1.ObjectIdentifier if ct, ok := oidOf(ci[0]); !ok || !bytes.Equal(ct, oidSignedData) {
if _, err := asn1.Unmarshal(ci[0], &oid); err != nil || !oid.Equal(oidSignedData) {
return nil, formErr("the content type is not id-signedData") return nil, formErr("the content type is not id-signedData")
} }
_, inner, err := der.Split(ci[1]) _, inner, err := der.Split(ci[1])
@ -163,7 +205,7 @@ func parse(b []byte, token bool) (*SignedData, error) {
rest = rest[1:] rest = rest[1:]
} }
if len(rest) > 0 && rest[0][0] == 0xa1 { if len(rest) > 0 && rest[0][0] == 0xa1 {
if err := parseCRLs(rest[0], out); err != nil { if err := parseCRLs(rest[0], token, out); err != nil {
return nil, err return nil, err
} }
rest = rest[1:] rest = rest[1:]
@ -197,20 +239,20 @@ func parse(b []byte, token bool) (*SignedData, error) {
// signature, and id-ct-TSTInfo with its content in a token. // signature, and id-ct-TSTInfo with its content in a token.
func parseEncap(b []byte, token bool, out *SignedData) error { func parseEncap(b []byte, token bool, out *SignedData) error {
_, kids, err := der.Split(b) _, kids, err := der.Split(b)
if err != nil || len(kids) < 1 || len(kids) > 2 || kids[0][0] != 0x06 { if err != nil || len(kids) < 1 || len(kids) > 2 {
return formErr("encapContentInfo") return formErr("encapContentInfo")
} }
var oid asn1.ObjectIdentifier ct, ok := oidOf(kids[0])
if _, err := asn1.Unmarshal(kids[0], &oid); err != nil { if !ok {
return formErr("encapContentInfo") return formErr("encapContentInfo")
} }
if !token { if !token {
if !oid.Equal(oidData) || len(kids) != 1 { if !bytes.Equal(ct, oidData) || len(kids) != 1 {
return formErr("a signature is detached: id-data and no eContent") return formErr("a signature is detached: id-data and no eContent")
} }
return nil return nil
} }
if !oid.Equal(oidTSTInfo) || len(kids) != 2 || kids[1][0] != 0xa0 { if !bytes.Equal(ct, oidTSTInfo) || len(kids) != 2 || kids[1][0] != 0xa0 {
return formErr("a token holds a TSTInfo") return formErr("a token holds a TSTInfo")
} }
_, e, err := der.Split(kids[1]) _, e, err := der.Split(kids[1])
@ -223,42 +265,51 @@ func parseEncap(b []byte, token bool, out *SignedData) error {
return nil return nil
} }
// parseCerts reads certificates. A certificate that breaks the profile of
// §29.10 decides nothing, as one that no SignerInfo names: an intermediate of
// another form is not a reason to refuse a signature, and the sid of a signer
// whose certificate breaks it names none. Two copies of a certificate are one.
func parseCerts(b []byte, out *SignedData) error { func parseCerts(b []byte, out *SignedData) error {
_, kids, err := der.Split(b) _, kids, err := der.Split(b)
if err != nil || !der.SetOfSorted(kids) { if err != nil || !der.SetOfSorted(kids) {
return formErr("certificates is not a SET OF in DER order") return formErr("certificates is not a SET OF in DER order")
} }
for _, k := range kids { for i, k := range kids {
if k[0] >= 0xa0 && k[0] <= 0xa3 { // another choice of CertificateChoices: it decides nothing if k[0] >= 0xa0 && k[0] <= 0xa3 { // another choice of CertificateChoices: it decides nothing
continue continue
} }
if k[0] != 0x30 { if k[0] != 0x30 {
return formErr("a CertificateChoice that is neither a certificate nor one of the other four choices") return formErr("a CertificateChoice that is neither a certificate nor one of the other four choices")
} }
c, err := ParseCert(k) if i > 0 && bytes.Equal(kids[i-1], k) {
if err != nil { continue
return formErr("%v", err)
} }
if c, err := ParseCert(k); err == nil {
out.Certs = append(out.Certs, c) out.Certs = append(out.Certs, c)
} }
}
return nil return nil
} }
func parseCRLs(b []byte, out *SignedData) error { // parseCRLs reads crls: in a signature, only OCSP responses (spec §29.10 rule
// 3); in a token, whatever it holds decides nothing (§29.11).
func parseCRLs(b []byte, token bool, out *SignedData) error {
_, kids, err := der.Split(b) _, kids, err := der.Split(b)
if err != nil || !der.SetOfSorted(kids) { if err != nil || !der.SetOfSorted(kids) {
return formErr("crls is not a SET OF in DER order") return formErr("crls is not a SET OF in DER order")
} }
if token {
return nil
}
for _, k := range kids { for _, k := range kids {
if k[0] != 0xa1 { if k[0] != 0xa1 {
return formErr("crls holds only OCSP responses") return formErr("crls holds only OCSP responses")
} }
_, f, err := der.Split(k) _, f, err := der.Split(k)
if err != nil || len(f) != 2 || f[0][0] != 0x06 { if err != nil || len(f) != 2 {
return formErr("an OtherRevocationInfoFormat") return formErr("an OtherRevocationInfoFormat")
} }
var oid asn1.ObjectIdentifier if o, ok := oidOf(f[0]); !ok || !bytes.Equal(o, oidRIOCSP) {
if _, err := asn1.Unmarshal(f[0], &oid); err != nil || !oid.Equal(oidRIOCSP) {
return formErr("crls holds only OCSP responses") return formErr("crls holds only OCSP responses")
} }
out.OCSP = append(out.OCSP, f[1]) out.OCSP = append(out.OCSP, f[1])
@ -303,7 +354,9 @@ func parseSignerInfo(b []byte, sd *SignedData, token bool) (*SignerInfo, error)
return s, nil return s, nil
} }
// findSigner returns the one certificate that the sid names. // findSigner returns the one certificate that the sid names, comparing the
// DER of the issuer and the content of the serial number, or the
// keyIdentifier.
func findSigner(sid []byte, certs []*Cert) (*Cert, error) { func findSigner(sid []byte, certs []*Cert) (*Cert, error) {
var found *Cert var found *Cert
n := 0 n := 0
@ -313,10 +366,7 @@ func findSigner(sid []byte, certs []*Cert) (*Cert, error) {
if err != nil || len(p) != 2 || p[0][0] != 0x30 || p[1][0] != 0x02 { if err != nil || len(p) != 2 || p[0][0] != 0x30 || p[1][0] != 0x02 {
return nil, formErr("issuerAndSerialNumber") return nil, formErr("issuerAndSerialNumber")
} }
var serial *big.Int serial, _ := der.Content(p[1])
if _, err := asn1.Unmarshal(p[1], &serial); err != nil {
return nil, formErr("issuerAndSerialNumber")
}
for _, c := range certs { for _, c := range certs {
if c.hasSID(p[0], serial) { if c.hasSID(p[0], serial) {
found, n = c, n+1 found, n = c, n+1
@ -336,20 +386,21 @@ func findSigner(sid []byte, certs []*Cert) (*Cert, error) {
return nil, formErr("a SignerIdentifier") return nil, formErr("a SignerIdentifier")
} }
if n != 1 { if n != 1 {
return nil, formErr("a sid that names %d certificates, not one", n) return nil, formErr("a sid that names %d certificates of the profile, not one", n)
} }
return found, nil return found, nil
} }
// attrSet holds the attributes of a SET OF Attribute: the values of each type // attrSet holds the attributes of a SET OF Attribute: the values of each type
// and the number of attributes of each type, which is not the number of values. // and the number of attributes of each type, which is not the number of
// values. Both are kept by the bytes of the object identifier.
type attrSet struct { type attrSet struct {
vals map[string][][]byte vals map[string][][]byte
count map[string]int count map[string]int
} }
func (a attrSet) get(oid asn1.ObjectIdentifier) ([][]byte, int) { func (a attrSet) get(o []byte) ([][]byte, int) {
return a.vals[oid.String()], a.count[oid.String()] return a.vals[string(o)], a.count[string(o)]
} }
// attrs reads the SET OF Attribute b. An attribute needs at least one value // attrs reads the SET OF Attribute b. An attribute needs at least one value
@ -363,26 +414,26 @@ func attrs(b []byte) (attrSet, error) {
out := attrSet{vals: map[string][][]byte{}, count: map[string]int{}} out := attrSet{vals: map[string][][]byte{}, count: map[string]int{}}
for _, a := range kids { for _, a := range kids {
_, p, err := der.Split(a) _, p, err := der.Split(a)
if err != nil || len(p) != 2 || a[0] != 0x30 || p[0][0] != 0x06 || p[1][0] != 0x31 { if err != nil || len(p) != 2 || a[0] != 0x30 || p[1][0] != 0x31 {
return attrSet{}, formErr("an Attribute") return attrSet{}, formErr("an Attribute")
} }
var oid asn1.ObjectIdentifier o, ok := oidOf(p[0])
if _, err := asn1.Unmarshal(p[0], &oid); err != nil { if !ok {
return attrSet{}, formErr("an Attribute") return attrSet{}, formErr("an Attribute")
} }
_, vals, err := der.Split(p[1]) _, vals, err := der.Split(p[1])
if err != nil || len(vals) == 0 || !der.SetOfSorted(vals) { if err != nil || len(vals) == 0 || !der.SetOfSorted(vals) {
return attrSet{}, formErr("the values of an attribute are not a non-empty SET OF in DER order") return attrSet{}, formErr("the values of an attribute are not a non-empty SET OF in DER order")
} }
out.vals[oid.String()] = append(out.vals[oid.String()], vals...) out.vals[string(o)] = append(out.vals[string(o)], vals...)
out.count[oid.String()]++ out.count[string(o)]++
} }
return out, nil return out, nil
} }
// one returns the only value of the only attribute of the type. // one returns the only value of the only attribute of the type.
func one(m attrSet, oid asn1.ObjectIdentifier, name string) ([]byte, error) { func one(m attrSet, o []byte, name string) ([]byte, error) {
v, n := m.get(oid) v, n := m.get(o)
if n != 1 || len(v) != 1 { if n != 1 || len(v) != 1 {
return nil, formErr("%s: %d attributes with %d values, not one with one", name, n, len(v)) return nil, formErr("%s: %d attributes with %d values, not one with one", name, n, len(v))
} }
@ -401,13 +452,12 @@ func parseSignedAttrs(s *SignerInfo, token bool) error {
if err != nil { if err != nil {
return err return err
} }
want := oidData want, name := oidData, "id-data"
if token { if token {
want = oidTSTInfo want, name = oidTSTInfo, "id-ct-TSTInfo"
} }
var got asn1.ObjectIdentifier if got, ok := oidOf(ct); !ok || !bytes.Equal(got, want) {
if _, err := asn1.Unmarshal(ct, &got); err != nil || !got.Equal(want) { return formErr("content-type is not %s", name)
return formErr("content-type is not %v", want)
} }
md, err := one(m, oidMessageDig, "message-digest") md, err := one(m, oidMessageDig, "message-digest")
if err != nil { if err != nil {

@ -6,9 +6,8 @@ import (
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
"crypto/rsa" "crypto/rsa"
"crypto/x509"
"encoding/asn1"
"errors" "errors"
"math/big"
"time" "time"
"g.activething.com/go/DateKeys/internal/der" "g.activething.com/go/DateKeys/internal/der"
@ -29,15 +28,19 @@ const (
) )
var ( var (
oidRSAEncryption = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} oidRSAEncryption = oid("1.2.840.113549.1.1.1")
oidSHA256RSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} oidSHA256RSA = oid("1.2.840.113549.1.1.11")
oidSHA384RSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} oidSHA384RSA = oid("1.2.840.113549.1.1.12")
oidSHA512RSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} oidSHA512RSA = oid("1.2.840.113549.1.1.13")
oidPSS = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10} oidPSS = oid("1.2.840.113549.1.1.10")
oidMGF1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 8} oidMGF1 = oid("1.2.840.113549.1.1.8")
oidECDSA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2} oidECDSA256 = oid("1.2.840.10045.4.3.2")
oidECDSA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3} oidECDSA384 = oid("1.2.840.10045.4.3.3")
oidECDSA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4} oidECDSA512 = oid("1.2.840.10045.4.3.4")
oidECPublicKey = oid("1.2.840.10045.2.1")
oidP256 = oid("1.2.840.10045.3.1.7")
oidP384 = oid("1.3.132.0.34")
oidP521 = oid("1.3.132.0.35")
) )
type scheme int type scheme int
@ -65,24 +68,24 @@ func (s *SignerInfo) params() (crypto.Hash, scheme, bool) {
default: default:
h = crypto.SHA512 h = crypto.SHA512
} }
oid, params := s.SigAlg.OID, s.SigAlg.Params o, params := s.SigAlg.OID, s.SigAlg.Params
nullOrAbsent := params == nil || bytes.Equal(params, []byte{5, 0}) nullOrAbsent := params == nil || bytes.Equal(params, []byte{5, 0})
switch { switch {
case oid.Equal(oidRSAEncryption): case bytes.Equal(o, oidRSAEncryption):
return h, schemePKCS1, nullOrAbsent return h, schemePKCS1, nullOrAbsent
case oid.Equal(oidSHA256RSA): case bytes.Equal(o, oidSHA256RSA):
return h, schemePKCS1, nullOrAbsent && h == crypto.SHA256 return h, schemePKCS1, nullOrAbsent && h == crypto.SHA256
case oid.Equal(oidSHA384RSA): case bytes.Equal(o, oidSHA384RSA):
return h, schemePKCS1, nullOrAbsent && h == crypto.SHA384 return h, schemePKCS1, nullOrAbsent && h == crypto.SHA384
case oid.Equal(oidSHA512RSA): case bytes.Equal(o, oidSHA512RSA):
return h, schemePKCS1, nullOrAbsent && h == crypto.SHA512 return h, schemePKCS1, nullOrAbsent && h == crypto.SHA512
case oid.Equal(oidECDSA256): case bytes.Equal(o, oidECDSA256):
return h, schemeECDSA, params == nil && h == crypto.SHA256 return h, schemeECDSA, params == nil && h == crypto.SHA256
case oid.Equal(oidECDSA384): case bytes.Equal(o, oidECDSA384):
return h, schemeECDSA, params == nil && h == crypto.SHA384 return h, schemeECDSA, params == nil && h == crypto.SHA384
case oid.Equal(oidECDSA512): case bytes.Equal(o, oidECDSA512):
return h, schemeECDSA, params == nil && h == crypto.SHA512 return h, schemeECDSA, params == nil && h == crypto.SHA512
case oid.Equal(oidPSS): case bytes.Equal(o, oidPSS):
return h, schemePSS, pssParamsOK(params, newHash().Size(), s.DigestAlg) return h, schemePSS, pssParamsOK(params, newHash().Size(), s.DigestAlg)
} }
return 0, 0, false return 0, 0, false
@ -109,18 +112,17 @@ func pssParamsOK(params []byte, hashLen int, digest algID) bool {
switch e[0] { switch e[0] {
case 0xa0: case 0xa0:
a, err := parseAlgID(in[0]) a, err := parseAlgID(in[0])
hashOK = err == nil && a.OID.Equal(digest.OID) && (a.Params == nil || bytes.Equal(a.Params, []byte{5, 0})) hashOK = err == nil && bytes.Equal(a.OID, digest.OID) && (a.Params == nil || bytes.Equal(a.Params, []byte{5, 0}))
case 0xa1: case 0xa1:
a, err := parseAlgID(in[0]) a, err := parseAlgID(in[0])
if err != nil || !a.OID.Equal(oidMGF1) || a.Params == nil { if err != nil || !bytes.Equal(a.OID, oidMGF1) || a.Params == nil {
return false return false
} }
inner, err := parseAlgID(a.Params) inner, err := parseAlgID(a.Params)
mgfOK = err == nil && inner.OID.Equal(digest.OID) && (inner.Params == nil || bytes.Equal(inner.Params, []byte{5, 0})) mgfOK = err == nil && bytes.Equal(inner.OID, digest.OID) && (inner.Params == nil || bytes.Equal(inner.Params, []byte{5, 0}))
case 0xa2: case 0xa2:
var n int n, ok := smallInt(in[0])
_, err := asn1.Unmarshal(in[0], &n) saltOK = ok && n == hashLen
saltOK = err == nil && n == hashLen
default: // [3] trailerField is 1, its DEFAULT: DER does not write it default: // [3] trailerField is 1, its DEFAULT: DER does not write it
return false return false
} }
@ -130,73 +132,124 @@ func pssParamsOK(params []byte, hashLen int, digest algID) bool {
return hashOK && mgfOK && saltOK return hashOK && mgfOK && saltOK
} }
// publicKey returns the key of the certificate when it is in the table: // smallInt reads an INTEGER element of at most 4 bytes that is not negative.
// RSA of 2048 to 4096 bits with an odd exponent from 3 to 2^31 - 1, or ECDSA func smallInt(b []byte) (int, bool) {
// on P-256, P-384 or P-521. if len(b) == 0 || b[0] != 0x02 {
return 0, false
}
c, err := der.Content(b)
if err != nil || len(c) == 0 || len(c) > 4 || c[0]&0x80 != 0 {
return 0, false
}
n := 0
for _, x := range c {
n = n<<8 | int(x)
}
return n, true
}
// publicKey returns the key of the certificate when the table has it (spec
// §29.10): a SubjectPublicKeyInfo of rsaEncryption with NULL parameters and
// an RSAPublicKey of exactly a modulus and an exponent, the modulus odd and
// of 2048 to 4096 bits and the exponent odd from 3 to 2^31 - 1; or of
// id-ecPublicKey with the named curve P-256, P-384 or P-521 and the
// uncompressed form of a point of it. Anything else is not usable.
func (c *Cert) publicKey() (any, scheme, bool) { func (c *Cert) publicKey() (any, scheme, bool) {
k, err := x509.ParsePKIXPublicKey(c.SPKI) _, f, err := der.Split(c.SPKI)
if err != nil || len(f) != 2 || f[0][0] != 0x30 || f[1][0] != 0x03 {
return nil, 0, false
}
alg, err := parseAlgID(f[0])
if err != nil { if err != nil {
return nil, 0, false return nil, 0, false
} }
switch k := k.(type) { bits, err := der.Content(f[1])
case *rsa.PublicKey: if err != nil || len(bits) < 2 || bits[0] != 0 {
bits := k.N.BitLen()
if bits < 2048 || bits > 4096 || k.E < 3 || k.E%2 == 0 || int64(k.E) > 1<<31-1 {
return nil, 0, false return nil, 0, false
} }
return k, schemePKCS1, true key := bits[1:]
case *ecdsa.PublicKey: switch {
switch k.Curve { case bytes.Equal(alg.OID, oidRSAEncryption) && bytes.Equal(alg.Params, []byte{5, 0}):
case elliptic.P256(), elliptic.P384(), elliptic.P521(): if der.Check(key) != nil {
return k, schemeECDSA, true return nil, 0, false
} }
id, ne, err := der.Split(key)
if err != nil || id != 0x30 || len(ne) != 2 || ne[0][0] != 0x02 || ne[1][0] != 0x02 {
return nil, 0, false
} }
nb, _ := der.Content(ne[0])
eb, _ := der.Content(ne[1])
if nb[0]&0x80 != 0 || eb[0]&0x80 != 0 || len(eb) > 4 {
return nil, 0, false
}
n := new(big.Int).SetBytes(nb)
e := new(big.Int).SetBytes(eb).Int64()
if b := n.BitLen(); b < 2048 || b > 4096 || n.Bit(0) == 0 || e < 3 || e%2 == 0 || e > 1<<31-1 {
return nil, 0, false return nil, 0, false
}
// algorithmsOK reports whether the algorithms of the SignerInfo and the key of
// its certificate are in the table of spec §29.10.
func (s *SignerInfo) algorithmsOK() bool {
_, sch, ok := s.params()
if !ok {
return false
} }
_, ksch, ok := s.Cert.publicKey() return &rsa.PublicKey{N: n, E: int(e)}, schemePKCS1, true
case bytes.Equal(alg.OID, oidECPublicKey):
curveOID, ok := oidOf(alg.Params)
if !ok { if !ok {
return false return nil, 0, false
}
var curve elliptic.Curve
switch {
case bytes.Equal(curveOID, oidP256):
curve = elliptic.P256()
case bytes.Equal(curveOID, oidP384):
curve = elliptic.P384()
case bytes.Equal(curveOID, oidP521):
curve = elliptic.P521()
default:
return nil, 0, false
}
k, err := ecdsa.ParseUncompressedPublicKey(curve, key)
if err != nil {
return nil, 0, false
} }
return sch == ksch || sch == schemePSS && ksch == schemePKCS1 return k, schemeECDSA, true
}
return nil, 0, false
} }
// Check checks the signature of the SignerInfo over message, the bytes that // Check checks the signature of the SignerInfo over message, the bytes that
// the signature is detached from (spec §29.10): not verifiable for an // the signature is detached from (spec §29.10), in the order of the spec: not
// algorithm outside the table; invalid when the message-digest is not the // verifiable for an algorithm, a key or a curve outside the table; invalid
// hash of message or the signature of the signedAttrs does not verify. // when the message-digest is not the hash of message, or the signature of the
// signedAttrs does not verify with the key of the certificate, which is the
// case of a key of a scheme other than the one of the algorithm.
func (s *SignerInfo) Check(message []byte) Result { func (s *SignerInfo) Check(message []byte) Result {
if !s.algorithmsOK() { h, sch, ok := s.params()
if !ok {
return NotVerifiable
}
key, ksch, ok := s.Cert.publicKey()
if !ok {
return NotVerifiable return NotVerifiable
} }
h, sch, _ := s.params()
if sum := hashBytes(h, message); !bytes.Equal(sum, s.MessageDigest) { if sum := hashBytes(h, message); !bytes.Equal(sum, s.MessageDigest) {
return Invalid return Invalid
} }
if sch != ksch && !(sch == schemePSS && ksch == schemePKCS1) {
return Invalid
}
// The signature covers the signedAttrs with the tag of a SET. // The signature covers the signedAttrs with the tag of a SET.
attrs := bytes.Clone(s.SignedAttrs) attrs := bytes.Clone(s.SignedAttrs)
attrs[0] = 0x31 attrs[0] = 0x31
digest := hashBytes(h, attrs) digest := hashBytes(h, attrs)
key, _, _ := s.Cert.publicKey() var valid bool
var ok bool
switch k := key.(type) { switch k := key.(type) {
case *rsa.PublicKey: case *rsa.PublicKey:
if sch == schemePSS { if sch == schemePSS {
ok = rsa.VerifyPSS(k, h, digest, s.Signature, &rsa.PSSOptions{SaltLength: h.Size(), Hash: h}) == nil valid = rsa.VerifyPSS(k, h, digest, s.Signature, &rsa.PSSOptions{SaltLength: h.Size(), Hash: h}) == nil
} else { } else {
ok = rsa.VerifyPKCS1v15(k, h, digest, s.Signature) == nil valid = rsa.VerifyPKCS1v15(k, h, digest, s.Signature) == nil
} }
case *ecdsa.PublicKey: case *ecdsa.PublicKey:
ok = ecdsa.VerifyASN1(k, digest, s.Signature) valid = ecdsa.VerifyASN1(k, digest, s.Signature)
} }
if !ok { if !valid {
return Invalid return Invalid
} }
return Valid return Valid
@ -216,7 +269,7 @@ type Token struct {
GenTime time.Time GenTime time.Time
Accuracy time.Duration Accuracy time.Duration
// ImprintAlg is the hash of the messageImprint, and Imprint the hash. // ImprintAlg is the hash of the messageImprint, and Imprint the hash.
ImprintAlg asn1.ObjectIdentifier ImprintAlg algID
Imprint []byte Imprint []byte
// TSA is the certificate of the time-stamping authority. // TSA is the certificate of the time-stamping authority.
TSA *Cert TSA *Cert
@ -224,9 +277,9 @@ type Token struct {
data *SignedData data *SignedData
} }
// maxAccuracy bounds the seconds of accuracy: far above any real one, and far // maxAccuracy bounds the seconds of accuracy (spec §29.11): far above any
// below what would overflow a Duration. // real one, and far below what would overflow a Duration.
const maxAccuracy = 1 << 31 const maxAccuracy = 1<<31 - 1
// ParseToken reads a time-stamp token. It fails with ErrForm when the form // ParseToken reads a time-stamp token. It fails with ErrForm when the form
// breaks the profile, and with ErrAlgorithm when an algorithm is outside the // breaks the profile, and with ErrAlgorithm when an algorithm is outside the
@ -237,23 +290,23 @@ func ParseToken(b []byte) (*Token, error) {
return nil, err return nil, err
} }
// The TSTInfo is an OCTET STRING inside the token, so the check of the // The TSTInfo is an OCTET STRING inside the token, so the check of the
// token did not reach it, and encoding/asn1 would let through what DER // token did not reach it: it is read here, field by field.
// forbids: it is read here, field by field.
t, imprintAlg, hash, err := parseTSTInfo(sd.EContent) t, imprintAlg, hash, err := parseTSTInfo(sd.EContent)
if err != nil { if err != nil {
return nil, err return nil, err
} }
t.TSA, t.data = sd.Signers[0].Cert, sd t.TSA, t.data = sd.Signers[0].Cert, sd
ia, err := parseAlgID(imprintAlg) if t.ImprintAlg, err = parseAlgID(imprintAlg); err != nil {
if err != nil {
return nil, err return nil, err
} }
t.ImprintAlg, t.Imprint = ia.OID, hash t.Imprint = hash
newHash, ok := ia.hashOf() if _, ok := t.ImprintAlg.hashOf(); !ok {
if ok && len(hash) != newHash().Size() { return nil, ErrAlgorithm
return nil, formErr("a messageImprint of the wrong length") }
if _, _, ok := sd.Signers[0].params(); !ok {
return nil, ErrAlgorithm
} }
if !ok || !sd.Signers[0].algorithmsOK() { if _, _, ok := t.TSA.publicKey(); !ok {
return nil, ErrAlgorithm return nil, ErrAlgorithm
} }
return t, nil return t, nil
@ -273,11 +326,7 @@ func parseTSTInfo(b []byte) (*Token, []byte, []byte, error) {
if err != nil || id != 0x30 || len(f) < 5 { if err != nil || id != 0x30 || len(f) < 5 {
return bad("not a SEQUENCE of at least five fields") return bad("not a SEQUENCE of at least five fields")
} }
var version int if v, ok := smallInt(f[0]); !ok || v != 1 {
if f[0][0] != 0x02 {
return bad("the version")
}
if _, err := asn1.Unmarshal(f[0], &version); err != nil || version != 1 {
return bad("the version is not 1") return bad("the version is not 1")
} }
if f[1][0] != 0x06 || f[3][0] != 0x02 || f[4][0] != 0x18 { if f[1][0] != 0x06 || f[3][0] != 0x02 || f[4][0] != 0x18 {
@ -291,9 +340,9 @@ func parseTSTInfo(b []byte) (*Token, []byte, []byte, error) {
if err != nil { if err != nil {
return bad("the messageImprint") return bad("the messageImprint")
} }
gen, err := parseGenTime(f[4]) gen, _, err := der.ParseTime(f[4])
if err != nil { if err != nil {
return bad(err.Error()) return bad("genTime: " + err.Error())
} }
t := &Token{GenTime: gen} t := &Token{GenTime: gen}
rest := f[5:] rest := f[5:]
@ -324,47 +373,10 @@ func parseTSTInfo(b []byte) (*Token, []byte, []byte, error) {
return t, mi[0], hash, nil return t, mi[0], hash, nil
} }
// parseGenTime reads a GeneralizedTime as RFC 3161 and DER write it: // parseAccuracy reads Accuracy: seconds from 0 to 2^31 - 1, and millis and
// YYYYMMDDHHMMSS, a fraction without a trailing zero, and Z. // micros from 1 to 999, in that order, each optional (RFC 3161 2.4.2, spec
func parseGenTime(b []byte) (time.Time, error) { // §29.11), each a minimal INTEGER. A negative number would make a seal after
c, err := der.Content(b) // the opening date look before it.
if err != nil {
return time.Time{}, errors.New("genTime")
}
s := string(c)
if len(s) < 15 || s[len(s)-1] != 'Z' {
return time.Time{}, errors.New("genTime is not in UTC with the letter Z")
}
body := s[:len(s)-1]
t, err := time.Parse("20060102150405", body[:14])
if err != nil {
return time.Time{}, errors.New("genTime does not parse")
}
if frac := body[14:]; frac != "" {
if len(frac) < 2 || frac[0] != '.' || frac[len(frac)-1] == '0' {
return time.Time{}, errors.New("genTime has a fraction that DER does not write")
}
var nanos int
digits := frac[1:]
for i := 0; i < len(digits); i++ {
if digits[i] < '0' || digits[i] > '9' {
return time.Time{}, errors.New("genTime has a fraction that is not digits")
}
}
for i := 0; i < 9; i++ {
nanos *= 10
if i < len(digits) {
nanos += int(digits[i] - '0')
}
}
t = t.Add(time.Duration(nanos))
}
return t, nil
}
// parseAccuracy reads Accuracy: seconds from 0, and millis and micros from 1
// to 999, in that order, each optional (RFC 3161 2.4.2). A negative number
// would make a seal after the opening date look before it.
func parseAccuracy(b []byte) (time.Duration, error) { func parseAccuracy(b []byte) (time.Duration, error) {
_, f, err := der.Split(b) _, f, err := der.Split(b)
if err != nil { if err != nil {
@ -372,9 +384,9 @@ func parseAccuracy(b []byte) (time.Duration, error) {
} }
var total time.Duration var total time.Duration
if len(f) > 0 && f[0][0] == 0x02 { if len(f) > 0 && f[0][0] == 0x02 {
var secs int64 secs, ok := smallInt(f[0])
if _, err := asn1.Unmarshal(f[0], &secs); err != nil || secs < 0 || secs > maxAccuracy { if !ok || secs > maxAccuracy {
return 0, errors.New("accuracy seconds outside 0 to 2^31") return 0, errors.New("accuracy seconds outside 0 to 2^31 - 1")
} }
total += time.Duration(secs) * time.Second total += time.Duration(secs) * time.Second
f = f[1:] f = f[1:]
@ -385,8 +397,8 @@ func parseAccuracy(b []byte) (time.Duration, error) {
}{{0x80, time.Millisecond}, {0x81, time.Microsecond}} { }{{0x80, time.Millisecond}, {0x81, time.Microsecond}} {
if len(f) > 0 && f[0][0] == part.tag { if len(f) > 0 && f[0][0] == part.tag {
c, err := der.Content(f[0]) c, err := der.Content(f[0])
if err != nil || len(c) < 1 || len(c) > 2 || c[0]&0x80 != 0 { if err != nil || len(c) < 1 || len(c) > 2 || c[0]&0x80 != 0 || len(c) == 2 && c[0] == 0 && c[1]&0x80 == 0 {
return 0, errors.New("accuracy millis or micros") return 0, errors.New("accuracy millis or micros that are not a minimal INTEGER")
} }
n := 0 n := 0
for _, x := range c { for _, x := range c {
@ -407,18 +419,19 @@ func parseAccuracy(b []byte) (time.Duration, error) {
// ImprintIsSHA256 reports whether the messageImprint uses SHA-256, which a // ImprintIsSHA256 reports whether the messageImprint uses SHA-256, which a
// seal of seal_type 2 requires (spec §29.11). // seal of seal_type 2 requires (spec §29.11).
func (t *Token) ImprintIsSHA256() bool { return t.ImprintAlg.Equal(oidSHA256) } func (t *Token) ImprintIsSHA256() bool { return bytes.Equal(t.ImprintAlg.OID, oidSHA256) }
// Check verifies the token over subject, the bytes that it seals: the // Check verifies the token over subject, the bytes that it seals: the
// message-digest is the hash of the TSTInfo, the signature of the TSA // message-digest is the hash of the TSTInfo, the signature of the TSA
// verifies, the messageImprint is the hash of subject and the certificate of // verifies, the messageImprint is the hash of subject, of any length, and the
// the TSA is valid at genTime. It returns false for the verdict S3. // certificate of the TSA is valid at genTime. It returns false for the
// verdict S3.
func (t *Token) Check(subject []byte) bool { func (t *Token) Check(subject []byte) bool {
s := t.data.Signers[0] s := t.data.Signers[0]
if s.Check(t.data.EContent) != Valid { if s.Check(t.data.EContent) != Valid {
return false return false
} }
newHash, _ := algID{OID: t.ImprintAlg}.hashOf() newHash, _ := t.ImprintAlg.hashOf()
h := newHash() h := newHash()
h.Write(subject) h.Write(subject)
return bytes.Equal(h.Sum(nil), t.Imprint) && t.TSA.ValidAt(t.GenTime) return bytes.Equal(h.Sum(nil), t.Imprint) && t.TSA.ValidAt(t.GenTime)

@ -2,9 +2,10 @@
// Rules of X.690, as spec v0.11 §29.10 asks of a CMS signature before it // Rules of X.690, as spec v0.11 §29.10 asks of a CMS signature before it
// looks inside it: definite and minimal lengths, no high tag numbers, no // looks inside it: definite and minimal lengths, no high tag numbers, no
// constructed form of a type that DER only has primitive, canonical BOOLEAN, // constructed form of a type that DER only has primitive, canonical BOOLEAN,
// INTEGER, NULL, OBJECT IDENTIFIER and BIT STRING, and no bytes after the // INTEGER, NULL, OBJECT IDENTIFIER, BIT STRING, UTCTime and GeneralizedTime,
// element. Go's encoding/asn1 accepts some of what DER forbids, and a // and no bytes after the element. Go's encoding/asn1 accepts some of what DER
// signature that two implementations read the same must not depend on it. // forbids, and a signature that two implementations read the same must not
// depend on it.
// //
// The order of the elements of a SET OF cannot be checked without a schema: // The order of the elements of a SET OF cannot be checked without a schema:
// SetOfSorted does it for the callers that know theirs. // SetOfSorted does it for the callers that know theirs.
@ -14,6 +15,8 @@ import (
"bytes" "bytes"
"errors" "errors"
"fmt" "fmt"
"strings"
"time"
) )
const maxDepth = 32 const maxDepth = 32
@ -63,17 +66,98 @@ func Content(b []byte) ([]byte, error) {
} }
// SetOfSorted reports whether the encodings are in ascending order of their // SetOfSorted reports whether the encodings are in ascending order of their
// bytes, as DER requires of the elements of a SET OF (X.690 11.6), and // bytes, as DER requires of the elements of a SET OF (X.690 11.6). Equal
// without repetitions: a SET OF of this profile has none. // elements may repeat, side by side: X.690 does not forbid it, and a time-
// stamping authority may send its certificate twice. Whoever counts the
// elements of a SET OF decides what a repetition means.
func SetOfSorted(elems [][]byte) bool { func SetOfSorted(elems [][]byte) bool {
for i := 1; i < len(elems); i++ { for i := 1; i < len(elems); i++ {
if bytes.Compare(elems[i-1], elems[i]) >= 0 { if bytes.Compare(elems[i-1], elems[i]) > 0 {
return false return false
} }
} }
return true return true
} }
// ParseTime reads a UTCTime or a GeneralizedTime element as DER writes them
// (X.690 11.7 and 11.8): YYMMDDHHMMSSZ, with the years 50 to 99 in the 20th
// century (RFC 5280 4.1.2.5.1), or YYYYMMDDHHMMSS, an optional fraction of
// seconds without a trailing zero, and Z. A date or a time that does not
// exist, a second 60 included, is refused. fraction reports whether a
// GeneralizedTime has one.
func ParseTime(b []byte) (t time.Time, fraction bool, err error) {
if len(b) < 2 || (b[0] != 0x17 && b[0] != 0x18) {
return time.Time{}, false, errors.New("der: not a UTCTime or a GeneralizedTime")
}
c, err := Content(b)
if err != nil {
return time.Time{}, false, err
}
return parseTime(b[0], string(c))
}
func parseTime(tag byte, s string) (time.Time, bool, error) {
bad := errors.New("der: a time that is not in the form of DER")
if len(s) < 13 || s[len(s)-1] != 'Z' {
return time.Time{}, false, bad
}
body, frac := s[:len(s)-1], ""
var year int
switch tag {
case 0x17:
if len(body) != 12 || !digits(body) {
return time.Time{}, false, bad
}
year = atoi(body[:2]) + 1900
if year < 1950 {
year += 100
}
body = body[2:]
default:
if dot := strings.IndexByte(body, '.'); dot >= 0 {
body, frac = body[:dot], body[dot+1:]
if frac == "" || frac[len(frac)-1] == '0' || !digits(frac) {
return time.Time{}, false, bad
}
}
if len(body) != 14 || !digits(body) {
return time.Time{}, false, bad
}
year = atoi(body[:4])
body = body[4:]
}
month, day, hour, minute, second := atoi(body[0:2]), atoi(body[2:4]), atoi(body[4:6]), atoi(body[6:8]), atoi(body[8:10])
if month < 1 || month > 12 || day < 1 || hour > 23 || minute > 59 || second > 59 ||
day > time.Date(year, time.Month(month)+1, 0, 0, 0, 0, 0, time.UTC).Day() {
return time.Time{}, false, errors.New("der: a date or a time that does not exist")
}
nanos := 0
for i := 0; i < 9; i++ {
nanos *= 10
if i < len(frac) {
nanos += int(frac[i] - '0')
}
}
return time.Date(year, time.Month(month), day, hour, minute, second, nanos, time.UTC), frac != "", nil
}
func digits(s string) bool {
for i := 0; i < len(s); i++ {
if s[i] < '0' || s[i] > '9' {
return false
}
}
return s != ""
}
func atoi(s string) int {
n := 0
for i := 0; i < len(s); i++ {
n = n*10 + int(s[i]-'0')
}
return n
}
// header returns the length of the identifier and length octets of the // header returns the length of the identifier and length octets of the
// element at the start of b, and the length of its content, which must fit // element at the start of b, and the length of its content, which must fit
// in b. // in b.
@ -183,7 +267,11 @@ func checkPrimitive(tag byte, c []byte) error {
} }
start = x&0x80 == 0 start = x&0x80 == 0
} }
case 4, 12, 19, 20, 22, 23, 24, 26, 28, 30: // OCTET STRING and the string and time types of X.509 case 23, 24: // UTCTime, GeneralizedTime
if _, _, err := parseTime(tag, string(c)); err != nil {
return err
}
case 4, 12, 19, 20, 22, 26, 28, 30: // OCTET STRING and the string types of X.509
case 16, 17: case 16, 17:
return fmt.Errorf("der: the universal type %d in primitive form", tag) return fmt.Errorf("der: the universal type %d in primitive form", tag)
default: default:

@ -3,6 +3,7 @@ package der
import ( import (
"encoding/hex" "encoding/hex"
"testing" "testing"
"time"
) )
func TestCheck(t *testing.T) { func TestCheck(t *testing.T) {
@ -14,6 +15,7 @@ func TestCheck(t *testing.T) {
{"a long length", "04" + "8180" + zeros(128), true}, {"a long length", "04" + "8180" + zeros(128), true},
{"a long form under 128", "0481" + "01" + "00", false}, {"a long form under 128", "0481" + "01" + "00", false},
{"a length with a leading zero", "04820001" + "00", false}, {"a length with a leading zero", "04820001" + "00", false},
{"a length of 128 with a leading zero", "04820080" + zeros(128), false},
{"an indefinite length", "30800000", false}, {"an indefinite length", "30800000", false},
{"a high tag number", "1f0100", false}, {"a high tag number", "1f0100", false},
{"truncated", "0402aa", false}, {"truncated", "0402aa", false},
@ -38,6 +40,19 @@ func TestCheck(t *testing.T) {
{"a reserved universal tag", "0e0141", false}, {"a reserved universal tag", "0e0141", false},
{"a SEQUENCE of the end of contents", "30020000", false}, {"a SEQUENCE of the end of contents", "30020000", false},
{"UTF8String", "0c026162", true}, {"UTF8String", "0c026162", true},
// Times in the forms of DER (X.690 11.7, 11.8).
{"UTCTime", "170d" + hex.EncodeToString([]byte("250101120000Z")), true},
{"UTCTime without seconds", "170b" + hex.EncodeToString([]byte("2501011200Z")), false},
{"UTCTime with an offset", "1711" + hex.EncodeToString([]byte("250101120000+0100")), false},
{"UTCTime of 30 February", "170d" + hex.EncodeToString([]byte("250230120000Z")), false},
{"UTCTime with second 60", "170d" + hex.EncodeToString([]byte("250101235960Z")), false},
{"a UTCTime that is not a time", "170a" + hex.EncodeToString([]byte("not a time")), false},
{"GeneralizedTime", "180f" + hex.EncodeToString([]byte("20250101120000Z")), true},
{"GeneralizedTime with a fraction", "1812" + hex.EncodeToString([]byte("20250101120000.25Z")), true},
{"GeneralizedTime with a trailing zero", "1813" + hex.EncodeToString([]byte("20250101120000.250Z")), false},
{"GeneralizedTime with an empty fraction", "1810" + hex.EncodeToString([]byte("20250101120000.Z")), false},
{"GeneralizedTime without Z", "180e" + hex.EncodeToString([]byte("20250101120000")), false},
{"GeneralizedTime with a comma", "1812" + hex.EncodeToString([]byte("20250101120000,25Z")), false},
} { } {
b, err := hex.DecodeString(tc.hex) b, err := hex.DecodeString(tc.hex)
if err != nil { if err != nil {
@ -57,13 +72,54 @@ func zeros(n int) string {
return string(b) return string(b)
} }
// The elements of a SET OF go in ascending order, and equal ones may repeat
// (X.690 11.6).
func TestSetOfSorted(t *testing.T) { func TestSetOfSorted(t *testing.T) {
a, b := []byte{0x02, 0x01, 0x01}, []byte{0x02, 0x01, 0x02} a, b := []byte{0x02, 0x01, 0x01}, []byte{0x02, 0x01, 0x02}
if !SetOfSorted([][]byte{a, b}) || SetOfSorted([][]byte{b, a}) || SetOfSorted([][]byte{a, a}) { if !SetOfSorted([][]byte{a, b}) || SetOfSorted([][]byte{b, a}) || !SetOfSorted([][]byte{a, a, b}) || SetOfSorted([][]byte{a, b, a}) {
t.Error("SetOfSorted") t.Error("SetOfSorted")
} }
} }
// Elements nested 32 levels below the outer one are DER; one more is too deep.
func TestDepth(t *testing.T) {
nested := func(n int) []byte {
b := []byte{0x05, 0x00}
for range n {
b = append([]byte{0x30, byte(len(b))}, b...)
}
return b
}
if err := Check(nested(maxDepth)); err != nil {
t.Errorf("%d levels: %v", maxDepth, err)
}
if err := Check(nested(maxDepth + 1)); err == nil {
t.Errorf("%d levels: accepted", maxDepth+1)
}
}
func TestParseTime(t *testing.T) {
for _, c := range []struct {
el string
want time.Time
frac bool
}{
{"\x17\x0d" + "491231235959Z", time.Date(2049, 12, 31, 23, 59, 59, 0, time.UTC), false},
{"\x17\x0d" + "500101000000Z", time.Date(1950, 1, 1, 0, 0, 0, 0, time.UTC), false},
{"\x18\x13" + "20240229120000.125Z", time.Date(2024, 2, 29, 12, 0, 0, 125e6, time.UTC), true},
} {
got, frac, err := ParseTime([]byte(c.el))
if err != nil || !got.Equal(c.want) || frac != c.frac {
t.Errorf("%q: %v %v %v", c.el, got, frac, err)
}
}
for _, bad := range []string{"\x18\x0f" + "20230229120000Z", "\x04\x0d" + "491231235959Z", "\x17"} {
if _, _, err := ParseTime([]byte(bad)); err == nil {
t.Errorf("%q: accepted", bad)
}
}
}
func TestSplit(t *testing.T) { func TestSplit(t *testing.T) {
b, _ := hex.DecodeString("30050201010500") b, _ := hex.DecodeString("30050201010500")
if err := Check(b); err != nil { if err := Check(b); err != nil {

Loading…
Cancel
Save

Powered by TurnKey Linux.