-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: qmuntal <qmuntaldiaz@microsoft.com>
- Loading branch information
Showing
9 changed files
with
507 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} | ||
} |
Oops, something went wrong.