Skip to content

Commit

Permalink
ietf-cms: add support for user-supplied signed attributes
Browse files Browse the repository at this point in the history
Some use cases for CMS (notably the code signatures in Apple signed MachO
binaries) require additional attributes to be signed. This change adds a
SignWithAttrs method to cms.SignedData that accepts additional signed
attributes to include.
  • Loading branch information
jkt-signal committed Mar 19, 2024
1 parent 3564e86 commit 793b36a
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 1 deletion.
7 changes: 6 additions & 1 deletion ietf-cms/protocol/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,11 @@ func NewSignedData(eci EncapsulatedContentInfo) (*SignedData, error) {

// AddSignerInfo adds a SignerInfo to the SignedData.
func (sd *SignedData) AddSignerInfo(chain []*x509.Certificate, signer crypto.Signer) error {
return sd.AddSignerInfoWithAttrs(nil, chain, signer)
}

// AddSignerInfoWithAttrs adds a SignerInfo to the SignedData, allowing for the caller to supply extra Attributes to sign.
func (sd *SignedData) AddSignerInfoWithAttrs(attrs []Attribute, chain []*x509.Certificate, signer crypto.Signer) error {
// figure out which certificate is associated with signer.
pub, err := x509.MarshalPKIXPublicKey(signer.Public())
if err != nil {
Expand Down Expand Up @@ -712,7 +717,7 @@ func (sd *SignedData) AddSignerInfo(chain []*x509.Certificate, signer crypto.Sig
}

// sort attributes to match required order in marshaled form
si.SignedAttrs, err = sortAttributes(stAttr, mdAttr, ctAttr)
si.SignedAttrs, err = sortAttributes(append([]Attribute{stAttr, mdAttr, ctAttr}, attrs...)...)
if err != nil {
return err
}
Expand Down
93 changes: 93 additions & 0 deletions ietf-cms/protocol/protocol_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,99 @@ func TestSignerInfo(t *testing.T) {
}
}

func TestSignerInfoWithExtraAttrs(t *testing.T) {
priv, cert, err := pkcs12.Decode(fixturePFX, "asdf")
if err != nil {
t.Fatal(err)
}

msg := []byte("hello, world!")

eci, err := NewEncapsulatedContentInfo(oid.ContentTypeData, msg)
if err != nil {
t.Fatal(err)
}

sd, err := NewSignedData(eci)
if err != nil {
t.Fatal(err)
}

extraAttr, err := NewAttribute(asn1.ObjectIdentifier{1, 2, 3, 4, 5, 6}, []byte("this is an attribute"))
if err != nil {
t.Fatal(err)
}

chain := []*x509.Certificate{cert}
if err = sd.AddSignerInfoWithAttrs(Attributes{extraAttr}, chain, priv.(*ecdsa.PrivateKey)); err != nil {
t.Fatal(err)
}

der, err := sd.ContentInfoDER()
if err != nil {
t.Fatal(err)
}

ci, err := ParseContentInfo(der)
if err != nil {
t.Fatal(err)
}

sd2, err := ci.SignedDataContent()
if err != nil {
t.Fatal(err)
}

msg2, err := sd2.EncapContentInfo.DataEContent()
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(msg, msg2) {
t.Fatal()
}

attrs := sd2.SignerInfos[0].SignedAttrs
found := false
for _, attr := range attrs {
if attr.Type.Equal(extraAttr.Type) {
if !bytes.Equal(attr.RawValue.FullBytes, extraAttr.RawValue.FullBytes) {
t.Fatalf("Extra signed attribute data round trip mismatch: want %#v, got %#v", extraAttr, attr)
}
found = true
break
}
}
if !found {
t.Fatalf("Extra attribute not present in signer info")
}

// Make detached
sd.EncapContentInfo.EContent = asn1.RawValue{}

der, err = sd.ContentInfoDER()
if err != nil {
t.Fatal(err)
}

ci, err = ParseContentInfo(der)
if err != nil {
t.Fatal(err)
}

sd2, err = ci.SignedDataContent()
if err != nil {
t.Fatal(err)
}

msg2, err = sd2.EncapContentInfo.DataEContent()
if err != nil {
t.Fatal(err)
}
if msg2 != nil {
t.Fatal()
}
}

func TestEncapsulatedContentInfo(t *testing.T) {
ci, _ := ParseContentInfo(fixtureSignatureOpenSSLAttached)
sd, _ := ci.SignedDataContent()
Expand Down
7 changes: 7 additions & 0 deletions ietf-cms/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package cms
import (
"crypto"
"crypto/x509"

"github.com/github/smimesign/ietf-cms/protocol"
)

// Sign creates a CMS SignedData from the content and signs it with signer. At
Expand Down Expand Up @@ -47,3 +49,8 @@ func SignDetached(data []byte, chain []*x509.Certificate, signer crypto.Signer)
func (sd *SignedData) Sign(chain []*x509.Certificate, signer crypto.Signer) error {
return sd.psd.AddSignerInfo(chain, signer)
}

// SignWithAttrs adds a signature with extra signed attributes to the SignedData.
func (sd *SignedData) SignWithAttrs(attrs protocol.Attributes, chain []*x509.Certificate, signer crypto.Signer) error {
return sd.psd.AddSignerInfoWithAttrs(attrs, chain, signer)
}

0 comments on commit 793b36a

Please sign in to comment.