You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
691 lines
23 KiB
691 lines
23 KiB
// Package cmstest builds the CMS signatures and the RFC 3161 tokens that the
|
|
// tests of internal/cms and of capsule read, and that the generator of the
|
|
// test vectors writes: certificates of test keys, made by crypto/x509 or field
|
|
// by field (cert.go), a detached signature of a message with its signedAttrs
|
|
// and, optionally, a time-stamp token of its signature, with options to write
|
|
// what the profiles of spec §29.10 and §29.11 reject, or what verifies to
|
|
// something else, and edits of the DER of the result (edit.go). It is the
|
|
// encoder that a signing application has; nothing outside tests uses it.
|
|
package cmstest
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto"
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/sha1"
|
|
"crypto/sha256"
|
|
"crypto/sha512"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/asn1"
|
|
"fmt"
|
|
"math/big"
|
|
"slices"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// Cert is a certificate as a signature or a token names it.
|
|
type Cert struct {
|
|
// Raw is the DER of the certificate.
|
|
Raw []byte
|
|
// RawIssuer is the DER of its issuer, Serial the DER of its serialNumber,
|
|
// an INTEGER, and SubjectKeyId the keyIdentifier of its extension
|
|
// subjectKeyIdentifier, nil without it: what a sid names.
|
|
RawIssuer, Serial, SubjectKeyId []byte
|
|
}
|
|
|
|
// Signer is a certificate with its private key.
|
|
type Signer struct {
|
|
Cert *Cert
|
|
Key crypto.Signer
|
|
}
|
|
|
|
// RSAKey returns a new RSA key of bits bits.
|
|
func RSAKey(bits int) *rsa.PrivateKey {
|
|
k, err := rsa.GenerateKey(rand.Reader, bits)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return k
|
|
}
|
|
|
|
// ECKey returns a new ECDSA key on curve.
|
|
func ECKey(curve elliptic.Curve) *ecdsa.PrivateKey {
|
|
k, err := ecdsa.GenerateKey(curve, rand.Reader)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return k
|
|
}
|
|
|
|
// NewRSA returns a signer with an RSA key of bits bits, valid in
|
|
// [notBefore, notAfter], named cn, with a certificate that crypto/x509 makes.
|
|
func NewRSA(cn string, bits int, notBefore, notAfter time.Time) Signer {
|
|
return newSigner(cn, RSAKey(bits), notBefore, notAfter)
|
|
}
|
|
|
|
// NewECDSA returns a signer with an ECDSA key on curve, with a certificate
|
|
// that crypto/x509 makes.
|
|
func NewECDSA(cn string, curve elliptic.Curve, notBefore, notAfter time.Time) Signer {
|
|
return newSigner(cn, ECKey(curve), notBefore, notAfter)
|
|
}
|
|
|
|
func newSigner(cn string, k crypto.Signer, notBefore, notAfter time.Time) Signer {
|
|
serial, _ := rand.Int(rand.Reader, big.NewInt(1<<62))
|
|
t := &x509.Certificate{
|
|
SerialNumber: serial,
|
|
Subject: pkix.Name{CommonName: cn, Organization: []string{"DateKeys test"}},
|
|
NotBefore: notBefore, NotAfter: notAfter,
|
|
KeyUsage: x509.KeyUsageDigitalSignature,
|
|
SubjectKeyId: []byte(cn),
|
|
}
|
|
raw, err := x509.CreateCertificate(rand.Reader, t, t, k.Public(), k)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
c, err := x509.ParseCertificate(raw)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return Signer{Cert: &Cert{Raw: c.Raw, RawIssuer: c.RawIssuer, Serial: mustMarshal(c.SerialNumber), SubjectKeyId: c.SubjectKeyId}, Key: k}
|
|
}
|
|
|
|
// The DER building blocks.
|
|
|
|
func tlv(tag byte, content ...[]byte) []byte {
|
|
c := bytes.Join(content, nil)
|
|
out := []byte{tag}
|
|
switch n := len(c); {
|
|
case n < 0x80:
|
|
out = append(out, byte(n))
|
|
case n < 0x100:
|
|
out = append(out, 0x81, byte(n))
|
|
case n < 0x10000:
|
|
out = append(out, 0x82, byte(n>>8), byte(n))
|
|
default:
|
|
out = append(out, 0x83, byte(n>>16), byte(n>>8), byte(n))
|
|
}
|
|
return append(out, c...)
|
|
}
|
|
|
|
// TLV builds an element of any tag from the encodings of its content.
|
|
func TLV(tag byte, content ...[]byte) []byte { return tlv(tag, content...) }
|
|
|
|
// Seq is a SEQUENCE.
|
|
func Seq(content ...[]byte) []byte { return tlv(0x30, content...) }
|
|
|
|
// Set is a SET OF with the identifier octet tag, in DER order. A SET OF in
|
|
// another order is TLV(tag, elems...).
|
|
func Set(tag byte, elems ...[]byte) []byte {
|
|
e := slices.Clone(elems)
|
|
slices.SortFunc(e, bytes.Compare)
|
|
return tlv(tag, e...)
|
|
}
|
|
|
|
// OID is an OBJECT IDENTIFIER.
|
|
func OID(oid asn1.ObjectIdentifier) []byte { return mustMarshal(oid) }
|
|
|
|
// OIDBytes is an OBJECT IDENTIFIER with the content as given: an arc of any
|
|
// size, or a content that is not one.
|
|
func OIDBytes(content []byte) []byte { return tlv(0x06, content) }
|
|
|
|
// Octets is an OCTET STRING.
|
|
func Octets(b []byte) []byte { return tlv(0x04, b) }
|
|
|
|
// Int is an INTEGER.
|
|
func Int(n int64) []byte { return mustMarshal(n) }
|
|
|
|
// BigInt is an INTEGER of any size.
|
|
func BigInt(n *big.Int) []byte { return mustMarshal(n) }
|
|
|
|
// IntBytes is an INTEGER with the content as given, minimal or not.
|
|
func IntBytes(content []byte) []byte { return tlv(0x02, content) }
|
|
|
|
// Null is a NULL.
|
|
func Null() []byte { return []byte{0x05, 0x00} }
|
|
|
|
// Bool is a BOOLEAN, as DER writes it.
|
|
func Bool(v bool) []byte {
|
|
if v {
|
|
return []byte{0x01, 0x01, 0xff}
|
|
}
|
|
return []byte{0x01, 0x01, 0x00}
|
|
}
|
|
|
|
// BitString is a BIT STRING of whole bytes.
|
|
func BitString(b []byte) []byte { return tlv(0x03, append([]byte{0}, b...)) }
|
|
|
|
// UTCTime builds a UTCTime with the text s.
|
|
func UTCTime(s string) []byte { return tlv(0x17, []byte(s)) }
|
|
|
|
// GeneralizedTime builds a GeneralizedTime with the text s.
|
|
func GeneralizedTime(s string) []byte { return tlv(0x18, []byte(s)) }
|
|
|
|
// GeneralizedTimeOf builds the GeneralizedTime of t in UTC as DER writes it:
|
|
// the fraction of a second only when there is one, without trailing zeros.
|
|
func GeneralizedTimeOf(t time.Time) []byte {
|
|
t = t.UTC()
|
|
s := t.Format("20060102150405")
|
|
if ns := t.Nanosecond(); ns != 0 {
|
|
s += "." + strings.TrimRight(fmt.Sprintf("%09d", ns), "0")
|
|
}
|
|
return GeneralizedTime(s + "Z")
|
|
}
|
|
|
|
// CertTimeOf builds a time of validity as RFC 5280 4.1.2.5 writes it: UTCTime
|
|
// until 2049 and GeneralizedTime from 2050, without a fraction.
|
|
func CertTimeOf(t time.Time) []byte {
|
|
t = t.UTC().Truncate(time.Second)
|
|
if t.Year() < 2050 {
|
|
return UTCTime(t.Format("060102150405") + "Z")
|
|
}
|
|
return GeneralizedTimeOf(t)
|
|
}
|
|
|
|
// AlgID is an AlgorithmIdentifier.
|
|
func AlgID(oid asn1.ObjectIdentifier, params ...[]byte) []byte {
|
|
return Seq(append([][]byte{OID(oid)}, params...)...)
|
|
}
|
|
|
|
func mustMarshal(v any) []byte {
|
|
b, err := asn1.Marshal(v)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return b
|
|
}
|
|
|
|
// The object identifiers.
|
|
var (
|
|
OIDData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 1}
|
|
OIDSignedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 2}
|
|
OIDContentType = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 3}
|
|
OIDMessageDigest = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 4}
|
|
OIDSigningTime = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 5}
|
|
OIDSigCertV1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 2, 12}
|
|
OIDSigCertV2 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 2, 47}
|
|
OIDTimeStamp = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 2, 14}
|
|
OIDTSTInfo = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 1, 4}
|
|
OIDOCSP = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 16, 2}
|
|
|
|
OIDSHA1 = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26}
|
|
OIDSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1}
|
|
OIDSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2}
|
|
OIDSHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3}
|
|
|
|
OIDRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
|
|
OIDMGF1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 8}
|
|
OIDPSS = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10}
|
|
OIDSHA256RSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
|
|
OIDSHA384RSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
|
|
OIDSHA512RSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
|
|
OIDECDSASHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
|
|
OIDECDSA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
|
|
OIDECDSA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
|
|
OIDECDSA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}
|
|
OIDECPublicKey = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
|
|
OIDP256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7}
|
|
OIDP384 = asn1.ObjectIdentifier{1, 3, 132, 0, 34}
|
|
OIDP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35}
|
|
OIDBrainpoolP256 = asn1.ObjectIdentifier{1, 3, 36, 3, 3, 2, 8, 1, 1, 7}
|
|
)
|
|
|
|
// HashAlg is the AlgorithmIdentifier of h, without parameters: SHA-1,
|
|
// SHA-256, SHA-384 or SHA-512.
|
|
func HashAlg(h crypto.Hash) []byte { return AlgID(hashOID(h)) }
|
|
|
|
func hashOID(h crypto.Hash) asn1.ObjectIdentifier {
|
|
switch h {
|
|
case crypto.SHA1:
|
|
return OIDSHA1
|
|
case crypto.SHA384:
|
|
return OIDSHA384
|
|
case crypto.SHA512:
|
|
return OIDSHA512
|
|
}
|
|
return OIDSHA256
|
|
}
|
|
|
|
func ecdsaOID(h crypto.Hash) asn1.ObjectIdentifier {
|
|
switch h {
|
|
case crypto.SHA1:
|
|
return OIDECDSASHA1
|
|
case crypto.SHA384:
|
|
return OIDECDSA384
|
|
case crypto.SHA512:
|
|
return OIDECDSA512
|
|
}
|
|
return OIDECDSA256
|
|
}
|
|
|
|
func sum(h crypto.Hash, b []byte) []byte {
|
|
switch h {
|
|
case crypto.SHA1:
|
|
s := sha1.Sum(b)
|
|
return s[:]
|
|
case crypto.SHA384:
|
|
s := sha512.Sum384(b)
|
|
return s[:]
|
|
case crypto.SHA512:
|
|
s := sha512.Sum512(b)
|
|
return s[:]
|
|
}
|
|
s := sha256.Sum256(b)
|
|
return s[:]
|
|
}
|
|
|
|
// hashNamed returns the hash that the AlgorithmIdentifier a names, SHA-256
|
|
// for one that this package does not write.
|
|
func hashNamed(a []byte) crypto.Hash {
|
|
for _, h := range []crypto.Hash{crypto.SHA1, crypto.SHA384, crypto.SHA512} {
|
|
if bytes.HasPrefix(a[2:], OID(hashOID(h))) {
|
|
return h
|
|
}
|
|
}
|
|
return crypto.SHA256
|
|
}
|
|
|
|
// Options changes what Signature and Token write, to make signatures that the
|
|
// profile rejects or that verify to something else. The zero value writes
|
|
// what AutoFirma writes.
|
|
type Options struct {
|
|
// Hash is the digest of the signature, SHA-256 by default; SHA-1 writes
|
|
// ecdsa-with-SHA1 for an ECDSA key.
|
|
Hash crypto.Hash
|
|
// PSS signs with RSASSA-PSS instead of PKCS #1 v1.5, and PSSTrailer
|
|
// writes trailerField [3] 1 in its parameters, which is its default and
|
|
// DER does not write.
|
|
PSS, PSSTrailer bool
|
|
// SKI names the signer by subjectKeyIdentifier instead of by issuer and
|
|
// serial.
|
|
SKI bool
|
|
// Version is the version of each SignerInfo; 0 writes 1 with
|
|
// issuerAndSerialNumber and 3 with subjectKeyIdentifier (RFC 5652 5.3).
|
|
Version int
|
|
// DigestAlg, when not nil, is written as the digestAlgorithm of each
|
|
// SignerInfo instead of that of Hash.
|
|
DigestAlg []byte
|
|
// SigAlg, when not nil, is written as the signatureAlgorithm instead of
|
|
// the one of the key; the key still signs with its own scheme.
|
|
SigAlg []byte
|
|
// CorruptSignature flips a bit of each signature value, after signing.
|
|
CorruptSignature bool
|
|
|
|
// Message is what the message-digest covers, when not the signed message.
|
|
Message []byte
|
|
// ContentType2 adds a second content-type attribute, the same as the
|
|
// first, and NoMessageDigest leaves the message-digest out.
|
|
ContentType2, NoMessageDigest bool
|
|
// SigCertV1 adds a signing-certificate attribute (RFC 2634) beside the
|
|
// v2. SigCertV2 writes signing-certificate-v2 in a token, which writes
|
|
// signing-certificate by default; NoSigCertV2 leaves it out of a
|
|
// signature.
|
|
SigCertV1, SigCertV2, NoSigCertV2 bool
|
|
// ESSHashAlg, when not nil, is written as the hashAlgorithm of the
|
|
// ESSCertIDv2, which DER leaves out for SHA-256, its default; certHash is
|
|
// the hash that it names. ESSCert, when not nil, is the certificate whose
|
|
// hash certHash gives, instead of that of the signer.
|
|
ESSHashAlg, ESSCert []byte
|
|
// ExtraAttrs are added to the signed attributes, as the DER of each.
|
|
ExtraAttrs [][]byte
|
|
// UnsortedAttrs writes the signed attributes in descending order: not a
|
|
// SET OF of DER. They are signed as written.
|
|
UnsortedAttrs bool
|
|
// Mutate edits the signedAttrs, as a list of the DER of each attribute,
|
|
// before they are signed.
|
|
Mutate func(attrs [][]byte) [][]byte
|
|
|
|
// Token, when not nil, is the time-stamp token of the signature that
|
|
// Signature puts as an unsigned attribute: it receives the signature
|
|
// value. Token2 puts it in one attribute with a second value, and
|
|
// TimeStamps2 adds a second signature-time-stamp attribute with a token
|
|
// of its own.
|
|
Token func(signature []byte) []byte
|
|
Token2, TimeStamps2 bool
|
|
// Junk adds an unsigned attribute of this many bytes, which decides
|
|
// nothing, to make a signature as large as a chain of certificates.
|
|
Junk int
|
|
// ExtraUnsigned are added to the unsigned attributes, as the DER of each.
|
|
ExtraUnsigned [][]byte
|
|
|
|
// OmitCert leaves the certificate of each signer out of certificates,
|
|
// and ExtraCerts adds these, as the DER of each: the certificate of a
|
|
// signer for a repetition, a certificate that breaks the profile, or
|
|
// another choice of CertificateChoices.
|
|
OmitCert bool
|
|
ExtraCerts [][]byte
|
|
// OCSP adds this response in crls, as an OtherRevocationInfoFormat of
|
|
// id-ri-ocsp-response, and CRLs adds these elements in crls as given.
|
|
OCSP []byte
|
|
CRLs [][]byte
|
|
// SignerInfoTwice writes each SignerInfo twice, Unsorted writes
|
|
// signerInfos in descending order, and BER writes the ContentInfo with
|
|
// an indefinite length.
|
|
SignerInfoTwice, Unsorted, BER bool
|
|
// EditSignerInfo edits the fields of each SignerInfo after it is signed,
|
|
// and EditSignedData the fields of the SignedData.
|
|
EditSignerInfo, EditSignedData func(fields [][]byte) [][]byte
|
|
}
|
|
|
|
// Attr is an Attribute of the type with the values, in DER order.
|
|
func Attr(oid asn1.ObjectIdentifier, values ...[]byte) []byte {
|
|
return Seq(OID(oid), Set(0x31, values...))
|
|
}
|
|
|
|
// BigArcAttr is an attribute whose type has an arc of 2^31: an identifier
|
|
// that the profile does not name, and decides nothing (spec §29.10).
|
|
func BigArcAttr() []byte {
|
|
// 1.2.840.113549.1.9.2147483648: the last arc is 2^31.
|
|
return Seq(OIDBytes([]byte{0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x88, 0x80, 0x80, 0x80, 0x00}), Set(0x31, Null()))
|
|
}
|
|
|
|
// Signature returns the detached CMS signature of message by the signers, in
|
|
// DER, as AutoFirma writes it: one SignerInfo each, with content-type,
|
|
// message-digest and signing-certificate-v2 as signed attributes.
|
|
func Signature(message []byte, opts Options, signers ...Signer) []byte {
|
|
return build(message, opts, false, signers)
|
|
}
|
|
|
|
// TokenRaw returns a token that tsa signs over the given TSTInfo, as it is:
|
|
// for tests that need a TSTInfo that Token would not write.
|
|
func TokenRaw(tstInfo []byte, tsa Signer) []byte {
|
|
return build(tstInfo, Options{Hash: crypto.SHA256}, true, []Signer{tsa})
|
|
}
|
|
|
|
// TokenRawWith is TokenRaw with the options of the SignedData of the token.
|
|
func TokenRawWith(tstInfo []byte, o Options, tsa Signer) []byte {
|
|
return build(tstInfo, o, true, []Signer{tsa})
|
|
}
|
|
|
|
func build(content []byte, o Options, token bool, signers []Signer) []byte {
|
|
if o.Hash == 0 {
|
|
o.Hash = crypto.SHA256
|
|
}
|
|
var certs, infos [][]byte
|
|
for _, s := range signers {
|
|
if !o.OmitCert {
|
|
certs = append(certs, s.Cert.Raw)
|
|
}
|
|
info := signerInfo(content, o, token, s)
|
|
infos = append(infos, info)
|
|
if o.SignerInfoTwice {
|
|
infos = append(infos, info)
|
|
}
|
|
}
|
|
certs = append(certs, o.ExtraCerts...)
|
|
fields := [][]byte{Int(1), Set(0x31, HashAlg(o.Hash)), encap(content, token)}
|
|
if len(certs) > 0 {
|
|
fields = append(fields, Set(0xa0, certs...))
|
|
}
|
|
var crls [][]byte
|
|
if o.OCSP != nil {
|
|
crls = append(crls, tlv(0xa1, OID(OIDOCSP), o.OCSP))
|
|
}
|
|
if crls = append(crls, o.CRLs...); len(crls) > 0 {
|
|
fields = append(fields, Set(0xa1, crls...))
|
|
}
|
|
if o.Unsorted {
|
|
slices.SortFunc(infos, func(a, b []byte) int { return bytes.Compare(b, a) })
|
|
fields = append(fields, tlv(0x31, infos...))
|
|
} else {
|
|
fields = append(fields, Set(0x31, infos...))
|
|
}
|
|
if o.EditSignedData != nil {
|
|
fields = o.EditSignedData(fields)
|
|
}
|
|
ci := Seq(OID(OIDSignedData), tlv(0xa0, Seq(fields...)))
|
|
if o.BER {
|
|
return Indefinite(ci)
|
|
}
|
|
return ci
|
|
}
|
|
|
|
func encap(content []byte, token bool) []byte {
|
|
if !token {
|
|
return Seq(OID(OIDData))
|
|
}
|
|
return Seq(OID(OIDTSTInfo), tlv(0xa0, Octets(content)))
|
|
}
|
|
|
|
// essCertID returns the SigningCertificate (v1, SHA-1) or the
|
|
// SigningCertificateV2 of a certificate.
|
|
func essCertID(cert []byte, o Options, v2 bool) []byte {
|
|
if o.ESSCert != nil {
|
|
cert = o.ESSCert
|
|
}
|
|
if !v2 {
|
|
h := sha1.Sum(cert)
|
|
return Seq(Seq(Seq(Octets(h[:]))))
|
|
}
|
|
if o.ESSHashAlg == nil {
|
|
h := sha256.Sum256(cert)
|
|
return Seq(Seq(Seq(Octets(h[:]))))
|
|
}
|
|
return Seq(Seq(Seq(o.ESSHashAlg, Octets(sum(hashNamed(o.ESSHashAlg), cert)))))
|
|
}
|
|
|
|
func signerInfo(message []byte, o Options, token bool, s Signer) []byte {
|
|
sid := Seq(s.Cert.RawIssuer, s.Cert.Serial)
|
|
if o.SKI {
|
|
sid = tlv(0x80, s.Cert.SubjectKeyId)
|
|
}
|
|
contentType := OIDData
|
|
if token {
|
|
contentType = OIDTSTInfo
|
|
}
|
|
md := message
|
|
if o.Message != nil {
|
|
md = o.Message
|
|
}
|
|
attrs := [][]byte{Attr(OIDContentType, OID(contentType))}
|
|
if o.ContentType2 {
|
|
attrs = append(attrs, attrs[0])
|
|
}
|
|
if !o.NoMessageDigest {
|
|
attrs = append(attrs, Attr(OIDMessageDigest, Octets(sum(o.Hash, md))))
|
|
}
|
|
switch {
|
|
case token && !o.SigCertV2:
|
|
attrs = append(attrs, Attr(OIDSigCertV1, essCertID(s.Cert.Raw, o, false)))
|
|
case !o.NoSigCertV2:
|
|
attrs = append(attrs, Attr(OIDSigCertV2, essCertID(s.Cert.Raw, o, true)))
|
|
}
|
|
if o.SigCertV1 {
|
|
attrs = append(attrs, Attr(OIDSigCertV1, essCertID(s.Cert.Raw, Options{}, false)))
|
|
}
|
|
attrs = append(attrs, o.ExtraAttrs...)
|
|
if o.Mutate != nil {
|
|
attrs = o.Mutate(attrs)
|
|
}
|
|
var signed []byte
|
|
if o.UnsortedAttrs {
|
|
attrs = slices.Clone(attrs)
|
|
slices.SortFunc(attrs, func(a, b []byte) int { return bytes.Compare(b, a) })
|
|
signed = tlv(0xa0, attrs...)
|
|
} else {
|
|
signed = Set(0xa0, attrs...)
|
|
}
|
|
// The signature covers the signedAttrs with the tag of a SET.
|
|
digest := sum(o.Hash, append([]byte{0x31}, signed[1:]...))
|
|
sigAlg, sig := sign(s.Key, o, digest)
|
|
if o.SigAlg != nil {
|
|
sigAlg = o.SigAlg
|
|
}
|
|
if o.CorruptSignature {
|
|
sig[len(sig)/2] ^= 1
|
|
}
|
|
version := int64(1)
|
|
if o.SKI {
|
|
version = 3
|
|
}
|
|
if o.Version != 0 {
|
|
version = int64(o.Version)
|
|
}
|
|
digestAlg := HashAlg(o.Hash)
|
|
if o.DigestAlg != nil {
|
|
digestAlg = o.DigestAlg
|
|
}
|
|
f := [][]byte{Int(version), sid, digestAlg, signed, sigAlg, Octets(sig)}
|
|
var unsigned [][]byte
|
|
if o.Junk > 0 {
|
|
unsigned = append(unsigned, Attr(asn1.ObjectIdentifier{1, 2, 3, 4, 5}, Octets(make([]byte, o.Junk))))
|
|
}
|
|
if o.Token != nil {
|
|
t := o.Token(sig)
|
|
if o.Token2 {
|
|
unsigned = append(unsigned, Seq(OID(OIDTimeStamp), Set(0x31, t, Seq(OID(OIDData)))))
|
|
} else {
|
|
unsigned = append(unsigned, Attr(OIDTimeStamp, t))
|
|
}
|
|
if o.TimeStamps2 {
|
|
unsigned = append(unsigned, Attr(OIDTimeStamp, o.Token(sig)))
|
|
}
|
|
}
|
|
unsigned = append(unsigned, o.ExtraUnsigned...)
|
|
if len(unsigned) > 0 {
|
|
f = append(f, Set(0xa1, unsigned...))
|
|
}
|
|
if o.EditSignerInfo != nil {
|
|
f = o.EditSignerInfo(f)
|
|
}
|
|
return Seq(f...)
|
|
}
|
|
|
|
// sign signs digest with key and returns the signatureAlgorithm of the key
|
|
// and the signature value.
|
|
func sign(key crypto.Signer, o Options, digest []byte) (sigAlg, sig []byte) {
|
|
var err error
|
|
switch k := key.(type) {
|
|
case *rsa.PrivateKey:
|
|
if o.PSS {
|
|
params := [][]byte{tlv(0xa0, HashAlg(o.Hash)), tlv(0xa1, AlgID(OIDMGF1, HashAlg(o.Hash))), tlv(0xa2, Int(int64(o.Hash.Size())))}
|
|
if o.PSSTrailer {
|
|
params = append(params, tlv(0xa3, Int(1)))
|
|
}
|
|
sigAlg = AlgID(OIDPSS, Seq(params...))
|
|
sig, err = rsa.SignPSS(rand.Reader, k, o.Hash, digest, &rsa.PSSOptions{SaltLength: o.Hash.Size(), Hash: o.Hash})
|
|
} else {
|
|
sigAlg = AlgID(OIDRSA, Null())
|
|
sig, err = rsa.SignPKCS1v15(rand.Reader, k, o.Hash, digest)
|
|
}
|
|
case *ecdsa.PrivateKey:
|
|
sigAlg = AlgID(ecdsaOID(o.Hash))
|
|
sig, err = ecdsa.SignASN1(rand.Reader, k, digest)
|
|
default:
|
|
panic(fmt.Sprintf("cmstest: a key of type %T", key))
|
|
}
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return sigAlg, sig
|
|
}
|
|
|
|
// TokenOptions changes what Token writes.
|
|
type TokenOptions struct {
|
|
// Hash is the hash of the messageImprint, SHA-256 by default.
|
|
Hash crypto.Hash
|
|
// Accuracy is written as its seconds, millis and micros, each only when
|
|
// it is not zero; zero writes no accuracy. AccuracyRaw, when not nil, is
|
|
// the element of the accuracy as given: negative, not minimal, 0 or 1000.
|
|
Accuracy time.Duration
|
|
AccuracyRaw []byte
|
|
// Version is the version of the TSTInfo, 1 by default.
|
|
Version int
|
|
// Policy is the policy of the TSTInfo, 1.2.3.4 by default; BTSPPolicy
|
|
// is that of ETSI EN 319 421.
|
|
Policy asn1.ObjectIdentifier
|
|
// Imprint, when not nil, is written as the hashed message instead of the
|
|
// hash of the subject, of any length.
|
|
Imprint []byte
|
|
// GenTimeRaw, when not nil, is written as genTime instead of the
|
|
// GeneralizedTime of the time given: free text, or another type.
|
|
GenTimeRaw []byte
|
|
// OrderingFalse writes ordering FALSE, its default, which DER does not
|
|
// write; After are elements written after the accuracy and the ordering:
|
|
// nonce, tsa and extensions, or anything after the last field.
|
|
OrderingFalse bool
|
|
After [][]byte
|
|
// SigCertV2 signs with signing-certificate-v2 instead of
|
|
// signing-certificate (ESSCertID).
|
|
SigCertV2 bool
|
|
// NoMessageDigest leaves the message-digest out of the token, CRL puts
|
|
// this element in its crls, and TSATwice writes the certificate of the
|
|
// authority twice in its certificates.
|
|
NoMessageDigest, TSATwice bool
|
|
CRL []byte
|
|
// CMS are the options of the SignedData of the token; its Hash is the
|
|
// digest of the signature of the authority, SHA-256 by default.
|
|
CMS Options
|
|
}
|
|
|
|
// BTSPPolicy is the best practices time-stamp policy of ETSI EN 319 421,
|
|
// 0.4.0.2023.1.1, whose tokens carry accuracy.
|
|
var BTSPPolicy = asn1.ObjectIdentifier{0, 4, 0, 2023, 1, 1}
|
|
|
|
// AccuracyOf is the Accuracy element of d: its seconds, millis and micros,
|
|
// each only when it is not zero.
|
|
func AccuracyOf(d time.Duration) []byte {
|
|
var f [][]byte
|
|
if s := d / time.Second; s != 0 {
|
|
f = append(f, Int(int64(s)))
|
|
}
|
|
if ms := d % time.Second / time.Millisecond; ms != 0 {
|
|
f = append(f, tlv(0x80, Int(int64(ms))[2:]))
|
|
}
|
|
if us := d % time.Millisecond / time.Microsecond; us != 0 {
|
|
f = append(f, tlv(0x81, Int(int64(us))[2:]))
|
|
}
|
|
return Seq(f...)
|
|
}
|
|
|
|
// TSTInfo returns the TSTInfo that Token signs.
|
|
func TSTInfo(subject []byte, genTime time.Time, o TokenOptions) []byte {
|
|
if o.Hash == 0 {
|
|
o.Hash = crypto.SHA256
|
|
}
|
|
if o.Version == 0 {
|
|
o.Version = 1
|
|
}
|
|
imprint := sum(o.Hash, subject)
|
|
if o.Imprint != nil {
|
|
imprint = o.Imprint
|
|
}
|
|
gt := GeneralizedTimeOf(genTime)
|
|
if o.GenTimeRaw != nil {
|
|
gt = o.GenTimeRaw
|
|
}
|
|
if o.Policy == nil {
|
|
o.Policy = asn1.ObjectIdentifier{1, 2, 3, 4}
|
|
}
|
|
info := [][]byte{Int(int64(o.Version)), OID(o.Policy), Seq(HashAlg(o.Hash), Octets(imprint)), Int(42), gt}
|
|
switch {
|
|
case o.AccuracyRaw != nil:
|
|
info = append(info, o.AccuracyRaw)
|
|
case o.Accuracy != 0:
|
|
info = append(info, AccuracyOf(o.Accuracy))
|
|
}
|
|
if o.OrderingFalse {
|
|
info = append(info, Bool(false))
|
|
}
|
|
return Seq(append(info, o.After...)...)
|
|
}
|
|
|
|
// Token returns the RFC 3161 time-stamp token that tsa issues over subject
|
|
// at genTime.
|
|
func Token(subject []byte, genTime time.Time, o TokenOptions, tsa Signer) []byte {
|
|
cms := o.CMS
|
|
cms.SigCertV2 = cms.SigCertV2 || o.SigCertV2
|
|
cms.NoMessageDigest = cms.NoMessageDigest || o.NoMessageDigest
|
|
if o.CRL != nil {
|
|
cms.CRLs = append(slices.Clone(cms.CRLs), o.CRL)
|
|
}
|
|
if o.TSATwice {
|
|
cms.ExtraCerts = append(slices.Clone(cms.ExtraCerts), tsa.Cert.Raw)
|
|
}
|
|
return build(TSTInfo(subject, genTime, o), cms, true, []Signer{tsa})
|
|
}
|