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.
DateKeys/internal/cms/cmstest/cmstest.go

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

Powered by TurnKey Linux.