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.
547 lines
16 KiB
547 lines
16 KiB
// Package cms reads the CMS signatures (RFC 5652) and the RFC 3161 time-stamp
|
|
// tokens that spec §29.10 and §29.11 define, with the CAdES profile that
|
|
// AutoFirma and other signing applications produce, and checks them with a
|
|
// closed table of algorithms. It uses the standard library only.
|
|
//
|
|
// It checks the signature and the dates, never who issued a certificate or
|
|
// whether it was revoked: a validator of the country that corresponds does
|
|
// that (spec §29.10). Object identifiers are compared by the bytes of their
|
|
// DER, so that an arc of any size is only an identifier that the table does
|
|
// not have.
|
|
package cms
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha1"
|
|
"crypto/sha256"
|
|
"crypto/sha512"
|
|
"errors"
|
|
"fmt"
|
|
"hash"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"g.activething.com/go/DateKeys/internal/der"
|
|
)
|
|
|
|
// ErrForm is the error of a signature or a token whose form breaks the
|
|
// profile of spec §29.10 or §29.11: the verdicts F1 and S2.
|
|
var ErrForm = errors.New("cms: the form breaks the profile")
|
|
|
|
// ErrAlgorithm is the error of a token that uses an algorithm outside the
|
|
// table of spec §29.10: the verdict S1.
|
|
var ErrAlgorithm = errors.New("cms: an algorithm outside the table")
|
|
|
|
func formErr(format string, args ...any) error {
|
|
return fmt.Errorf("%w: %s", ErrForm, fmt.Sprintf(format, args...))
|
|
}
|
|
|
|
// oid returns the content of the DER of the object identifier s, written
|
|
// with dots: the bytes that this package compares.
|
|
func oid(s string) []byte {
|
|
parts := strings.Split(s, ".")
|
|
arcs := make([]uint64, len(parts))
|
|
for i, p := range parts {
|
|
n, err := strconv.ParseUint(p, 10, 64)
|
|
if err != nil || len(parts) < 2 {
|
|
panic("cms: a bad object identifier " + s)
|
|
}
|
|
arcs[i] = n
|
|
}
|
|
out := base128(nil, arcs[0]*40+arcs[1])
|
|
for _, a := range arcs[2:] {
|
|
out = base128(out, a)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func base128(out []byte, v uint64) []byte {
|
|
var tmp [10]byte
|
|
i := len(tmp) - 1
|
|
tmp[i] = byte(v & 0x7f)
|
|
for v >>= 7; v > 0; v >>= 7 {
|
|
i--
|
|
tmp[i] = byte(v&0x7f) | 0x80
|
|
}
|
|
return append(out, tmp[i:]...)
|
|
}
|
|
|
|
// oidOf returns the content of the object identifier element b.
|
|
func oidOf(b []byte) ([]byte, bool) {
|
|
if len(b) == 0 || b[0] != 0x06 {
|
|
return nil, false
|
|
}
|
|
c, err := der.Content(b)
|
|
return c, err == nil
|
|
}
|
|
|
|
// The object identifiers of the profile.
|
|
var (
|
|
oidData = oid("1.2.840.113549.1.7.1")
|
|
oidSignedData = oid("1.2.840.113549.1.7.2")
|
|
oidContentType = oid("1.2.840.113549.1.9.3")
|
|
oidMessageDig = oid("1.2.840.113549.1.9.4")
|
|
oidSigCertV1 = oid("1.2.840.113549.1.9.16.2.12")
|
|
oidSigCertV2 = oid("1.2.840.113549.1.9.16.2.47")
|
|
oidSigTimeStamp = oid("1.2.840.113549.1.9.16.2.14")
|
|
oidTSTInfo = oid("1.2.840.113549.1.9.16.1.4")
|
|
oidRIOCSP = oid("1.3.6.1.5.5.7.16.2")
|
|
|
|
oidSHA256 = oid("2.16.840.1.101.3.4.2.1")
|
|
oidSHA384 = oid("2.16.840.1.101.3.4.2.2")
|
|
oidSHA512 = oid("2.16.840.1.101.3.4.2.3")
|
|
)
|
|
|
|
// SignedData is the part of a CMS SignedData that the profile uses.
|
|
type SignedData struct {
|
|
// Certs are the certificates that meet the profile of §29.10, each once:
|
|
// another one decides nothing, unless a SignerInfo names it.
|
|
Certs []*Cert
|
|
// OCSP are the OCSP responses of crls.
|
|
OCSP [][]byte
|
|
// Signers are the SignerInfo, in the order of the encoding.
|
|
Signers []*SignerInfo
|
|
// EContent is the content of a token, nil in a detached signature.
|
|
EContent []byte
|
|
}
|
|
|
|
// SignerInfo is a SignerInfo with the certificate that its sid names.
|
|
type SignerInfo struct {
|
|
Cert *Cert
|
|
// DigestAlg and SigAlg are the algorithm identifiers as written.
|
|
DigestAlg, SigAlg algID
|
|
// SignedAttrs is the DER of the signedAttrs as stored, with the
|
|
// context tag [0]; the signature covers it with the tag of a SET.
|
|
SignedAttrs []byte
|
|
MessageDigest []byte
|
|
Signature []byte
|
|
// Token is the signature-time-stamp attribute, the DER of its
|
|
// ContentInfo, nil when there is none.
|
|
Token []byte
|
|
}
|
|
|
|
type algID struct {
|
|
OID []byte // the content of the object identifier
|
|
Params []byte // the DER of the parameters, nil when absent
|
|
}
|
|
|
|
func parseAlgID(b []byte) (algID, error) {
|
|
id, kids, err := der.Split(b)
|
|
if err != nil || id != 0x30 || len(kids) < 1 || len(kids) > 2 {
|
|
return algID{}, formErr("an AlgorithmIdentifier")
|
|
}
|
|
var a algID
|
|
var ok bool
|
|
if a.OID, ok = oidOf(kids[0]); !ok {
|
|
return algID{}, formErr("an AlgorithmIdentifier without an object identifier")
|
|
}
|
|
if len(kids) == 2 {
|
|
a.Params = kids[1]
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
// hashOf returns the hash that an identifier of the table names, and false
|
|
// for any other.
|
|
func (a algID) hashOf() (func() hash.Hash, bool) {
|
|
if a.Params != nil && !bytes.Equal(a.Params, []byte{5, 0}) {
|
|
return nil, false
|
|
}
|
|
switch {
|
|
case bytes.Equal(a.OID, oidSHA256):
|
|
return sha256.New, true
|
|
case bytes.Equal(a.OID, oidSHA384):
|
|
return sha512.New384, true
|
|
case bytes.Equal(a.OID, oidSHA512):
|
|
return sha512.New, true
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
// ParseSignature reads the detached CMS signature of an author-signature of
|
|
// alg 2 (spec §29.10), checking its form in the order of the spec.
|
|
func ParseSignature(b []byte) (*SignedData, error) {
|
|
return parse(b, false)
|
|
}
|
|
|
|
func parse(b []byte, token bool) (*SignedData, error) {
|
|
if err := der.Check(b); err != nil {
|
|
return nil, formErr("%v", err)
|
|
}
|
|
id, ci, err := der.Split(b)
|
|
if err != nil || id != 0x30 || len(ci) != 2 || ci[1][0] != 0xa0 {
|
|
return nil, formErr("a ContentInfo")
|
|
}
|
|
if ct, ok := oidOf(ci[0]); !ok || !bytes.Equal(ct, oidSignedData) {
|
|
return nil, formErr("the content type is not id-signedData")
|
|
}
|
|
_, inner, err := der.Split(ci[1])
|
|
if err != nil || len(inner) != 1 || inner[0][0] != 0x30 {
|
|
return nil, formErr("a SignedData")
|
|
}
|
|
_, sd, err := der.Split(inner[0])
|
|
if err != nil || len(sd) < 4 || sd[0][0] != 0x02 || sd[1][0] != 0x31 || sd[2][0] != 0x30 {
|
|
return nil, formErr("a SignedData")
|
|
}
|
|
// digestAlgorithms: a SET OF in order.
|
|
_, algs, err := der.Split(sd[1])
|
|
if err != nil || !der.SetOfSorted(algs) {
|
|
return nil, formErr("digestAlgorithms is not a SET OF in DER order")
|
|
}
|
|
for _, a := range algs {
|
|
if _, err := parseAlgID(a); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
out := &SignedData{}
|
|
if err := parseEncap(sd[2], token, out); err != nil {
|
|
return nil, err
|
|
}
|
|
rest := sd[3:]
|
|
if len(rest) > 0 && rest[0][0] == 0xa0 {
|
|
if err := parseCerts(rest[0], out); err != nil {
|
|
return nil, err
|
|
}
|
|
rest = rest[1:]
|
|
}
|
|
if len(rest) > 0 && rest[0][0] == 0xa1 {
|
|
if err := parseCRLs(rest[0], token, out); err != nil {
|
|
return nil, err
|
|
}
|
|
rest = rest[1:]
|
|
}
|
|
if len(rest) != 1 || rest[0][0] != 0x31 {
|
|
return nil, formErr("signerInfos")
|
|
}
|
|
_, infos, err := der.Split(rest[0])
|
|
if err != nil || len(infos) == 0 || !der.SetOfSorted(infos) {
|
|
return nil, formErr("signerInfos is not a SET OF in DER order, or is empty")
|
|
}
|
|
if token && len(infos) != 1 {
|
|
return nil, formErr("a token has one SignerInfo, not %d", len(infos))
|
|
}
|
|
used := map[*Cert]bool{}
|
|
for _, si := range infos {
|
|
s, err := parseSignerInfo(si, out, token)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if used[s.Cert] {
|
|
return nil, formErr("two SignerInfo for one certificate")
|
|
}
|
|
used[s.Cert] = true
|
|
out.Signers = append(out.Signers, s)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// parseEncap checks encapContentInfo: id-data without content in a detached
|
|
// signature, and id-ct-TSTInfo with its content in a token.
|
|
func parseEncap(b []byte, token bool, out *SignedData) error {
|
|
_, kids, err := der.Split(b)
|
|
if err != nil || len(kids) < 1 || len(kids) > 2 {
|
|
return formErr("encapContentInfo")
|
|
}
|
|
ct, ok := oidOf(kids[0])
|
|
if !ok {
|
|
return formErr("encapContentInfo")
|
|
}
|
|
if !token {
|
|
if !bytes.Equal(ct, oidData) || len(kids) != 1 {
|
|
return formErr("a signature is detached: id-data and no eContent")
|
|
}
|
|
return nil
|
|
}
|
|
if !bytes.Equal(ct, oidTSTInfo) || len(kids) != 2 || kids[1][0] != 0xa0 {
|
|
return formErr("a token holds a TSTInfo")
|
|
}
|
|
_, e, err := der.Split(kids[1])
|
|
if err != nil || len(e) != 1 || e[0][0] != 0x04 {
|
|
return formErr("eContent")
|
|
}
|
|
if out.EContent, err = der.Content(e[0]); err != nil {
|
|
return formErr("eContent")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// parseCerts reads certificates. A certificate that breaks the profile of
|
|
// §29.10 decides nothing, as one that no SignerInfo names: an intermediate of
|
|
// another form is not a reason to refuse a signature, and the sid of a signer
|
|
// whose certificate breaks it names none. Two copies of a certificate are one.
|
|
func parseCerts(b []byte, out *SignedData) error {
|
|
_, kids, err := der.Split(b)
|
|
if err != nil || !der.SetOfSorted(kids) {
|
|
return formErr("certificates is not a SET OF in DER order")
|
|
}
|
|
for i, k := range kids {
|
|
if k[0] >= 0xa0 && k[0] <= 0xa3 { // another choice of CertificateChoices: it decides nothing
|
|
continue
|
|
}
|
|
if k[0] != 0x30 {
|
|
return formErr("a CertificateChoice that is neither a certificate nor one of the other four choices")
|
|
}
|
|
if i > 0 && bytes.Equal(kids[i-1], k) {
|
|
continue
|
|
}
|
|
if c, err := ParseCert(k); err == nil {
|
|
out.Certs = append(out.Certs, c)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// parseCRLs reads crls: in a signature, only OCSP responses (spec §29.10 rule
|
|
// 3); in a token, whatever it holds decides nothing (§29.11).
|
|
func parseCRLs(b []byte, token bool, out *SignedData) error {
|
|
_, kids, err := der.Split(b)
|
|
if err != nil || !der.SetOfSorted(kids) {
|
|
return formErr("crls is not a SET OF in DER order")
|
|
}
|
|
if token {
|
|
return nil
|
|
}
|
|
for _, k := range kids {
|
|
if k[0] != 0xa1 {
|
|
return formErr("crls holds only OCSP responses")
|
|
}
|
|
_, f, err := der.Split(k)
|
|
if err != nil || len(f) != 2 {
|
|
return formErr("an OtherRevocationInfoFormat")
|
|
}
|
|
if o, ok := oidOf(f[0]); !ok || !bytes.Equal(o, oidRIOCSP) {
|
|
return formErr("crls holds only OCSP responses")
|
|
}
|
|
out.OCSP = append(out.OCSP, f[1])
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func parseSignerInfo(b []byte, sd *SignedData, token bool) (*SignerInfo, error) {
|
|
id, f, err := der.Split(b)
|
|
if err != nil || id != 0x30 || len(f) < 6 || f[0][0] != 0x02 || f[2][0] != 0x30 || f[3][0] != 0xa0 || f[4][0] != 0x30 || f[5][0] != 0x04 {
|
|
return nil, formErr("a SignerInfo with signedAttrs")
|
|
}
|
|
// RFC 5652 5.3: version 1 with issuerAndSerialNumber, version 3 with
|
|
// subjectKeyIdentifier.
|
|
if v, _ := der.Content(f[0]); !(len(v) == 1 && (v[0] == 1 && f[1][0] == 0x30 || v[0] == 3 && f[1][0] == 0x80)) {
|
|
return nil, formErr("the version of a SignerInfo does not match its sid")
|
|
}
|
|
s := &SignerInfo{SignedAttrs: f[3]}
|
|
if s.Cert, err = findSigner(f[1], sd.Certs); err != nil {
|
|
return nil, err
|
|
}
|
|
if s.DigestAlg, err = parseAlgID(f[2]); err != nil {
|
|
return nil, err
|
|
}
|
|
if s.SigAlg, err = parseAlgID(f[4]); err != nil {
|
|
return nil, err
|
|
}
|
|
if s.Signature, err = der.Content(f[5]); err != nil {
|
|
return nil, formErr("signature")
|
|
}
|
|
if len(f) > 7 || len(f) == 7 && f[6][0] != 0xa1 {
|
|
return nil, formErr("a SignerInfo with something after its signature")
|
|
}
|
|
if err := parseSignedAttrs(s, token); err != nil {
|
|
return nil, err
|
|
}
|
|
if len(f) == 7 {
|
|
if err := parseUnsignedAttrs(s, f[6]); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
// findSigner returns the one certificate that the sid names, comparing the
|
|
// DER of the issuer and the content of the serial number, or the
|
|
// keyIdentifier.
|
|
func findSigner(sid []byte, certs []*Cert) (*Cert, error) {
|
|
var found *Cert
|
|
n := 0
|
|
switch sid[0] {
|
|
case 0x30: // issuerAndSerialNumber
|
|
_, p, err := der.Split(sid)
|
|
if err != nil || len(p) != 2 || p[0][0] != 0x30 || p[1][0] != 0x02 {
|
|
return nil, formErr("issuerAndSerialNumber")
|
|
}
|
|
serial, _ := der.Content(p[1])
|
|
for _, c := range certs {
|
|
if c.hasSID(p[0], serial) {
|
|
found, n = c, n+1
|
|
}
|
|
}
|
|
case 0x80: // subjectKeyIdentifier
|
|
ski, err := der.Content(sid)
|
|
if err != nil {
|
|
return nil, formErr("subjectKeyIdentifier")
|
|
}
|
|
for _, c := range certs {
|
|
if c.SKI != nil && bytes.Equal(c.SKI, ski) {
|
|
found, n = c, n+1
|
|
}
|
|
}
|
|
default:
|
|
return nil, formErr("a SignerIdentifier")
|
|
}
|
|
if n != 1 {
|
|
return nil, formErr("a sid that names %d certificates of the profile, not one", n)
|
|
}
|
|
return found, nil
|
|
}
|
|
|
|
// attrSet holds the attributes of a SET OF Attribute: the values of each type
|
|
// and the number of attributes of each type, which is not the number of
|
|
// values. Both are kept by the bytes of the object identifier.
|
|
type attrSet struct {
|
|
vals map[string][][]byte
|
|
count map[string]int
|
|
}
|
|
|
|
func (a attrSet) get(o []byte) ([][]byte, int) {
|
|
return a.vals[string(o)], a.count[string(o)]
|
|
}
|
|
|
|
// attrs reads the SET OF Attribute b. An attribute needs at least one value
|
|
// (RFC 5652 5.3), so that two attributes of one type never hide behind an empty
|
|
// set of values.
|
|
func attrs(b []byte) (attrSet, error) {
|
|
_, kids, err := der.Split(b)
|
|
if err != nil || !der.SetOfSorted(kids) {
|
|
return attrSet{}, formErr("attributes are not a SET OF in DER order")
|
|
}
|
|
out := attrSet{vals: map[string][][]byte{}, count: map[string]int{}}
|
|
for _, a := range kids {
|
|
_, p, err := der.Split(a)
|
|
if err != nil || len(p) != 2 || a[0] != 0x30 || p[1][0] != 0x31 {
|
|
return attrSet{}, formErr("an Attribute")
|
|
}
|
|
o, ok := oidOf(p[0])
|
|
if !ok {
|
|
return attrSet{}, formErr("an Attribute")
|
|
}
|
|
_, vals, err := der.Split(p[1])
|
|
if err != nil || len(vals) == 0 || !der.SetOfSorted(vals) {
|
|
return attrSet{}, formErr("the values of an attribute are not a non-empty SET OF in DER order")
|
|
}
|
|
out.vals[string(o)] = append(out.vals[string(o)], vals...)
|
|
out.count[string(o)]++
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// one returns the only value of the only attribute of the type.
|
|
func one(m attrSet, o []byte, name string) ([]byte, error) {
|
|
v, n := m.get(o)
|
|
if n != 1 || len(v) != 1 {
|
|
return nil, formErr("%s: %d attributes with %d values, not one with one", name, n, len(v))
|
|
}
|
|
return v[0], nil
|
|
}
|
|
|
|
// parseSignedAttrs checks the signedAttrs of the profile (spec §29.10 rule 4,
|
|
// §29.11): content-type, message-digest and the signing certificate.
|
|
func parseSignedAttrs(s *SignerInfo, token bool) error {
|
|
m, err := attrs(s.SignedAttrs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Each of the three is one attribute with one value.
|
|
ct, err := one(m, oidContentType, "content-type")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
want, name := oidData, "id-data"
|
|
if token {
|
|
want, name = oidTSTInfo, "id-ct-TSTInfo"
|
|
}
|
|
if got, ok := oidOf(ct); !ok || !bytes.Equal(got, want) {
|
|
return formErr("content-type is not %s", name)
|
|
}
|
|
md, err := one(m, oidMessageDig, "message-digest")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if md[0] != 0x04 {
|
|
return formErr("message-digest is not an OCTET STRING")
|
|
}
|
|
if s.MessageDigest, err = der.Content(md); err != nil {
|
|
return formErr("message-digest")
|
|
}
|
|
// signing-certificate-v2 is the one that counts: a signature has it, and a
|
|
// token has it or, failing that, signing-certificate. The other attributes
|
|
// decide nothing, a signing-certificate beside the v2 among them.
|
|
v2, n2 := m.get(oidSigCertV2)
|
|
v1, n1 := m.get(oidSigCertV1)
|
|
switch {
|
|
case n2 == 1 && len(v2) == 1:
|
|
return checkESSCert(s.Cert, v2[0], true)
|
|
case token && n2 == 0 && n1 == 1 && len(v1) == 1:
|
|
return checkESSCert(s.Cert, v1[0], false)
|
|
}
|
|
return formErr("signing-certificate: one attribute of one value is required")
|
|
}
|
|
|
|
// checkESSCert checks that the first ESSCertID of a signing-certificate or
|
|
// signing-certificate-v2 (RFC 2634, RFC 5035) is the hash of the certificate.
|
|
func checkESSCert(c *Cert, v []byte, v2 bool) error {
|
|
id, sc, err := der.Split(v)
|
|
if err != nil || id != 0x30 || len(sc) < 1 || sc[0][0] != 0x30 {
|
|
return formErr("a SigningCertificate")
|
|
}
|
|
_, ids, err := der.Split(sc[0])
|
|
if err != nil || len(ids) < 1 || ids[0][0] != 0x30 {
|
|
return formErr("an ESSCertID")
|
|
}
|
|
_, f, err := der.Split(ids[0])
|
|
if err != nil || len(f) < 1 {
|
|
return formErr("an ESSCertID")
|
|
}
|
|
newHash := sha1.New
|
|
if v2 {
|
|
newHash = sha256.New
|
|
if f[0][0] == 0x30 { // hashAlgorithm, which defaults to SHA-256
|
|
a, err := parseAlgID(f[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
h, ok := a.hashOf()
|
|
if !ok {
|
|
return formErr("the hash of the ESSCertIDv2 is outside the table")
|
|
}
|
|
newHash, f = h, f[1:]
|
|
}
|
|
}
|
|
if len(f) < 1 || f[0][0] != 0x04 {
|
|
return formErr("certHash")
|
|
}
|
|
hv, err := der.Content(f[0])
|
|
if err != nil {
|
|
return formErr("certHash")
|
|
}
|
|
h := newHash()
|
|
h.Write(c.Raw)
|
|
if !bytes.Equal(h.Sum(nil), hv) {
|
|
return formErr("the certHash is not that of the certificate of the signer")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// parseUnsignedAttrs reads the signature-time-stamp, at most one with one
|
|
// value (spec §29.10 rule 4); the other attributes decide nothing.
|
|
func parseUnsignedAttrs(s *SignerInfo, b []byte) error {
|
|
m, err := attrs(b)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch v, n := m.get(oidSigTimeStamp); {
|
|
case n == 0:
|
|
case n == 1 && len(v) == 1:
|
|
s.Token = v[0]
|
|
default:
|
|
return formErr("signature-time-stamp: %d attributes with %d values, not one with one", n, len(v))
|
|
}
|
|
return nil
|
|
}
|