From 9c5fb16aba67d83c8cf45fdd6c6234cde2d04513 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Mon, 28 Mar 2022 10:16:46 +0200 Subject: [PATCH 1/3] add conformance tests Signed-off-by: qmuntal --- conformance_test.go | 283 ++++++++++++++++++++++++++++++++ testdata/sign1-sign-0000.json | 35 ++++ testdata/sign1-sign-0001.json | 34 ++++ testdata/sign1-sign-0002.json | 35 ++++ testdata/sign1-sign-0003.json | 36 ++++ testdata/sign1-verify-0000.json | 21 +++ testdata/sign1-verify-0001.json | 20 +++ testdata/sign1-verify-0002.json | 21 +++ testdata/sign1-verify-0003.json | 22 +++ 9 files changed, 507 insertions(+) create mode 100644 conformance_test.go create mode 100644 testdata/sign1-sign-0000.json create mode 100644 testdata/sign1-sign-0001.json create mode 100644 testdata/sign1-sign-0002.json create mode 100644 testdata/sign1-sign-0003.json create mode 100644 testdata/sign1-verify-0000.json create mode 100644 testdata/sign1-verify-0001.json create mode 100644 testdata/sign1-verify-0002.json create mode 100644 testdata/sign1-verify-0003.json diff --git a/conformance_test.go b/conformance_test.go new file mode 100644 index 0000000..c60930e --- /dev/null +++ b/conformance_test.go @@ -0,0 +1,283 @@ +package cose_test + +import ( + "bytes" + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "encoding/base64" + "encoding/hex" + "encoding/json" + "errors" + "math/big" + "os" + "path/filepath" + "testing" + + "github.com/veraison/go-cose" +) + +type TestCase struct { + UUID string `json:"uuid"` + Title string `json:"title"` + Description string `json:"description"` + Key Key `json:"key"` + Alg string `json:"alg"` + Sign1 *Sign1 `json:"sign1::sign"` + Verify1 *Verify1 `json:"sign1::verify"` +} + +type Key map[string]string + +type Sign1 struct { + Payload string `json:"payload"` + ProtectedHeaders *CBOR `json:"protectedHeaders"` + UnprotectedHeaders *CBOR `json:"unprotectedHeaders"` + External string `json:"external"` + Detached bool `json:"detached"` + TBS CBOR `json:"tbsHex"` + Output CBOR `json:"expectedOutput"` + OutputLength int `json:"fixedOutputLength"` +} + +type Verify1 struct { + TaggedCOSESign1 CBOR `json:"taggedCOSESign1"` + External string `json:"external"` + Verify bool `json:"shouldVerify"` +} + +type CBOR struct { + CBORHex string `json:"cborHex"` + CBORDiag string `json:"cborDiag"` +} + +// Conformance samples are taken from +// https://github.com/cose-wg/Examples. +var testCases = []string{ + "sign1-sign-0000", + "sign1-sign-0001", + "sign1-sign-0002", + "sign1-sign-0003", + "sign1-verify-0000", + "sign1-verify-0001", + "sign1-verify-0002", + "sign1-verify-0003", +} + +func TestConformance(t *testing.T) { + for _, name := range testCases { + t.Run(name, func(t *testing.T) { + data, err := os.ReadFile(filepath.Join("testdata", name+".json")) + if err != nil { + t.Fatal(err) + } + var tc TestCase + err = json.Unmarshal(data, &tc) + if err != nil { + t.Fatal(err) + } + processTestCase(t, &tc) + }) + } +} + +func processTestCase(t *testing.T, tc *TestCase) { + if tc.Sign1 != nil { + testSign1(t, tc) + } else if tc.Verify1 != nil { + testVerify1(t, tc) + } else { + t.Fatal("test case not supported") + } +} + +func testVerify1(t *testing.T, tc *TestCase) { + signer, err := getSigner(tc, false) + if err != nil { + t.Fatal(err) + } + var sigMsg cose.Sign1Message + err = sigMsg.UnmarshalCBOR(mustHexToBytes(tc.Verify1.TaggedCOSESign1.CBORHex)) + if err != nil { + t.Fatal(err) + } + external := []byte("") + if tc.Verify1.External != "" { + external = mustHexToBytes(tc.Verify1.External) + } + err = sigMsg.Verify(external, *signer.Verifier()) + if tc.Verify1.Verify && err != nil { + t.Fatal(err) + } else if !tc.Verify1.Verify && err == nil { + t.Fatal("Verify1 should have failed") + } +} + +func testSign1(t *testing.T, tc *TestCase) { + signer, err := getSigner(tc, true) + if err != nil { + t.Fatal(err) + } + sig := tc.Sign1 + sigMsg := cose.NewSign1Message() + sigMsg.Payload = mustHexToBytes(sig.Payload) + sigMsg.Headers, err = decodeHeaders(mustHexToBytes(sig.ProtectedHeaders.CBORHex), mustHexToBytes(sig.UnprotectedHeaders.CBORHex)) + if err != nil { + t.Fatal(err) + } + external := []byte("") + if sig.External != "" { + external = mustHexToBytes(sig.External) + } + err = sigMsg.Sign(new(zeroSource), external, *signer) + if err != nil { + t.Fatal(err) + } + err = sigMsg.Verify(external, *signer.Verifier()) + if err != nil { + t.Fatal(err) + } + got, err := sigMsg.MarshalCBOR() + if err != nil { + t.Fatal(err) + } + want := mustHexToBytes(sig.Output.CBORHex) + if sig.OutputLength > 0 { + got = got[:sig.OutputLength] + want = want[:sig.OutputLength] + } + if !bytes.Equal(want, got) { + t.Fatalf("unexpected output:\nwant: %x\n got: %x", want, got) + } +} + +func getSigner(tc *TestCase, private bool) (*cose.Signer, error) { + pkey, err := getKey(tc.Key, private) + if err != nil { + return nil, err + } + alg := mustNameToAlg(tc.Alg) + signer, err := cose.NewSignerFromKey(alg, pkey) + if err != nil { + return nil, err + } + return signer, nil +} + +func getKey(key Key, private bool) (crypto.PrivateKey, error) { + switch key["kty"] { + case "EC": + var c elliptic.Curve + switch key["crv"] { + case "P-224": + c = elliptic.P224() + case "P-256": + c = elliptic.P256() + case "P-384": + c = elliptic.P384() + case "P-521": + c = elliptic.P521() + default: + return nil, errors.New("unsupported EC curve: " + key["crv"]) + } + pkey := &ecdsa.PrivateKey{ + PublicKey: ecdsa.PublicKey{ + X: mustBase64ToBigInt(key["x"]), + Y: mustBase64ToBigInt(key["y"]), + Curve: c, + }, + } + if private { + pkey.D = mustBase64ToBigInt(key["d"]) + } + return pkey, nil + } + return nil, errors.New("unsupported key type: " + key["kty"]) +} + +// zeroSource is an io.Reader that returns an unlimited number of zero bytes. +type zeroSource struct{} + +func (zeroSource) Read(b []byte) (n int, err error) { + for i := range b { + b[i] = 0 + } + + return len(b), nil +} + +func decodeHeaders(protected, unprotected []byte) (*cose.Headers, error) { + var hdr cose.Headers + hdr.Protected = make(map[interface{}]interface{}) + hdr.Unprotected = make(map[interface{}]interface{}) + err := hdr.DecodeProtected(protected) + if err != nil { + return nil, err + } + b, err := cose.Unmarshal(unprotected) + if err != nil { + return nil, err + } + err = hdr.DecodeUnprotected(b) + if err != nil { + return nil, err + } + hdr.Protected = fixHeader(hdr.Protected) + hdr.Unprotected = fixHeader(hdr.Unprotected) + return &hdr, nil +} + +func fixHeader(m map[interface{}]interface{}) map[interface{}]interface{} { + ret := make(map[interface{}]interface{}) + for k, v := range m { + switch k1 := k.(type) { + case int64: + k = int(k1) + } + switch v1 := v.(type) { + case int64: + v = int(v1) + } + ret[k] = v + } + return ret +} + +func mustHexToInt(s string) int { + return int(mustHexToBigInt(s).Int64()) +} + +func mustHexToBytes(s string) []byte { + b, err := hex.DecodeString(s) + if err != nil { + panic(err) + } + return b +} + +func mustHexToBigInt(s string) *big.Int { + return new(big.Int).SetBytes(mustHexToBytes(s)) +} + +func mustBase64ToBigInt(s string) *big.Int { + val, err := base64.RawURLEncoding.DecodeString(s) + if err != nil { + panic(err) + } + return new(big.Int).SetBytes(val) +} + +// mustNameToAlg returns the algorithm associated to name. +// The content of name is not defined in any RFC, +// but it's what the test cases use to identify algorithms. +func mustNameToAlg(name string) *cose.Algorithm { + switch name { + case "ES256": + return cose.ES256 + case "ES384": + return cose.ES384 + case "ES512": + return cose.ES512 + } + panic("algorithm name not found: " + name) +} diff --git a/testdata/sign1-sign-0000.json b/testdata/sign1-sign-0000.json new file mode 100644 index 0000000..cee5cb5 --- /dev/null +++ b/testdata/sign1-sign-0000.json @@ -0,0 +1,35 @@ +{ + "uuid": "D55A49BD-53D9-42B1-9E76-E0CF2AD33E9D", + "title": "Sign1 w/ external input - ECDSA w/ SHA-256 (sign)", + "description": "Sign with one signer using ECDSA w/ SHA-256 supplying external input", + "key": { + "kty": "EC", + "crv": "P-256", + "x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8", + "y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4", + "d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM" + }, + "alg": "ES256", + "sign1::sign": { + "payload": "546869732069732074686520636f6e74656e742e", + "protectedHeaders": { + "cborHex": "a10126", + "cborDiag": "{1: -7}" + }, + "unprotectedHeaders": { + "cborHex": "a104423131", + "cborDiag": "{4: '11'}" + }, + "tbsHex": { + "cborHex": "846a5369676e61747572653143a101264c11aa22bb33cc44dd5500669954546869732069732074686520636f6e74656e742e", + "cborDiag": "[\"Signature1\", h'A10126', h'11AA22BB33CC44DD55006699', h'546869732069732074686520636F6E74656E742E']" + }, + "external": "11aa22bb33cc44dd55006699", + "detached": false, + "expectedOutput": { + "cborHex": "d28443a10126a10442313154546869732069732074686520636f6e74656e742e58403a7487d9a528cb61dd8e99bd652c12577fc47d70ee5af2e703c420584f060fc7a8d61e4a35862b2b531a8447030ab966aeed8dd45ebc507c761431e349995770", + "cborDiag": "18([h'A10126', {4: '11'}, h'546869732069732074686520636F6E74656E742E', h'3A7487D9A528CB61DD8E99BD652C12577FC47D70EE5AF2E703C420584F060FC7A8D61E4A35862B2B531A8447030AB966AEED8DD45EBC507C761431E349995770'])" + }, + "fixedOutputLength": 32 + } +} diff --git a/testdata/sign1-sign-0001.json b/testdata/sign1-sign-0001.json new file mode 100644 index 0000000..4c2ca63 --- /dev/null +++ b/testdata/sign1-sign-0001.json @@ -0,0 +1,34 @@ +{ + "uuid": "0F78DB1C-C30F-47B1-AF19-6D0C0B2F3803", + "title": "Sign1 - ECDSA w/ SHA-256 (sign)", + "description": "Sign with one signer using ECDSA w/ SHA-256", + "key": { + "kty": "EC", + "crv": "P-256", + "x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8", + "y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4", + "d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM" + }, + "alg": "ES256", + "sign1::sign": { + "payload": "546869732069732074686520636f6e74656e742e", + "protectedHeaders": { + "cborHex": "a201260300", + "cborDiag": "{1: -7, 3: 0}" + }, + "unprotectedHeaders": { + "cborHex": "a104423131", + "cborDiag": "{4: '11'}" + }, + "tbsHex": { + "cborHex": "846a5369676e61747572653145a2012603004054546869732069732074686520636f6e74656e742e", + "cborDiag": "[\"Signature1\", h'A201260300', h'', h'546869732069732074686520636F6E74656E742E']" + }, + "detached": false, + "expectedOutput": { + "cborHex": "d28445a201260300a10442313154546869732069732074686520636f6e74656e742e58402ad3b9dcc1e13d04f357e11cc8acd825196620e62f0d8deca72672508b829d90e07a3f23be6aa36fd6ebd31e2ed08d1760bffd981f991bfc94a45199a54875c4", + "cborDiag": "18([h'A201260300', {4: '11'}, h'546869732069732074686520636F6E74656E742E', h'2AD3B9DCC1E13D04F357E11CC8ACD825196620E62F0D8DECA72672508B829D90E07A3F23BE6AA36FD6EBD31E2ED08D1760BFFD981F991BFC94A45199A54875C4'])" + }, + "fixedOutputLength": 34 + } +} diff --git a/testdata/sign1-sign-0002.json b/testdata/sign1-sign-0002.json new file mode 100644 index 0000000..2183ad8 --- /dev/null +++ b/testdata/sign1-sign-0002.json @@ -0,0 +1,35 @@ +{ + "uuid": "E693D0C8-C702-4E6C-A70D-0D4DA4C408A0", + "title": "Sign1 - ECDSA w/ SHA-384 (sign)", + "description": "Sign with one signer using ECDSA w/ SHA-384", + "key": { + "kty": "EC", + "kid": "P384", + "crv": "P-384", + "x": "kTJyP2KSsBBhnb4kjWmMF7WHVsY55xUPgb7k64rDcjatChoZ1nvjKmYmPh5STRKc", + "y": "mM0weMVU2DKsYDxDJkEP9hZiRZtB8fPfXbzINZj_fF7YQRynNWedHEyzAJOX2e8s", + "d": "ok3Nq97AXlpEusO7jIy1FZATlBP9PNReMU7DWbkLQ5dU90snHuuHVDjEPmtV0fTo" + }, + "alg": "ES384", + "sign1::sign": { + "payload": "546869732069732074686520636f6e74656e742e", + "protectedHeaders": { + "cborHex": "a1013822", + "cborDiag": "{1: -35}" + }, + "unprotectedHeaders": { + "cborHex": "a1044450333834", + "cborDiag": "{4: 'P384'}" + }, + "tbsHex": { + "cborHex": "846a5369676e61747572653144a10138224054546869732069732074686520636f6e74656e742e", + "cborDiag": "[\"Signature1\", h'A1013822', h'', h'546869732069732074686520636F6E74656E742E']" + }, + "detached": false, + "expectedOutput": { + "cborHex": "d28444a1013822a104445033383454546869732069732074686520636f6e74656e742e5860aa46c1ab71cd3c1e68ed62c27653797cb72cba3a856fd5e2f38794eee0d666e88139ec51fb62466f4865ca56df493905911e329e829c1887f6259681360a8e7f7d3fd080dcb0720066f13e1621656700c99d6e3771ac2549fde998ee9b1e2cad", + "cborDiag": "18([h'A1013822', {4: 'P384'}, h'546869732069732074686520636F6E74656E742E', h'AA46C1AB71CD3C1E68ED62C27653797CB72CBA3A856FD5E2F38794EEE0D666E88139EC51FB62466F4865CA56DF493905911E329E829C1887F6259681360A8E7F7D3FD080DCB0720066F13E1621656700C99D6E3771AC2549FDE998EE9B1E2CAD'])" + }, + "fixedOutputLength": 35 + } +} diff --git a/testdata/sign1-sign-0003.json b/testdata/sign1-sign-0003.json new file mode 100644 index 0000000..463136d --- /dev/null +++ b/testdata/sign1-sign-0003.json @@ -0,0 +1,36 @@ +{ + "uuid": "06EFA821-9026-4CDD-A4FB-634103472BC3", + "title": "Sign1 - ECDSA w/ SHA-512 (sign)", + "description": "Sign with one signer using ECDSA w/ SHA-512", + "key": { + "kty": "EC", + "kid": "bilbo.baggins@hobbiton.example", + "use": "sig", + "crv": "P-521", + "x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt", + "y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVySsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1", + "d": "AAhRON2r9cqXX1hg-RoI6R1tX5p2rUAYdmpHZoC1XNM56KtscrX6zbKipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt" + }, + "alg": "ES512", + "sign1::sign": { + "payload": "546869732069732074686520636f6e74656e742e", + "protectedHeaders": { + "cborHex": "a1013823", + "cborDiag": "{1: -36}" + }, + "unprotectedHeaders": { + "cborHex": "a104581e62696c626f2e62616767696e7340686f626269746f6e2e6578616d706c65", + "cborDiag": "{4: 'bilbo.baggins@hobbiton.example'}" + }, + "tbsHex": { + "cborHex": "846a5369676e61747572653144a10138234054546869732069732074686520636f6e74656e742e", + "cborDiag": "[\"Signature1\", h'A1013823', h'', h'546869732069732074686520636F6E74656E742E']" + }, + "detached": false, + "expectedOutput": { + "cborHex": "d28444a1013823a104581e62696c626f2e62616767696e7340686f626269746f6e2e6578616d706c6554546869732069732074686520636f6e74656e742e58840128bbda237a1b55568da74cefe02cf2d2a6216f80ac757bea8effc056d2f634f6e257077b0dabe9d4b3689eb8228e20f60bc74ff84ae3a38ee9a69e158cbf80f93a017acf5877e5083548a45143b602ccd776c5eb39537a2e68dc8c47ff62e10fc42f045b781e4313fbf421903785c3dfeb181c3a93b46a67a9b0e82947ee83f7b44cf0", + "cborDiag": "18([h'A1013823', {4: 'bilbo.baggins@hobbiton.example'}, h'546869732069732074686520636F6E74656E742E', h'0128BBDA237A1B55568DA74CEFE02CF2D2A6216F80AC757BEA8EFFC056D2F634F6E257077B0DABE9D4B3689EB8228E20F60BC74FF84AE3A38EE9A69E158CBF80F93A017ACF5877E5083548A45143B602CCD776C5EB39537A2E68DC8C47FF62E10FC42F045B781E4313FBF421903785C3DFEB181C3A93B46A67A9B0E82947EE83F7B44CF0'])" + }, + "fixedOutputLength": 62 + } +} diff --git a/testdata/sign1-verify-0000.json b/testdata/sign1-verify-0000.json new file mode 100644 index 0000000..edbbb31 --- /dev/null +++ b/testdata/sign1-verify-0000.json @@ -0,0 +1,21 @@ +{ + "uuid": "66584A57-390B-4A52-B7B6-B7CA4FC4204F", + "title": "Sign1 w/ external input - ECDSA w/ SHA-256 (verify)", + "description": "Verify signature with one signer using ECDSA w/ SHA-256 supplying external input", + "key": { + "kty": "EC", + "crv": "P-256", + "x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8", + "y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4", + "d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM" + }, + "alg": "ES256", + "sign1::verify": { + "taggedCOSESign1": { + "cborHex": "d28443a10126a10442313154546869732069732074686520636f6e74656e742e58403a7487d9a528cb61dd8e99bd652c12577fc47d70ee5af2e703c420584f060fc7a8d61e4a35862b2b531a8447030ab966aeed8dd45ebc507c761431e349995770", + "cborDiag": "18([h'A10126', {4: h'3131'}, h'546869732069732074686520636F6E74656E742E', h'3A7487D9A528CB61DD8E99BD652C12577FC47D70EE5AF2E703C420584F060FC7A8D61E4A35862B2B531A8447030AB966AEED8DD45EBC507C761431E349995770'])" + }, + "external": "11aa22bb33cc44dd55006699", + "shouldVerify": true + } +} diff --git a/testdata/sign1-verify-0001.json b/testdata/sign1-verify-0001.json new file mode 100644 index 0000000..15174b2 --- /dev/null +++ b/testdata/sign1-verify-0001.json @@ -0,0 +1,20 @@ +{ + "uuid": "2AF74107-34AB-4DD5-BC3C-E83895CAE1A4", + "title": "Sign1 - ECDSA w/ SHA-256 (verify)", + "description": "Verify signature with one signer using ECDSA w/ SHA-256", + "key": { + "kty": "EC", + "crv": "P-256", + "x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8", + "y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4", + "d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM" + }, + "alg": "ES256", + "sign1::verify": { + "taggedCOSESign1": { + "cborHex": "d28445a201260300a10442313154546869732069732074686520636f6e74656e742e58402ad3b9dcc1e13d04f357e11cc8acd825196620e62f0d8deca72672508b829d90e07a3f23be6aa36fd6ebd31e2ed08d1760bffd981f991bfc94a45199a54875c4", + "cborDiag": "18([h'A201260300', {4: '11'}, h'546869732069732074686520636F6E74656E742E', h'2AD3B9DCC1E13D04F357E11CC8ACD825196620E62F0D8DECA72672508B829D90E07A3F23BE6AA36FD6EBD31E2ED08D1760BFFD981F991BFC94A45199A54875C4'])" + }, + "shouldVerify": true + } +} diff --git a/testdata/sign1-verify-0002.json b/testdata/sign1-verify-0002.json new file mode 100644 index 0000000..763dea2 --- /dev/null +++ b/testdata/sign1-verify-0002.json @@ -0,0 +1,21 @@ +{ + "uuid": "C5763BDB-5A23-4E9E-9AA2-463A8B107033", + "title": "Sign1 - ECDSA w/ SHA-384 (verify)", + "description": "Verify signature with one signer using ECDSA w/ SHA-384", + "key": { + "kty": "EC", + "kid": "P384", + "crv": "P-384", + "x": "kTJyP2KSsBBhnb4kjWmMF7WHVsY55xUPgb7k64rDcjatChoZ1nvjKmYmPh5STRKc", + "y": "mM0weMVU2DKsYDxDJkEP9hZiRZtB8fPfXbzINZj_fF7YQRynNWedHEyzAJOX2e8s", + "d": "ok3Nq97AXlpEusO7jIy1FZATlBP9PNReMU7DWbkLQ5dU90snHuuHVDjEPmtV0fTo" + }, + "alg": "ES384", + "sign1::verify": { + "taggedCOSESign1": { + "cborHex": "d28444a1013822a104445033383454546869732069732074686520636f6e74656e742e5860aa46c1ab71cd3c1e68ed62c27653797cb72cba3a856fd5e2f38794eee0d666e88139ec51fb62466f4865ca56df493905911e329e829c1887f6259681360a8e7f7d3fd080dcb0720066f13e1621656700c99d6e3771ac2549fde998ee9b1e2cad", + "cborDiag": "18([h'A1013822', {4: 'P384'}, h'546869732069732074686520636F6E74656E742E', h'AA46C1AB71CD3C1E68ED62C27653797CB72CBA3A856FD5E2F38794EEE0D666E88139EC51FB62466F4865CA56DF493905911E329E829C1887F6259681360A8E7F7D3FD080DCB0720066F13E1621656700C99D6E3771AC2549FDE998EE9B1E2CAD'])" + }, + "shouldVerify": true + } +} diff --git a/testdata/sign1-verify-0003.json b/testdata/sign1-verify-0003.json new file mode 100644 index 0000000..d478298 --- /dev/null +++ b/testdata/sign1-verify-0003.json @@ -0,0 +1,22 @@ +{ + "uuid": "5F6E65B5-1E2F-4242-87CE-C76E26E927D8", + "title": "Sign1 - ECDSA w/ SHA-512 (verify)", + "description": "Verify signature with one signer using ECDSA w/ SHA-512", + "key": { + "kty": "EC", + "kid": "bilbo.baggins@hobbiton.example", + "use": "sig", + "crv": "P-521", + "x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt", + "y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVySsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1", + "d": "AAhRON2r9cqXX1hg-RoI6R1tX5p2rUAYdmpHZoC1XNM56KtscrX6zbKipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt" + }, + "alg": "ES512", + "sign1::verify": { + "taggedCOSESign1": { + "cborHex": "d28444a1013823a104581e62696c626f2e62616767696e7340686f626269746f6e2e6578616d706c6554546869732069732074686520636f6e74656e742e58840128bbda237a1b55568da74cefe02cf2d2a6216f80ac757bea8effc056d2f634f6e257077b0dabe9d4b3689eb8228e20f60bc74ff84ae3a38ee9a69e158cbf80f93a017acf5877e5083548a45143b602ccd776c5eb39537a2e68dc8c47ff62e10fc42f045b781e4313fbf421903785c3dfeb181c3a93b46a67a9b0e82947ee83f7b44cf0", + "cborDiag": "18([h'A1013823', {4: 'bilbo.baggins@hobbiton.example'}, h'546869732069732074686520636F6E74656E742E', h'0128BBDA237A1B55568DA74CEFE02CF2D2A6216F80AC757BEA8EFFC056D2F634F6E257077B0DABE9D4B3689EB8228E20F60BC74FF84AE3A38EE9A69E158CBF80F93A017ACF5877E5083548A45143B602CCD776C5EB39537A2E68DC8C47FF62E10FC42F045B781E4313FBF421903785C3DFEB181C3A93B46A67A9B0E82947EE83F7B44CF0'])" + }, + "shouldVerify": true + } +} From aec6397a81a44b0c97f7c945835e4104987adbcb Mon Sep 17 00:00:00 2001 From: qmuntal Date: Mon, 28 Mar 2022 10:24:01 +0200 Subject: [PATCH 2/3] remove legacy example tests Signed-off-by: qmuntal --- helpers_test.go | 129 +-------------------------- sign_verify_cose_wg_examples_test.go | 112 ----------------------- 2 files changed, 3 insertions(+), 238 deletions(-) delete mode 100644 sign_verify_cose_wg_examples_test.go diff --git a/helpers_test.go b/helpers_test.go index ec1fcb1..fd2d557 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -1,14 +1,8 @@ package cose import ( - "crypto/ecdsa" - "crypto/elliptic" "encoding/hex" - "encoding/json" "fmt" - "io/ioutil" - "log" - "path/filepath" ) // consts and helper functions for loading tests @@ -656,10 +650,10 @@ var RSA_PARAMS = COSERustSignatureParameters{ } type RustTestCase struct { - Title string // test fn name in cose-rust/examples/sign_verify/main.rs + Title string // test fn name in cose-rust/examples/sign_verify/main.rs SignAlg *Algorithm // COSE signing algorithm to use - SignPayload []byte // payload to sign - VerifyPayload []byte // payload to verify (defaults to SignPayload) + SignPayload []byte // payload to sign + VerifyPayload []byte // payload to verify (defaults to SignPayload) Certs [][]byte Params []COSERustSignatureParameters // SignError // expected error nil for success/ok @@ -829,52 +823,6 @@ var RustTestCases = []RustTestCase{ var algTag = GetCommonHeaderTagOrPanic("alg") var kidTag = GetCommonHeaderTagOrPanic("kid") -// WGExample -// autogenerated from pass and fail examples on https://mholt.github.io/json-to-go/ -// then combined (added .Fail and .Input.Failures) -type WGExample struct { - Title string `json:"title"` - Fail bool `json:"fail"` - Input struct { - Plaintext string `json:"plaintext"` - Sign struct { - Protected struct { - Ctyp int `json:"ctyp"` - } `json:"protected"` - Signers []struct { - Key struct { - Kty string `json:"kty"` - Kid string `json:"kid"` - Crv string `json:"crv"` - X string `json:"x"` - Y string `json:"y"` - D string `json:"d"` - } `json:"key"` - Unprotected struct { - Kid string `json:"kid"` - } `json:"unprotected"` - Protected struct { - Alg string `json:"alg"` - } `json:"protected"` - External string `json:"external"` - } `json:"signers"` - } `json:"sign"` - Failures struct { - ChangeCBORTag int `json:"ChangeCBORTag"` - } `json:"failures"` - RngDescription string `json:"rng_description"` - } `json:"input"` - Intermediates struct { - Signers []struct { - ToBeSignHex string `json:"ToBeSign_hex"` - } `json:"signers"` - } `json:"intermediates"` - Output struct { - CborDiag string `json:"cbor_diag"` - Cbor string `json:"cbor"` - } `json:"output"` -} - func HexToBytesOrDie(s string) []byte { b, err := hex.DecodeString(s) if err != nil { @@ -882,74 +830,3 @@ func HexToBytesOrDie(s string) []byte { } return b } - -func LoadPrivateKey(example *WGExample) (key ecdsa.PrivateKey) { - if len(example.Input.Sign.Signers) != 1 { - panic(fmt.Sprintf("Not one signer in example: %s", example.Title)) - } - signerInput := example.Input.Sign.Signers[0] - - var curve elliptic.Curve - switch signerInput.Key.Crv { - case "P-256": - curve = elliptic.P256() - case "P-384": - curve = elliptic.P384() - case "P-521": - curve = elliptic.P521() - default: - log.Fatalf("Can't load Private key with curve type: %s", signerInput.Key.Crv) - } - - return ecdsa.PrivateKey{ - PublicKey: ecdsa.PublicKey{ - Curve: curve, - X: FromBase64Int(signerInput.Key.X), - Y: FromBase64Int(signerInput.Key.Y), - }, - D: FromBase64Int(signerInput.Key.D), - } -} - -func LoadExample(path string) WGExample { - var content, err = ioutil.ReadFile(path) - if err != nil { - log.Fatal(err) - } - - var example WGExample - err = json.Unmarshal(content, &example) - if err != nil { - log.Fatal(err) - } - return example -} - -// LoadExamples -func LoadExamples(path string) []WGExample { - files, err := ioutil.ReadDir(path) - if err != nil { - log.Fatal(err) - } - - examples := make([]WGExample, 0) - for _, file := range files { - if file.IsDir() { - continue - } - - var content, err = ioutil.ReadFile(filepath.Join(path, file.Name())) - if err != nil { - log.Fatal(err) - } - - var example WGExample - err = json.Unmarshal(content, &example) - if err != nil { - log.Fatal(err) - } - - examples = append(examples, example) - } - return examples -} diff --git a/sign_verify_cose_wg_examples_test.go b/sign_verify_cose_wg_examples_test.go deleted file mode 100644 index a96f9db..0000000 --- a/sign_verify_cose_wg_examples_test.go +++ /dev/null @@ -1,112 +0,0 @@ -package cose - -import ( - "crypto/rand" - "encoding/hex" - "fmt" - "github.com/stretchr/testify/assert" - "strings" - "testing" -) - -func WGExampleSignsAndVerifies(t *testing.T, example WGExample) { - assert := assert.New(t) - privateKey := LoadPrivateKey(&example) - - // testcases only include one signature - assert.Equal(len(example.Input.Sign.Signers), 1) - - signerInput := example.Input.Sign.Signers[0] - alg := getAlgByNameOrPanic(signerInput.Protected.Alg) - external := HexToBytesOrDie(signerInput.External) - - decoded, err := Unmarshal(HexToBytesOrDie(example.Output.Cbor)) - assert.Nil(err, fmt.Sprintf("%s: Error decoding example CBOR", example.Title)) - - if ExpectCastToFail(example.Title) { - return - } - - message, ok := decoded.(SignMessage) - assert.True(ok, fmt.Sprintf("%s: Error casting example CBOR to SignMessage", example.Title)) - - signer, err := NewSignerFromKey(alg, &privateKey) - assert.Nil(err, fmt.Sprintf("%s: Error creating signer %s", example.Title, err)) - - verifier := signer.Verifier() - - // Test Verify - signatures CBOR decoded from example - assert.NotNil(message.Signatures[0].SignatureBytes) - err = message.Verify(external, []Verifier{*verifier}) - if example.Fail { - assert.NotNil(err, fmt.Sprintf("%s: verifying signature did not fail. Got nil instead of error from signature verification failure", example.Title)) - - // signing should not necessarily fail and the - // intermediates are wrong for fail test cases - return - } - assert.Nil(err, fmt.Sprintf("%s: error verifying signature %+v", example.Title, err)) - - // Test Sign - - // clear the signature - message.Signatures[0].SignatureBytes = nil - - err = message.Sign(rand.Reader, external, []Signer{*signer}) - assert.Nil(err, fmt.Sprintf("%s: signing failed with err %s", example.Title, err)) - - // check intermediate - ToBeSigned, err := message.SigStructure(external, &message.Signatures[0]) - assert.Nil(err, fmt.Sprintf("%s: signing failed with err %s", example.Title, err)) - assert.Equal(example.Intermediates.Signers[0].ToBeSignHex, - strings.ToUpper(hex.EncodeToString(ToBeSigned)), - fmt.Sprintf("%s: signing wrong Hex Intermediate", example.Title)) - - // Verify our signature (round trip) - digest, err := hashSigStructure(ToBeSigned, alg.HashFunc) - assert.Nil(err, fmt.Sprintf("%s: round trip failed to hash signature %s", example.Title, err)) - - err = verifier.Verify(digest, message.Signatures[0].SignatureBytes) - assert.Nil(err, fmt.Sprintf("%s: round trip signature verification failed with err %s", example.Title, err)) -} - -var SkipExampleTitles = map[string]bool{ - "ECDSA-01: ECDSA - P-256": false, // ecdsa-01.json - "ECDSA-02: ECDSA - P-384": false, // ecdsa-02.json - - "ECDSA-03: ECDSA - P-512": false, // ecdsa-03.json - - // not recommended "SHA-256 be used only with curve P-256, - // SHA-384 be used only with curve P-384, and SHA-512 be used - // with curve P-521" - "ECDSA-01: ECDSA - P-256 w/ SHA-512": true, // ecdsa-04.json - - // unsupported message types - "ECDSA-01: ECDSA - P-256 - sign0": true, // ecdsa-sig-01.json - "ECDSA-sig-02: ECDSA - P-384 - sign1": true, // ecdsa-sig-02.json - "ECDSA-03: ECDSA - P-512 - sign0": true, // ecdsa-sig-03.json - "ECDSA-sig-01: ECDSA - P-256 w/ SHA-512 - implicit": true, // ecdsa-sig-04.json -} - -func ExpectCastToFail(title string) (shouldFail bool) { - // (g-k) these decode but not to SignMessages since I - // haven't found a way to get ugorji/go/codec to use our - // extension to decode without the right CBOR tag - return title == "sign-pass-03: Remove CBOR Tag" || title == "sign-fail-01: Wrong CBOR Tag" -} - -func TestWGExamples(t *testing.T) { - examples := append( - LoadExamples("./test/cose-wg-examples/sign-tests"), - LoadExamples("./test/cose-wg-examples/ecdsa-examples")..., - ) - - for _, example := range examples { - t.Run(fmt.Sprintf("Example: %s %v", example.Title, example.Fail), func(t *testing.T) { - if v, ok := SkipExampleTitles[example.Title]; ok && v { - return - } - WGExampleSignsAndVerifies(t, example) - }) - } -} From 4d621e9615073eb2ee8cf9222bd45fc3c54a6bec Mon Sep 17 00:00:00 2001 From: qmuntal Date: Tue, 29 Mar 2022 15:53:20 +0200 Subject: [PATCH 3/3] fix url sample source Signed-off-by: qmuntal --- conformance_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conformance_test.go b/conformance_test.go index c60930e..7caff86 100644 --- a/conformance_test.go +++ b/conformance_test.go @@ -52,7 +52,7 @@ type CBOR struct { } // Conformance samples are taken from -// https://github.com/cose-wg/Examples. +// https://github.com/gluecose/test-vectors. var testCases = []string{ "sign1-sign-0000", "sign1-sign-0001",