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