|
|
|
|
// Package cmstest builds the CMS signatures and the RFC 3161 tokens that the
|
|
|
|
|
// tests of internal/cms and of capsule read: certificates of test keys, a
|
|
|
|
|
// detached signature of a message with its signedAttrs and, optionally, a
|
|
|
|
|
// time-stamp token of its signature. 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/x509"
|
|
|
|
|
"crypto/x509/pkix"
|
|
|
|
|
"encoding/asn1"
|
|
|
|
|
"math/big"
|
|
|
|
|
"slices"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Signer is a certificate with its private key.
|
|
|
|
|
type Signer struct {
|
|
|
|
|
Cert *x509.Certificate
|
|
|
|
|
Key crypto.Signer
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewRSA returns a signer with an RSA key of bits bits, valid in
|
|
|
|
|
// [notBefore, notAfter], named cn.
|
|
|
|
|
func NewRSA(cn string, bits int, notBefore, notAfter time.Time) Signer {
|
|
|
|
|
k, err := rsa.GenerateKey(rand.Reader, bits)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
return newSigner(cn, k, notBefore, notAfter)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewECDSA returns a signer with an ECDSA key on curve.
|
|
|
|
|
func NewECDSA(cn string, curve elliptic.Curve, notBefore, notAfter time.Time) Signer {
|
|
|
|
|
k, err := ecdsa.GenerateKey(curve, rand.Reader)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
return newSigner(cn, k, 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: c, 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...)
|
|
|
|
|
}
|
|
|
|
|
|
Review fixes: the CMS reader, the area after the signature, and the issuer on screen
The TSTInfo is read field by field in DER, with accuracy from zero and
millis and micros from 1 to 999, genTime in UTC with Z, no default written
and nothing after the last field. The ContentInfo and the SignerInfo must be
SEQUENCEs, a SignerInfo version must match its sid, an attribute needs a
value and is counted by attribute and not by value, a signing-certificate
beside the v2 decides nothing, PSS parameters come in order without the
trailer, and der.Check refuses the end of contents and the universal tags
the profile does not use.
The writer signs before L is fixed: write asks prepare for the final L, so
the area grows to 64 KiB only when what was signed does not fit and LargeArea
allows it, and nobody signs twice for it. Typed nils are nil, the exclusions
are checked before a file is read, Encrypt refuses the signing options, and
EvaluateSecurityIn gives X if a parser panics. The issuer of a certificate is
filtered like its holder.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
1 week ago
|
|
|
// TLV builds an element of any tag from the encodings of its content.
|
|
|
|
|
func TLV(tag byte, content ...[]byte) []byte { return tlv(tag, content...) }
|
|
|
|
|
|
|
|
|
|
// 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})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GeneralizedTime builds a GeneralizedTime with the text s.
|
|
|
|
|
func GeneralizedTime(s string) []byte { return tlv(0x18, []byte(s)) }
|
|
|
|
|
|
|
|
|
|
// Seq is a SEQUENCE.
|
|
|
|
|
func Seq(content ...[]byte) []byte { return tlv(0x30, content...) }
|
|
|
|
|
|
|
|
|
|
// Set is a SET OF, in DER order.
|
|
|
|
|
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 {
|
|
|
|
|
b, err := asn1.Marshal(oid)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
return b
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Octets is an OCTET STRING.
|
|
|
|
|
func Octets(b []byte) []byte { return tlv(0x04, b) }
|
|
|
|
|
|
|
|
|
|
// Int is an INTEGER.
|
|
|
|
|
func Int(n int64) []byte {
|
|
|
|
|
b, err := asn1.Marshal(n)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
return b
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
OIDData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 1}
|
|
|
|
|
OIDSignedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 2}
|
|
|
|
|
oidContent = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 3}
|
|
|
|
|
oidDigest = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 4}
|
|
|
|
|
OIDSigCertV2 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 2, 47}
|
|
|
|
|
oidSigCertV1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 2, 12}
|
|
|
|
|
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}
|
|
|
|
|
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}
|
|
|
|
|
oidPSS = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10}
|
|
|
|
|
oidMGF1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 8}
|
|
|
|
|
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}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func hashAlg(h crypto.Hash) []byte {
|
|
|
|
|
switch h {
|
|
|
|
|
case crypto.SHA384:
|
|
|
|
|
return Seq(OID(oidSHA384))
|
|
|
|
|
case crypto.SHA512:
|
|
|
|
|
return Seq(OID(oidSHA512))
|
|
|
|
|
}
|
|
|
|
|
return Seq(OID(oidSHA256))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func hashOID(h crypto.Hash) asn1.ObjectIdentifier {
|
|
|
|
|
switch h {
|
|
|
|
|
case crypto.SHA384:
|
|
|
|
|
return oidSHA384
|
|
|
|
|
case crypto.SHA512:
|
|
|
|
|
return oidSHA512
|
|
|
|
|
}
|
|
|
|
|
return oidSHA256
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func sum(h crypto.Hash, b []byte) []byte {
|
|
|
|
|
x := h.New()
|
|
|
|
|
x.Write(b)
|
|
|
|
|
return x.Sum(nil)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Options changes what Build writes, to make signatures that the profile
|
|
|
|
|
// rejects or that verify to something else.
|
|
|
|
|
type Options struct {
|
|
|
|
|
// Hash is the digest of the signature, SHA-256 by default.
|
|
|
|
|
Hash crypto.Hash
|
|
|
|
|
// PSS signs with RSASSA-PSS instead of PKCS #1 v1.5.
|
|
|
|
|
PSS bool
|
|
|
|
|
// SKI names the signer by subjectKeyIdentifier instead of by issuer
|
|
|
|
|
// and serial.
|
|
|
|
|
SKI bool
|
|
|
|
|
// Token, when not nil, is the time-stamp token of the signature that
|
|
|
|
|
// Build wants as an unsigned attribute: it receives the signature value.
|
|
|
|
|
Token func(signature []byte) []byte
|
|
|
|
|
// Message is what the message-digest covers, when not the signed message.
|
|
|
|
|
Message []byte
|
|
|
|
|
// Mutate edits the signedAttrs, as a list of the DER of each attribute,
|
|
|
|
|
// before they are signed.
|
|
|
|
|
Mutate func(attrs [][]byte) [][]byte
|
|
|
|
|
// Token2 puts two signature-time-stamp attributes, to break the profile.
|
|
|
|
|
Token2 bool
|
|
|
|
|
// OCSP adds this response in crls.
|
|
|
|
|
OCSP []byte
|
|
|
|
|
// OmitCert leaves the certificate of the signer out of certificates.
|
|
|
|
|
OmitCert bool
|
Review fixes: the CMS reader, the area after the signature, and the issuer on screen
The TSTInfo is read field by field in DER, with accuracy from zero and
millis and micros from 1 to 999, genTime in UTC with Z, no default written
and nothing after the last field. The ContentInfo and the SignerInfo must be
SEQUENCEs, a SignerInfo version must match its sid, an attribute needs a
value and is counted by attribute and not by value, a signing-certificate
beside the v2 decides nothing, PSS parameters come in order without the
trailer, and der.Check refuses the end of contents and the universal tags
the profile does not use.
The writer signs before L is fixed: write asks prepare for the final L, so
the area grows to 64 KiB only when what was signed does not fit and LargeArea
allows it, and nobody signs twice for it. Typed nils are nil, the exclusions
are checked before a file is read, Encrypt refuses the signing options, and
EvaluateSecurityIn gives X if a parser panics. The issuer of a certificate is
filtered like its holder.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
1 week ago
|
|
|
// PSSTrailer writes trailerField [3] 1 in the PSS parameters, which is
|
|
|
|
|
// its default and DER does not write it.
|
|
|
|
|
PSSTrailer 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
|
|
|
|
|
// SigCertV1 adds a signing-certificate attribute beside the v2.
|
|
|
|
|
SigCertV1 bool
|
|
|
|
|
// ExtraAttrs are added to the signed attributes, as the DER of each.
|
|
|
|
|
ExtraAttrs [][]byte
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func attr(oid asn1.ObjectIdentifier, values ...[]byte) []byte {
|
|
|
|
|
return Seq(OID(oid), Set(0x31, values...))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func build(message []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)
|
|
|
|
|
}
|
|
|
|
|
infos = append(infos, signerInfo(message, o, token, s))
|
|
|
|
|
}
|
|
|
|
|
var body []byte
|
|
|
|
|
body = append(body, Int(1)...)
|
|
|
|
|
body = append(body, Set(0x31, hashAlg(o.Hash))...)
|
|
|
|
|
body = append(body, encap(message, token)...)
|
|
|
|
|
if len(certs) > 0 {
|
|
|
|
|
body = append(body, Set(0xa0, certs...)...)
|
|
|
|
|
}
|
|
|
|
|
if o.OCSP != nil {
|
|
|
|
|
body = append(body, Set(0xa1, tlv(0xa1, OID(oidOCSP), o.OCSP))...)
|
|
|
|
|
}
|
|
|
|
|
body = append(body, Set(0x31, infos...)...)
|
|
|
|
|
return Seq(OID(OIDSignedData), tlv(0xa0, Seq(body)))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func encap(content []byte, token bool) []byte {
|
|
|
|
|
if !token {
|
|
|
|
|
return Seq(OID(OIDData))
|
|
|
|
|
}
|
|
|
|
|
return Seq(OID(oidTSTInfo), tlv(0xa0, Octets(content)))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func signerInfo(message []byte, o Options, token bool, s Signer) []byte {
|
|
|
|
|
var sid []byte
|
|
|
|
|
if o.SKI {
|
|
|
|
|
sid = tlv(0x80, s.Cert.SubjectKeyId)
|
|
|
|
|
} else {
|
|
|
|
|
sid = Seq(s.Cert.RawIssuer, mustMarshal(s.Cert.SerialNumber))
|
|
|
|
|
}
|
|
|
|
|
contentType := OIDData
|
|
|
|
|
if token {
|
|
|
|
|
contentType = oidTSTInfo
|
|
|
|
|
}
|
|
|
|
|
md := message
|
|
|
|
|
if o.Message != nil {
|
|
|
|
|
md = o.Message
|
|
|
|
|
}
|
|
|
|
|
essHash := sha256.Sum256(s.Cert.Raw)
|
|
|
|
|
attrs := [][]byte{
|
|
|
|
|
attr(oidContent, OID(contentType)),
|
|
|
|
|
attr(oidDigest, Octets(sum(o.Hash, md))),
|
|
|
|
|
}
|
|
|
|
|
if token {
|
|
|
|
|
h := sha1.Sum(s.Cert.Raw)
|
|
|
|
|
attrs = append(attrs, attr(oidSigCertV1, Seq(Seq(Seq(Octets(h[:]))))))
|
|
|
|
|
} else {
|
|
|
|
|
attrs = append(attrs, attr(OIDSigCertV2, Seq(Seq(Seq(Octets(essHash[:]))))))
|
|
|
|
|
}
|
Review fixes: the CMS reader, the area after the signature, and the issuer on screen
The TSTInfo is read field by field in DER, with accuracy from zero and
millis and micros from 1 to 999, genTime in UTC with Z, no default written
and nothing after the last field. The ContentInfo and the SignerInfo must be
SEQUENCEs, a SignerInfo version must match its sid, an attribute needs a
value and is counted by attribute and not by value, a signing-certificate
beside the v2 decides nothing, PSS parameters come in order without the
trailer, and der.Check refuses the end of contents and the universal tags
the profile does not use.
The writer signs before L is fixed: write asks prepare for the final L, so
the area grows to 64 KiB only when what was signed does not fit and LargeArea
allows it, and nobody signs twice for it. Typed nils are nil, the exclusions
are checked before a file is read, Encrypt refuses the signing options, and
EvaluateSecurityIn gives X if a parser panics. The issuer of a certificate is
filtered like its holder.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
1 week ago
|
|
|
if o.SigCertV1 {
|
|
|
|
|
h := sha1.Sum(s.Cert.Raw)
|
|
|
|
|
attrs = append(attrs, attr(oidSigCertV1, Seq(Seq(Seq(Octets(h[:]))))))
|
|
|
|
|
}
|
|
|
|
|
attrs = append(attrs, o.ExtraAttrs...)
|
|
|
|
|
if o.Mutate != nil {
|
|
|
|
|
attrs = o.Mutate(attrs)
|
|
|
|
|
}
|
|
|
|
|
signed := Set(0xa0, attrs...)
|
|
|
|
|
forSig := append([]byte{0x31}, signed[1:]...)
|
|
|
|
|
digest := sum(o.Hash, forSig)
|
|
|
|
|
|
|
|
|
|
var sigAlg, sig []byte
|
|
|
|
|
switch k := s.Key.(type) {
|
|
|
|
|
case *rsa.PrivateKey:
|
|
|
|
|
if o.PSS {
|
|
|
|
|
params := Seq(tlv(0xa0, hashAlg(o.Hash)), tlv(0xa1, Seq(OID(oidMGF1), hashAlg(o.Hash))), tlv(0xa2, Int(int64(o.Hash.Size()))))
|
Review fixes: the CMS reader, the area after the signature, and the issuer on screen
The TSTInfo is read field by field in DER, with accuracy from zero and
millis and micros from 1 to 999, genTime in UTC with Z, no default written
and nothing after the last field. The ContentInfo and the SignerInfo must be
SEQUENCEs, a SignerInfo version must match its sid, an attribute needs a
value and is counted by attribute and not by value, a signing-certificate
beside the v2 decides nothing, PSS parameters come in order without the
trailer, and der.Check refuses the end of contents and the universal tags
the profile does not use.
The writer signs before L is fixed: write asks prepare for the final L, so
the area grows to 64 KiB only when what was signed does not fit and LargeArea
allows it, and nobody signs twice for it. Typed nils are nil, the exclusions
are checked before a file is read, Encrypt refuses the signing options, and
EvaluateSecurityIn gives X if a parser panics. The issuer of a certificate is
filtered like its holder.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
1 week ago
|
|
|
if o.PSSTrailer {
|
|
|
|
|
params = Seq(tlv(0xa0, hashAlg(o.Hash)), tlv(0xa1, Seq(OID(oidMGF1), hashAlg(o.Hash))), tlv(0xa2, Int(int64(o.Hash.Size()))), tlv(0xa3, Int(1)))
|
|
|
|
|
}
|
|
|
|
|
sigAlg = Seq(OID(oidPSS), params)
|
|
|
|
|
sig, _ = rsa.SignPSS(rand.Reader, k, o.Hash, digest, &rsa.PSSOptions{SaltLength: o.Hash.Size(), Hash: o.Hash})
|
|
|
|
|
} else {
|
|
|
|
|
sigAlg = Seq(OID(oidRSA), []byte{5, 0})
|
|
|
|
|
sig, _ = rsa.SignPKCS1v15(rand.Reader, k, o.Hash, digest)
|
|
|
|
|
}
|
|
|
|
|
case *ecdsa.PrivateKey:
|
|
|
|
|
oid := oidECDSA256
|
|
|
|
|
switch o.Hash {
|
|
|
|
|
case crypto.SHA384:
|
|
|
|
|
oid = oidECDSA384
|
|
|
|
|
case crypto.SHA512:
|
|
|
|
|
oid = oidECDSA512
|
|
|
|
|
}
|
|
|
|
|
sigAlg = Seq(OID(oid))
|
|
|
|
|
sig, _ = ecdsa.SignASN1(rand.Reader, k, digest)
|
|
|
|
|
}
|
Review fixes: the CMS reader, the area after the signature, and the issuer on screen
The TSTInfo is read field by field in DER, with accuracy from zero and
millis and micros from 1 to 999, genTime in UTC with Z, no default written
and nothing after the last field. The ContentInfo and the SignerInfo must be
SEQUENCEs, a SignerInfo version must match its sid, an attribute needs a
value and is counted by attribute and not by value, a signing-certificate
beside the v2 decides nothing, PSS parameters come in order without the
trailer, and der.Check refuses the end of contents and the universal tags
the profile does not use.
The writer signs before L is fixed: write asks prepare for the final L, so
the area grows to 64 KiB only when what was signed does not fit and LargeArea
allows it, and nobody signs twice for it. Typed nils are nil, the exclusions
are checked before a file is read, Encrypt refuses the signing options, and
EvaluateSecurityIn gives X if a parser panics. The issuer of a certificate is
filtered like its holder.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
1 week ago
|
|
|
version := int64(1)
|
|
|
|
|
if o.SKI {
|
|
|
|
|
version = 3
|
|
|
|
|
}
|
|
|
|
|
f := [][]byte{Int(version), sid, hashAlg(o.Hash), 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)
|
|
|
|
|
v := attr(OIDTimeStamp, t)
|
|
|
|
|
if o.Token2 {
|
|
|
|
|
v = Seq(OID(OIDTimeStamp), Set(0x31, t, Seq(OID(OIDData))))
|
|
|
|
|
}
|
Review fixes: the CMS reader, the area after the signature, and the issuer on screen
The TSTInfo is read field by field in DER, with accuracy from zero and
millis and micros from 1 to 999, genTime in UTC with Z, no default written
and nothing after the last field. The ContentInfo and the SignerInfo must be
SEQUENCEs, a SignerInfo version must match its sid, an attribute needs a
value and is counted by attribute and not by value, a signing-certificate
beside the v2 decides nothing, PSS parameters come in order without the
trailer, and der.Check refuses the end of contents and the universal tags
the profile does not use.
The writer signs before L is fixed: write asks prepare for the final L, so
the area grows to 64 KiB only when what was signed does not fit and LargeArea
allows it, and nobody signs twice for it. Typed nils are nil, the exclusions
are checked before a file is read, Encrypt refuses the signing options, and
EvaluateSecurityIn gives X if a parser panics. The issuer of a certificate is
filtered like its holder.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
1 week ago
|
|
|
unsigned = append(unsigned, v)
|
|
|
|
|
}
|
|
|
|
|
if len(unsigned) > 0 {
|
|
|
|
|
f = append(f, Set(0xa1, unsigned...))
|
|
|
|
|
}
|
|
|
|
|
return Seq(f...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func mustMarshal(v any) []byte {
|
|
|
|
|
b, err := asn1.Marshal(v)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
return b
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TokenOptions changes what Token writes.
|
|
|
|
|
type TokenOptions struct {
|
|
|
|
|
Hash crypto.Hash // the hash of the messageImprint, SHA-256 by default
|
|
|
|
|
Accuracy time.Duration // whole seconds; zero for none
|
|
|
|
|
Version int // the version of the TSTInfo, 1 by default
|
|
|
|
|
// Imprint, when not nil, is written as the hashed message instead of
|
|
|
|
|
// the hash of the subject.
|
|
|
|
|
Imprint []byte
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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 {
|
|
|
|
|
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, err := asn1.MarshalWithParams(genTime.UTC(), "generalized")
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
info := []byte{}
|
|
|
|
|
info = append(info, Int(int64(o.Version))...)
|
|
|
|
|
info = append(info, OID(asn1.ObjectIdentifier{1, 2, 3, 4})...)
|
|
|
|
|
info = append(info, Seq(hashAlg(o.Hash), Octets(imprint))...)
|
|
|
|
|
info = append(info, Int(42)...)
|
|
|
|
|
info = append(info, gt...)
|
|
|
|
|
if o.Accuracy != 0 {
|
|
|
|
|
info = append(info, Seq(Int(int64(o.Accuracy/time.Second)))...)
|
|
|
|
|
}
|
|
|
|
|
tst := Seq(info)
|
|
|
|
|
return build(tst, Options{Hash: crypto.SHA256}, true, []Signer{tsa})
|
|
|
|
|
}
|