go_S-MIME/cms/protocol/signerinfo.go

160 lines
4.8 KiB
Go

package protocol
import (
"bytes"
"crypto"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"fmt"
"time"
asn "git.ma-al.com/goc_marek/go_S-MIME/asn1"
oid "git.ma-al.com/goc_marek/go_S-MIME/oid"
)
// SignerInfo ::= SEQUENCE {
// version CMSVersion,
// sid SignerIdentifier,
// digestAlgorithm DigestAlgorithmIdentifier,
// signedAttrs [0] IMPLICIT SignedAttributes OPTIONAL,
// signatureAlgorithm SignatureAlgorithmIdentifier,
// signature SignatureValue,
// unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL }
type SignerInfo struct {
Version int `` // CMSVersion ::= INTEGER { v0(0), v1(1), v2(2), v3(3), v4(4), v5(5) }
SID SignerIdentifier `asn1:"choice"` //
DigestAlgorithm pkix.AlgorithmIdentifier `` // DigestAlgorithmIdentifier ::= AlgorithmIdentifier
SignedAttrs []Attribute `asn1:"set,optional,tag:0"` // SignedAttributes ::= SET SIZE (1..MAX) OF Attribute
SignatureAlgorithm pkix.AlgorithmIdentifier `` // SignatureAlgorithmIdentifier ::= AlgorithmIdentifier
Signature []byte `` // SignatureValue ::= OCTET STRING
UnsignedAttrs []Attribute `asn1:"set,optional,tag:1"` // UnsignedAttributes ::= SET SIZE (1..MAX) OF Attribute
}
// SignerIdentifier ::= CHOICE {
// issuerAndSerialNumber IssuerAndSerialNumber,
// subjectKeyIdentifier [0] SubjectKeyIdentifier }
type SignerIdentifier struct {
IAS IssuerAndSerialNumber `asn1:"optional"`
SKI []byte `asn1:"optional,tag:0"`
}
// FindCertificate finds this SignerInfo's certificate in a slice of
// certificates.
func (si SignerInfo) FindCertificate(certs []*x509.Certificate) (*x509.Certificate, error) {
switch si.Version {
case 1: // SID is issuer and serial number
isn := si.SID.IAS
for _, cert := range certs {
if bytes.Equal(cert.RawIssuer, isn.Issuer.FullBytes) && isn.SerialNumber.Cmp(cert.SerialNumber) == 0 {
return cert, nil
}
}
case 3: // SID is SubjectKeyIdentifier
ski := si.SID.SKI
for _, cert := range certs {
for _, ext := range cert.Extensions {
if oid.SubjectKeyIdentifier.Equal(ext.Id) {
if bytes.Equal(ski, ext.Value) {
return cert, nil
}
}
}
}
default:
return nil, ErrUnsupported
}
return nil, ErrNoCertificate
}
// Hash gets the crypto.Hash associated with this SignerInfo's DigestAlgorithm.
// 0 is returned for unrecognized algorithms.
func (si SignerInfo) Hash() (crypto.Hash, error) {
algo := si.DigestAlgorithm.Algorithm.String()
hash := oid.DigestAlgorithmToHash[algo]
if hash == 0 || !hash.Available() {
return 0, ErrUnsupported
}
return hash, nil
}
// X509SignatureAlgorithm gets the x509.SignatureAlgorithm that should be used
// for verifying this SignerInfo's signature.
func (si SignerInfo) X509SignatureAlgorithm() (sigAlg x509.SignatureAlgorithm, err error) {
var (
sigOID = si.SignatureAlgorithm.Algorithm.String()
digestOID = si.DigestAlgorithm.Algorithm.String()
)
sigAlg, ok := oid.SignatureAlgorithms[sigOID][digestOID]
if !ok {
err = fmt.Errorf("Signature algorithm with OID %s in combination with digest with OID %s not supported", sigOID, digestOID)
}
return
}
// GetContentTypeAttribute gets the signed ContentType attribute from the
// SignerInfo.
func (si SignerInfo) GetContentTypeAttribute() (asn1.ObjectIdentifier, error) {
var sa Attributes
sa = si.SignedAttrs
rv, err := sa.GetOnlyAttributeValueBytes(oid.AttributeContentType)
if err != nil {
return nil, err
}
var ct asn1.ObjectIdentifier
if rest, err := asn.Unmarshal(rv.FullBytes, &ct); err != nil {
return nil, err
} else if len(rest) > 0 {
return nil, ErrTrailingData
}
return ct, nil
}
// GetMessageDigestAttribute gets the signed MessageDigest attribute from the
// SignerInfo.
func (si SignerInfo) GetMessageDigestAttribute() ([]byte, error) {
var sa Attributes
sa = si.SignedAttrs
rv, err := sa.GetOnlyAttributeValueBytes(oid.AttributeMessageDigest)
if err != nil {
return nil, err
}
if rv.Class != asn1.ClassUniversal || rv.Tag != asn1.TagOctetString {
return nil, ASN1Error{"bad class or tag"}
}
return rv.Bytes, nil
}
// GetSigningTimeAttribute gets the signed SigningTime attribute from the
// SignerInfo.
func (si SignerInfo) GetSigningTimeAttribute() (time.Time, error) {
var t time.Time
var sa Attributes
sa = si.SignedAttrs
rv, err := sa.GetOnlyAttributeValueBytes(oid.AttributeSigningTime)
if err != nil {
return t, err
}
if rv.Class != asn1.ClassUniversal || (rv.Tag != asn1.TagUTCTime && rv.Tag != asn1.TagGeneralizedTime) {
return t, ASN1Error{"bad class or tag"}
}
if rest, err := asn.Unmarshal(rv.FullBytes, &t); err != nil {
return t, err
} else if len(rest) > 0 {
return t, ErrTrailingData
}
return t, nil
}