Skip to content

Commit 0bbdd6c

Browse files
fix(corim): handle silly RFC9360 microoptimisation
x5chain is of type COSE_X509, which is defined as: COSE_X509 = bstr / [ 2*certs: bstr ] If there is only one element, it should be encoded as a simple bstr instead of being represented as an array. Signed-off-by: Thomas Fossati <[email protected]>
1 parent 328f8d7 commit 0bbdd6c

File tree

2 files changed

+73
-26
lines changed

2 files changed

+73
-26
lines changed

corim/signedcorim.go

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,9 @@ func (o *SignedCorim) processHdrs() error {
8282
return errors.New("missing mandatory corim.meta")
8383
}
8484

85+
// Process optional x5chain
8586
if v, ok := hdr.Protected[cose.HeaderLabelX5Chain]; ok {
86-
arr, ok := v.([]interface{})
87-
if !ok {
88-
return fmt.Errorf("decoding x5chain: got %T, want []interface{}", v)
89-
}
90-
91-
if err := o.extractX5Chain(arr); err != nil {
87+
if err := o.extractX5Chain(v); err != nil {
9288
return err
9389
}
9490
}
@@ -114,30 +110,38 @@ func (o *SignedCorim) extractMeta(v interface{}) error {
114110
return nil
115111
}
116112

117-
func (o *SignedCorim) extractX5Chain(arr []interface{}) error {
113+
func (o *SignedCorim) extractX5Chain(x5chain interface{}) error {
118114
var buf bytes.Buffer
119115

120-
for i, elem := range arr {
121-
cert, ok := elem.([]byte)
122-
if !ok {
123-
return fmt.Errorf("accessing x5chain[%d]: got %T, want []byte", i, elem)
124-
}
116+
switch t := x5chain.(type) {
117+
case []interface{}:
118+
for i, elem := range t {
119+
cert, ok := elem.([]byte)
120+
if !ok {
121+
return fmt.Errorf("accessing x5chain[%d]: got %T, want []byte", i, elem)
122+
}
125123

126-
switch i {
127-
case 0:
128-
if err := o.AddSigningCert(cert); err != nil {
129-
return fmt.Errorf("decoding x5chain[0]: %w", err)
124+
switch i {
125+
case 0:
126+
if err := o.AddSigningCert(cert); err != nil {
127+
return fmt.Errorf("decoding x5chain: %w", err)
128+
}
129+
default:
130+
buf.Write(cert)
130131
}
131-
default:
132-
buf.Write(cert)
133132
}
134133

135-
}
136-
137-
if buf.Len() > 0 {
138-
if err := o.AddIntermediateCerts(buf.Bytes()); err != nil {
134+
if buf.Len() > 0 {
135+
if err := o.AddIntermediateCerts(buf.Bytes()); err != nil {
136+
return fmt.Errorf("decoding x5chain: %w", err)
137+
}
138+
}
139+
case []byte:
140+
if err := o.AddSigningCert(t); err != nil {
139141
return fmt.Errorf("decoding x5chain: %w", err)
140142
}
143+
default:
144+
return fmt.Errorf("decoding x5chain: got %T, want []interface{} or []byte", t)
141145
}
142146

143147
return nil
@@ -247,11 +251,18 @@ func (o *SignedCorim) Sign(signer cose.Signer) ([]byte, error) {
247251
o.message.Headers.Protected[HeaderLabelCorimMeta] = metaCBOR
248252

249253
if o.SigningCert != nil {
250-
certChain := [][]byte{o.SigningCert.Raw}
251-
for _, cert := range o.IntermediateCerts {
252-
certChain = append(certChain, cert.Raw)
254+
// COSE_X509 = bstr / [ 2*certs: bstr ]
255+
//
256+
// handle alt (1): bstr
257+
if len(o.IntermediateCerts) == 0 {
258+
o.message.Headers.Protected[cose.HeaderLabelX5Chain] = o.SigningCert.Raw
259+
} else { // handle alt (2): [ 2*certs: bstr ]
260+
certChain := [][]byte{o.SigningCert.Raw}
261+
for _, cert := range o.IntermediateCerts {
262+
certChain = append(certChain, cert.Raw)
263+
}
264+
o.message.Headers.Protected[cose.HeaderLabelX5Chain] = certChain
253265
}
254-
o.message.Headers.Protected[cose.HeaderLabelX5Chain] = certChain
255266
}
256267

257268
err = o.message.Sign(rand.Reader, NoExternalData, signer)

corim/signedcorim_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,3 +685,39 @@ func TestSignedCorim_SignVerify_with_x5chain_ok(t *testing.T) {
685685
assert.Equal(t, signedCorimIn.SigningCert, signedCorimOut.SigningCert)
686686
assert.Equal(t, signedCorimIn.IntermediateCerts, signedCorimOut.IntermediateCerts)
687687
}
688+
689+
func TestSignedCorim_SignVerify_with_single_cert_x5chain_ok(t *testing.T) {
690+
signer, err := NewSignerFromJWK(testEndEntityKey)
691+
require.NoError(t, err)
692+
693+
var signedCorimIn SignedCorim
694+
695+
signedCorimIn.UnsignedCorim = *unsignedCorimFromCBOR(t, testGoodUnsignedCorimCBOR)
696+
signedCorimIn.Meta = *metaGood(t)
697+
698+
endEntityCertPath := filepath.Join("..", "misc", "endEntity.der")
699+
endEntityCert, err := os.ReadFile(endEntityCertPath)
700+
require.NoError(t, err, "Failed to read EE certificate")
701+
702+
err = signedCorimIn.AddSigningCert(endEntityCert)
703+
require.NoError(t, err, "Failed to add EE certificate")
704+
705+
cbor, err := signedCorimIn.Sign(signer)
706+
assert.Nil(t, err)
707+
708+
var signedCorimOut SignedCorim
709+
710+
fmt.Printf("signed-corim: %x\n", cbor)
711+
712+
err = signedCorimOut.FromCOSE(cbor)
713+
assert.Nil(t, err)
714+
715+
pk, err := NewPublicKeyFromJWK(testEndEntityKey)
716+
require.NoError(t, err)
717+
718+
err = signedCorimOut.Verify(pk)
719+
assert.Nil(t, err)
720+
721+
assert.Equal(t, signedCorimIn.SigningCert, signedCorimOut.SigningCert)
722+
assert.Equal(t, signedCorimIn.IntermediateCerts, signedCorimOut.IntermediateCerts)
723+
}

0 commit comments

Comments
 (0)