methods for creating protocol types
This commit is contained in:
Родитель
0fc8e3cc0d
Коммит
05aed43318
|
@ -10,6 +10,11 @@ type AnySet struct {
|
|||
Elements []asn1.RawValue `asn1:"set"`
|
||||
}
|
||||
|
||||
// NewAnySet creates a new AnySet.
|
||||
func NewAnySet(elts ...asn1.RawValue) AnySet {
|
||||
return AnySet{elts}
|
||||
}
|
||||
|
||||
// DecodeAnySet manually decodes a SET OF ANY type, since Go's parser can't
|
||||
// handle them.
|
||||
func DecodeAnySet(rv asn1.RawValue) (as AnySet, err error) {
|
||||
|
|
|
@ -53,8 +53,8 @@ var (
|
|||
// X509 extensions
|
||||
oidSubjectKeyIdentifier = asn1.ObjectIdentifier{2, 5, 29, 14}
|
||||
|
||||
// digestAlgorithmHash maps digest OIDs to crypto.Hash values.
|
||||
digestAlgorithmHash = map[string]crypto.Hash{
|
||||
// digestAlgorithmToHash maps digest OIDs to crypto.Hash values.
|
||||
digestAlgorithmToHash = map[string]crypto.Hash{
|
||||
oidDigestAlgorithmSHA1.String(): crypto.SHA1,
|
||||
oidDigestAlgorithmMD5.String(): crypto.MD5,
|
||||
oidDigestAlgorithmSHA256.String(): crypto.SHA256,
|
||||
|
@ -62,9 +62,23 @@ var (
|
|||
oidDigestAlgorithmSHA512.String(): crypto.SHA512,
|
||||
}
|
||||
|
||||
// signatureAlgorithmToDigestAlgorithm maps x509.SignatureAlgorithm to
|
||||
// digestAlgorithm OIDs.
|
||||
signatureAlgorithmToDigestAlgorithm = map[x509.SignatureAlgorithm]asn1.ObjectIdentifier{
|
||||
x509.SHA1WithRSA: oidDigestAlgorithmSHA1,
|
||||
x509.MD5WithRSA: oidDigestAlgorithmMD5,
|
||||
x509.SHA256WithRSA: oidDigestAlgorithmSHA256,
|
||||
x509.SHA384WithRSA: oidDigestAlgorithmSHA384,
|
||||
x509.SHA512WithRSA: oidDigestAlgorithmSHA512,
|
||||
x509.ECDSAWithSHA1: oidDigestAlgorithmSHA1,
|
||||
x509.ECDSAWithSHA256: oidDigestAlgorithmSHA256,
|
||||
x509.ECDSAWithSHA384: oidDigestAlgorithmSHA384,
|
||||
x509.ECDSAWithSHA512: oidDigestAlgorithmSHA512,
|
||||
}
|
||||
|
||||
// digestAlgorithmHash maps digest and signature OIDs to
|
||||
// x509.SignatureAlgorithm values.
|
||||
signatureAlgorithmHash = map[string]map[string]x509.SignatureAlgorithm{
|
||||
signatureAlgorithms = map[string]map[string]x509.SignatureAlgorithm{
|
||||
oidSignatureAlgorithmRSA.String(): map[string]x509.SignatureAlgorithm{
|
||||
oidDigestAlgorithmSHA1.String(): x509.SHA1WithRSA,
|
||||
oidDigestAlgorithmMD5.String(): x509.MD5WithRSA,
|
||||
|
@ -119,6 +133,25 @@ type EncapsulatedContentInfo struct {
|
|||
EContent asn1.RawValue `asn1:"optional,explicit,tag:0"`
|
||||
}
|
||||
|
||||
// NewDataEncapsulatedContentInfo creates a new EncapsulatedContentInfo of type
|
||||
// id-data.
|
||||
func NewDataEncapsulatedContentInfo(data []byte) (EncapsulatedContentInfo, error) {
|
||||
octetString, err := asn1.Marshal(data)
|
||||
if err != nil {
|
||||
return EncapsulatedContentInfo{}, err
|
||||
}
|
||||
|
||||
return EncapsulatedContentInfo{
|
||||
EContentType: oidData,
|
||||
EContent: asn1.RawValue{
|
||||
Class: asn1.ClassContextSpecific,
|
||||
Tag: 0,
|
||||
Bytes: octetString,
|
||||
IsCompound: true,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DataEContent gets the EContent assuming EContentType is data. A nil byte
|
||||
// slice is returned if the OPTIONAL eContent field is missing.
|
||||
func (eci EncapsulatedContentInfo) DataEContent() ([]byte, error) {
|
||||
|
@ -152,6 +185,27 @@ type Attribute struct {
|
|||
RawValue asn1.RawValue
|
||||
}
|
||||
|
||||
// NewAttribute creates a single-value Attribute.
|
||||
func NewAttribute(typ asn1.ObjectIdentifier, val interface{}) (attr Attribute, err error) {
|
||||
var der []byte
|
||||
if der, err = asn1.Marshal(val); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var rv asn1.RawValue
|
||||
if _, err = asn1.Unmarshal(der, &rv); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = NewAnySet(rv).Encode(&attr.RawValue); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
attr.Type = typ
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Value further decodes the attribute Value as a SET OF ANY, which Go's asn1
|
||||
// parser can't handle directly.
|
||||
func (a Attribute) Value() (AnySet, error) {
|
||||
|
@ -243,6 +297,29 @@ type IssuerAndSerialNumber struct {
|
|||
SerialNumber *big.Int
|
||||
}
|
||||
|
||||
// NewIssuerAndSerialNumber creates a IssuerAndSerialNumber SID for the given
|
||||
// cert.
|
||||
func NewIssuerAndSerialNumber(cert *x509.Certificate) (rv asn1.RawValue, err error) {
|
||||
sid := IssuerAndSerialNumber{
|
||||
SerialNumber: new(big.Int).Set(cert.SerialNumber),
|
||||
}
|
||||
|
||||
if _, err = asn1.Unmarshal(cert.RawIssuer, &sid.Issuer); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var der []byte
|
||||
if der, err = asn1.Marshal(sid); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = asn1.Unmarshal(der, &rv); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SignerInfo ::= SEQUENCE {
|
||||
// version CMSVersion,
|
||||
// sid SignerIdentifier,
|
||||
|
@ -342,7 +419,7 @@ func (si SignerInfo) subjectKeyIdentifierSID() ([]byte, error) {
|
|||
// Hash gets the crypto.Hash associated with this SignerInfo's DigestAlgorithm.
|
||||
// 0 is returned for unrecognized algorithms.
|
||||
func (si SignerInfo) Hash() crypto.Hash {
|
||||
return digestAlgorithmHash[si.DigestAlgorithm.Algorithm.String()]
|
||||
return digestAlgorithmToHash[si.DigestAlgorithm.Algorithm.String()]
|
||||
}
|
||||
|
||||
// X509SignatureAlgorithm gets the x509.SignatureAlgorithm that should be used
|
||||
|
@ -353,7 +430,7 @@ func (si SignerInfo) X509SignatureAlgorithm() x509.SignatureAlgorithm {
|
|||
digestOID = si.DigestAlgorithm.Algorithm.String()
|
||||
)
|
||||
|
||||
return signatureAlgorithmHash[sigOID][digestOID]
|
||||
return signatureAlgorithms[sigOID][digestOID]
|
||||
}
|
||||
|
||||
// GetContentTypeAttribute gets the signed ContentType attribute from the
|
||||
|
|
|
@ -11,27 +11,170 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
func TestEncapsulatedContentInfo(t *testing.T) {
|
||||
ci, _ := ParseContentInfo(fixtureSignatureOpenSSLAttached)
|
||||
sd, _ := ci.SignedDataContent()
|
||||
oldECI := sd.EncapContentInfo
|
||||
|
||||
oldData, err := oldECI.DataEContent()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
newECI, err := NewDataEncapsulatedContentInfo(oldData)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
newData, err := newECI.DataEContent()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(oldData, newData) {
|
||||
t.Fatal("ECI data round trip mismatch: ", oldData, " != ", newData)
|
||||
}
|
||||
|
||||
oldDER, err := asn1.Marshal(oldECI)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
newDER, err := asn1.Marshal(newECI)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(oldDER, newDER) {
|
||||
t.Fatal("ECI round trip mismatch: ", oldDER, " != ", newDER)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessageDigestAttribute(t *testing.T) {
|
||||
ci, _ := ParseContentInfo(fixtureSignatureOpenSSLAttached)
|
||||
sd, _ := ci.SignedDataContent()
|
||||
si := sd.SignerInfos[0]
|
||||
|
||||
oldAttrVal, err := si.GetMessageDigestAttribute()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var oldAttr Attribute
|
||||
for _, attr := range si.SignedAttrs {
|
||||
if attr.Type.Equal(oidAttributeMessageDigest) {
|
||||
oldAttr = attr
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
newAttr, err := NewAttribute(oidAttributeMessageDigest, oldAttrVal)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(oldAttr.RawValue.Bytes, newAttr.RawValue.Bytes) {
|
||||
t.Fatal("raw value mismatch")
|
||||
}
|
||||
|
||||
oldDER, err := asn1.Marshal(oldAttr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
newDER, err := asn1.Marshal(newAttr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(oldDER, newDER) {
|
||||
t.Fatal("der mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestContentTypeAttribute(t *testing.T) {
|
||||
ci, _ := ParseContentInfo(fixtureSignatureOpenSSLAttached)
|
||||
sd, _ := ci.SignedDataContent()
|
||||
si := sd.SignerInfos[0]
|
||||
|
||||
oldAttrVal, err := si.GetContentTypeAttribute()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var oldAttr Attribute
|
||||
for _, attr := range si.SignedAttrs {
|
||||
if attr.Type.Equal(oidAttributeContentType) {
|
||||
oldAttr = attr
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
newAttr, err := NewAttribute(oidAttributeContentType, oldAttrVal)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(oldAttr.RawValue.Bytes, newAttr.RawValue.Bytes) {
|
||||
t.Fatal("raw value mismatch")
|
||||
}
|
||||
|
||||
oldDER, err := asn1.Marshal(oldAttr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
newDER, err := asn1.Marshal(newAttr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(oldDER, newDER) {
|
||||
t.Fatal("der mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssuerAndSerialNumber(t *testing.T) {
|
||||
ci, _ := ParseContentInfo(fixtureSignatureOpenSSLAttached)
|
||||
sd, _ := ci.SignedDataContent()
|
||||
si := sd.SignerInfos[0]
|
||||
certs, _ := sd.X509Certificates()
|
||||
cert, _ := si.FindCertificate(certs)
|
||||
|
||||
newISN, err := NewIssuerAndSerialNumber(cert)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
oldDER, _ := asn1.Marshal(si.SID)
|
||||
newDER, _ := asn1.Marshal(newISN)
|
||||
|
||||
if !bytes.Equal(oldDER, newDER) {
|
||||
t.Fatal("SID mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseFixtureSignatureOne(t *testing.T) {
|
||||
ParseContentInfoHelper(t, fixtureSignatureOne)
|
||||
testParseContentInfo(t, fixtureSignatureOne)
|
||||
}
|
||||
|
||||
func TestParseSignatureGPGSM(t *testing.T) {
|
||||
ParseContentInfoHelper(t, fixtureSignatureGPGSM)
|
||||
testParseContentInfo(t, fixtureSignatureGPGSM)
|
||||
}
|
||||
|
||||
func TestParseSignatureNoCertsGPGSM(t *testing.T) {
|
||||
ParseContentInfoHelper(t, fixtureSignatureNoCertsGPGSM)
|
||||
testParseContentInfo(t, fixtureSignatureNoCertsGPGSM)
|
||||
}
|
||||
|
||||
func TestParseSignatureOpenSSLAttached(t *testing.T) {
|
||||
ParseContentInfoHelper(t, fixtureSignatureOpenSSLAttached)
|
||||
testParseContentInfo(t, fixtureSignatureOpenSSLAttached)
|
||||
}
|
||||
|
||||
func TestParseSignatureOpenSSLDetached(t *testing.T) {
|
||||
ParseContentInfoHelper(t, fixtureSignatureOpenSSLDetached)
|
||||
testParseContentInfo(t, fixtureSignatureOpenSSLDetached)
|
||||
}
|
||||
|
||||
func ParseContentInfoHelper(t *testing.T, ber []byte) {
|
||||
func testParseContentInfo(t *testing.T, ber []byte) {
|
||||
ci, err := ParseContentInfo(ber)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package cms
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
)
|
||||
|
||||
func (sd *SignedData) Sign(cert *x509.Certificate, signer crypto.Signer) error {
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package cms
|
||||
|
||||
import "github.com/mastahyeti/cms/protocol"
|
||||
|
||||
// SignedData represents a signed message or detached signature.
|
||||
type SignedData struct {
|
||||
psd protocol.SignedData
|
||||
}
|
||||
|
||||
// ParseSignedData parses a SignedData from BER encoded data.
|
||||
func ParseSignedData(ber []byte) (*SignedData, error) {
|
||||
ci, err := protocol.ParseContentInfo(ber)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
psd, err := ci.SignedDataContent()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &SignedData{psd}, nil
|
||||
}
|
||||
|
||||
// GetData gets the encapsulated data from the SignedData. Nil will be returned
|
||||
// if this is a detached signature. A protocol.ErrWrongType will be returned if
|
||||
// the SignedData encapsulates something other than data (1.2.840.113549.1.7.1).
|
||||
func (sd *SignedData) GetData() ([]byte, error) {
|
||||
return sd.psd.EncapContentInfo.DataEContent()
|
||||
}
|
|
@ -5,37 +5,8 @@ import (
|
|||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/mastahyeti/cms/protocol"
|
||||
)
|
||||
|
||||
// SignedData represents a signed message or detached signature.
|
||||
type SignedData struct {
|
||||
psd protocol.SignedData
|
||||
}
|
||||
|
||||
// ParseSignedData parses a SignedData from BER encoded data.
|
||||
func ParseSignedData(ber []byte) (*SignedData, error) {
|
||||
ci, err := protocol.ParseContentInfo(ber)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
psd, err := ci.SignedDataContent()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &SignedData{psd}, nil
|
||||
}
|
||||
|
||||
// Data gets the encapsulated data from the SignedData. Nil will be returned if
|
||||
// this is a detached signature. A protocol.ErrWrongType will be returned if the
|
||||
// SignedData encapsulates something other than data (1.2.840.113549.1.7.1).
|
||||
func (sd *SignedData) Data() ([]byte, error) {
|
||||
return sd.psd.EncapContentInfo.DataEContent()
|
||||
}
|
||||
|
||||
// Verify verifies the SingerInfos' signatures.
|
||||
func (sd *SignedData) Verify() error {
|
||||
data, err := sd.psd.EncapContentInfo.DataEContent()
|
Загрузка…
Ссылка в новой задаче