Skip to content

Commit dfe52f2

Browse files
committed
ietf-cms: add support for user-supplied signed attributes
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.
1 parent 3564e86 commit dfe52f2

File tree

3 files changed

+106
-1
lines changed

3 files changed

+106
-1
lines changed

ietf-cms/protocol/protocol.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,11 @@ func NewSignedData(eci EncapsulatedContentInfo) (*SignedData, error) {
626626

627627
// AddSignerInfo adds a SignerInfo to the SignedData.
628628
func (sd *SignedData) AddSignerInfo(chain []*x509.Certificate, signer crypto.Signer) error {
629+
return sd.AddSignerInfoWithAttrs(nil, chain, signer)
630+
}
631+
632+
// AddSignerInfoWithAttrs adds a SignerInfo to the SignedData, allowing for the caller to supply extra Attributes to sign.
633+
func (sd *SignedData) AddSignerInfoWithAttrs(attrs []Attribute, chain []*x509.Certificate, signer crypto.Signer) error {
629634
// figure out which certificate is associated with signer.
630635
pub, err := x509.MarshalPKIXPublicKey(signer.Public())
631636
if err != nil {
@@ -712,7 +717,7 @@ func (sd *SignedData) AddSignerInfo(chain []*x509.Certificate, signer crypto.Sig
712717
}
713718

714719
// sort attributes to match required order in marshaled form
715-
si.SignedAttrs, err = sortAttributes(stAttr, mdAttr, ctAttr)
720+
si.SignedAttrs, err = sortAttributes(append([]Attribute{stAttr, mdAttr, ctAttr}, attrs...)...)
716721
if err != nil {
717722
return err
718723
}

ietf-cms/protocol/protocol_test.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,99 @@ func TestSignerInfo(t *testing.T) {
8888
}
8989
}
9090

91+
func TestSignerInfoWithExtraAttrs(t *testing.T) {
92+
priv, cert, err := pkcs12.Decode(fixturePFX, "asdf")
93+
if err != nil {
94+
t.Fatal(err)
95+
}
96+
97+
msg := []byte("hello, world!")
98+
99+
eci, err := NewEncapsulatedContentInfo(oid.ContentTypeData, msg)
100+
if err != nil {
101+
t.Fatal(err)
102+
}
103+
104+
sd, err := NewSignedData(eci)
105+
if err != nil {
106+
t.Fatal(err)
107+
}
108+
109+
extraAttr, err := NewAttribute(asn1.ObjectIdentifier{1, 2, 3, 4, 5, 6}, []byte("this is an attribute"))
110+
if err != nil {
111+
t.Fatal(err)
112+
}
113+
114+
chain := []*x509.Certificate{cert}
115+
if err = sd.AddSignerInfoWithAttrs(Attributes{extraAttr}, chain, priv.(*ecdsa.PrivateKey)); err != nil {
116+
t.Fatal(err)
117+
}
118+
119+
der, err := sd.ContentInfoDER()
120+
if err != nil {
121+
t.Fatal(err)
122+
}
123+
124+
ci, err := ParseContentInfo(der)
125+
if err != nil {
126+
t.Fatal(err)
127+
}
128+
129+
sd2, err := ci.SignedDataContent()
130+
if err != nil {
131+
t.Fatal(err)
132+
}
133+
134+
msg2, err := sd2.EncapContentInfo.DataEContent()
135+
if err != nil {
136+
t.Fatal(err)
137+
}
138+
if !bytes.Equal(msg, msg2) {
139+
t.Fatal()
140+
}
141+
142+
attrs := sd2.SignerInfos[0].SignedAttrs
143+
found := false
144+
for _, attr := range attrs {
145+
if attr.Type.Equal(extraAttr.Type) {
146+
if !bytes.Equal(attr.RawValue.FullBytes, extraAttr.RawValue.FullBytes) {
147+
t.Fatalf("Extra signed attribute data round trip mismatch: want %#v, got %#v", extraAttr, attr)
148+
}
149+
found = true
150+
break
151+
}
152+
}
153+
if !found {
154+
t.Fatalf("Extra attribute not present in signer info")
155+
}
156+
157+
// Make detached
158+
sd.EncapContentInfo.EContent = asn1.RawValue{}
159+
160+
der, err = sd.ContentInfoDER()
161+
if err != nil {
162+
t.Fatal(err)
163+
}
164+
165+
ci, err = ParseContentInfo(der)
166+
if err != nil {
167+
t.Fatal(err)
168+
}
169+
170+
sd2, err = ci.SignedDataContent()
171+
if err != nil {
172+
t.Fatal(err)
173+
}
174+
175+
msg2, err = sd2.EncapContentInfo.DataEContent()
176+
if err != nil {
177+
t.Fatal(err)
178+
}
179+
if msg2 != nil {
180+
t.Fatal()
181+
}
182+
}
183+
91184
func TestEncapsulatedContentInfo(t *testing.T) {
92185
ci, _ := ParseContentInfo(fixtureSignatureOpenSSLAttached)
93186
sd, _ := ci.SignedDataContent()

ietf-cms/sign.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package cms
33
import (
44
"crypto"
55
"crypto/x509"
6+
7+
"github.com/github/smimesign/ietf-cms/protocol"
68
)
79

810
// Sign creates a CMS SignedData from the content and signs it with signer. At
@@ -47,3 +49,8 @@ func SignDetached(data []byte, chain []*x509.Certificate, signer crypto.Signer)
4749
func (sd *SignedData) Sign(chain []*x509.Certificate, signer crypto.Signer) error {
4850
return sd.psd.AddSignerInfo(chain, signer)
4951
}
52+
53+
// SignWithAttrs adds a signature with extra signed attributes to the SignedData.
54+
func (sd *SignedData) SignWithAttrs(attrs protocol.Attributes, chain []*x509.Certificate, signer crypto.Signer) error {
55+
return sd.psd.AddSignerInfoWithAttrs(attrs, chain, signer)
56+
}

0 commit comments

Comments
 (0)