diff --git a/corim/signedcorim.go b/corim/signedcorim.go index abe12364..ad7ee1fe 100644 --- a/corim/signedcorim.go +++ b/corim/signedcorim.go @@ -59,7 +59,21 @@ func (o *SignedCorim) processHdrs() error { return errors.New("missing mandatory protected header") } - v, ok := hdr.Protected[cose.HeaderLabelContentType] + v, ok := hdr.Protected[cose.HeaderLabelAlgorithm] + if !ok { + return errors.New("missing mandatory algorithm") + } + + // TODO: make this consistent, either int64 or cose.Algorithm + // cose.Algorithm is an alias to int64 defined in veraison/go-cose + switch v.(type) { + case int64: + case cose.Algorithm: + default: + return fmt.Errorf("expecting integer CoRIM Algorithm, got %T instead", v) + } + + v, ok = hdr.Protected[cose.HeaderLabelContentType] if !ok { return errors.New("missing mandatory content type") } @@ -68,9 +82,15 @@ func (o *SignedCorim) processHdrs() error { return fmt.Errorf("expecting content type %q, got %q instead", ContentType, v) } - // TODO(tho) key id is apparently mandatory, which doesn't look right. - // TODO(tho) Check with the CoRIM design team. - // See https://github.com/veraison/corim/issues/14 + v, ok = hdr.Protected[cose.HeaderLabelKeyID] + if !ok { + return errors.New("missing mandatory key id") + } + + _, ok = v.([]byte) + if !ok { + return fmt.Errorf("expecting byte string CoRIM Key ID, got %T instead", v) + } v, ok = hdr.Protected[HeaderLabelCorimMeta] if !ok { @@ -129,7 +149,7 @@ func (o *SignedCorim) FromCOSE(buf []byte) error { // Sign returns the serialized signed-corim, signed by the supplied cose Signer. // The target SignedCorim must have its UnsignedCorim field correctly // populated. -func (o *SignedCorim) Sign(signer cose.Signer) ([]byte, error) { +func (o *SignedCorim) Sign(signer cose.Signer, kid []byte) ([]byte, error) { if signer == nil { return nil, errors.New("nil signer") } @@ -159,6 +179,7 @@ func (o *SignedCorim) Sign(signer cose.Signer) ([]byte, error) { o.message.Headers.Protected.SetAlgorithm(alg) o.message.Headers.Protected[cose.HeaderLabelContentType] = ContentType + o.message.Headers.Protected[cose.HeaderLabelKeyID] = kid o.message.Headers.Protected[HeaderLabelCorimMeta] = metaCBOR err = o.message.Sign(rand.Reader, NoExternalData, signer) diff --git a/corim/signedcorim_test.go b/corim/signedcorim_test.go index 2e286489..a0774034 100644 --- a/corim/signedcorim_test.go +++ b/corim/signedcorim_test.go @@ -286,6 +286,7 @@ func TestSignedCorim_FromCOSE_fail_corim_bad_cbor(t *testing.T) { / protected / << { / alg / 1: -7, / ECDSA 256 / / content-type / 3: "application/rim+cbor", + / kid / 4: h'1', / corim-meta / 8: h'a200a1006941434d45204c74642e01a101c11a5fad2056' } >>, / unprotected / {}, @@ -295,12 +296,12 @@ func TestSignedCorim_FromCOSE_fail_corim_bad_cbor(t *testing.T) { ) */ tv := []byte{ - 0xd2, 0x84, 0x58, 0x32, 0xa3, 0x01, 0x26, 0x03, 0x74, 0x61, 0x70, 0x70, + 0xd2, 0x84, 0x58, 0x35, 0xa4, 0x01, 0x26, 0x03, 0x74, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x72, 0x69, 0x6d, - 0x2b, 0x63, 0x62, 0x6f, 0x72, 0x08, 0x57, 0xa2, 0x00, 0xa1, 0x00, 0x69, - 0x41, 0x43, 0x4d, 0x45, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x01, 0xa1, 0x01, - 0xc1, 0x1a, 0x5f, 0xad, 0x20, 0x56, 0xa0, 0x44, 0xba, 0xdc, 0xb0, 0x30, - 0x44, 0xde, 0xad, 0xbe, 0xef, + 0x2b, 0x63, 0x62, 0x6f, 0x72, 0x04, 0x41, 0x31, 0x08, 0x57, 0xa2, 0x00, + 0xa1, 0x00, 0x69, 0x41, 0x43, 0x4d, 0x45, 0x20, 0x4c, 0x74, 0x64, 0x2e, + 0x01, 0xa1, 0x01, 0xc1, 0x1a, 0x5f, 0xad, 0x20, 0x56, 0xa0, 0x44, 0xba, + 0xdc, 0xb0, 0x30, 0x44, 0xde, 0xad, 0xbe, 0xef, } var actual SignedCorim @@ -316,6 +317,7 @@ func TestSignedCorim_FromCOSE_fail_invalid_corim(t *testing.T) { / protected / << { / alg / 1: -7, / ECDSA 256 / / content-type / 3: "application/rim+cbor", + / kid / 4: h'1', / corim-meta / 8: h'a200a1006941434d45204c74642e01a101c11a5fad2056' } >>, / unprotected / {}, @@ -327,13 +329,13 @@ func TestSignedCorim_FromCOSE_fail_invalid_corim(t *testing.T) { ) */ tv := []byte{ - 0xd2, 0x84, 0x58, 0x32, 0xa3, 0x01, 0x26, 0x03, 0x74, 0x61, 0x70, 0x70, + 0xd2, 0x84, 0x58, 0x35, 0xa4, 0x01, 0x26, 0x03, 0x74, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x72, 0x69, 0x6d, - 0x2b, 0x63, 0x62, 0x6f, 0x72, 0x08, 0x57, 0xa2, 0x00, 0xa1, 0x00, 0x69, - 0x41, 0x43, 0x4d, 0x45, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x01, 0xa1, 0x01, - 0xc1, 0x1a, 0x5f, 0xad, 0x20, 0x56, 0xa0, 0x50, 0xa1, 0x00, 0x6d, 0x69, - 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x63, 0x6f, 0x72, 0x69, 0x6d, - 0x44, 0xde, 0xad, 0xbe, 0xef, + 0x2b, 0x63, 0x62, 0x6f, 0x72, 0x04, 0x41, 0x31, 0x08, 0x57, 0xa2, 0x00, + 0xa1, 0x00, 0x69, 0x41, 0x43, 0x4d, 0x45, 0x20, 0x4c, 0x74, 0x64, 0x2e, + 0x01, 0xa1, 0x01, 0xc1, 0x1a, 0x5f, 0xad, 0x20, 0x56, 0xa0, 0x50, 0xa1, + 0x00, 0x6d, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x63, 0x6f, + 0x72, 0x69, 0x6d, 0x44, 0xde, 0xad, 0xbe, 0xef, } var actual SignedCorim @@ -435,13 +437,15 @@ func TestSignedCorim_SignVerify_ok(t *testing.T) { } { signer, err := NewSignerFromJWK(key) require.NoError(t, err) + kid, err := getKidFromJWK(key) + require.NoError(t, err) var SignedCorimIn SignedCorim SignedCorimIn.UnsignedCorim = *unsignedCorimFromCBOR(t, testGoodUnsignedCorimCBOR) SignedCorimIn.Meta = *metaGood(t) - cbor, err := SignedCorimIn.Sign(signer) + cbor, err := SignedCorimIn.Sign(signer, kid) assert.Nil(t, err) var SignedCorimOut SignedCorim @@ -462,12 +466,14 @@ func TestSignedCorim_SignVerify_ok(t *testing.T) { func TestSignedCorim_SignVerify_fail_tampered(t *testing.T) { signer, err := NewSignerFromJWK(testES256Key) require.NoError(t, err) + kid, err := getKidFromJWK(testES256Key) + require.NoError(t, err) var SignedCorimIn SignedCorim SignedCorimIn.UnsignedCorim = *unsignedCorimFromCBOR(t, testGoodUnsignedCorimCBOR) - cbor, err := SignedCorimIn.Sign(signer) + cbor, err := SignedCorimIn.Sign(signer, kid) assert.Nil(t, err) var SignedCorimOut SignedCorim @@ -493,6 +499,8 @@ func TestSignedCorim_SignVerify_fail_tampered(t *testing.T) { func TestSignedCorim_Sign_fail_bad_corim(t *testing.T) { signer, err := NewSignerFromJWK(testES256Key) require.NoError(t, err) + kid, err := getKidFromJWK(testES256Key) + require.NoError(t, err) var SignedCorimIn SignedCorim @@ -501,7 +509,7 @@ func TestSignedCorim_Sign_fail_bad_corim(t *testing.T) { SignedCorimIn.UnsignedCorim = *emptyCorim - _, err = SignedCorimIn.Sign(signer) + _, err = SignedCorimIn.Sign(signer, kid) assert.EqualError(t, err, "failed validation of unsigned CoRIM: empty id") } @@ -513,7 +521,7 @@ func TestSignedCorim_Sign_fail_no_signer(t *testing.T) { SignedCorimIn.UnsignedCorim = *emptyCorim - _, err := SignedCorimIn.Sign(nil) + _, err := SignedCorimIn.Sign(nil, nil) assert.EqualError(t, err, "nil signer") } diff --git a/corim/signer.go b/corim/signer.go index cf971292..9916cc3d 100644 --- a/corim/signer.go +++ b/corim/signer.go @@ -154,6 +154,25 @@ func getAlgAndKeyFromJWK(j []byte) (cose.Algorithm, crypto.Signer, error) { return alg, key, nil } +func getKidFromJWK(j []byte) ([]byte, error) { + k, err := jwk.ParseKey(j) + if err != nil { + return nil, err + } + + if k.KeyID() != "" { + return []byte(k.KeyID()), nil + } + + // Generate a key ID from the JWK Thumbprint if none exist + // See https://datatracker.ietf.org/doc/html/rfc7638 + kid, err := k.Thumbprint(crypto.SHA256) + if err != nil { + return nil, err + } + return kid, nil +} + func ellipticCurveToAlg(c elliptic.Curve) cose.Algorithm { switch c { case elliptic.P256(): diff --git a/corim/testcases/regen-from-src.sh b/corim/testcases/regen-from-src.sh old mode 100644 new mode 100755 index f119e952..f5b6c065 --- a/corim/testcases/regen-from-src.sh +++ b/corim/testcases/regen-from-src.sh @@ -7,7 +7,7 @@ GEN_TESTCASE=$(go env GOPATH)/bin/gen-testcase if [[ ! -f ${GEN_TESTCASE} ]]; then echo "installing gen-testcase" - go install github.com/veraison/gen-testcase@v0.0.1 + go install github.com/veraison/gen-testcase@v0.0.2 fi testcases=( diff --git a/corim/testcases/signed-corim-with-extensions.cbor b/corim/testcases/signed-corim-with-extensions.cbor index 4bfca682..32f1a04d 100644 Binary files a/corim/testcases/signed-corim-with-extensions.cbor and b/corim/testcases/signed-corim-with-extensions.cbor differ diff --git a/corim/testcases/signed-example-corim.cbor b/corim/testcases/signed-example-corim.cbor index f6c4f461..5f2c9b42 100644 Binary files a/corim/testcases/signed-example-corim.cbor and b/corim/testcases/signed-example-corim.cbor differ diff --git a/corim/testcases/signed-good-corim.cbor b/corim/testcases/signed-good-corim.cbor index 50dae691..38762116 100644 Binary files a/corim/testcases/signed-good-corim.cbor and b/corim/testcases/signed-good-corim.cbor differ diff --git a/corim/testcases/unsigned-corim-with-extensions.cbor b/corim/testcases/unsigned-corim-with-extensions.cbor index 0e056df9..4cd240a9 100644 Binary files a/corim/testcases/unsigned-corim-with-extensions.cbor and b/corim/testcases/unsigned-corim-with-extensions.cbor differ diff --git a/corim/testcases/unsigned-example-corim.cbor b/corim/testcases/unsigned-example-corim.cbor index 0c715c8f..8047897e 100644 Binary files a/corim/testcases/unsigned-example-corim.cbor and b/corim/testcases/unsigned-example-corim.cbor differ diff --git a/corim/testcases/unsigned-good-corim.cbor b/corim/testcases/unsigned-good-corim.cbor index 3c1fcb6c..66a9c21d 100644 Binary files a/corim/testcases/unsigned-good-corim.cbor and b/corim/testcases/unsigned-good-corim.cbor differ