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/form_test.go

349 lines
26 KiB

Tests of the CMS reader: certificates field by field, every check, fuzzing The builder of the tests, cmstest: - NewCert writes a certificate from the DER of its tbsCertificate, field by field: names of any string type with any bytes (UTF8String, PrintableString with an underscore or an at sign, IA5String, TeletexString, BMPString of odd length or with a surrogate, VisibleString, NumericString), an attribute twice or none, no version, times with a fraction, an extension twice, a compressed EC key, an even modulus, and any signature. A Signer made so serves Signature and Token. - Options for the version of a SignerInfo, the hashAlgorithm and the certHash of an ESSCertIDv2, a signatureAlgorithm other than the one of the key, a certificate twice, two content-type attributes, an attribute with an arc of 2^31, a SignerInfo twice, BER, signerInfos out of order, two signature-time-stamp attributes, and edits of the SignedData and of each SignerInfo. - Token options for any accuracy, a genTime of free text, ordering FALSE, a field after the last, an imprint of any length, no message-digest, a CRL in crls and the certificate of the authority twice. - Edits of the DER after signing: Edit, Retag, Withdraw (a SignerInfo removed), WithoutTimeStamp (a CAdES-T removed), Merge (a co-signature) and Indefinite. The tests of internal/cms and internal/der fail for each check of cms.go, cert.go, verify.go and der.go. A mutation run, which replaces each leaf of each condition by false and by true, one at a time, kills every mutant that is not equivalent to the code it mutates. FuzzParseSignature, FuzzParseToken, FuzzParseCert and FuzzDERCheck, seeded with security_cms.json and with what cmstest builds: no panic, what Check accepts Split reads, and the parsers fail only with ErrForm or ErrAlgorithm. capsule: the case of a seal outside the validity of the certificate gave an invalid seal; it now tests a certificate that expired before a valid seal (out of validity) apart from an authority that was not valid at its time (invalid seal). SIGNERS out of order, empty, too long, with 31 bytes or with a hash twice are F1 beside a CMS signature that is valid for the AUTHOR_MESSAGE of those SIGNERS, and the names of certificates show as spec v0.12 §29.7 says. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 days ago
package cms_test
import (
"bytes"
"crypto"
"crypto/sha256"
"encoding/asn1"
"slices"
"testing"
"time"
"g.activething.com/go/DateKeys/internal/cms"
"g.activething.com/go/DateKeys/internal/cms/cmstest"
)
// Spec §29.10, "Forma", rules 1 to 4, and the rules that follow them: each
// case breaks one check of the reader, and the signature is F1.
func TestSignatureFormRules(t *testing.T) {
good := cmstest.Signature(msg, cmstest.Options{}, ana)
both := cmstest.Signature(msg, cmstest.Options{}, ana, luis)
tok := func(sig []byte) []byte { return cmstest.Token(sig, now, cmstest.TokenOptions{}, tsa) }
sealed := cmstest.Signature(msg, cmstest.Options{Token: tok}, ana)
sd := cmstest.SignedDataPath
si := firstSignerInfo(good)
attrsOf := func(mutate func(attrs [][]byte) [][]byte) []byte {
return cmstest.Signature(msg, cmstest.Options{Mutate: mutate}, ana)
}
extra := func(attrs ...[]byte) []byte { return cmstest.Signature(msg, cmstest.Options{ExtraAttrs: attrs}, ana) }
h := sha256.Sum256(ana.Cert.Raw)
v2 := func(value []byte) func([][]byte) [][]byte {
return func(a [][]byte) [][]byte {
a[2] = cmstest.Seq(cmstest.OID(cmstest.OIDSigCertV2), cmstest.Set(0x31, value))
return a
}
}
unknown := asn1.ObjectIdentifier{1, 2, 3, 4}
reversed := func(b []byte) []byte {
k := slices.Clone(cmstest.Children(b))
slices.Reverse(k)
return cmstest.TLV(b[0], k...)
}
same := func(c1, c2 cmstest.CertSpec) []byte {
a, b := cmstest.NewCert(c1, ecKey), cmstest.NewCert(c2, ecKey2)
return cmstest.Signature(msg, cmstest.Options{ExtraCerts: [][]byte{b.Cert.Raw}}, a)
}
ocsp := func(n int64) []byte {
return cmstest.TLV(0xa1, cmstest.OID(cmstest.OIDOCSP), cmstest.Seq(cmstest.Int(n)))
}
twoOCSP := cmstest.Signature(msg, cmstest.Options{CRLs: [][]byte{ocsp(1), ocsp(2)}}, ana)
for name, b := range map[string][]byte{
// Rule 1: DER, the ContentInfo and the SignedData.
"BER": cmstest.Signature(msg, cmstest.Options{BER: true}, ana),
"a ContentInfo with a third element": cmstest.Edit(good, cmstest.Append(cmstest.Null())),
"a ContentInfo of only its type": cmstest.Seq(cmstest.OID(cmstest.OIDSignedData)),
"the content as [1]": cmstest.Edit(good, cmstest.Retag(0xa1), 1),
"the content type id-data": cmstest.Edit(good, cmstest.Replace(cmstest.OID(cmstest.OIDData)), 0),
"the content type an INTEGER": cmstest.Edit(good, cmstest.Replace(cmstest.Int(1)), 0),
"[0] with an element more": cmstest.Edit(good, cmstest.Append(cmstest.Null()), 1),
"[0] empty": cmstest.Edit(good, cmstest.Replace(cmstest.TLV(0xa0)), 1),
"[0] holding a SET": cmstest.Edit(good, cmstest.Retag(0x31), sd...),
"a SignedData of two fields": cmstest.Edit(good, func(b []byte) []byte { return cmstest.Seq(cmstest.Children(b)[:2]...) }, sd...),
"a SignedData without signerInfos": cmstest.Edit(good, func(b []byte) []byte { k := cmstest.Children(b); return cmstest.Seq(k[:len(k)-1]...) }, sd...),
"a field after signerInfos": cmstest.Edit(good, cmstest.Append(cmstest.Set(0x31)), sd...),
"the version as an OCTET STRING": cmstest.Edit(good, cmstest.Retag(0x04), path(sd, 0)...),
"digestAlgorithms as a SEQUENCE": cmstest.Edit(good, cmstest.Retag(0x30), path(sd, 1)...),
"digestAlgorithms out of order": cmstest.Edit(good, cmstest.Replace(cmstest.TLV(0x31, cmstest.HashAlg(crypto.SHA512), cmstest.HashAlg(crypto.SHA256))), path(sd, 1)...),
"a digestAlgorithm that is an INTEGER": cmstest.Edit(good, cmstest.Replace(cmstest.Set(0x31, cmstest.Int(1))), path(sd, 1)...),
"a digestAlgorithm that is a SET": cmstest.Edit(good, cmstest.Replace(cmstest.Set(0x31, cmstest.TLV(0x31, cmstest.OID(cmstest.OIDSHA256)))), path(sd, 1)...),
"a digestAlgorithm without an OID": cmstest.Edit(good, cmstest.Replace(cmstest.Set(0x31, cmstest.Seq(cmstest.Int(1)))), path(sd, 1)...),
"an empty digestAlgorithm": cmstest.Edit(good, cmstest.Replace(cmstest.Set(0x31, cmstest.Seq())), path(sd, 1)...),
"a digestAlgorithm of three fields": cmstest.Edit(good, cmstest.Replace(cmstest.Set(0x31, cmstest.AlgID(cmstest.OIDSHA256, cmstest.Null(), cmstest.Null()))), path(sd, 1)...),
"encapContentInfo as a SET": cmstest.Edit(good, cmstest.Retag(0x31), path(sd, 2)...),
"an empty encapContentInfo": cmstest.Edit(good, cmstest.Replace(cmstest.Seq()), path(sd, 2)...),
"encapContentInfo of three fields": cmstest.Edit(good, cmstest.Append(cmstest.TLV(0xa0, cmstest.Octets(msg)), cmstest.Null()), path(sd, 2)...),
"eContentType an INTEGER": cmstest.Edit(good, cmstest.Replace(cmstest.Seq(cmstest.Int(1))), path(sd, 2)...),
"eContentType id-ct-TSTInfo": cmstest.Edit(good, cmstest.Replace(cmstest.Seq(cmstest.OID(cmstest.OIDTSTInfo))), path(sd, 2)...),
"rule 2: an eContent in the signature": cmstest.Edit(good, cmstest.Append(cmstest.TLV(0xa0, cmstest.Octets(msg))), path(sd, 2)...),
"certificates out of order": cmstest.Edit(both, reversed, path(sd, 3)...),
"a CertificateChoice [4]": cmstest.Signature(msg, cmstest.Options{ExtraCerts: [][]byte{cmstest.TLV(0xa4, cmstest.Null())}}, ana),
"a CertificateChoice that is a SET": cmstest.Signature(msg, cmstest.Options{ExtraCerts: [][]byte{cmstest.Set(0x31, cmstest.Null())}}, ana),
"rule 3: a CRL in crls": cmstest.Signature(msg, cmstest.Options{CRLs: [][]byte{cmstest.Seq(cmstest.Int(1))}}, ana),
"rule 3: a CRL of the shape of an OCSP": cmstest.Signature(msg, cmstest.Options{CRLs: [][]byte{cmstest.Seq(cmstest.OID(cmstest.OIDOCSP), cmstest.Seq(cmstest.Int(0)))}}, ana),
"rule 3: crls out of order": cmstest.Edit(twoOCSP, reversed, path(sd, 4)...),
"rule 3: another revocation format": cmstest.Signature(msg, cmstest.Options{CRLs: [][]byte{cmstest.TLV(0xa1, cmstest.OID(unknown), cmstest.Null())}}, ana),
"rule 3: a revocation format of one": cmstest.Signature(msg, cmstest.Options{CRLs: [][]byte{cmstest.TLV(0xa1, cmstest.OID(cmstest.OIDOCSP))}}, ana),
"rule 3: a revocation format by number": cmstest.Signature(msg, cmstest.Options{CRLs: [][]byte{cmstest.TLV(0xa1, cmstest.Int(2), cmstest.Null())}}, ana),
"signerInfos as a SEQUENCE": cmstest.Edit(good, cmstest.Retag(0x30), cmstest.SignerInfosPath(good)...),
"no SignerInfo": cmstest.Edit(good, cmstest.Replace(cmstest.TLV(0x31)), cmstest.SignerInfosPath(good)...),
"signerInfos out of order": cmstest.Signature(msg, cmstest.Options{Unsorted: true}, ana, luis),
// Rule 3: the SignerInfo and its sid.
"a SignerInfo without signedAttrs": cmstest.Edit(good, func(b []byte) []byte { k := cmstest.Children(b); return cmstest.Seq(k[0], k[1], k[2], k[4], k[5]) }, si...),
"signedAttrs as [1]": cmstest.Edit(good, cmstest.Retag(0xa1), path(si, 3)...),
"a SignerInfo without its signature": cmstest.Edit(good, func(b []byte) []byte { return cmstest.Seq(cmstest.Children(b)[:5]...) }, si...),
"the version of a SignerInfo as OCTETS": cmstest.Edit(good, cmstest.Retag(0x04), path(si, 0)...),
"the version 2": cmstest.Signature(msg, cmstest.Options{Version: 2}, ana),
"the version 3 with issuerAndSerialNumber": cmstest.Signature(msg, cmstest.Options{Version: 3}, ana),
"the version 1 with subjectKeyIdentifier": cmstest.Signature(msg, cmstest.Options{Version: 1, SKI: true}, ana),
"the version 4 with subjectKeyIdentifier": cmstest.Signature(msg, cmstest.Options{Version: 4, SKI: true}, ana),
"the version 257": cmstest.Edit(good, cmstest.Replace(cmstest.Int(257)), path(si, 0)...),
"the digestAlgorithm as a SET": cmstest.Edit(good, cmstest.Retag(0x31), path(si, 2)...),
"the signatureAlgorithm as a SET": cmstest.Edit(good, cmstest.Retag(0x31), path(si, 4)...),
"the digestAlgorithm of a SignerInfo, no OID": cmstest.Edit(good, cmstest.Replace(cmstest.Seq(cmstest.Int(1))), path(si, 2)...),
"a signatureAlgorithm without an OID": cmstest.Edit(good, cmstest.Replace(cmstest.Seq(cmstest.Int(1))), path(si, 4)...),
"an empty signatureAlgorithm": cmstest.Edit(good, cmstest.Replace(cmstest.Seq()), path(si, 4)...),
"the signature as a BIT STRING": cmstest.Edit(good, func(b []byte) []byte { return cmstest.BitString(contentOf(b)) }, path(si, 5)...),
"a field after the unsigned attributes": cmstest.Signature(msg, cmstest.Options{Token: tok, EditSignerInfo: func(f [][]byte) [][]byte { return append(f, cmstest.Null()) }}, ana),
"a field [2] after the signature": cmstest.Signature(msg, cmstest.Options{EditSignerInfo: func(f [][]byte) [][]byte { return append(f, cmstest.TLV(0xa2, cmstest.BigArcAttr())) }}, ana),
"a sid of three elements": cmstest.Edit(good, cmstest.Append(cmstest.Null()), path(si, 1)...),
"a sid of one element": cmstest.Edit(good, func(b []byte) []byte { return cmstest.Seq(cmstest.Children(b)[0]) }, path(si, 1)...),
"the issuer of the sid as a SET": cmstest.Edit(good, cmstest.Retag(0x31), path(si, 1, 0)...),
"the serial of the sid as an OCTET STRING": cmstest.Edit(good, cmstest.Retag(0x04), path(si, 1, 1)...),
"the serial of another certificate": cmstest.Edit(good, cmstest.Replace(cmstest.Int(12345)), path(si, 1, 1)...),
"a sid of another choice": cmstest.Edit(good, cmstest.Retag(0x81), path(si, 1)...),
"a subjectKeyIdentifier of no certificate": cmstest.Edit(cmstest.Signature(msg, cmstest.Options{SKI: true}, ana), cmstest.Replace(cmstest.TLV(0x80, []byte("other"))), path(si, 1)...),
"no certificate of the signer": cmstest.Signature(msg, cmstest.Options{OmitCert: true}, ana),
"two certificates of one issuer and serial": same(cmstest.CertSpec{CN: "A", Serial: cmstest.Int(7)}, cmstest.CertSpec{CN: "B", Serial: cmstest.Int(7), Issuer: cmstest.Name(cmstest.ATV(cmstest.OIDCommonName, cmstest.UTF8("A")))}),
"two certificates of one keyIdentifier": cmstest.Signature(msg, cmstest.Options{SKI: true, ExtraCerts: [][]byte{cmstest.NewCert(cmstest.CertSpec{CN: "B", SKI: ana.Cert.SubjectKeyId}, ecKey2).Cert.Raw}}, ana),
"an empty keyIdentifier, a certificate of none": cmstest.Signature(msg, cmstest.Options{SKI: true}, cmstest.NewCert(cmstest.CertSpec{CN: "Sin SKI", NoExtensions: true}, ecKey)),
"two SignerInfo of one certificate": cmstest.Signature(msg, cmstest.Options{}, ana, ana),
"one SignerInfo twice": cmstest.Signature(msg, cmstest.Options{SignerInfoTwice: true}, ana),
"the signer's certificate breaks the profile": cmstest.Signature(msg, cmstest.Options{}, cmstest.NewCert(cmstest.CertSpec{CN: "Ana", NoVersion: true}, ecKey)),
// Rule 4 and the rules after it: the attributes.
"signedAttrs out of order": cmstest.Signature(msg, cmstest.Options{UnsortedAttrs: true}, ana),
"an attribute as a SET": extra(cmstest.TLV(0x31, cmstest.OID(unknown), cmstest.Set(0x31, cmstest.Null()))),
"an attribute of three fields": extra(cmstest.Seq(cmstest.OID(unknown), cmstest.Set(0x31, cmstest.Null()), cmstest.Null())),
"an attribute of one field": extra(cmstest.Seq(cmstest.OID(unknown))),
"an attribute that is an INTEGER": extra(cmstest.Int(5)),
"the values of an attribute as a SEQUENCE": extra(cmstest.Seq(cmstest.OID(unknown), cmstest.Seq(cmstest.Null()))),
"an attribute whose type is an INTEGER": extra(cmstest.Seq(cmstest.Int(1), cmstest.Set(0x31, cmstest.Null()))),
"an unknown attribute without a value": extra(cmstest.Seq(cmstest.OID(unknown), cmstest.Set(0x31))),
"values of an attribute out of order": extra(cmstest.Seq(cmstest.OID(unknown), cmstest.TLV(0x31, cmstest.Int(2), cmstest.Int(1)))),
"two content-type attributes": cmstest.Signature(msg, cmstest.Options{ContentType2: true}, ana),
// Two values, id-data first in DER order: one value is required, not
// the first of several.
"a content-type of two values": attrsOf(func(a [][]byte) [][]byte {
a[0] = cmstest.Attr(cmstest.OIDContentType, cmstest.OID(cmstest.OIDData), cmstest.OID(cmstest.OIDTSTInfo))
return a
}),
"a message-digest of two values": attrsOf(func(a [][]byte) [][]byte {
s := sha256.Sum256(msg)
a[1] = cmstest.Attr(cmstest.OIDMessageDigest, cmstest.Octets(s[:]), cmstest.Octets(append(s[:], 0)))
return a
}),
"a second content-type without a value": extra(cmstest.Seq(cmstest.OID(cmstest.OIDContentType), cmstest.Set(0x31))),
"no content-type": attrsOf(func(a [][]byte) [][]byte { return a[1:] }),
"the content-type id-signedData": attrsOf(func(a [][]byte) [][]byte {
a[0] = cmstest.Attr(cmstest.OIDContentType, cmstest.OID(cmstest.OIDSignedData))
return a
}),
"the content-type id-ct-TSTInfo": attrsOf(func(a [][]byte) [][]byte {
a[0] = cmstest.Attr(cmstest.OIDContentType, cmstest.OID(cmstest.OIDTSTInfo))
return a
}),
"a content-type that is OCTETS": attrsOf(func(a [][]byte) [][]byte {
a[0] = cmstest.Attr(cmstest.OIDContentType, cmstest.Octets([]byte("data")))
return a
}),
"no message-digest": cmstest.Signature(msg, cmstest.Options{NoMessageDigest: true}, ana),
"two message-digest attributes": attrsOf(func(a [][]byte) [][]byte { return append(a, a[1]) }),
"a message-digest that is [0]": attrsOf(func(a [][]byte) [][]byte {
s := sha256.Sum256(msg)
a[1] = cmstest.Attr(cmstest.OIDMessageDigest, cmstest.TLV(0x80, s[:]))
return a
}),
"no signing-certificate-v2": cmstest.Signature(msg, cmstest.Options{NoSigCertV2: true}, ana),
"only a signing-certificate": cmstest.Signature(msg, cmstest.Options{NoSigCertV2: true, SigCertV1: true}, ana),
"two signing-certificate-v2 attributes": attrsOf(func(a [][]byte) [][]byte { return append(a, a[2]) }),
"a signing-certificate-v2 of two values": v2Values(h[:]),
"a SigningCertificateV2 as a SET": attrsOf(v2(cmstest.TLV(0x31, cmstest.Seq(cmstest.Seq(cmstest.Octets(h[:])))))),
"an empty SigningCertificateV2": attrsOf(v2(cmstest.Seq())),
"its certs as a SET": attrsOf(v2(cmstest.Seq(cmstest.TLV(0x31, cmstest.Seq(cmstest.Octets(h[:])))))),
"its certs empty": attrsOf(v2(cmstest.Seq(cmstest.Seq()))),
"an ESSCertIDv2 as a SET": attrsOf(v2(cmstest.Seq(cmstest.Seq(cmstest.TLV(0x31, cmstest.Octets(h[:])))))),
"an empty ESSCertIDv2": attrsOf(v2(cmstest.Seq(cmstest.Seq(cmstest.Seq())))),
"an ESSCertIDv2 of only its algorithm": attrsOf(v2(cmstest.Seq(cmstest.Seq(cmstest.Seq(cmstest.HashAlg(crypto.SHA256)))))),
"a certHash as [0]": attrsOf(v2(cmstest.Seq(cmstest.Seq(cmstest.Seq(cmstest.TLV(0x80, h[:])))))),
"a hashAlgorithm without an OID": attrsOf(v2(cmstest.Seq(cmstest.Seq(cmstest.Seq(cmstest.Seq(cmstest.Int(1)), cmstest.Octets(h[:])))))),
"an ESSCertIDv2 of SHA-1": cmstest.Signature(msg, cmstest.Options{ESSHashAlg: cmstest.HashAlg(crypto.SHA1)}, ana),
"an ESSCertIDv2 of SHA-256 with an INTEGER": cmstest.Signature(msg, cmstest.Options{ESSHashAlg: cmstest.AlgID(cmstest.OIDSHA256, cmstest.Int(0))}, ana),
"the hash of another certificate": cmstest.Signature(msg, cmstest.Options{ESSCert: luis.Cert.Raw}, ana),
"two signature-time-stamp attributes": cmstest.Signature(msg, cmstest.Options{Token: tok, TimeStamps2: true}, ana),
"a signature-time-stamp of two values": cmstest.Signature(msg, cmstest.Options{Token: tok, Token2: true}, ana),
"unsigned attributes out of order": cmstest.Edit(sealed, func(b []byte) []byte {
k := append(slices.Clone(cmstest.Children(b)), cmstest.Attr(asn1.ObjectIdentifier{1, 2, 3, 4}, cmstest.Null()))
slices.SortFunc(k, func(x, y []byte) int { return bytes.Compare(y, x) })
return cmstest.TLV(0xa1, k...)
}, path(firstSignerInfo(sealed), 6)...),
"an unsigned attribute without a value": cmstest.Signature(msg, cmstest.Options{ExtraUnsigned: [][]byte{cmstest.Seq(cmstest.OID(unknown), cmstest.Set(0x31))}}, ana),
} {
wantForm(t, name, b)
}
}
// v2Values is a signature whose signing-certificate-v2 has two values, the
// first of them in DER order the right one: a longer certHash sorts after.
func v2Values(h []byte) []byte {
return cmstest.Signature(msg, cmstest.Options{Mutate: func(a [][]byte) [][]byte {
a[2] = cmstest.Seq(cmstest.OID(cmstest.OIDSigCertV2), cmstest.Set(0x31, cmstest.Seq(cmstest.Seq(cmstest.Seq(cmstest.Octets(h)))), cmstest.Seq(cmstest.Seq(cmstest.Seq(cmstest.Octets(make([]byte, 33)))))))
return a
}}, ana)
}
// What decides nothing (spec §29.10): another choice of CertificateChoices, a
// certificate twice or one that breaks the profile, OCSP responses, the other
// attributes, signed or not, a signing-certificate beside the v2, and an
// ESSCertIDv2 with its hashAlgorithm written.
func TestSignatureDecidesNothing(t *testing.T) {
tok := func(sig []byte) []byte { return cmstest.Token(sig, now, cmstest.TokenOptions{}, tsa) }
v1 := cmstest.NewCert(cmstest.CertSpec{CN: "Intermedia v1", NoVersion: true}, ecKey2)
for name, o := range map[string]cmstest.Options{
"an attribute certificate [1]": {ExtraCerts: [][]byte{cmstest.TLV(0xa1, cmstest.Seq(cmstest.Int(1)))}},
"the other choices [0], [2] and [3]": {ExtraCerts: [][]byte{cmstest.TLV(0xa0, cmstest.Null()), cmstest.TLV(0xa2, cmstest.Null()), cmstest.TLV(0xa3, cmstest.Null())}},
"the certificate twice": {ExtraCerts: [][]byte{ana.Cert.Raw}},
"a certificate of version 1": {ExtraCerts: [][]byte{v1.Cert.Raw}},
"a certificate that is not one": {ExtraCerts: [][]byte{cmstest.Seq(cmstest.Int(1))}},
"two OCSP responses": {OCSP: cmstest.Seq(cmstest.Int(0)), CRLs: [][]byte{cmstest.TLV(0xa1, cmstest.OID(cmstest.OIDOCSP), cmstest.Seq(cmstest.Int(9)))}},
"an attribute with an arc of 2^31": {ExtraAttrs: [][]byte{cmstest.BigArcAttr()}},
"a signing-time": {ExtraAttrs: [][]byte{cmstest.Attr(cmstest.OIDSigningTime, cmstest.UTCTime("260930120000Z"))}},
"an unknown attribute of two values": {ExtraAttrs: [][]byte{cmstest.Attr(asn1.ObjectIdentifier{1, 2, 3, 4}, cmstest.Int(1), cmstest.Int(2))}},
"a signing-certificate beside the v2": {SigCertV1: true},
"a wrong signing-certificate, and v2": {ExtraAttrs: [][]byte{cmstest.Attr(cmstest.OIDSigCertV1, cmstest.Seq(cmstest.Seq(cmstest.Seq(cmstest.Octets(make([]byte, 20))))))}},
"an ESSCertIDv2 of SHA-256 written": {ESSHashAlg: cmstest.HashAlg(crypto.SHA256)},
"an ESSCertIDv2 of SHA-256 with NULL": {ESSHashAlg: cmstest.AlgID(cmstest.OIDSHA256, cmstest.Null())},
"an ESSCertIDv2 of SHA-384": {ESSHashAlg: cmstest.HashAlg(crypto.SHA384)},
"an ESSCertIDv2 of SHA-512": {ESSHashAlg: cmstest.HashAlg(crypto.SHA512)},
"an unknown unsigned attribute": {Token: tok, ExtraUnsigned: [][]byte{cmstest.BigArcAttr()}},
"unsigned attributes, no time-stamp": {ExtraUnsigned: [][]byte{cmstest.BigArcAttr()}},
"a sid by subjectKeyIdentifier": {SKI: true},
"the version written 1, as by default": {Version: 1},
} {
sd, err := cms.ParseSignature(cmstest.Signature(msg, o, ana))
if err != nil || len(sd.Signers) != 1 || sd.Signers[0].Cert.Hash != sha256.Sum256(ana.Cert.Raw) || sd.Signers[0].Check(msg) != cms.Valid {
t.Errorf("%s: %v", name, err)
continue
}
if o.Token != nil && sd.Signers[0].Token == nil {
t.Errorf("%s: the token is lost", name)
}
}
// The certificates of the profile, each once, and the OCSP responses.
sd, err := cms.ParseSignature(cmstest.Signature(msg, cmstest.Options{OCSP: cmstest.Seq(cmstest.Int(0)), ExtraCerts: [][]byte{ana.Cert.Raw, v1.Cert.Raw}}, ana, luis))
if err != nil || len(sd.Certs) != 2 || len(sd.OCSP) != 1 || !bytes.Equal(sd.OCSP[0], cmstest.Seq(cmstest.Int(0))) || sd.EContent != nil {
t.Errorf("certificates and OCSP: %v %+v", err, sd)
}
}
// A co-signature finds each certificate by the bytes of its issuer and its
// serial, both, or by its keyIdentifier: two certificates that share one of
// them are two signers, not an ambiguity.
func TestCoSignatureIdentifiers(t *testing.T) {
issuer := cmstest.Name(cmstest.ATV(cmstest.OIDCommonName, cmstest.UTF8("CA de prueba")))
for name, pair := range map[string][2]cmstest.CertSpec{
"one issuer, two serials": {{CN: "A", Issuer: issuer, Serial: cmstest.Int(1)}, {CN: "B", Issuer: issuer, Serial: cmstest.Int(2)}},
"one serial, two issuers": {{CN: "A", Serial: cmstest.Int(5)}, {CN: "B", Serial: cmstest.Int(5)}},
"one serial, two issuers by SKI": {{CN: "A", Serial: cmstest.Int(5)}, {CN: "B", Serial: cmstest.Int(5)}},
} {
a, b := cmstest.NewCert(pair[0], ecKey), cmstest.NewCert(pair[1], ecKey2)
sd, err := cms.ParseSignature(cmstest.Signature(msg, cmstest.Options{SKI: name == "one serial, two issuers by SKI"}, a, b))
if err != nil || len(sd.Signers) != 2 || sd.Signers[0].Cert == sd.Signers[1].Cert {
t.Errorf("%s: %v", name, err)
continue
}
for _, s := range sd.Signers {
if s.Check(msg) != cms.Valid {
t.Errorf("%s: %s does not verify", name, s.Cert.Holder())
}
}
}
}
// The tokens have the form of §29.11, step 1: one SignerInfo, the TSTInfo in
// its eContent, its content-type, its message-digest and the certificate of
// the authority named by signing-certificate or signing-certificate-v2.
func TestTokenFormRules(t *testing.T) {
subject := []byte("seal subject")
good := cmstest.Token(subject, now, cmstest.TokenOptions{}, tsa)
info := cmstest.TSTInfo(subject, now, cmstest.TokenOptions{})
encap := path(cmstest.SignedDataPath, 2)
other := cmstest.NewCert(cmstest.CertSpec{CN: "Otra TSA"}, ecKey)
withAttrs := func(mutate func(a [][]byte) [][]byte) []byte {
return cmstest.Token(subject, now, cmstest.TokenOptions{CMS: cmstest.Options{Mutate: mutate}}, tsa)
}
ess1 := func(c []byte) []byte {
return cmstest.Attr(cmstest.OIDSigCertV1, cmstest.Seq(cmstest.Seq(cmstest.Seq(cmstest.Octets(sha1Sum(c))))))
}
ess2 := func(c []byte) []byte {
h := sha256.Sum256(c)
return cmstest.Attr(cmstest.OIDSigCertV2, cmstest.Seq(cmstest.Seq(cmstest.Seq(cmstest.Octets(h[:])))))
}
twoCRLs := cmstest.Token(subject, now, cmstest.TokenOptions{CMS: cmstest.Options{CRLs: [][]byte{cmstest.Seq(cmstest.Int(1)), cmstest.Seq(cmstest.Int(2))}}}, tsa)
for name, b := range map[string][]byte{
"two SignerInfo": cmstest.Merge(cmstest.TokenRaw(info, tsa), cmstest.TokenRaw(info, other)),
"BER": cmstest.Token(subject, now, cmstest.TokenOptions{CMS: cmstest.Options{BER: true}}, tsa),
"id-data": cmstest.Edit(good, cmstest.Replace(cmstest.Seq(cmstest.OID(cmstest.OIDData), cmstest.TLV(0xa0, cmstest.Octets(info)))), encap...),
"no eContent": cmstest.Edit(good, cmstest.Replace(cmstest.Seq(cmstest.OID(cmstest.OIDTSTInfo))), encap...),
"an eContent [1]": cmstest.Edit(good, cmstest.Replace(cmstest.Seq(cmstest.OID(cmstest.OIDTSTInfo), cmstest.TLV(0xa1, cmstest.Octets(info)))), encap...),
"an eContent of two OCTET STRINGs": cmstest.Edit(good, cmstest.Replace(cmstest.Seq(cmstest.OID(cmstest.OIDTSTInfo), cmstest.TLV(0xa0, cmstest.Octets(info), cmstest.Octets(info)))), encap...),
"an empty eContent": cmstest.Edit(good, cmstest.Replace(cmstest.Seq(cmstest.OID(cmstest.OIDTSTInfo), cmstest.TLV(0xa0))), encap...),
"an eContent that is a SEQUENCE": cmstest.Edit(good, cmstest.Replace(cmstest.Seq(cmstest.OID(cmstest.OIDTSTInfo), cmstest.TLV(0xa0, cmstest.Seq(info)))), encap...),
"the content-type id-data": withAttrs(func(a [][]byte) [][]byte {
a[0] = cmstest.Attr(cmstest.OIDContentType, cmstest.OID(cmstest.OIDData))
return a
}),
"no message-digest": cmstest.Token(subject, now, cmstest.TokenOptions{NoMessageDigest: true}, tsa),
"no signing certificate": withAttrs(func(a [][]byte) [][]byte { return a[:2] }),
"two signing-certificate attributes": withAttrs(func(a [][]byte) [][]byte { return append(a, a[2]) }),
"a signing-certificate of two values": withAttrs(func(a [][]byte) [][]byte {
// The right value first in DER order, and a longer one after it.
a[2] = cmstest.Seq(cmstest.OID(cmstest.OIDSigCertV1), cmstest.Set(0x31, cmstest.Children(cmstest.Children(a[2])[1])[0], cmstest.Seq(cmstest.Seq(cmstest.Seq(cmstest.Octets(make([]byte, 21)))))))
return a
}),
"two signing-certificate-v2 and a v1": withAttrs(func(a [][]byte) [][]byte { return append(a, ess2(tsa.Cert.Raw), ess2(tsa.Cert.Raw)) }),
"a signing-certificate of another TSA": withAttrs(func(a [][]byte) [][]byte { a[2] = ess1(other.Cert.Raw); return a }),
"a signing-certificate-v2 of another": cmstest.Token(subject, now, cmstest.TokenOptions{SigCertV2: true, CMS: cmstest.Options{ESSCert: other.Cert.Raw}}, tsa),
"no certificate of the authority": cmstest.Token(subject, now, cmstest.TokenOptions{CMS: cmstest.Options{OmitCert: true}}, tsa),
"crls out of order": cmstest.Edit(twoCRLs, func(b []byte) []byte {
k := slices.Clone(cmstest.Children(b))
slices.Reverse(k)
return cmstest.TLV(0xa1, k...)
}, path(cmstest.SignedDataPath, 4)...),
} {
if _, err := cms.ParseToken(b); err == nil || !isForm(err) {
t.Errorf("%s: %v, want a form error", name, err)
}
}
// What decides nothing in a token: crls, its certificate twice, and the
// signing-certificate-v2 in the place of signing-certificate.
for name, o := range map[string]cmstest.TokenOptions{
"a CRL": {CRL: cmstest.Seq(cmstest.Int(1))},
"two CRLs": {CMS: cmstest.Options{CRLs: [][]byte{cmstest.Seq(cmstest.Int(1)), cmstest.Seq(cmstest.Int(2))}}},
"the certificate twice": {TSATwice: true},
"signing-certificate-v2": {SigCertV2: true},
"v2 beside a wrong v1": {SigCertV2: true, CMS: cmstest.Options{ExtraAttrs: [][]byte{ess1(other.Cert.Raw)}}},
"a sid by keyIdentifier": {CMS: cmstest.Options{SKI: true}},
"a signature of SHA-512": {CMS: cmstest.Options{Hash: crypto.SHA512}},
"an imprint of SHA-512": {Hash: crypto.SHA512},
"an accuracy of 1.5 s": {Accuracy: 1500 * time.Millisecond},
"an accuracy of 2 s and 3µs": {Accuracy: 2*time.Second + 3*time.Microsecond},
} {
tok, err := cms.ParseToken(cmstest.Token(subject, now, o, tsa))
if err != nil || !tok.Check(subject) || tok.Accuracy != o.Accuracy || tok.TSA.Hash != sha256.Sum256(tsa.Cert.Raw) {
t.Errorf("%s: %v", name, err)
}
}
}

Powered by TurnKey Linux.