go_S-MIME/cms/protocol/authenvdata.go

149 lines
3.3 KiB
Go

package protocol
import (
"crypto/tls"
"encoding/asn1"
"log"
asn "git.ma-al.com/goc_marek/go_S-MIME/asn1"
oid "git.ma-al.com/goc_marek/go_S-MIME/oid"
)
// AuthEnvelopedData ::= SEQUENCE {
// version CMSVersion,
// originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
// recipientInfos RecipientInfos,
// authEncryptedContentInfo EncryptedContentInfo,
//
// / authAttrs [1] IMPLICIT AuthAttributes OPTIONAL,
//
// mac MessageAuthenticationCode,
// unauthAttrs [2] IMPLICIT UnauthAttributes OPTIONAL }
//
// https://tools.ietf.org/html/rfc5083##section-2.1
type AuthEnvelopedData struct {
Version int
OriginatorInfo asn1.RawValue `asn1:"optional,tag:0"`
RecipientInfos []RecipientInfo `asn1:"set,choice"`
AECI EncryptedContentInfo
AauthAttrs []Attribute `asn1:"set,optional,tag:1"`
MAC []byte
UnAauthAttrs []Attribute `asn1:"set,optional,tag:2"`
}
// Decrypt decrypts AuthEnvelopedData and returns the plaintext.
func (ed *AuthEnvelopedData) Decrypt(keyPair []tls.Certificate) (plain []byte, err error) {
// Find the right key
var key []byte
for i := range keyPair {
key, err = ed.decryptKey(keyPair[i])
switch err {
case ErrNoKeyFound:
continue
case nil:
break
default:
return
}
}
encAlg := &oid.EncryptionAlgorithm{
Key: key,
ContentEncryptionAlgorithmIdentifier: ed.AECI.ContentEncryptionAlgorithm,
}
encAlg.MAC = ed.MAC
plain, err = encAlg.Decrypt(ed.AECI.EContent)
return
}
func (ed *AuthEnvelopedData) decryptKey(keyPair tls.Certificate) (key []byte, err error) {
for i := range ed.RecipientInfos {
key, err = ed.RecipientInfos[i].decryptKey(keyPair)
if key != nil {
return
}
}
return nil, ErrNoKeyFound
}
// NewAuthEnvelopedData creates AuthEnvelopedData from an EncryptedContentInfo with mac and given RecipientInfos.
func NewAuthEnvelopedData(eci *EncryptedContentInfo, reciInfos []RecipientInfo, mac []byte) AuthEnvelopedData {
version := 0
ed := AuthEnvelopedData{
Version: version,
RecipientInfos: reciInfos,
AECI: *eci,
MAC: mac,
}
return ed
}
func authcontentInfo(ed AuthEnvelopedData) (ci ContentInfo, err error) {
der, err := asn.Marshal(ed)
if err != nil {
return
}
ci = ContentInfo{
ContentType: oid.AuthEnvelopedData,
Content: asn1.RawValue{
Class: asn1.ClassContextSpecific,
Tag: 0,
Bytes: der,
IsCompound: true,
},
}
return
}
// ContentInfo marshals AuthEnvelopedData and returns ContentInfo.
func (ed AuthEnvelopedData) ContentInfo() (ContentInfo, error) {
nilCI := *new(ContentInfo)
der, err := asn.Marshal(ed)
if err != nil {
log.Fatal(err)
}
if err != nil {
return nilCI, err
}
return ContentInfo{
ContentType: oid.AuthEnvelopedData,
Content: asn1.RawValue{
Class: asn1.ClassContextSpecific,
Tag: 0,
Bytes: der,
IsCompound: true,
},
}, nil
}
// AuthEnvelopedDataContent unmarshals ContentInfo and returns AuthEnvelopedData if
// content type is AuthEnvelopedData.
func (ci ContentInfo) AuthEnvelopedDataContent() (*AuthEnvelopedData, error) {
if !ci.ContentType.Equal(oid.AuthEnvelopedData) {
return nil, ErrWrongType
}
ed := new(AuthEnvelopedData)
if rest, err := asn.Unmarshal(ci.Content.Bytes, ed); err != nil {
return nil, err
} else if len(rest) > 0 {
return nil, ErrTrailingData
}
return ed, nil
}