From 8284110a8d90fb237adf24ea24c667788f7c66f9 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Tue, 11 Jul 2023 14:39:12 +0200 Subject: [PATCH] refactor Key to support custom parameters Signed-off-by: qmuntal --- common.go | 96 ----- common_test.go | 143 -------- key.go | 423 +++++++++++++-------- key_test.go | 977 +++++++++++++++++++++++++++++++++++-------------- 4 files changed, 960 insertions(+), 679 deletions(-) delete mode 100644 common.go delete mode 100644 common_test.go diff --git a/common.go b/common.go deleted file mode 100644 index 32294a5..0000000 --- a/common.go +++ /dev/null @@ -1,96 +0,0 @@ -package cose - -import ( - "errors" - "fmt" -) - -// intOrStr is a value that can be either an int or a tstr when serialized to -// CBOR. -type intOrStr struct { - intVal int64 - strVal string - isString bool -} - -func newIntOrStr(v interface{}) *intOrStr { - var ios intOrStr - if err := ios.Set(v); err != nil { - return nil - } - return &ios -} - -func (ios intOrStr) Int() int64 { - return ios.intVal -} - -func (ios intOrStr) String() string { - if ios.IsString() { - return ios.strVal - } - return fmt.Sprint(ios.intVal) -} - -func (ios intOrStr) IsInt() bool { - return !ios.isString -} - -func (ios intOrStr) IsString() bool { - return ios.isString -} - -func (ios intOrStr) Value() interface{} { - if ios.IsInt() { - return ios.intVal - } - - return ios.strVal -} - -func (ios *intOrStr) Set(v interface{}) error { - switch t := v.(type) { - case int64: - ios.intVal = t - ios.strVal = "" - ios.isString = false - case int: - ios.intVal = int64(t) - ios.strVal = "" - ios.isString = false - case string: - ios.strVal = t - ios.intVal = 0 - ios.isString = true - default: - return fmt.Errorf("must be int or string, found %T", t) - } - - return nil -} - -// MarshalCBOR returns the encoded CBOR representation of the intOrString, as -// either int or tstr, depending on the value. If no value has been set, -// intOrStr is encoded as a zero-length tstr. -func (ios intOrStr) MarshalCBOR() ([]byte, error) { - if ios.IsInt() { - return encMode.Marshal(ios.intVal) - } - - return encMode.Marshal(ios.strVal) -} - -// UnmarshalCBOR unmarshals the provided CBOR encoded data (must be an int, -// uint, or tstr). -func (ios *intOrStr) UnmarshalCBOR(data []byte) error { - if len(data) == 0 { - return errors.New("zero length buffer") - } - - var val interface{} - if err := decMode.Unmarshal(data, &val); err != nil { - return err - } - - return ios.Set(val) -} diff --git a/common_test.go b/common_test.go deleted file mode 100644 index ed981b7..0000000 --- a/common_test.go +++ /dev/null @@ -1,143 +0,0 @@ -package cose - -import ( - "bytes" - "reflect" - "testing" - - "github.com/fxamacker/cbor/v2" -) - -func Test_intOrStr(t *testing.T) { - ios := newIntOrStr(3) - assertEqual(t, true, ios.IsInt()) - assertEqual(t, false, ios.IsString()) - assertEqual(t, 3, ios.Int()) - assertEqual(t, "3", ios.String()) - - ios = newIntOrStr("foo") - assertEqual(t, false, ios.IsInt()) - assertEqual(t, true, ios.IsString()) - assertEqual(t, 0, ios.Int()) - assertEqual(t, "foo", ios.String()) - - ios = newIntOrStr(3.5) - if ios != nil { - t.Errorf("Expected nil, got %v", ios) - } -} - -func Test_intOrStr_CBOR(t *testing.T) { - ios := newIntOrStr(3) - data, err := ios.MarshalCBOR() - requireNoError(t, err) - assertEqual(t, []byte{0x03}, data) - - ios = &intOrStr{} - err = ios.UnmarshalCBOR(data) - requireNoError(t, err) - assertEqual(t, true, ios.IsInt()) - assertEqual(t, 3, ios.Int()) - - ios = newIntOrStr("foo") - data, err = ios.MarshalCBOR() - requireNoError(t, err) - assertEqual(t, []byte{0x63, 0x66, 0x6f, 0x6f}, data) - - ios = &intOrStr{} - err = ios.UnmarshalCBOR(data) - requireNoError(t, err) - assertEqual(t, true, ios.IsString()) - assertEqual(t, "foo", ios.String()) - - // empty value as field - s := struct { - Field1 intOrStr `cbor:"1,keyasint"` - Field2 int `cbor:"2,keyasint"` - }{Field1: intOrStr{}, Field2: 7} - - data, err = cbor.Marshal(s) - requireNoError(t, err) - assertEqual(t, []byte{0xa2, 0x1, 0x00, 0x2, 0x7}, data) - - ios = &intOrStr{} - data = []byte{0x22} - err = ios.UnmarshalCBOR(data) - requireNoError(t, err) - assertEqual(t, true, ios.IsInt()) - assertEqual(t, -3, ios.Int()) - - data = []byte{} - err = ios.UnmarshalCBOR(data) - assertEqualError(t, err, "zero length buffer") - - data = []byte{0x40} - err = ios.UnmarshalCBOR(data) - assertEqualError(t, err, "must be int or string, found []uint8") - - data = []byte{0xff, 0xff} - err = ios.UnmarshalCBOR(data) - assertEqualError(t, err, "cbor: unexpected \"break\" code") -} - -func requireNoError(t *testing.T, err error) { - t.Helper() - if err != nil { - t.Errorf("Unexpected error: %q", err) - t.Fail() - } -} - -func assertEqualError(t *testing.T, err error, expected string) { - t.Helper() - if err == nil || err.Error() != expected { - t.Errorf("Unexpected error: want %q, got %q", expected, err) - } -} - -func assertEqual(t *testing.T, expected, actual interface{}) { - t.Helper() - if !objectsAreEqualValues(expected, actual) { - t.Errorf("Unexpected value: want %v, got %v", expected, actual) - } -} - -// taken from github.com/stretchr/testify -func objectsAreEqualValues(expected, actual interface{}) bool { - if objectsAreEqual(expected, actual) { - return true - } - - actualType := reflect.TypeOf(actual) - if actualType == nil { - return false - } - expectedValue := reflect.ValueOf(expected) - if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) { - // Attempt comparison after type conversion - return reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), actual) - } - - return false -} - -// taken from github.com/stretchr/testify -func objectsAreEqual(expected, actual interface{}) bool { - if expected == nil || actual == nil { - return expected == actual - } - - exp, ok := expected.([]byte) - if !ok { - return reflect.DeepEqual(expected, actual) - } - - act, ok := actual.([]byte) - if !ok { - return false - } - if exp == nil || act == nil { - return exp == nil && act == nil - } - return bytes.Equal(exp, act) -} diff --git a/key.go b/key.go index 80a22d8..b4b11d8 100644 --- a/key.go +++ b/key.go @@ -8,9 +8,29 @@ import ( "errors" "fmt" "math/big" + "reflect" "strconv" +) + +const ( + KeyLabelOKPCurve int64 = -1 + KeyLabelOKPX int64 = -2 + KeyLabelOKPD int64 = -4 + + KeyLabelEC2Curve int64 = -1 + KeyLabelEC2X int64 = -2 + KeyLabelEC2Y int64 = -3 + KeyLabelEC2D int64 = -4 - cbor "github.com/fxamacker/cbor/v2" + KeyLabelSymmetricK int64 = -1 +) + +const ( + keyLabelKeyType int64 = 1 + keyLabelKeyID int64 = 2 + keyLabelAlgorithm int64 = 3 + keyLabelKeyOps int64 = 4 + keyLabelBaseIV int64 = 5 ) const ( @@ -55,26 +75,26 @@ type KeyOp int64 // KeyOpFromString returns the KeyOp corresponding to the specified name. // The values are taken from https://www.rfc-editor.org/rfc/rfc7517#section-4.3 -func KeyOpFromString(val string) (KeyOp, error) { +func KeyOpFromString(val string) (KeyOp, bool) { switch val { case "sign": - return KeyOpSign, nil + return KeyOpSign, true case "verify": - return KeyOpVerify, nil + return KeyOpVerify, true case "encrypt": - return KeyOpEncrypt, nil + return KeyOpEncrypt, true case "decrypt": - return KeyOpDecrypt, nil + return KeyOpDecrypt, true case "wrapKey": - return KeyOpWrapKey, nil + return KeyOpWrapKey, true case "unwrapKey": - return KeyOpUnwrapKey, nil + return KeyOpUnwrapKey, true case "deriveKey": - return KeyOpDeriveKey, nil + return KeyOpDeriveKey, true case "deriveBits": - return KeyOpDeriveBits, nil + return KeyOpDeriveBits, true default: - return KeyOpInvalid, fmt.Errorf("unknown key_ops value %q", val) + return KeyOpInvalid, false } } @@ -110,35 +130,6 @@ func (ko KeyOp) String() string { } } -// MarshalCBOR marshals the KeyOp as a CBOR int. -func (ko KeyOp) MarshalCBOR() ([]byte, error) { - return encMode.Marshal(int64(ko)) -} - -// UnmarshalCBOR populates the KeyOp from the provided CBOR value (must be int -// or tstr). -func (ko *KeyOp) UnmarshalCBOR(data []byte) error { - var raw intOrStr - - if err := raw.UnmarshalCBOR(data); err != nil { - return fmt.Errorf("invalid key_ops value %w", err) - } - - if raw.IsString() { - v, err := KeyOpFromString(raw.String()) - if err != nil { - return err - } - - *ko = v - } else { - v := raw.Int() - *ko = KeyOp(v) - } - - return nil -} - // KeyType identifies the family of keys represented by the associated Key. // This determines which files within the Key must be set in order for it to be // valid. @@ -229,38 +220,22 @@ func (c Curve) String() string { // Key represents a COSE_Key structure, as defined by RFC8152. // Note: currently, this does NOT support RFC8230 (RSA algorithms). type Key struct { - // Common parameters. These are independent of the key type. Only - // KeyType common parameter MUST be set. - // KeyType identifies the family of keys for this structure, and thus, // which of the key-type-specific parameters need to be set. - KeyType KeyType `cbor:"1,keyasint"` + KeyType KeyType // KeyID is the identification value matched to the kid in the message. - KeyID []byte `cbor:"2,keyasint,omitempty"` + KeyID []byte // Algorithm is used to restrict the algorithm that is used with the // key. If it is set, the application MUST verify that it matches the // algorithm for which the Key is being used. - Algorithm Algorithm `cbor:"3,keyasint,omitempty"` + Algorithm Algorithm // KeyOps can be set to restrict the set of operations that the Key is used for. - KeyOps []KeyOp `cbor:"4,keyasint,omitempty"` + KeyOps []KeyOp // BaseIV is the Base IV to be xor-ed with Partial IVs. - BaseIV []byte `cbor:"5,keyasint,omitempty"` - - // Curve is EC identifier -- taken form "COSE Elliptic Curves" IANA registry. - // Populated from keyStruct.RawKeyParam when key type is EC2 or OKP. - Curve Curve `cbor:"-"` - // K is the key value. Populated from keyStruct.RawKeyParam when key - // type is Symmetric. - K []byte `cbor:"-"` - - // EC2/OKP params - - // X is the x-coordinate - X []byte `cbor:"-2,keyasint,omitempty"` - // Y is the y-coordinate (sign bits are not supported) - Y []byte `cbor:"-3,keyasint,omitempty"` - // D is the private key - D []byte `cbor:"-4,keyasint,omitempty"` + BaseIV []byte + + // Any additional parameter (label,value) pairs. + Params map[interface{}]interface{} } // NewOKPKey returns a Key created using the provided Octet Key Pair data. @@ -272,9 +247,15 @@ func NewOKPKey(alg Algorithm, x, d []byte) (*Key, error) { key := &Key{ KeyType: KeyTypeOKP, Algorithm: alg, - Curve: CurveEd25519, - X: x, - D: d, + Params: map[interface{}]interface{}{ + KeyLabelOKPCurve: CurveEd25519, + }, + } + if x != nil { + key.Params[KeyLabelOKPX] = x + } + if d != nil { + key.Params[KeyLabelOKPD] = d } if err := key.validate(KeyOpInvalid); err != nil { return nil, err @@ -282,6 +263,21 @@ func NewOKPKey(alg Algorithm, x, d []byte) (*Key, error) { return key, nil } +// OKP returns the Octet Key Pair parameters for the key. +func (k *Key) OKP() (crv Curve, x []byte, d []byte) { + var ok bool + crv, ok = k.Params[KeyLabelOKPCurve].(Curve) + if !ok { + v := reflect.ValueOf(k.Params[KeyLabelOKPCurve]) + if v.CanInt() { + crv = Curve(v.Int()) + } + } + x, _ = k.Params[KeyLabelOKPX].([]byte) + d, _ = k.Params[KeyLabelOKPD].([]byte) + return +} + // NewEC2Key returns a Key created using the provided elliptic curve key // data. func NewEC2Key(alg Algorithm, x, y, d []byte) (*Key, error) { @@ -301,10 +297,18 @@ func NewEC2Key(alg Algorithm, x, y, d []byte) (*Key, error) { key := &Key{ KeyType: KeyTypeEC2, Algorithm: alg, - Curve: curve, - X: x, - Y: y, - D: d, + Params: map[interface{}]interface{}{ + KeyLabelEC2Curve: curve, + }, + } + if x != nil { + key.Params[KeyLabelEC2X] = x + } + if y != nil { + key.Params[KeyLabelEC2Y] = y + } + if d != nil { + key.Params[KeyLabelEC2D] = d } if err := key.validate(KeyOpInvalid); err != nil { return nil, err @@ -312,15 +316,39 @@ func NewEC2Key(alg Algorithm, x, y, d []byte) (*Key, error) { return key, nil } +// EC2 returns the Elliptic Curve parameters for the key. +func (k *Key) EC2() (crv Curve, x []byte, y, d []byte) { + var ok bool + crv, ok = k.Params[KeyLabelEC2Curve].(Curve) + if !ok { + v := reflect.ValueOf(k.Params[KeyLabelEC2Curve]) + if v.CanInt() { + crv = Curve(v.Int()) + } + } + x, _ = k.Params[KeyLabelEC2X].([]byte) + y, _ = k.Params[KeyLabelEC2Y].([]byte) + d, _ = k.Params[KeyLabelEC2D].([]byte) + return +} + // NewSymmetricKey returns a Key created using the provided Symmetric key // bytes. func NewSymmetricKey(k []byte) *Key { return &Key{ KeyType: KeyTypeSymmetric, - K: k, + Params: map[interface{}]interface{}{ + KeyLabelSymmetricK: k, + }, } } +// Symmetric returns the Symmetric parameters for the key. +func (key *Key) Symmetric() (k []byte) { + k, _ = key.Params[KeyLabelSymmetricK].([]byte) + return +} + // NewKeyFromPublic returns a Key created using the provided crypto.PublicKey. // Supported key formats are: *ecdsa.PublicKey and ed25519.PublicKey func NewKeyFromPublic(pub crypto.PublicKey) (*Key, error) { @@ -365,55 +393,62 @@ func NewKeyFromPrivate(priv crypto.PrivateKey) (*Key, error) { func (k Key) validate(op KeyOp) error { switch k.KeyType { case KeyTypeEC2: + crv, x, y, d := k.EC2() switch op { case KeyOpVerify: - if len(k.X) == 0 || len(k.Y) == 0 { + if len(x) == 0 || len(y) == 0 { return ErrEC2NoPub } case KeyOpSign: - if len(k.D) == 0 { + if len(d) == 0 { return ErrNotPrivKey } } - if k.Curve == CurveInvalid || (len(k.X) == 0 && len(k.Y) == 0 && len(k.D) == 0) { + if crv == CurveInvalid || (len(x) == 0 && len(y) == 0 && len(d) == 0) { return ErrInvalidKey } - switch k.Curve { + switch crv { case CurveX25519, CurveX448, CurveEd25519, CurveEd448: return fmt.Errorf( "Key type mismatch for curve %q (must be OKP, found EC2)", - k.Curve.String(), + crv.String(), ) default: // ok -- a key may contain a currently unsupported curve // see https://www.rfc-editor.org/rfc/rfc8152#section-13.1.1 } case KeyTypeOKP: + crv, x, d := k.OKP() switch op { case KeyOpVerify: - if len(k.X) == 0 { + if len(x) == 0 { return ErrOKPNoPub } case KeyOpSign: - if len(k.D) == 0 { + if len(d) == 0 { return ErrNotPrivKey } } - if k.Curve == CurveInvalid || (len(k.X) == 0 && len(k.D) == 0) { + if crv == CurveInvalid || (len(x) == 0 && len(d) == 0) { return ErrInvalidKey } - switch k.Curve { + switch crv { case CurveP256, CurveP384, CurveP521: return fmt.Errorf( "Key type mismatch for curve %q (must be EC2, found OKP)", - k.Curve.String(), + crv.String(), ) default: // ok -- a key may contain a currently unsupported curve // see https://www.rfc-editor.org/rfc/rfc8152#section-13.2 } case KeyTypeSymmetric: - // Nothing to validate + k := k.Symmetric() + if len(k) == 0 { + return ErrInvalidKey + } + case KeyTypeInvalid: + return errors.New("invalid kty value 0") default: // Unknown key type, we can't validate custom parameters. } @@ -449,85 +484,114 @@ func (k Key) canOp(op KeyOp) bool { return false } -type keyalias Key - -type marshaledKey struct { - keyalias - - // RawKeyParam contains the raw CBOR encoded data for the label -1. - // Depending on the KeyType this is used to populate either Curve or K - // below. - RawKeyParam cbor.RawMessage `cbor:"-1,keyasint,omitempty"` -} - // MarshalCBOR encodes Key into a COSE_Key object. func (k *Key) MarshalCBOR() ([]byte, error) { - tmp := marshaledKey{ - keyalias: keyalias(*k), + tmp := map[interface{}]interface{}{ + keyLabelKeyType: k.KeyType, } - var err error - - switch k.KeyType { - case KeyTypeSymmetric: - if tmp.RawKeyParam, err = encMode.Marshal(k.K); err != nil { - return nil, err + if k.KeyID != nil { + tmp[keyLabelKeyID] = k.KeyID + } + if k.Algorithm != AlgorithmInvalid { + tmp[keyLabelAlgorithm] = k.Algorithm + } + if k.KeyOps != nil { + tmp[keyLabelKeyOps] = k.KeyOps + } + if k.BaseIV != nil { + tmp[keyLabelBaseIV] = k.BaseIV + } + existing := make(map[interface{}]struct{}, len(k.Params)) + for label, v := range k.Params { + lbl, ok := normalizeLabel(label) + if !ok { + return nil, fmt.Errorf("invalid label type %T", label) } - case KeyTypeEC2, KeyTypeOKP: - if tmp.RawKeyParam, err = encMode.Marshal(k.Curve); err != nil { - return nil, err + if _, ok := existing[lbl]; ok { + return nil, fmt.Errorf("duplicate label %v", lbl) } - default: - return nil, fmt.Errorf("invalid key type: %q", k.KeyType.String()) + existing[lbl] = struct{}{} + tmp[lbl] = v } return encMode.Marshal(tmp) } // UnmarshalCBOR decodes a COSE_Key object into Key. func (k *Key) UnmarshalCBOR(data []byte) error { - var tmp marshaledKey - + var tmp map[interface{}]interface{} if err := decMode.Unmarshal(data, &tmp); err != nil { return err } - if tmp.KeyType == KeyTypeInvalid { - return errors.New("invalid key type value 0") - } - - *k = Key(tmp.keyalias) - - switch k.KeyType { - case KeyTypeEC2: - if tmp.RawKeyParam == nil { - return errors.New("missing Curve parameter (required for EC2 key type)") - } - - if err := decMode.Unmarshal(tmp.RawKeyParam, &k.Curve); err != nil { - return err - } - case KeyTypeOKP: - if tmp.RawKeyParam == nil { - return errors.New("missing Curve parameter (required for OKP key type)") - } - if err := decMode.Unmarshal(tmp.RawKeyParam, &k.Curve); err != nil { - return err - } - case KeyTypeSymmetric: - if tmp.RawKeyParam == nil { - return errors.New("missing K parameter (required for Symmetric key type)") + *k = Key{} + kty, exist, err := decodeInt(tmp, keyLabelKeyType) + if !exist { + return errors.New("kty: missing") + } + if err != nil { + return fmt.Errorf("kty: %w", err) + } + k.KeyType = KeyType(kty) + if k.KeyType == KeyTypeInvalid { + return errors.New("kty: invalid value 0") + } + k.KeyID, err = decodeBstr(tmp, keyLabelKeyID) + if err != nil { + return fmt.Errorf("kid: %w", err) + } + alg, _, err := decodeInt(tmp, keyLabelAlgorithm) + if err != nil { + return fmt.Errorf("alg: %w", err) + } + k.Algorithm = Algorithm(alg) + key_ops, err := decodeArray(tmp, keyLabelKeyOps) + if err != nil { + return fmt.Errorf("key_ops: %w", err) + } + if len(key_ops) > 0 { + k.KeyOps = make([]KeyOp, len(key_ops)) + for i, op := range key_ops { + switch op := op.(type) { + case int64: + k.KeyOps[i] = KeyOp(op) + case string: + var ok bool + if k.KeyOps[i], ok = KeyOpFromString(op); !ok { + return fmt.Errorf("key_ops: unknown entry value %q", op) + } + default: + return fmt.Errorf("key_ops: invalid entry type %T", op) + } } - - if err := decMode.Unmarshal(tmp.RawKeyParam, &k.K); err != nil { - return err + } + k.BaseIV, err = decodeBstr(tmp, keyLabelBaseIV) + if err != nil { + return fmt.Errorf("base_iv: %w", err) + } + + delete(tmp, keyLabelKeyType) + delete(tmp, keyLabelKeyID) + delete(tmp, keyLabelAlgorithm) + delete(tmp, keyLabelKeyOps) + delete(tmp, keyLabelBaseIV) + + if len(tmp) > 0 { + k.Params = make(map[interface{}]interface{}, len(tmp)) + for lbl, v := range tmp { + switch lbl := lbl.(type) { + case int64: + if (k.KeyType == KeyTypeEC2 || k.KeyType == KeyTypeOKP) && + (lbl == KeyLabelEC2Curve || lbl == KeyLabelOKPCurve) { + v = Curve(v.(int64)) + } + k.Params[lbl] = v + case string: + k.Params[lbl] = v + default: + return fmt.Errorf("invalid label type %T", lbl) + } } - default: - // this should not be reachable as KeyType.UnmarshalCBOR would - // result in an error during decMode.Unmarshal() above, if the - // value in the data doesn't correspond to one of the above - // types. - return fmt.Errorf("unexpected key type %q", k.KeyType.String()) } - return k.validate(KeyOpInvalid) } @@ -554,13 +618,16 @@ func (k *Key) PublicKey() (crypto.PublicKey, error) { curve = elliptic.P521() } + _, x, y, _ := k.EC2() + pub := &ecdsa.PublicKey{Curve: curve, X: new(big.Int), Y: new(big.Int)} - pub.X.SetBytes(k.X) - pub.Y.SetBytes(k.Y) + pub.X.SetBytes(x) + pub.Y.SetBytes(y) return pub, nil case AlgorithmEd25519: - return ed25519.PublicKey(k.X), nil + _, x, _ := k.OKP() + return ed25519.PublicKey(x), nil default: return nil, ErrAlgorithmNotSupported } @@ -578,10 +645,11 @@ func (k *Key) PrivateKey() (crypto.PrivateKey, error) { switch alg { case AlgorithmES256, AlgorithmES384, AlgorithmES512: + _, x, y, d := k.EC2() // RFC8152 allows omitting X and Y from private keys; // crypto.PrivateKey assumes they are available. // see https://www.rfc-editor.org/rfc/rfc8152#section-13.1.1 - if len(k.X) == 0 || len(k.Y) == 0 { + if len(x) == 0 || len(y) == 0 { return nil, ErrEC2NoPub } @@ -600,23 +668,24 @@ func (k *Key) PrivateKey() (crypto.PrivateKey, error) { PublicKey: ecdsa.PublicKey{Curve: curve, X: new(big.Int), Y: new(big.Int)}, D: new(big.Int), } - priv.X.SetBytes(k.X) - priv.Y.SetBytes(k.Y) - priv.D.SetBytes(k.D) + priv.X.SetBytes(x) + priv.Y.SetBytes(y) + priv.D.SetBytes(d) return priv, nil case AlgorithmEd25519: + _, x, d := k.OKP() // RFC8152 allows omitting X from private keys; // crypto.PrivateKey assumes it is available. // see https://www.rfc-editor.org/rfc/rfc8152#section-13.2 - if len(k.X) == 0 { + if len(x) == 0 { return nil, ErrOKPNoPub } buf := make([]byte, ed25519.PrivateKeySize) - copy(buf, k.D) - copy(buf[32:], k.X) + copy(buf, d) + copy(buf[32:], x) return ed25519.PrivateKey(buf), nil default: @@ -684,7 +753,8 @@ func (k *Key) Verifier() (Verifier, error) { func (k *Key) deriveAlgorithm() (Algorithm, error) { switch k.KeyType { case KeyTypeEC2: - switch k.Curve { + crv, _, _, _ := k.EC2() + switch crv { case CurveP256: return AlgorithmES256, nil case CurveP384: @@ -693,15 +763,16 @@ func (k *Key) deriveAlgorithm() (Algorithm, error) { return AlgorithmES512, nil default: return AlgorithmInvalid, fmt.Errorf( - "unsupported curve %q for key type EC2", k.Curve.String()) + "unsupported curve %q for key type EC2", crv.String()) } case KeyTypeOKP: - switch k.Curve { + crv, _, _ := k.OKP() + switch crv { case CurveEd25519: return AlgorithmEd25519, nil default: return AlgorithmInvalid, fmt.Errorf( - "unsupported curve %q for key type OKP", k.Curve.String()) + "unsupported curve %q for key type OKP", crv.String()) } default: // Symmetric algorithms are not supported in the current inmplementation. @@ -721,3 +792,39 @@ func algorithmFromEllipticCurve(c elliptic.Curve) Algorithm { return AlgorithmInvalid } } + +func decodeBstr(dic map[interface{}]interface{}, lbl interface{}) ([]byte, error) { + v, ok := dic[lbl] + if !ok { + return nil, nil + } + bstr, ok := v.([]byte) + if !ok { + return nil, fmt.Errorf("invalid type: expected []uint8, got %T", v) + } + return bstr, nil +} + +func decodeInt(dic map[interface{}]interface{}, lbl interface{}) (int64, bool, error) { + v, ok := dic[lbl] + if !ok { + return 0, false, nil + } + cint, ok := v.(int64) + if !ok { + return 0, true, fmt.Errorf("invalid type: expected int64, got %T", v) + } + return cint, true, nil +} + +func decodeArray(dic map[interface{}]interface{}, lbl interface{}) ([]interface{}, error) { + v, ok := dic[lbl] + if !ok { + return nil, nil + } + arr, ok := v.([]interface{}) + if !ok { + return nil, fmt.Errorf("invalid type: expected []interface{}, got %T", v) + } + return arr, nil +} diff --git a/key_test.go b/key_test.go index aa60f64..1e12ba7 100644 --- a/key_test.go +++ b/key_test.go @@ -6,99 +6,35 @@ import ( "crypto/ed25519" "crypto/elliptic" "crypto/rand" + "encoding/hex" "math/big" "reflect" "testing" - - "github.com/fxamacker/cbor/v2" ) -func Test_KeyOp(t *testing.T) { - - tvs := []struct { - Name string - Value KeyOp +func TestKeyOp_String(t *testing.T) { + tests := []struct { + op KeyOp + want string }{ - {"sign", KeyOpSign}, - {"verify", KeyOpVerify}, - {"encrypt", KeyOpEncrypt}, - {"decrypt", KeyOpDecrypt}, - {"wrapKey", KeyOpWrapKey}, - {"unwrapKey", KeyOpUnwrapKey}, - {"deriveKey", KeyOpDeriveKey}, - {"deriveBits", KeyOpDeriveBits}, + {KeyOpSign, "sign"}, + {KeyOpVerify, "verify"}, + {KeyOpEncrypt, "encrypt"}, + {KeyOpDecrypt, "decrypt"}, + {KeyOpWrapKey, "wrapKey"}, + {KeyOpUnwrapKey, "unwrapKey"}, + {KeyOpDeriveKey, "deriveKey"}, + {KeyOpDeriveBits, "deriveBits"}, + {KeyOpMACCreate, "MAC create"}, + {KeyOpMACVerify, "MAC verify"}, + {42, "unknown key_op value 42"}, } - for _, tv := range tvs { - if tv.Name != tv.Value.String() { - t.Errorf( - "String value mismatch: expected %q, got %q", - tv.Name, - tv.Value.String(), - ) - } - - data, err := cbor.Marshal(tv.Name) - if err != nil { - t.Errorf("Unexpected error: %s", err) - return - } - - var ko KeyOp - err = cbor.Unmarshal(data, &ko) - if err != nil { - t.Errorf("Unexpected error: %s", err) - return - } - if tv.Value != ko { - t.Errorf( - "Value mismatch: want %v, got %v", - tv.Value, - ko, - ) - } - - data, err = cbor.Marshal(int(tv.Value)) - if err != nil { - t.Errorf("Unexpected error: %q", err) - return - } - - err = cbor.Unmarshal(data, &ko) - if err != nil { - t.Errorf("Unexpected error: %q", err) - return - } - if tv.Value != ko { - t.Errorf( - "Value mismatch: want %v, got %v", - tv.Value, - ko, - ) + for _, tt := range tests { + if got := tt.op.String(); got != tt.want { + t.Errorf("KeyOp.String() = %v, want %v", got, tt.want) } } - - var ko KeyOp - - data := []byte{0x63, 0x66, 0x6f, 0x6f} - err := ko.UnmarshalCBOR(data) - assertEqualError(t, err, `unknown key_ops value "foo"`) - - data = []byte{0x40} - err = ko.UnmarshalCBOR(data) - assertEqualError(t, err, "invalid key_ops value must be int or string, found []uint8") - - if KeyOpMACCreate.String() != "MAC create" { - t.Errorf("Unexpected value: %q", KeyOpMACCreate.String()) - } - - if KeyOpMACVerify.String() != "MAC verify" { - t.Errorf("Unexpected value: %q", KeyOpMACVerify.String()) - } - - if KeyOp(42).String() != "unknown key_op value 42" { - t.Errorf("Unexpected value: %q", KeyOp(42).String()) - } } func TestKey_UnmarshalCBOR(t *testing.T) { @@ -109,64 +45,144 @@ func TestKey_UnmarshalCBOR(t *testing.T) { wantErr string }{ { - name: "ok OKP", + name: "invalid COSE_Key CBOR type", data: []byte{ - 0xa5, // map (5) + 0x82, // array(2) 0x01, 0x01, // kty: OKP - 0x03, 0x27, // alg: EdDSA w/ Ed25519 - 0x04, // key ops - 0x81, // array (1) - 0x02, // verify - 0x20, 0x06, // curve: Ed25519 - 0x21, 0x58, 0x20, // x-coordinate: bytes(32) - 0x15, 0x52, 0x2e, 0xf1, 0x57, 0x29, 0xcc, 0xf3, // 32-byte value - 0x95, 0x09, 0xea, 0x5c, 0x15, 0xa2, 0x6b, 0xe9, - 0x49, 0xe3, 0x88, 0x07, 0xa5, 0xc2, 0x6e, 0xf9, - 0x28, 0x14, 0x87, 0xef, 0x4a, 0xe6, 0x7b, 0x46, }, - want: &Key{ - KeyType: KeyTypeOKP, - Algorithm: AlgorithmEd25519, - KeyOps: []KeyOp{KeyOpVerify}, - Curve: CurveEd25519, - X: []byte{ - 0x15, 0x52, 0x2e, 0xf1, 0x57, 0x29, 0xcc, 0xf3, - 0x95, 0x09, 0xea, 0x5c, 0x15, 0xa2, 0x6b, 0xe9, - 0x49, 0xe3, 0x88, 0x07, 0xa5, 0xc2, 0x6e, 0xf9, - 0x28, 0x14, 0x87, 0xef, 0x4a, 0xe6, 0x7b, 0x46, - }, + want: nil, + wantErr: "cbor: cannot unmarshal array into Go value of type map[interface {}]interface {}", + }, { + name: "invalid kty value", + data: []byte{ + 0xa2, // map(2) + 0x01, 0x41, 0x01, // kty: bytes(1) + 0x02, 0x43, 0x01, 0x02, 0x03, // kdi: bytes(3) }, - wantErr: "", - }, - { + want: nil, + wantErr: "kty: invalid type: expected int64, got []uint8", + }, { + name: "missing kty", + data: []byte{ + 0xa1, // map(1) + 0x02, 0x41, 0x01, // kdi: bytes(1) + }, + want: nil, + wantErr: "kty: missing", + }, { name: "invalid key type", data: []byte{ 0xa1, // map (2) 0x01, 0x00, // kty: invalid }, want: nil, - wantErr: "invalid key type value 0", - }, - { - name: "missing curve OKP", + wantErr: "kty: invalid value 0", + }, { + name: "invalid kdi type", + data: []byte{ + 0xa2, // map(2) + 0x01, 0x01, // kty: OKP + 0x02, 0x01, // kdi: int(1) + }, + want: nil, + wantErr: "kid: invalid type: expected []uint8, got int64", + }, { + name: "invalid alg type", + data: []byte{ + 0xa2, // map(2) + 0x01, 0x01, // kty: OKP + 0x03, 0x41, 0x01, // alg: bstr(1) + }, + want: nil, + wantErr: "alg: invalid type: expected int64, got []uint8", + }, { + name: "invalid key_ops type", + data: []byte{ + 0xa2, // map(2) + 0x01, 0x01, // kty: OKP + 0x04, 0x41, 0x01, // key_ops: bstr(1) + }, + want: nil, + wantErr: "key_ops: invalid type: expected []interface{}, got []uint8", + }, { + name: "unknown key_ops entry value", + data: []byte{ + 0xa2, // map(2) + 0x01, 0x01, // kty: OKP + 0x04, 0x82, // key_ops: array (2) + 0x02, // verify + 0x63, 0x66, 0x6f, 0x6f, // tstr: foo + }, + want: nil, + wantErr: `key_ops: unknown entry value "foo"`, + }, { + name: "invalid key_ops entry type", + data: []byte{ + 0xa2, // map(2) + 0x01, 0x01, // kty: OKP + 0x04, 0x82, // key_ops: array (2) + 0x02, // verify + 0xf6, // nil + }, + want: nil, + wantErr: `key_ops: invalid entry type `, + }, { + name: "invalid base_iv type", + data: []byte{ + 0xa2, // map(2) + 0x01, 0x01, // kty: OKP + 0x05, 0x01, // base_iv: int(1) + }, + want: nil, + wantErr: "base_iv: invalid type: expected []uint8, got int64", + }, { + name: "custom key invalid param type", + data: []byte{ + 0xa3, // map (3) + 0x01, 0x3a, 0x00, 0x01, 0x11, 0x6f, // kty: -70000 + 0x20, 0x06, // 0x20: 0x06 + 0xf6, 0xf6, // nil: nil + }, + want: nil, + wantErr: "invalid label type ", + }, { + name: "duplicated param", + data: []byte{ + 0xa3, // map(3) + 0x01, 0x01, // kty: OKP + 0x18, 0x66, 0x18, 0x67, // 66: 67 + 0x18, 0x66, 0x18, 0x47, // 66: 47 + }, + want: nil, + wantErr: `cbor: found duplicate map key "102" at map element index 2`, + }, { + name: "duplicated kty", + data: []byte{ + 0xa3, // map(3) + 0x01, 0x01, // kty: OKP + 0x02, 0x41, 0x01, // kdi: bytes(1) + 0x01, 0x01, // kty: OKP (duplicated) + }, + want: nil, + wantErr: `cbor: found duplicate map key "1" at map element index 2`, + }, { + name: "OKP missing curve", data: []byte{ 0xa1, // map (2) 0x01, 0x01, // kty: OKP }, want: nil, - wantErr: "missing Curve parameter (required for OKP key type)", - }, - { - name: "missing curve EC2", + wantErr: ErrInvalidKey.Error(), + }, { + name: "EC2 missing curve", data: []byte{ 0xa1, // map (2) 0x01, 0x02, // kty: EC2 }, want: nil, - wantErr: "missing Curve parameter (required for EC2 key type)", - }, - { - name: "invalid curve OKP", + wantErr: ErrInvalidKey.Error(), + }, { + name: "OKP invalid curve", data: []byte{ 0xa3, // map (3) 0x01, 0x01, // kty: OKP @@ -179,9 +195,8 @@ func TestKey_UnmarshalCBOR(t *testing.T) { }, want: nil, wantErr: `Key type mismatch for curve "P-256" (must be EC2, found OKP)`, - }, - { - name: "invalid curve EC2", + }, { + name: "EC2 invalid curve", data: []byte{ 0xa4, // map (4) 0x01, 0x02, // kty: EC2 @@ -199,9 +214,80 @@ func TestKey_UnmarshalCBOR(t *testing.T) { }, want: nil, wantErr: `Key type mismatch for curve "Ed25519" (must be OKP, found EC2)`, - }, - { - name: "ok Symmetric", + }, { + name: "Symmetric missing K", + data: []byte{ + 0xa1, // map (1) + 0x01, 0x04, // kty: Symmetric + }, + want: nil, + wantErr: ErrInvalidKey.Error(), + }, { + name: "EC2 invalid algorithm", + data: []byte{ + 0xa4, // map (3) + 0x01, 0x01, // kty: OKP + 0x03, 0x26, // alg: ECDSA w/ SHA-256 + 0x20, 0x06, // curve: Ed25519 + 0x21, 0x58, 0x20, // x-coordinate: bytes(32) + 0x15, 0x52, 0x2e, 0xf1, 0x57, 0x29, 0xcc, 0xf3, // 32-byte value + 0x95, 0x09, 0xea, 0x5c, 0x15, 0xa2, 0x6b, 0xe9, + 0x49, 0xe3, 0x88, 0x07, 0xa5, 0xc2, 0x6e, 0xf9, + 0x28, 0x14, 0x87, 0xef, 0x4a, 0xe6, 0x7b, 0x46, + }, + want: nil, + wantErr: `found algorithm "ES256" (expected "EdDSA")`, + }, { + name: "custom key", + data: []byte{ + 0xa3, // map (3) + 0x01, 0x3a, 0x00, 0x01, 0x11, 0x6f, // kty: -70000 + 0x20, 0x06, // 0x20: 0x06 + 0x61, 0x66, 0x63, 0x66, 0x6f, 0x6f, // 0x21: foo + }, + want: &Key{ + KeyType: -70000, + Params: map[interface{}]interface{}{ + int64(-1): int64(6), + "f": "foo", + }, + }, + }, { + name: "OKP", + data: []byte{ + 0xa6, // map (6) + 0x01, 0x01, // kty: OKP + 0x03, 0x27, // alg: EdDSA w/ Ed25519 + 0x04, // key ops + 0x82, // array (2) + 0x02, // verify + 0x64, 0x73, 0x69, 0x67, 0x6e, // tstr: sign + 0x05, 0x43, 0x03, 0x02, 0x01, // base_iv: bytes(5) + 0x20, 0x06, // curve: Ed25519 + 0x21, 0x58, 0x20, // x-coordinate: bytes(32) + 0x15, 0x52, 0x2e, 0xf1, 0x57, 0x29, 0xcc, 0xf3, // 32-byte value + 0x95, 0x09, 0xea, 0x5c, 0x15, 0xa2, 0x6b, 0xe9, + 0x49, 0xe3, 0x88, 0x07, 0xa5, 0xc2, 0x6e, 0xf9, + 0x28, 0x14, 0x87, 0xef, 0x4a, 0xe6, 0x7b, 0x46, + }, + want: &Key{ + KeyType: KeyTypeOKP, + Algorithm: AlgorithmEd25519, + KeyOps: []KeyOp{KeyOpVerify, KeyOpSign}, + BaseIV: []byte{0x03, 0x02, 0x01}, + Params: map[interface{}]interface{}{ + KeyLabelOKPCurve: CurveEd25519, + KeyLabelOKPX: []byte{ + 0x15, 0x52, 0x2e, 0xf1, 0x57, 0x29, 0xcc, 0xf3, + 0x95, 0x09, 0xea, 0x5c, 0x15, 0xa2, 0x6b, 0xe9, + 0x49, 0xe3, 0x88, 0x07, 0xa5, 0xc2, 0x6e, 0xf9, + 0x28, 0x14, 0x87, 0xef, 0x4a, 0xe6, 0x7b, 0x46, + }, + }, + }, + wantErr: "", + }, { + name: "Symmetric", data: []byte{ 0xa2, // map (2) 0x01, 0x04, // kty: Symmetric @@ -213,39 +299,106 @@ func TestKey_UnmarshalCBOR(t *testing.T) { }, want: &Key{ KeyType: KeyTypeSymmetric, - K: []byte{ - 0x15, 0x52, 0x2e, 0xf1, 0x57, 0x29, 0xcc, 0xf3, - 0x95, 0x09, 0xea, 0x5c, 0x15, 0xa2, 0x6b, 0xe9, - 0x49, 0xe3, 0x88, 0x07, 0xa5, 0xc2, 0x6e, 0xf9, - 0x28, 0x14, 0x87, 0xef, 0x4a, 0xe6, 0x7b, 0x46, + Params: map[interface{}]interface{}{ + KeyLabelSymmetricK: []byte{ + 0x15, 0x52, 0x2e, 0xf1, 0x57, 0x29, 0xcc, 0xf3, + 0x95, 0x09, 0xea, 0x5c, 0x15, 0xa2, 0x6b, 0xe9, + 0x49, 0xe3, 0x88, 0x07, 0xa5, 0xc2, 0x6e, 0xf9, + 0x28, 0x14, 0x87, 0xef, 0x4a, 0xe6, 0x7b, 0x46, + }, }, }, wantErr: "", }, + // The following samples are taken from RFC8152 C.7.1. { - name: "missing K", - data: []byte{ - 0xa1, // map (1) - 0x01, 0x04, // kty: Symmetric + name: "EC2 P-256 public", + data: mustHexToBytes("a5" + + "0102" + + "0258246d65726961646f632e6272616e64796275636b406275636b6c616e642e6578616d706c65" + + "2001" + + "21582065eda5a12577c2bae829437fe338701a10aaa375e1bb5b5de108de439c08551d" + + "2258201e52ed75701163f7f9e40ddf9f341b3dc9ba860af7e0ca7ca7e9eecd0084d19c"), + want: &Key{ + KeyType: KeyTypeEC2, + KeyID: []byte("meriadoc.brandybuck@buckland.example"), + Params: map[interface{}]interface{}{ + KeyLabelEC2Curve: CurveP256, + KeyLabelEC2X: mustHexToBytes("65eda5a12577c2bae829437fe338701a10aaa375e1bb5b5de108de439c08551d"), + KeyLabelEC2Y: mustHexToBytes("1e52ed75701163f7f9e40ddf9f341b3dc9ba860af7e0ca7ca7e9eecd0084d19c"), + }, }, - want: nil, - wantErr: "missing K parameter (required for Symmetric key type)", }, { - name: "wrong algorithm", - data: []byte{ - 0xa4, // map (3) - 0x01, 0x01, // kty: OKP - 0x03, 0x26, // alg: ECDSA w/ SHA-256 - 0x20, 0x06, // curve: Ed25519 - 0x21, 0x58, 0x20, // x-coordinate: bytes(32) - 0x15, 0x52, 0x2e, 0xf1, 0x57, 0x29, 0xcc, 0xf3, // 32-byte value - 0x95, 0x09, 0xea, 0x5c, 0x15, 0xa2, 0x6b, 0xe9, - 0x49, 0xe3, 0x88, 0x07, 0xa5, 0xc2, 0x6e, 0xf9, - 0x28, 0x14, 0x87, 0xef, 0x4a, 0xe6, 0x7b, 0x46, + name: "EC2 P-521 public", + data: mustHexToBytes("a5" + + "0102" + + "02581e62696c626f2e62616767696e7340686f626269746f6e2e6578616d706c65" + + "2003" + + "2158420072992cb3ac08ecf3e5c63dedec0d51a8c1f79ef2f82f94f3c737bf5de7986671eac625fe8257bbd0394644caaa3aaf8f27a4585fbbcad0f2457620085e5c8f42ad" + + "22584201dca6947bce88bc5790485ac97427342bc35f887d86d65a089377e247e60baa55e4e8501e2ada5724ac51d6909008033ebc10ac999b9d7f5cc2519f3fe1ea1d9475"), + want: &Key{ + KeyType: KeyTypeEC2, + KeyID: []byte("bilbo.baggins@hobbiton.example"), + Params: map[interface{}]interface{}{ + KeyLabelEC2Curve: CurveP521, + KeyLabelEC2X: mustHexToBytes("0072992cb3ac08ecf3e5c63dedec0d51a8c1f79ef2f82f94f3c737bf5de7986671eac625fe8257bbd0394644caaa3aaf8f27a4585fbbcad0f2457620085e5c8f42ad"), + KeyLabelEC2Y: mustHexToBytes("01dca6947bce88bc5790485ac97427342bc35f887d86d65a089377e247e60baa55e4e8501e2ada5724ac51d6909008033ebc10ac999b9d7f5cc2519f3fe1ea1d9475"), + }, + }, + }, + // The following samples are taken from RFC8152 C.7.2. + { + name: "EC2 P-256 private", + data: mustHexToBytes("a6" + + "0102" + + "0258246d65726961646f632e6272616e64796275636b406275636b6c616e642e6578616d706c65" + + "2001" + + "21582065eda5a12577c2bae829437fe338701a10aaa375e1bb5b5de108de439c08551d" + + "2258201e52ed75701163f7f9e40ddf9f341b3dc9ba860af7e0ca7ca7e9eecd0084d19c" + + "235820aff907c99f9ad3aae6c4cdf21122bce2bd68b5283e6907154ad911840fa208cf"), + want: &Key{ + KeyType: KeyTypeEC2, + KeyID: []byte("meriadoc.brandybuck@buckland.example"), + Params: map[interface{}]interface{}{ + KeyLabelEC2Curve: CurveP256, + KeyLabelEC2X: mustHexToBytes("65eda5a12577c2bae829437fe338701a10aaa375e1bb5b5de108de439c08551d"), + KeyLabelEC2Y: mustHexToBytes("1e52ed75701163f7f9e40ddf9f341b3dc9ba860af7e0ca7ca7e9eecd0084d19c"), + KeyLabelEC2D: mustHexToBytes("aff907c99f9ad3aae6c4cdf21122bce2bd68b5283e6907154ad911840fa208cf"), + }, + }, + }, { + name: "EC2 P-521 private", + data: mustHexToBytes("a6" + + "0102" + + "02581e62696c626f2e62616767696e7340686f626269746f6e2e6578616d706c65" + + "2003" + + "2158420072992cb3ac08ecf3e5c63dedec0d51a8c1f79ef2f82f94f3c737bf5de7986671eac625fe8257bbd0394644caaa3aaf8f27a4585fbbcad0f2457620085e5c8f42ad" + + "22584201dca6947bce88bc5790485ac97427342bc35f887d86d65a089377e247e60baa55e4e8501e2ada5724ac51d6909008033ebc10ac999b9d7f5cc2519f3fe1ea1d9475" + + "23584200085138ddabf5ca975f5860f91a08e91d6d5f9a76ad4018766a476680b55cd339e8ab6c72b5facdb2a2a50ac25bd086647dd3e2e6e99e84ca2c3609fdf177feb26d"), + want: &Key{ + KeyType: KeyTypeEC2, + KeyID: []byte("bilbo.baggins@hobbiton.example"), + Params: map[interface{}]interface{}{ + KeyLabelEC2Curve: CurveP521, + KeyLabelEC2X: mustHexToBytes("0072992cb3ac08ecf3e5c63dedec0d51a8c1f79ef2f82f94f3c737bf5de7986671eac625fe8257bbd0394644caaa3aaf8f27a4585fbbcad0f2457620085e5c8f42ad"), + KeyLabelEC2Y: mustHexToBytes("01dca6947bce88bc5790485ac97427342bc35f887d86d65a089377e247e60baa55e4e8501e2ada5724ac51d6909008033ebc10ac999b9d7f5cc2519f3fe1ea1d9475"), + KeyLabelEC2D: mustHexToBytes("00085138ddabf5ca975f5860f91a08e91d6d5f9a76ad4018766a476680b55cd339e8ab6c72b5facdb2a2a50ac25bd086647dd3e2e6e99e84ca2c3609fdf177feb26d"), + }, + }, + }, { + name: "Symmetric", + data: mustHexToBytes("a3" + + "0104" + + "024a6f75722d736563726574" + + "205820849b57219dae48de646d07dbb533566e976686457c1491be3a76dcea6c427188"), + want: &Key{ + KeyType: KeyTypeSymmetric, + KeyID: []byte("our-secret"), + Params: map[interface{}]interface{}{ + KeyLabelSymmetricK: mustHexToBytes("849b57219dae48de646d07dbb533566e976686457c1491be3a76dcea6c427188"), + }, }, - want: nil, - wantErr: `found algorithm "ES256" (expected "EdDSA")`, }, } @@ -272,18 +425,137 @@ func TestKey_MarshalCBOR(t *testing.T) { wantErr string }{ { - name: "OKP", + name: "OKP with kty and kid", + key: &Key{ + KeyType: KeyTypeOKP, + KeyID: []byte{1, 2, 3}, + }, + want: []byte{ + 0xa2, // map (2) + 0x01, 0x01, // kty: OKP + 0x02, 0x43, 0x01, 0x02, 0x03, // kid: bytes(3) + }, + }, { + name: "OKP with only kty", + key: &Key{ + KeyType: KeyTypeOKP, + }, + want: []byte{ + 0xa1, // map (1) + 0x01, 0x01, // kty: OKP + }, + }, { + name: "OKP with kty and base_iv", + key: &Key{ + KeyType: KeyTypeOKP, + BaseIV: []byte{3, 2, 1}, + }, + want: []byte{ + 0xa2, // map (2) + 0x01, 0x01, // kty: OKP + 0x05, 0x43, 0x03, 0x02, 0x01, // base_iv: bytes(3) + }, + }, { + name: "OKP with kty and alg", + key: &Key{ + KeyType: KeyTypeOKP, + Algorithm: AlgorithmEd25519, + }, + want: []byte{ + 0xa2, // map (2) + 0x01, 0x01, // kty: OKP + 0x03, 0x27, // alg: EdDSA + }, + }, { + name: "OKP with kty and private alg", + key: &Key{ + KeyType: KeyTypeOKP, + Algorithm: -70_000, + }, + want: []byte{ + 0xa2, // map (2) + 0x01, 0x01, // kty: OKP + 0x03, 0x3a, 0x00, 0x01, 0x11, 0x6f, // alg: -70000 + }, + }, { + name: "OKP with kty and key_ops", + key: &Key{ + KeyType: KeyTypeOKP, + KeyID: []byte{1, 2, 3}, + KeyOps: []KeyOp{KeyOpEncrypt, KeyOpDecrypt, -70_000}, + }, + want: []byte{ + 0xa3, // map (3) + 0x01, 0x01, // kty: OKP + 0x02, 0x43, 0x01, 0x02, 0x03, // kid: bytes(3) + 0x04, 0x83, // key_ops: array(3) + 0x03, 0x04, 0x3a, 0x00, 0x01, 0x11, 0x6f, // -70000 + }, + }, { + name: "OKP with kty and private int params", + key: &Key{ + KeyType: KeyTypeOKP, + Params: map[interface{}]interface{}{ + 0x46: 0x47, + 0x66: 0x67, + }, + }, + want: []byte{ + 0xa3, // map (3) + 0x01, 0x01, // kty: OKP + 0x18, 0x46, 0x18, 0x47, // 0x46: 0x47 (note canonical ordering) + 0x18, 0x66, 0x18, 0x67, // 0x66: 0x67 + }, + }, { + name: "OKP with kty and private mixed params", + key: &Key{ + KeyType: KeyTypeOKP, + Params: map[interface{}]interface{}{ + 0x1234: 0x47, + "a": 0x67, + }, + }, + want: []byte{ + 0xa3, // map (3) + 0x01, 0x01, // kty: OKP + 0x19, 0x12, 0x34, 0x18, 0x47, // 0x1234: 0x47 (note canonical lexicographic ordering) + 0x61, 0x61, 0x18, 0x67, // "a": 0x67 + }, + }, { + name: "OKP duplicated params", + key: &Key{ + KeyType: KeyTypeOKP, + Params: map[interface{}]interface{}{ + int8(10): 0, + int32(10): 1, + }, + }, + wantErr: "duplicate label 10", + }, { + name: "OKP with invalid param label", key: &Key{ KeyType: KeyTypeOKP, - KeyOps: []KeyOp{KeyOpVerify, KeyOpEncrypt}, - X: []byte{ - 0x15, 0x52, 0x2e, 0xf1, 0x57, 0x29, 0xcc, 0xf3, - 0x95, 0x09, 0xea, 0x5c, 0x15, 0xa2, 0x6b, 0xe9, - 0x49, 0xe3, 0x88, 0x07, 0xa5, 0xc2, 0x6e, 0xf9, - 0x28, 0x14, 0x87, 0xef, 0x4a, 0xe6, 0x7b, 0x46, + Params: map[interface{}]interface{}{ + int8(10): 0, + -3.5: 1, }, + }, + wantErr: "invalid label type float64", + }, { + name: "OKP", + key: &Key{ + KeyType: KeyTypeOKP, Algorithm: AlgorithmEd25519, - Curve: CurveEd25519, + KeyOps: []KeyOp{KeyOpVerify, KeyOpEncrypt}, + Params: map[interface{}]interface{}{ + KeyLabelOKPCurve: CurveEd25519, + KeyLabelOKPX: []byte{ + 0x15, 0x52, 0x2e, 0xf1, 0x57, 0x29, 0xcc, 0xf3, + 0x95, 0x09, 0xea, 0x5c, 0x15, 0xa2, 0x6b, 0xe9, + 0x49, 0xe3, 0x88, 0x07, 0xa5, 0xc2, 0x6e, 0xf9, + 0x28, 0x14, 0x87, 0xef, 0x4a, 0xe6, 0x7b, 0x46, + }, + }, }, want: []byte{ 0xa5, // map (5) @@ -304,11 +576,13 @@ func TestKey_MarshalCBOR(t *testing.T) { name: "Symmetric", key: &Key{ KeyType: KeyTypeSymmetric, - K: []byte{ - 0x15, 0x52, 0x2e, 0xf1, 0x57, 0x29, 0xcc, 0xf3, - 0x95, 0x09, 0xea, 0x5c, 0x15, 0xa2, 0x6b, 0xe9, - 0x49, 0xe3, 0x88, 0x07, 0xa5, 0xc2, 0x6e, 0xf9, - 0x28, 0x14, 0x87, 0xef, 0x4a, 0xe6, 0x7b, 0x46, + Params: map[interface{}]interface{}{ + KeyLabelSymmetricK: []byte{ + 0x15, 0x52, 0x2e, 0xf1, 0x57, 0x29, 0xcc, 0xf3, + 0x95, 0x09, 0xea, 0x5c, 0x15, 0xa2, 0x6b, 0xe9, + 0x49, 0xe3, 0x88, 0x07, 0xa5, 0xc2, 0x6e, 0xf9, + 0x28, 0x14, 0x87, 0xef, 0x4a, 0xe6, 0x7b, 0x46, + }, }, }, want: []byte{ @@ -322,10 +596,13 @@ func TestKey_MarshalCBOR(t *testing.T) { }, wantErr: "", }, { - name: "unknown key type", - key: &Key{KeyType: 42}, - want: nil, - wantErr: `invalid key type: "unknown key type value 42"`, + name: "unknown key type", + key: &Key{KeyType: 42}, + want: []byte{ + 0xa1, // map (1) + 0x01, 0x18, 0x2a, // kty: 42 + }, + wantErr: "", }, } for _, tt := range tests { @@ -371,9 +648,11 @@ func TestNewOKPKey(t *testing.T) { want: &Key{ KeyType: KeyTypeOKP, Algorithm: AlgorithmEd25519, - Curve: CurveEd25519, - X: x, - D: d, + Params: map[interface{}]interface{}{ + KeyLabelOKPCurve: CurveEd25519, + KeyLabelOKPX: x, + KeyLabelOKPD: d, + }, }, wantErr: "", }, { @@ -421,10 +700,12 @@ func TestNewEC2Key(t *testing.T) { want: &Key{ KeyType: KeyTypeEC2, Algorithm: AlgorithmES256, - Curve: CurveP256, - X: x, - Y: y, - D: d, + Params: map[interface{}]interface{}{ + KeyLabelEC2Curve: CurveP256, + KeyLabelEC2X: x, + KeyLabelEC2Y: y, + KeyLabelEC2D: d, + }, }, wantErr: "", }, { @@ -432,10 +713,12 @@ func TestNewEC2Key(t *testing.T) { want: &Key{ KeyType: KeyTypeEC2, Algorithm: AlgorithmES384, - Curve: CurveP384, - X: x, - Y: y, - D: d, + Params: map[interface{}]interface{}{ + KeyLabelEC2Curve: CurveP384, + KeyLabelEC2X: x, + KeyLabelEC2Y: y, + KeyLabelEC2D: d, + }, }, wantErr: "", }, { @@ -443,10 +726,12 @@ func TestNewEC2Key(t *testing.T) { want: &Key{ KeyType: KeyTypeEC2, Algorithm: AlgorithmES512, - Curve: CurveP521, - X: x, - Y: y, - D: d, + Params: map[interface{}]interface{}{ + KeyLabelEC2Curve: CurveP521, + KeyLabelEC2X: x, + KeyLabelEC2Y: y, + KeyLabelEC2D: d, + }, }, wantErr: "", }, { @@ -484,7 +769,9 @@ func TestNewSymmetricKey(t *testing.T) { }{ {"valid", args{[]byte{1, 2, 3}}, &Key{ KeyType: KeyTypeSymmetric, - K: []byte{1, 2, 3}, + Params: map[interface{}]interface{}{ + KeyLabelSymmetricK: []byte{1, 2, 3}, + }, }}, } for _, tt := range tests { @@ -568,7 +855,9 @@ func TestKey_AlgorithmOrDefault(t *testing.T) { "OKP-Ed25519", &Key{ KeyType: KeyTypeOKP, - Curve: CurveEd25519, + Params: map[interface{}]interface{}{ + KeyLabelOKPCurve: CurveEd25519, + }, }, AlgorithmEd25519, "", @@ -577,7 +866,9 @@ func TestKey_AlgorithmOrDefault(t *testing.T) { "OKP-P256", &Key{ KeyType: KeyTypeOKP, - Curve: CurveP256, + Params: map[interface{}]interface{}{ + KeyLabelOKPCurve: CurveP256, + }, }, AlgorithmInvalid, `unsupported curve "P-256" for key type OKP`, @@ -586,7 +877,9 @@ func TestKey_AlgorithmOrDefault(t *testing.T) { "EC2-P256", &Key{ KeyType: KeyTypeEC2, - Curve: CurveP256, + Params: map[interface{}]interface{}{ + KeyLabelEC2Curve: CurveP256, + }, }, AlgorithmES256, "", @@ -595,7 +888,9 @@ func TestKey_AlgorithmOrDefault(t *testing.T) { "EC2-P384", &Key{ KeyType: KeyTypeEC2, - Curve: CurveP384, + Params: map[interface{}]interface{}{ + KeyLabelEC2Curve: CurveP384, + }, }, AlgorithmES384, "", @@ -604,7 +899,9 @@ func TestKey_AlgorithmOrDefault(t *testing.T) { "EC2-P521", &Key{ KeyType: KeyTypeEC2, - Curve: CurveP521, + Params: map[interface{}]interface{}{ + KeyLabelEC2Curve: CurveP521, + }, }, AlgorithmES512, "", @@ -613,7 +910,9 @@ func TestKey_AlgorithmOrDefault(t *testing.T) { "EC2-Ed25519", &Key{ KeyType: KeyTypeEC2, - Curve: CurveEd25519, + Params: map[interface{}]interface{}{ + KeyLabelEC2Curve: CurveEd25519, + }, }, AlgorithmInvalid, `unsupported curve "Ed25519" for key type EC2`, @@ -647,10 +946,12 @@ func TestNewKeyFromPrivate(t *testing.T) { }, &Key{ Algorithm: AlgorithmES256, KeyType: KeyTypeEC2, - Curve: CurveP256, - X: big.NewInt(1).Bytes(), - Y: big.NewInt(2).Bytes(), - D: big.NewInt(3).Bytes(), + Params: map[interface{}]interface{}{ + KeyLabelEC2Curve: CurveP256, + KeyLabelEC2X: big.NewInt(1).Bytes(), + KeyLabelEC2Y: big.NewInt(2).Bytes(), + KeyLabelEC2D: big.NewInt(3).Bytes(), + }, }, "", }, @@ -668,9 +969,12 @@ func TestNewKeyFromPrivate(t *testing.T) { 4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, &Key{ - Algorithm: AlgorithmEd25519, KeyType: KeyTypeOKP, Curve: CurveEd25519, - X: []byte{4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - D: []byte{1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + Algorithm: AlgorithmEd25519, KeyType: KeyTypeOKP, + Params: map[interface{}]interface{}{ + KeyLabelOKPCurve: CurveEd25519, + KeyLabelOKPX: []byte{4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + KeyLabelOKPD: []byte{1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }}, "", }, { @@ -705,9 +1009,11 @@ func TestNewKeyFromPublic(t *testing.T) { &Key{ Algorithm: AlgorithmES256, KeyType: KeyTypeEC2, - Curve: CurveP256, - X: big.NewInt(1).Bytes(), - Y: big.NewInt(2).Bytes(), + Params: map[interface{}]interface{}{ + KeyLabelEC2Curve: CurveP256, + KeyLabelEC2X: big.NewInt(1).Bytes(), + KeyLabelEC2Y: big.NewInt(2).Bytes(), + }, }, "", }, @@ -718,7 +1024,14 @@ func TestNewKeyFromPublic(t *testing.T) { }, { "ed25519", ed25519.PublicKey{1, 2, 3}, - &Key{Algorithm: AlgorithmEd25519, KeyType: KeyTypeOKP, Curve: CurveEd25519, X: []byte{1, 2, 3}}, + &Key{ + Algorithm: AlgorithmEd25519, + KeyType: KeyTypeOKP, + Params: map[interface{}]interface{}{ + KeyLabelOKPCurve: CurveEd25519, + KeyLabelOKPX: []byte{1, 2, 3}, + }, + }, "", }, { @@ -754,9 +1067,11 @@ func TestKey_Signer(t *testing.T) { "without algorithm", &Key{ KeyType: KeyTypeOKP, KeyOps: []KeyOp{KeyOpSign}, - Curve: CurveEd25519, - X: x, - D: d, + Params: map[interface{}]interface{}{ + KeyLabelOKPCurve: CurveEd25519, + KeyLabelOKPX: x, + KeyLabelOKPD: d, + }, }, AlgorithmEd25519, "", @@ -765,9 +1080,11 @@ func TestKey_Signer(t *testing.T) { "without key_ops", &Key{ KeyType: KeyTypeOKP, Algorithm: AlgorithmEd25519, - Curve: CurveEd25519, - X: x, - D: d, + Params: map[interface{}]interface{}{ + KeyLabelOKPCurve: CurveEd25519, + KeyLabelOKPX: x, + KeyLabelOKPD: d, + }, }, AlgorithmEd25519, "", @@ -775,9 +1092,11 @@ func TestKey_Signer(t *testing.T) { { "invalid algorithm", &Key{ KeyType: KeyTypeOKP, - Curve: CurveP256, - X: x, - D: d, + Params: map[interface{}]interface{}{ + KeyLabelOKPCurve: CurveP256, + KeyLabelOKPX: x, + KeyLabelOKPD: d, + }, }, AlgorithmInvalid, `Key type mismatch for curve "P-256" (must be EC2, found OKP)`, @@ -785,10 +1104,12 @@ func TestKey_Signer(t *testing.T) { { "can't sign", &Key{ KeyType: KeyTypeOKP, - Curve: CurveEd25519, KeyOps: []KeyOp{KeyOpVerify}, - X: x, - D: d, + Params: map[interface{}]interface{}{ + KeyLabelOKPCurve: CurveEd25519, + KeyLabelOKPX: x, + KeyLabelOKPD: d, + }, }, AlgorithmInvalid, ErrOpNotSupported.Error(), @@ -797,8 +1118,9 @@ func TestKey_Signer(t *testing.T) { "unsupported key", &Key{ KeyType: KeyTypeSymmetric, KeyOps: []KeyOp{KeyOpSign}, - K: x, - D: d, + Params: map[interface{}]interface{}{ + KeyLabelSymmetricK: d, + }, }, AlgorithmInvalid, `unexpected key type "Symmetric"`, @@ -832,8 +1154,10 @@ func TestKey_Verifier(t *testing.T) { "without algorithm", &Key{ KeyType: KeyTypeOKP, KeyOps: []KeyOp{KeyOpVerify}, - Curve: CurveEd25519, - X: x, + Params: map[interface{}]interface{}{ + KeyLabelOKPCurve: CurveEd25519, + KeyLabelOKPX: x, + }, }, AlgorithmEd25519, "", @@ -842,8 +1166,10 @@ func TestKey_Verifier(t *testing.T) { "without key_ops", &Key{ KeyType: KeyTypeOKP, Algorithm: AlgorithmEd25519, - Curve: CurveEd25519, - X: x, + Params: map[interface{}]interface{}{ + KeyLabelOKPCurve: CurveEd25519, + KeyLabelOKPX: x, + }, }, AlgorithmEd25519, "", @@ -851,8 +1177,10 @@ func TestKey_Verifier(t *testing.T) { { "invalid algorithm", &Key{ KeyType: KeyTypeOKP, - Curve: CurveP256, - X: x, + Params: map[interface{}]interface{}{ + KeyLabelOKPCurve: CurveP256, + KeyLabelOKPX: x, + }, }, AlgorithmInvalid, `Key type mismatch for curve "P-256" (must be EC2, found OKP)`, @@ -860,9 +1188,11 @@ func TestKey_Verifier(t *testing.T) { { "can't verify", &Key{ KeyType: KeyTypeOKP, - Curve: CurveEd25519, KeyOps: []KeyOp{KeyOpSign}, - X: x, + Params: map[interface{}]interface{}{ + KeyLabelOKPCurve: CurveEd25519, + KeyLabelOKPX: x, + }, }, AlgorithmInvalid, ErrOpNotSupported.Error(), @@ -871,7 +1201,9 @@ func TestKey_Verifier(t *testing.T) { "unsupported key", &Key{ KeyType: KeyTypeSymmetric, KeyOps: []KeyOp{KeyOpVerify}, - K: x, + Params: map[interface{}]interface{}{ + KeyLabelSymmetricK: x, + }, }, AlgorithmInvalid, `unexpected key type "Symmetric"`, @@ -906,9 +1238,11 @@ func TestKey_PrivateKey(t *testing.T) { { "CurveEd25519", &Key{ KeyType: KeyTypeOKP, - Curve: CurveEd25519, - X: x, - D: d, + Params: map[interface{}]interface{}{ + KeyLabelOKPCurve: CurveEd25519, + KeyLabelOKPX: x, + KeyLabelOKPD: d, + }, }, ed25519.PrivateKey{ d[0], d[1], d[2], d[3], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -918,10 +1252,12 @@ func TestKey_PrivateKey(t *testing.T) { }, { "CurveP256", &Key{ KeyType: KeyTypeEC2, - Curve: CurveP256, - X: x, - Y: y, - D: d, + Params: map[interface{}]interface{}{ + KeyLabelEC2Curve: CurveP256, + KeyLabelEC2X: x, + KeyLabelEC2Y: y, + KeyLabelEC2D: d, + }, }, &ecdsa.PrivateKey{ PublicKey: ecdsa.PublicKey{ @@ -935,10 +1271,12 @@ func TestKey_PrivateKey(t *testing.T) { }, { "CurveP384", &Key{ KeyType: KeyTypeEC2, - Curve: CurveP384, - X: x, - Y: y, - D: d, + Params: map[interface{}]interface{}{ + KeyLabelEC2Curve: CurveP384, + KeyLabelEC2X: x, + KeyLabelEC2Y: y, + KeyLabelEC2D: d, + }, }, &ecdsa.PrivateKey{ PublicKey: ecdsa.PublicKey{ @@ -952,10 +1290,12 @@ func TestKey_PrivateKey(t *testing.T) { }, { "CurveP521", &Key{ KeyType: KeyTypeEC2, - Curve: CurveP521, - X: x, - Y: y, - D: d, + Params: map[interface{}]interface{}{ + KeyLabelEC2Curve: CurveP521, + KeyLabelEC2X: x, + KeyLabelEC2Y: y, + KeyLabelEC2D: d, + }, }, &ecdsa.PrivateKey{ PublicKey: ecdsa.PublicKey{ @@ -975,62 +1315,76 @@ func TestKey_PrivateKey(t *testing.T) { }, { "OKP missing X", &Key{ KeyType: KeyTypeOKP, - Curve: CurveEd25519, - D: d, + Params: map[interface{}]interface{}{ + KeyLabelOKPCurve: CurveEd25519, + KeyLabelOKPD: d, + }, }, nil, ErrOKPNoPub.Error(), }, { "OKP missing D", &Key{ KeyType: KeyTypeOKP, - Curve: CurveEd25519, - X: x, + Params: map[interface{}]interface{}{ + KeyLabelOKPCurve: CurveEd25519, + KeyLabelOKPX: x, + }, }, nil, ErrNotPrivKey.Error(), }, { "OKP unknown curve", &Key{ KeyType: KeyTypeOKP, - Curve: 70, - X: x, - D: d, + Params: map[interface{}]interface{}{ + KeyLabelOKPCurve: 70, + KeyLabelOKPX: x, + KeyLabelOKPD: d, + }, }, nil, `unsupported curve "unknown curve value 70" for key type OKP`, }, { "EC2 missing X", &Key{ KeyType: KeyTypeEC2, - Curve: CurveP256, - Y: y, - D: d, + Params: map[interface{}]interface{}{ + KeyLabelEC2Curve: CurveP256, + KeyLabelEC2Y: y, + KeyLabelEC2D: d, + }, }, nil, ErrEC2NoPub.Error(), }, { "EC2 missing Y", &Key{ KeyType: KeyTypeEC2, - Curve: CurveP256, - X: x, - D: d, + Params: map[interface{}]interface{}{ + KeyLabelEC2Curve: CurveP256, + KeyLabelEC2X: x, + KeyLabelEC2D: d, + }, }, nil, ErrEC2NoPub.Error(), }, { "EC2 missing D", &Key{ KeyType: KeyTypeEC2, - Curve: CurveP256, - X: x, - Y: y, + Params: map[interface{}]interface{}{ + KeyLabelEC2Curve: CurveP256, + KeyLabelEC2X: x, + KeyLabelEC2Y: y, + }, }, nil, ErrNotPrivKey.Error(), }, { "EC2 unknown curve", &Key{ KeyType: KeyTypeEC2, - Curve: 70, - X: x, - Y: y, - D: d, + Params: map[interface{}]interface{}{ + KeyLabelEC2Curve: 70, + KeyLabelEC2X: x, + KeyLabelEC2Y: y, + KeyLabelEC2D: d, + }, }, nil, `unsupported curve "unknown curve value 70" for key type EC2`, @@ -1062,17 +1416,21 @@ func TestKey_PublicKey(t *testing.T) { { "CurveEd25519", &Key{ KeyType: KeyTypeOKP, - Curve: CurveEd25519, - X: x, + Params: map[interface{}]interface{}{ + KeyLabelOKPCurve: CurveEd25519, + KeyLabelOKPX: x, + }, }, ed25519.PublicKey(x), "", }, { "CurveP256", &Key{ KeyType: KeyTypeEC2, - Curve: CurveP256, - X: x, - Y: y, + Params: map[interface{}]interface{}{ + KeyLabelEC2Curve: CurveP256, + KeyLabelEC2X: x, + KeyLabelEC2Y: y, + }, }, &ecdsa.PublicKey{ Curve: elliptic.P256(), @@ -1083,9 +1441,11 @@ func TestKey_PublicKey(t *testing.T) { }, { "CurveP384", &Key{ KeyType: KeyTypeEC2, - Curve: CurveP384, - X: x, - Y: y, + Params: map[interface{}]interface{}{ + KeyLabelEC2Curve: CurveP384, + KeyLabelEC2X: x, + KeyLabelEC2Y: y, + }, }, &ecdsa.PublicKey{ Curve: elliptic.P384(), @@ -1096,9 +1456,11 @@ func TestKey_PublicKey(t *testing.T) { }, { "CurveP521", &Key{ KeyType: KeyTypeEC2, - Curve: CurveP521, - X: x, - Y: y, + Params: map[interface{}]interface{}{ + KeyLabelEC2Curve: CurveP521, + KeyLabelEC2X: x, + KeyLabelEC2Y: y, + }, }, &ecdsa.PublicKey{ Curve: elliptic.P521(), @@ -1115,41 +1477,50 @@ func TestKey_PublicKey(t *testing.T) { }, { "OKP missing X", &Key{ KeyType: KeyTypeOKP, - Curve: CurveEd25519, + Params: map[interface{}]interface{}{ + KeyLabelOKPCurve: CurveEd25519, + }, }, nil, ErrOKPNoPub.Error(), }, { "OKP unknown curve", &Key{ KeyType: KeyTypeOKP, - Curve: 70, - X: x, - Y: y, + Params: map[interface{}]interface{}{ + KeyLabelOKPCurve: 70, + KeyLabelOKPX: x, + }, }, nil, `unsupported curve "unknown curve value 70" for key type OKP`, }, { "EC2 missing X", &Key{ KeyType: KeyTypeEC2, - Curve: CurveP256, - Y: y, + Params: map[interface{}]interface{}{ + KeyLabelEC2Curve: CurveP256, + KeyLabelEC2Y: y, + }, }, nil, ErrEC2NoPub.Error(), }, { "EC2 missing Y", &Key{ KeyType: KeyTypeEC2, - Curve: CurveP256, - X: x, + Params: map[interface{}]interface{}{ + KeyLabelEC2Curve: CurveP256, + KeyLabelEC2X: x, + }, }, nil, ErrEC2NoPub.Error(), }, { "EC2 unknown curve", &Key{ KeyType: KeyTypeEC2, - Curve: 70, - X: x, - Y: y, + Params: map[interface{}]interface{}{ + KeyLabelEC2Curve: 70, + KeyLabelEC2X: x, + KeyLabelEC2Y: y, + }, }, nil, `unsupported curve "unknown curve value 70" for key type EC2`, @@ -1188,12 +1559,16 @@ func TestKeyType_String(t *testing.T) { } func TestCurve_String(t *testing.T) { - // test string conversions not exercised by other test cases tests := []struct { kt Curve want string }{ + {CurveP256, "P-256"}, + {CurveP384, "P-384"}, + {CurveP521, "P-521"}, {CurveX25519, "X25519"}, + {CurveX448, "X448"}, + {CurveEd25519, "Ed25519"}, {CurveEd448, "Ed448"}, } for _, tt := range tests { @@ -1204,3 +1579,41 @@ func TestCurve_String(t *testing.T) { }) } } + +func TestKeyOpFromString(t *testing.T) { + tests := []struct { + val string + want KeyOp + want1 bool + }{ + {"sign", KeyOpSign, true}, + {"verify", KeyOpVerify, true}, + {"encrypt", KeyOpEncrypt, true}, + {"decrypt", KeyOpDecrypt, true}, + {"wrapKey", KeyOpWrapKey, true}, + {"unwrapKey", KeyOpUnwrapKey, true}, + {"deriveKey", KeyOpDeriveKey, true}, + {"deriveBits", KeyOpDeriveBits, true}, + {"", KeyOp(0), false}, + {"foo", KeyOp(0), false}, + } + for _, tt := range tests { + t.Run(tt.val, func(t *testing.T) { + got, got1 := KeyOpFromString(tt.val) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("KeyOpFromString() got = %v, want %v", got, tt.want) + } + if got1 != tt.want1 { + t.Errorf("KeyOpFromString() got1 = %v, want %v", got1, tt.want1) + } + }) + } +} + +func mustHexToBytes(s string) []byte { + b, err := hex.DecodeString(s) + if err != nil { + panic(err) + } + return b +}