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.
349 lines
26 KiB
349 lines
26 KiB
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)
|
|
}
|
|
}
|
|
}
|