149 lines
3.3 KiB
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
|
|
}
|