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.
471 lines
14 KiB
471 lines
14 KiB
// Package cms reads the CMS signatures (RFC 5652) and the RFC 3161 time-stamp
|
|
// tokens that spec v0.11 §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).
|
|
package cms
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha1"
|
|
"crypto/sha256"
|
|
"crypto/sha512"
|
|
"encoding/asn1"
|
|
"errors"
|
|
"fmt"
|
|
"hash"
|
|
"math/big"
|
|
|
|
"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...))
|
|
}
|
|
|
|
// The object identifiers of the profile.
|
|
var (
|
|
oidData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 1}
|
|
oidSignedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 2}
|
|
oidContentType = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 3}
|
|
oidMessageDig = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 4}
|
|
oidSigCertV1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 2, 12}
|
|
oidSigCertV2 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 2, 47}
|
|
oidSigTimeStamp = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 2, 14}
|
|
oidTSTInfo = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 1, 4}
|
|
oidRIOCSP = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 16, 2}
|
|
|
|
oidSHA1 = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26}
|
|
oidSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1}
|
|
oidSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2}
|
|
oidSHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3}
|
|
)
|
|
|
|
// SignedData is the part of a CMS SignedData that the profile uses.
|
|
type SignedData struct {
|
|
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 asn1.ObjectIdentifier
|
|
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 || kids[0][0] != 0x06 {
|
|
return algID{}, formErr("an AlgorithmIdentifier")
|
|
}
|
|
var a algID
|
|
if _, err := asn1.Unmarshal(kids[0], &a.OID); err != nil {
|
|
return algID{}, formErr("an AlgorithmIdentifier: %v", err)
|
|
}
|
|
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 a.OID.Equal(oidSHA256):
|
|
return sha256.New, true
|
|
case a.OID.Equal(oidSHA384):
|
|
return sha512.New384, true
|
|
case a.OID.Equal(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)
|
|
}
|
|
_, ci, err := der.Split(b)
|
|
if err != nil || len(ci) != 2 || ci[0][0] != 0x06 || ci[1][0] != 0xa0 {
|
|
return nil, formErr("a ContentInfo")
|
|
}
|
|
var oid asn1.ObjectIdentifier
|
|
if _, err := asn1.Unmarshal(ci[0], &oid); err != nil || !oid.Equal(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], 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 || kids[0][0] != 0x06 {
|
|
return formErr("encapContentInfo")
|
|
}
|
|
var oid asn1.ObjectIdentifier
|
|
if _, err := asn1.Unmarshal(kids[0], &oid); err != nil {
|
|
return formErr("encapContentInfo")
|
|
}
|
|
if !token {
|
|
if !oid.Equal(oidData) || len(kids) != 1 {
|
|
return formErr("a signature is detached: id-data and no eContent")
|
|
}
|
|
return nil
|
|
}
|
|
if !oid.Equal(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
|
|
}
|
|
|
|
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 _, k := range kids {
|
|
if k[0] != 0x30 { // another choice of CertificateChoices: it decides nothing
|
|
continue
|
|
}
|
|
c, err := ParseCert(k)
|
|
if err != nil {
|
|
return formErr("%v", err)
|
|
}
|
|
out.Certs = append(out.Certs, c)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func parseCRLs(b []byte, 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")
|
|
}
|
|
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 || f[0][0] != 0x06 {
|
|
return formErr("an OtherRevocationInfoFormat")
|
|
}
|
|
var oid asn1.ObjectIdentifier
|
|
if _, err := asn1.Unmarshal(f[0], &oid); err != nil || !oid.Equal(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) {
|
|
_, f, err := der.Split(b)
|
|
if err != nil || 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")
|
|
}
|
|
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.
|
|
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")
|
|
}
|
|
var serial *big.Int
|
|
if _, err := asn1.Unmarshal(p[1], &serial); err != nil {
|
|
return nil, formErr("issuerAndSerialNumber")
|
|
}
|
|
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, not one", n)
|
|
}
|
|
return found, nil
|
|
}
|
|
|
|
// attrs returns the values of each attribute type of the SET OF Attribute b.
|
|
func attrs(b []byte) (map[string][][]byte, error) {
|
|
_, kids, err := der.Split(b)
|
|
if err != nil || !der.SetOfSorted(kids) {
|
|
return nil, formErr("attributes are not a SET OF in DER order")
|
|
}
|
|
out := map[string][][]byte{}
|
|
for _, a := range kids {
|
|
_, p, err := der.Split(a)
|
|
if err != nil || len(p) != 2 || a[0] != 0x30 || p[0][0] != 0x06 || p[1][0] != 0x31 {
|
|
return nil, formErr("an Attribute")
|
|
}
|
|
var oid asn1.ObjectIdentifier
|
|
if _, err := asn1.Unmarshal(p[0], &oid); err != nil {
|
|
return nil, formErr("an Attribute")
|
|
}
|
|
_, vals, err := der.Split(p[1])
|
|
if err != nil || !der.SetOfSorted(vals) {
|
|
return nil, formErr("the values of an attribute are not a SET OF in DER order")
|
|
}
|
|
out[oid.String()] = append(out[oid.String()], vals...)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// one returns the only value of the attribute, which must exist once.
|
|
func one(m map[string][][]byte, oid asn1.ObjectIdentifier, name string) ([]byte, error) {
|
|
v := m[oid.String()]
|
|
if len(v) != 1 {
|
|
return nil, formErr("%s: %d values, not one", name, 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: attrs merges the
|
|
// values of attributes of one type, so two attributes show as two values.
|
|
ct, err := one(m, oidContentType, "content-type")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
want := oidData
|
|
if token {
|
|
want = oidTSTInfo
|
|
}
|
|
var got asn1.ObjectIdentifier
|
|
if _, err := asn1.Unmarshal(ct, &got); err != nil || !got.Equal(want) {
|
|
return formErr("content-type is not %v", want)
|
|
}
|
|
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")
|
|
}
|
|
switch {
|
|
case len(m[oidSigCertV2.String()]) == 1 && len(m[oidSigCertV1.String()]) == 0:
|
|
return checkESSCert(s.Cert, m[oidSigCertV2.String()][0], true)
|
|
case token && len(m[oidSigCertV1.String()]) == 1 && len(m[oidSigCertV2.String()]) == 0:
|
|
return checkESSCert(s.Cert, m[oidSigCertV1.String()][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 := m[oidSigTimeStamp.String()]; len(v) {
|
|
case 0:
|
|
case 1:
|
|
s.Token = v[0]
|
|
default:
|
|
return formErr("signature-time-stamp: %d values, not one", len(v))
|
|
}
|
|
return nil
|
|
}
|