From dd6e9671c900d160e305d7b47099d9f635c70a5a Mon Sep 17 00:00:00 2001 From: Filip Burlacu Date: Tue, 26 Sep 2023 01:11:06 -0400 Subject: [PATCH] feat: kms-crypto wrapper local&web suites, with APIs for VCS PR also cleans up duplication in public key parsing APIs Signed-off-by: Filip Burlacu --- doc/jose/jwk/jwksupport/jwk.go | 146 +++++++++++++----- doc/jose/jwk/jwksupport/jwk_test.go | 112 ++++++++++++++ doc/util/jwkkid/kid_creator.go | 70 ++------- doc/util/jwkkid/kid_creator_test.go | 28 ++-- mock/suite/suite.go | 68 ++++++++ mock/wrapper/wrapper.go | 69 ++++++++- wrapper/api.go | 60 ------- wrapper/api/api.go | 100 ++++++++++++ wrapper/internal/params/params.go | 58 +++++++ wrapper/internal/wrapperimpl/creator.go | 59 +++++++ wrapper/internal/wrapperimpl/creator_test.go | 75 +++++++++ wrapper/internal/wrapperimpl/encrypter.go | 44 ++++++ .../internal/wrapperimpl/encrypter_test.go | 79 ++++++++++ wrapper/internal/wrapperimpl/multisigner.go | 80 ++++++++++ .../internal/wrapperimpl/multisigner_test.go | 128 +++++++++++++++ wrapper/{ => internal/wrapperimpl}/signer.go | 20 ++- wrapper/{ => internal/wrapperimpl}/wrapper.go | 58 +++---- .../wrapperimpl}/wrapper_test.go | 2 +- wrapper/internal/wrapperimpl/wrapperimpl.go | 8 + wrapper/localsuite/localsuite.go | 54 +++++++ wrapper/localsuite/suite.go | 58 +++++++ wrapper/localsuite/suite_test.go | 90 +++++++++++ wrapper/params.go | 42 ----- wrapper/websuite/fixedkey.go | 44 ++++++ wrapper/websuite/kmscrypto.go | 111 +++++++++++++ wrapper/websuite/websuite.go | 94 +++++++++++ 26 files changed, 1501 insertions(+), 256 deletions(-) create mode 100644 mock/suite/suite.go delete mode 100644 wrapper/api.go create mode 100644 wrapper/api/api.go create mode 100644 wrapper/internal/params/params.go create mode 100644 wrapper/internal/wrapperimpl/creator.go create mode 100644 wrapper/internal/wrapperimpl/creator_test.go create mode 100644 wrapper/internal/wrapperimpl/encrypter.go create mode 100644 wrapper/internal/wrapperimpl/encrypter_test.go create mode 100644 wrapper/internal/wrapperimpl/multisigner.go create mode 100644 wrapper/internal/wrapperimpl/multisigner_test.go rename wrapper/{ => internal/wrapperimpl}/signer.go (55%) rename wrapper/{ => internal/wrapperimpl}/wrapper.go (57%) rename wrapper/{ => internal/wrapperimpl}/wrapper_test.go (99%) create mode 100644 wrapper/internal/wrapperimpl/wrapperimpl.go create mode 100644 wrapper/localsuite/localsuite.go create mode 100644 wrapper/localsuite/suite.go create mode 100644 wrapper/localsuite/suite_test.go delete mode 100644 wrapper/params.go create mode 100644 wrapper/websuite/fixedkey.go create mode 100644 wrapper/websuite/kmscrypto.go create mode 100644 wrapper/websuite/websuite.go diff --git a/doc/jose/jwk/jwksupport/jwk.go b/doc/jose/jwk/jwksupport/jwk.go index 8d57fa6..b8be73b 100644 --- a/doc/jose/jwk/jwksupport/jwk.go +++ b/doc/jose/jwk/jwksupport/jwk.go @@ -11,11 +11,14 @@ import ( "crypto/ed25519" "crypto/elliptic" "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" "encoding/json" "errors" "fmt" "math/big" + "github.com/btcsuite/btcd/btcec" "github.com/go-jose/go-jose/v3" "github.com/trustbloc/bbs-signature-go/bbs12381g2pub" @@ -56,53 +59,31 @@ func JWKFromKey(opaqueKey interface{}) (*jwk.JWK, error) { return key, nil } -// JWKFromX25519Key is similar to JWKFromKey but is specific to X25519 keys when using a public key as raw []byte. -// This builder function presets the curve and key type in the JWK. -// Using JWKFromKey for X25519 raw keys will not have these fields set and will not provide the right JWK output. -func JWKFromX25519Key(pubKey []byte) (*jwk.JWK, error) { - key := &jwk.JWK{ - JSONWebKey: jose.JSONWebKey{ - Key: pubKey, - }, - Crv: x25519Crv, - Kty: okpKty, - } - - // marshal/unmarshal to get all JWK's fields other than Key filled. - keyBytes, err := key.MarshalJSON() - if err != nil { - return nil, fmt.Errorf("create JWK: %w", err) - } - - err = key.UnmarshalJSON(keyBytes) - if err != nil { - return nil, fmt.Errorf("create JWK: %w", err) - } - - return key, nil -} - -// PubKeyBytesToJWK converts marshalled bytes of keyType into JWK. -func PubKeyBytesToJWK(bytes []byte, keyType kms.KeyType) (*jwk.JWK, error) { // nolint:gocyclo +// PubKeyBytesToKey creates an opaque key struct from the given public key bytes. +// It's e.g. *ecdsa.PublicKey, *ecdsa.PrivateKey, ed25519.VerificationMethod, *bbs12381g2pub.PrivateKey or +// *bbs12381g2pub.PublicKey. +func PubKeyBytesToKey(bytes []byte, keyType kms.KeyType) (interface{}, error) { // nolint:gocyclo switch keyType { case kms.ED25519Type: - return JWKFromKey(ed25519.PublicKey(bytes)) + return ed25519.PublicKey(bytes), nil + case kms.X25519ECDHKWType: + return bytes, nil case kms.BLS12381G2Type: - bbsKey, err := bbs12381g2pub.UnmarshalPublicKey(bytes) - if err != nil { - return nil, err - } - - return JWKFromKey(bbsKey) - case kms.ECDSAP256TypeIEEEP1363, kms.ECDSAP384TypeIEEEP1363, kms.ECDSAP521TypeIEEEP1363: + return bbs12381g2pub.UnmarshalPublicKey(bytes) + case kms.ECDSAP256TypeIEEEP1363, kms.ECDSAP384TypeIEEEP1363, kms.ECDSAP521TypeIEEEP1363, + kms.ECDSASecp256k1TypeIEEEP1363: crv := getECDSACurve(keyType) x, y := elliptic.Unmarshal(crv, bytes) - return JWKFromKey(&ecdsa.PublicKey{Curve: crv, X: x, Y: y}) + return &ecdsa.PublicKey{ + Curve: crv, + X: x, + Y: y, + }, nil case kms.ECDSAP256TypeDER, kms.ECDSAP384TypeDER, kms.ECDSAP521TypeDER: pubKey, err := x509.ParsePKIXPublicKey(bytes) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to parse ecdsa key in DER format: %w", err) } ecKey, ok := pubKey.(*ecdsa.PublicKey) @@ -110,7 +91,9 @@ func PubKeyBytesToJWK(bytes []byte, keyType kms.KeyType) (*jwk.JWK, error) { // return nil, errors.New("invalid EC key") } - return JWKFromKey(ecKey) + return ecKey, nil + case kms.ECDSASecp256k1TypeDER: + return parseSecp256k1DER(bytes) case kms.NISTP256ECDHKWType, kms.NISTP384ECDHKWType, kms.NISTP521ECDHKWType: crv := getECDSACurve(keyType) pubKey := &cryptoapi.PublicKey{} @@ -126,9 +109,62 @@ func PubKeyBytesToJWK(bytes []byte, keyType kms.KeyType) (*jwk.JWK, error) { // Y: new(big.Int).SetBytes(pubKey.Y), } - return JWKFromKey(ecdsaKey) + return ecdsaKey, nil + default: + return nil, fmt.Errorf("convertPubKeyJWK: invalid key type: %s", keyType) + } +} + +// JWKFromX25519Key is similar to JWKFromKey but is specific to X25519 keys when using a public key as raw []byte. +// This builder function presets the curve and key type in the JWK. +// Using JWKFromKey for X25519 raw keys will not have these fields set and will not provide the right JWK output. +func JWKFromX25519Key(pubKey []byte) (*jwk.JWK, error) { + key := &jwk.JWK{ + JSONWebKey: jose.JSONWebKey{ + Key: pubKey, + }, + Crv: x25519Crv, + Kty: okpKty, + } + + // marshal/unmarshal to get all JWK's fields other than Key filled. + keyBytes, err := key.MarshalJSON() + if err != nil { + return nil, fmt.Errorf("create JWK: %w", err) + } + + err = key.UnmarshalJSON(keyBytes) + if err != nil { + return nil, fmt.Errorf("create JWK: %w", err) + } + + return key, nil +} + +// PubKeyBytesToJWK converts marshalled bytes of keyType into JWK. +func PubKeyBytesToJWK(bytes []byte, keyType kms.KeyType) (*jwk.JWK, error) { + switch keyType { + case kms.ED25519Type: + return &jwk.JWK{ + JSONWebKey: jose.JSONWebKey{ + Key: ed25519.PublicKey(bytes), + }, + Kty: "OKP", + Crv: "Ed25519", + }, nil case kms.X25519ECDHKWType: return JWKFromX25519Key(bytes) + case kms.BLS12381G2Type, + kms.ECDSASecp256k1TypeIEEEP1363, kms.ECDSASecp256k1TypeDER, + kms.ECDSAP256TypeIEEEP1363, kms.ECDSAP384TypeIEEEP1363, kms.ECDSAP521TypeIEEEP1363, + kms.ECDSAP256TypeDER, kms.ECDSAP384TypeDER, kms.ECDSAP521TypeDER, + kms.NISTP256ECDHKWType, kms.NISTP384ECDHKWType, kms.NISTP521ECDHKWType: + key, err := PubKeyBytesToKey(bytes, keyType) + if err != nil { + return nil, err + } + + return JWKFromKey(key) default: return nil, fmt.Errorf("convertPubKeyJWK: invalid key type: %s", keyType) } @@ -142,11 +178,41 @@ func getECDSACurve(keyType kms.KeyType) elliptic.Curve { return elliptic.P384() case kms.ECDSAP521TypeIEEEP1363, kms.ECDSAP521TypeDER, kms.NISTP521ECDHKWType: return elliptic.P521() + case kms.ECDSASecp256k1TypeIEEEP1363, kms.ECDSASecp256k1TypeDER: + return btcec.S256() } return nil } +type publicKeyInfo struct { + Raw asn1.RawContent + Algorithm pkix.AlgorithmIdentifier + PublicKey asn1.BitString +} + +func parseSecp256k1DER(keyBytes []byte) (*ecdsa.PublicKey, error) { + var ( + pki publicKeyInfo + rest []byte + err error + pubKey *btcec.PublicKey + ) + + if rest, err = asn1.Unmarshal(keyBytes, &pki); err != nil { + return nil, err + } else if len(rest) != 0 { + return nil, fmt.Errorf("x509: trailing data after ASN.1 of public-key") + } + + pubKey, err = btcec.ParsePubKey(pki.PublicKey.RightAlign(), btcec.S256()) + if err != nil { + return nil, err + } + + return pubKey.ToECDSA(), nil +} + // PublicKeyFromJWK builds a cryptoapi.PublicKey from jwkKey. func PublicKeyFromJWK(jwkKey *jwk.JWK) (*cryptoapi.PublicKey, error) { if jwkKey != nil { diff --git a/doc/jose/jwk/jwksupport/jwk_test.go b/doc/jose/jwk/jwksupport/jwk_test.go index 52212b2..6c47af0 100644 --- a/doc/jose/jwk/jwksupport/jwk_test.go +++ b/doc/jose/jwk/jwksupport/jwk_test.go @@ -332,6 +332,118 @@ func TestBBSJWK(t *testing.T) { }) } +func TestPubKeyBytesToKey(t *testing.T) { + tt := []struct { + keyTypes []kms.KeyType + getKey func(keyType kms.KeyType) ([]byte, error) + expectType interface{} + }{ + { + keyTypes: []kms.KeyType{kms.ED25519Type}, + getKey: func(kms.KeyType) ([]byte, error) { + pubKey, _, err := ed25519.GenerateKey(rand.Reader) + return pubKey, err + }, + expectType: ed25519.PublicKey{}, + }, + { + keyTypes: []kms.KeyType{kms.X25519ECDHKWType}, + getKey: func(kms.KeyType) ([]byte, error) { + pubKeyBytes := make([]byte, 32) + _, err := rand.Read(pubKeyBytes) + + return pubKeyBytes, err + }, + expectType: []byte{}, + }, + { + keyTypes: []kms.KeyType{kms.BLS12381G2Type}, + getKey: func(kms.KeyType) ([]byte, error) { + pubKey, _, err := bbs12381g2pub.GenerateKeyPair(sha256.New, nil) + if err != nil { + return nil, err + } + + keyBytes, err := pubKey.Marshal() + return keyBytes, err + }, + expectType: &bbs12381g2pub.PublicKey{}, + }, + { + keyTypes: []kms.KeyType{ + kms.ECDSAP256TypeIEEEP1363, + kms.ECDSAP384TypeIEEEP1363, + kms.ECDSAP521TypeIEEEP1363, + }, + getKey: func(keyType kms.KeyType) ([]byte, error) { + crv := getECDSACurve(keyType) + privKey, err := ecdsa.GenerateKey(crv, rand.Reader) + if err != nil { + return nil, err + } + + keyBytes := elliptic.Marshal(crv, privKey.X, privKey.Y) + return keyBytes, nil + }, + expectType: &ecdsa.PublicKey{}, + }, + { + keyTypes: []kms.KeyType{ + kms.ECDSAP256TypeDER, + kms.ECDSAP384TypeDER, + kms.ECDSAP521TypeDER, + }, + getKey: func(keyType kms.KeyType) ([]byte, error) { + crv := getECDSACurve(keyType) + privKey, err := ecdsa.GenerateKey(crv, rand.Reader) + if err != nil { + return nil, err + } + + return x509.MarshalPKIXPublicKey(&privKey.PublicKey) + }, + expectType: &ecdsa.PublicKey{}, + }, + { + keyTypes: []kms.KeyType{ + kms.NISTP256ECDHKWType, + kms.NISTP384ECDHKWType, + kms.NISTP521ECDHKWType, + }, + getKey: func(keyType kms.KeyType) ([]byte, error) { + crv := getECDSACurve(keyType) + privKey, err := ecdsa.GenerateKey(crv, rand.Reader) + require.NoError(t, err) + + pubKey := &cryptoapi.PublicKey{ + X: privKey.X.Bytes(), + Y: privKey.Y.Bytes(), + Curve: crv.Params().Name, + Type: "EC", + } + + return json.Marshal(pubKey) + }, + expectType: &ecdsa.PublicKey{}, + }, + } + + for _, tc := range tt { + for _, keyType := range tc.keyTypes { + + t.Run(string(keyType), func(t *testing.T) { + pkBytes, err := tc.getKey(keyType) + require.NoError(t, err) + + pk, err := PubKeyBytesToKey(pkBytes, keyType) + require.NoError(t, err) + + require.IsType(t, tc.expectType, pk) + }) + } + } +} + func TestPubKeyBytesToJWK(t *testing.T) { tests := []struct { name string diff --git a/doc/util/jwkkid/kid_creator.go b/doc/util/jwkkid/kid_creator.go index 3ab29f9..89920f5 100644 --- a/doc/util/jwkkid/kid_creator.go +++ b/doc/util/jwkkid/kid_creator.go @@ -9,9 +9,7 @@ package jwkkid import ( "crypto" "crypto/ecdsa" - "crypto/ed25519" "crypto/elliptic" - "crypto/x509" "encoding/base64" "encoding/json" "errors" @@ -178,71 +176,25 @@ func curveSize(crv elliptic.Curve) int { // BuildJWK builds a go jose JWK from keyBytes with key type kt. func BuildJWK(keyBytes []byte, kt kms.KeyType) (*jwk.JWK, error) { //nolint: gocyclo - var ( - j *jwk.JWK - err error - ) - switch kt { - case kms.ECDSAP256TypeDER, kms.ECDSAP384TypeDER, kms.ECDSAP521TypeDER, kms.ECDSASecp256k1DER: - j, err = generateJWKFromDERECDSA(keyBytes) - if err != nil { - return nil, fmt.Errorf("buildJWK: failed to build JWK from ecdsa DER key: %w", err) - } - case kms.ED25519Type: - j, err = jwksupport.JWKFromKey(ed25519.PublicKey(keyBytes)) - if err != nil { - return nil, fmt.Errorf("buildJWK: failed to build JWK from ed25519 key: %w", err) - } - case kms.ECDSAP256TypeIEEEP1363, kms.ECDSAP384TypeIEEEP1363, kms.ECDSAP521TypeIEEEP1363, kms.ECDSASecp256k1IEEEP1363: - c := getCurveByKMSKeyType(kt) - x, y := elliptic.Unmarshal(c, keyBytes) - - pubKey := &ecdsa.PublicKey{ - Curve: c, - X: x, - Y: y, - } - - j, err = jwksupport.JWKFromKey(pubKey) - if err != nil { - return nil, fmt.Errorf("buildJWK: failed to build JWK from ecdsa key in IEEE1363 format: %w", err) - } - case kms.NISTP256ECDHKWType, kms.NISTP384ECDHKWType, kms.NISTP521ECDHKWType: - j, err = generateJWKFromECDH(keyBytes) - if err != nil { - return nil, fmt.Errorf("buildJWK: failed to build JWK from ecdh key: %w", err) - } - case kms.X25519ECDHKWType: - pubKey, err := unmarshalECDHKey(keyBytes) - if err != nil { - return nil, fmt.Errorf("buildJWK: failed to unmarshal public key from X25519 key: %w", err) - } - - j, err = jwksupport.JWKFromX25519Key(pubKey.X) - if err != nil { - return nil, fmt.Errorf("buildJWK: failed to build JWK from X25519 key: %w", err) - } + case + kms.ECDSAP256TypeDER, kms.ECDSAP384TypeDER, kms.ECDSAP521TypeDER, + kms.ECDSAP256TypeIEEEP1363, kms.ECDSAP384TypeIEEEP1363, kms.ECDSAP521TypeIEEEP1363, + kms.NISTP256ECDHKWType, kms.NISTP384ECDHKWType, kms.NISTP521ECDHKWType, + kms.ECDSASecp256k1DER, kms.ECDSASecp256k1IEEEP1363, + kms.ED25519Type, kms.X25519ECDHKWType, kms.BLS12381G2Type: + return jwksupport.PubKeyBytesToJWK(keyBytes, kt) default: return nil, fmt.Errorf("buildJWK: %w: '%s'", errInvalidKeyType, kt) } - - return j, nil -} - -func generateJWKFromDERECDSA(keyBytes []byte) (*jwk.JWK, error) { - pubKey, err := x509.ParsePKIXPublicKey(keyBytes) - if err != nil { - return nil, fmt.Errorf("generateJWKFromDERECDSA: failed to parse ecdsa key in DER format: %w", err) - } - - return jwksupport.JWKFromKey(pubKey) } func generateJWKFromECDH(keyBytes []byte) (*jwk.JWK, error) { - compositeKey, err := unmarshalECDHKey(keyBytes) + compositeKey := &cryptoapi.PublicKey{} + + err := json.Unmarshal(keyBytes, compositeKey) if err != nil { - return nil, fmt.Errorf("generateJWKFromECDH: %w", err) + return nil, fmt.Errorf("generateJWKFromECDH: unmarshalECDHKey: failed to unmarshal ECDH key: %w", err) } c, err := hybrid.GetCurve(compositeKey.Curve) diff --git a/doc/util/jwkkid/kid_creator_test.go b/doc/util/jwkkid/kid_creator_test.go index 8459629..01c1002 100644 --- a/doc/util/jwkkid/kid_creator_test.go +++ b/doc/util/jwkkid/kid_creator_test.go @@ -50,8 +50,7 @@ func Test_CreateKID(t *testing.T) { badPubKey := ed25519.PublicKey("badKey") _, err = CreateKID(badPubKey, kms.NISTP256ECDHKWType) - require.EqualError(t, err, "createKID: failed to build jwk: buildJWK: failed to build JWK from ecdh "+ - "key: generateJWKFromECDH: unmarshalECDHKey: failed to unmarshal ECDH key: invalid character 'b' looking for "+ + require.EqualError(t, err, "createKID: failed to build jwk: invalid character 'b' looking for "+ "beginning of value") }) }) @@ -141,24 +140,23 @@ func TestCreateKID(t *testing.T) { badPubKey := ed25519.PublicKey("badKey") _, err = CreateKID(badPubKey, kms.NISTP256ECDHKWType) - require.EqualError(t, err, "createKID: failed to build jwk: buildJWK: failed to build JWK from ecdh "+ - "key: generateJWKFromECDH: unmarshalECDHKey: failed to unmarshal ECDH key: invalid character 'b' looking for "+ + require.EqualError(t, err, "createKID: failed to build jwk: invalid character 'b' looking for "+ "beginning of value") _, err = CreateKID(badPubKey, kms.ECDSAP256TypeDER) - require.EqualError(t, err, "createKID: failed to build jwk: buildJWK: failed to build JWK from ecdsa DER "+ - "key: generateJWKFromDERECDSA: failed to parse ecdsa key in DER format: asn1: structure error: tags don't "+ - "match (16 vs {class:1 tag:2 length:97 isCompound:true}) {optional:false explicit:false application:false "+ - "private:false defaultValue: tag: stringType:0 timeType:0 set:false omitEmpty:false} publicKeyInfo "+ - "@2") + require.EqualError(t, err, + "createKID: failed to build jwk: failed to parse ecdsa key in DER format: asn1: structure error: tags don't "+ + "match (16 vs {class:1 tag:2 length:97 isCompound:true}) {optional:false explicit:false application:false "+ + "private:false defaultValue: tag: stringType:0 timeType:0 set:false omitEmpty:false} publicKeyInfo "+ + "@2") _, err = CreateKID(badPubKey, kms.X25519ECDHKWType) require.EqualError(t, err, "createKID: createX25519KID: unmarshalECDHKey: failed to unmarshal ECDH key: "+ "invalid character 'b' looking for beginning of value") _, err = CreateKID(badPubKey, kms.ECDSAP256TypeIEEEP1363) - require.EqualError(t, err, "createKID: failed to build jwk: buildJWK: failed to build JWK from ecdsa key "+ - "in IEEE1363 format: create JWK: go-jose/go-jose: invalid EC key (nil, or X/Y missing)") + require.EqualError(t, err, "createKID: failed to build jwk: create JWK: go-jose/go-jose: "+ + "invalid EC key (nil, or X/Y missing)") ecKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) require.NoError(t, err) @@ -257,14 +255,13 @@ func TestBuildJWKX25519(t *testing.T) { require.NoError(t, err) t.Run("success buildJWK for X25519", func(t *testing.T) { - _, err = BuildJWK(ecdhKeyMarshalled, kms.X25519ECDHKWType) + _, err = BuildJWK(x25519, kms.X25519ECDHKWType) require.NoError(t, err) }) t.Run("buildJWK for X25519 with invalid marshalled key", func(t *testing.T) { _, err = BuildJWK([]byte("invalidKey"), kms.X25519ECDHKWType) - require.EqualError(t, err, "buildJWK: failed to unmarshal public key from X25519 key: unmarshalECDHKey:"+ - " failed to unmarshal ECDH key: invalid character 'i' looking for beginning of value") + require.EqualError(t, err, "create JWK: marshalX25519: invalid key") }) t.Run("buildJWK for X25519 with invalid key size properly marshalled", func(t *testing.T) { @@ -277,8 +274,7 @@ func TestBuildJWKX25519(t *testing.T) { require.NoError(t, err) _, err = BuildJWK(ecdhKeyMarshalled, kms.X25519ECDHKWType) - require.EqualError(t, err, "buildJWK: failed to build JWK from X25519 key: create JWK: marshalX25519: "+ - "invalid key") + require.EqualError(t, err, "create JWK: marshalX25519: invalid key") }) } diff --git a/mock/suite/suite.go b/mock/suite/suite.go new file mode 100644 index 0000000..e6807e1 --- /dev/null +++ b/mock/suite/suite.go @@ -0,0 +1,68 @@ +/* +Copyright Gen Digital Inc. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ + +// Package suite contains mocks for kms+crypto wrapper suite. +package suite + +import ( + "github.com/trustbloc/kms-go/doc/jose/jwk" + "github.com/trustbloc/kms-go/mock/wrapper" + "github.com/trustbloc/kms-go/wrapper/api" +) + +// MockSuite mocks api.Suite. +type MockSuite wrapper.MockKMSCrypto + +// KMSCrypto mock. +func (m *MockSuite) KMSCrypto() (api.KMSCrypto, error) { + return (*wrapper.MockKMSCrypto)(m), nil +} + +// KeyCreator mock. +func (m *MockSuite) KeyCreator() (api.KeyCreator, error) { + return (*wrapper.MockKMSCrypto)(m), nil +} + +// RawKeyCreator mock. +func (m *MockSuite) RawKeyCreator() (api.RawKeyCreator, error) { + return (*wrapper.MockKMSCrypto)(m), nil +} + +// KMSCryptoSigner mock. +func (m *MockSuite) KMSCryptoSigner() (api.KMSCryptoSigner, error) { + return (*wrapper.MockKMSCrypto)(m), nil +} + +// KMSCryptoVerifier mock. +func (m *MockSuite) KMSCryptoVerifier() (api.KMSCryptoVerifier, error) { + return (*wrapper.MockKMSCrypto)(m), nil +} + +// KMSCryptoMultiSigner mock. +func (m *MockSuite) KMSCryptoMultiSigner() (api.KMSCryptoMultiSigner, error) { + return (*wrapper.MockKMSCrypto)(m), nil +} + +// EncrypterDecrypter mock. +func (m *MockSuite) EncrypterDecrypter() (api.EncrypterDecrypter, error) { + return (*wrapper.MockKMSCrypto)(m), nil +} + +// FixedKeyCrypto mock. +func (m *MockSuite) FixedKeyCrypto(pub *jwk.JWK) (api.FixedKeyCrypto, error) { + return (*wrapper.MockKMSCrypto)(m).FixedKeyCrypto(pub) +} + +// FixedKeySigner mock. +func (m *MockSuite) FixedKeySigner(kid string) (api.FixedKeySigner, error) { + return (*wrapper.MockKMSCrypto)(m).FixedKeySigner(nil) +} + +// FixedKeyMultiSigner mock. +func (m *MockSuite) FixedKeyMultiSigner(kid string) (api.FixedKeyMultiSigner, error) { + return (*wrapper.MockKMSCrypto)(m).FixedKeyMultiSigner(nil) +} + +var _ api.Suite = &MockSuite{} diff --git a/mock/wrapper/wrapper.go b/mock/wrapper/wrapper.go index 43a19b0..058c7f0 100644 --- a/mock/wrapper/wrapper.go +++ b/mock/wrapper/wrapper.go @@ -9,18 +9,25 @@ package wrapper import ( "github.com/trustbloc/kms-go/doc/jose/jwk" "github.com/trustbloc/kms-go/spi/kms" - "github.com/trustbloc/kms-go/wrapper" + wrapperapi "github.com/trustbloc/kms-go/wrapper/api" ) // MockKMSCrypto mocks wrapper.KMSCrypto. type MockKMSCrypto struct { CreateVal *jwk.JWK + CreateRawKID string + CreateRawVal interface{} CreateErr error SignVal []byte SignErr error VerifyErr error FixedKeyCryptoVal *MockFixedKeyCrypto FixedKeyCryptoErr error + EncryptVal []byte + EncryptNonce []byte + EncryptErr error + DecryptVal []byte + DecryptErr error } // Create mock. @@ -28,24 +35,67 @@ func (m *MockKMSCrypto) Create(keyType kms.KeyType) (*jwk.JWK, error) { return m.CreateVal, m.CreateErr } +// CreateRaw mock. +func (m *MockKMSCrypto) CreateRaw(keyType kms.KeyType) (string, interface{}, error) { + return m.CreateRawKID, m.CreateRawVal, m.CreateErr +} + // Sign mock. func (m *MockKMSCrypto) Sign(msg []byte, pub *jwk.JWK) ([]byte, error) { return m.SignVal, m.SignErr } +// SignMulti mock. +func (m *MockKMSCrypto) SignMulti(msgs [][]byte, pub *jwk.JWK) ([]byte, error) { + return m.SignVal, m.SignErr +} + // Verify mock. func (m *MockKMSCrypto) Verify(sig, msg []byte, pub *jwk.JWK) error { return m.VerifyErr } +// Encrypt mock. +func (m *MockKMSCrypto) Encrypt(msg, aad []byte, kid string) (cipher, nonce []byte, err error) { + return m.EncryptVal, m.EncryptNonce, m.EncryptErr +} + +// Decrypt mock. +func (m *MockKMSCrypto) Decrypt(cipher, aad, nonce []byte, kid string) (msg []byte, err error) { + return m.DecryptVal, m.DecryptErr +} + // FixedKeyCrypto mock. -func (m *MockKMSCrypto) FixedKeyCrypto(pub *jwk.JWK) (wrapper.FixedKeyCrypto, error) { - return m.FixedKeyCryptoVal, m.FixedKeyCryptoErr +func (m *MockKMSCrypto) FixedKeyCrypto(pub *jwk.JWK) (wrapperapi.FixedKeyCrypto, error) { + return makeMockFixedKey(m) } // FixedKeySigner mock. -func (m *MockKMSCrypto) FixedKeySigner(pub *jwk.JWK) (wrapper.FixedKeySigner, error) { - return m.FixedKeyCryptoVal, m.FixedKeyCryptoErr +func (m *MockKMSCrypto) FixedKeySigner(pub *jwk.JWK) (wrapperapi.FixedKeySigner, error) { + return makeMockFixedKey(m) +} + +// FixedMultiSignerGivenKID mock. +func (m *MockKMSCrypto) FixedMultiSignerGivenKID(kid string) (wrapperapi.FixedKeyMultiSigner, error) { + return makeMockFixedKey(m) +} + +// FixedKeyMultiSigner mock. +func (m *MockKMSCrypto) FixedKeyMultiSigner(pub *jwk.JWK) (wrapperapi.FixedKeyMultiSigner, error) { + return makeMockFixedKey(m) +} + +func makeMockFixedKey(m *MockKMSCrypto) (*MockFixedKeyCrypto, error) { + if m.FixedKeyCryptoVal != nil || m.FixedKeyCryptoErr != nil { + return m.FixedKeyCryptoVal, m.FixedKeyCryptoErr + } + + fkc := &MockFixedKeyCrypto{ + SignErr: m.SignErr, + VerifyErr: m.VerifyErr, + } + + return fkc, nil } // MockFixedKeyCrypto mocks kmscrypto.FixedKeyCrypto. @@ -60,9 +110,16 @@ func (m *MockFixedKeyCrypto) Sign(msg []byte) ([]byte, error) { return m.SignVal, m.SignErr } +// SignMulti mock. +func (m *MockFixedKeyCrypto) SignMulti(msgs [][]byte) ([]byte, error) { + return m.SignVal, m.SignErr +} + // Verify mock. func (m *MockFixedKeyCrypto) Verify(sig, msg []byte) error { return m.VerifyErr } -var _ wrapper.KMSCrypto = &MockKMSCrypto{} +var _ wrapperapi.KMSCryptoMultiSigner = &MockKMSCrypto{} + +var _ wrapperapi.KMSCrypto = &MockKMSCrypto{} diff --git a/wrapper/api.go b/wrapper/api.go deleted file mode 100644 index 4b825c8..0000000 --- a/wrapper/api.go +++ /dev/null @@ -1,60 +0,0 @@ -/* -Copyright Gen Digital Inc. All Rights Reserved. -SPDX-License-Identifier: Apache-2.0 -*/ - -package wrapper - -import ( - "github.com/trustbloc/kms-go/doc/jose/jwk" - kmsapi "github.com/trustbloc/kms-go/spi/kms" -) - -// KMSCryptoVerifier provides a signature verification interface. -type KMSCryptoVerifier interface { - Verify(sig, msg []byte, pub *jwk.JWK) error -} - -// KMSCrypto provides wrapped kms and crypto operations. -type KMSCrypto interface { - Create(keyType kmsapi.KeyType) (*jwk.JWK, error) - Sign(msg []byte, pub *jwk.JWK) ([]byte, error) - - KMSCryptoVerifier - - FixedKeyCrypto(pub *jwk.JWK) (FixedKeyCrypto, error) - FixedKeySigner(pub *jwk.JWK) (FixedKeySigner, error) -} - -// FixedKeyCrypto provides crypto operations using a fixed key. -type FixedKeyCrypto interface { - Sign(msg []byte) ([]byte, error) - Verify(sig, msg []byte) error -} - -// NewKMSCrypto creates a KMSCrypto instance. -func NewKMSCrypto(kms keyManager, crypto signerVerifier) KMSCrypto { - return &kmsCryptoImpl{ - kms: kms, - cr: crypto, - } -} - -// KMSCryptoSigner provides signing operations. -type KMSCryptoSigner interface { - Sign(msg []byte, pub *jwk.JWK) ([]byte, error) - FixedKeySigner(pub *jwk.JWK) (FixedKeySigner, error) -} - -// FixedKeySigner provides the common signer interface, using a fixed key for each signer instance. -type FixedKeySigner interface { - Sign(msg []byte) ([]byte, error) -} - -// NewKMSCryptoSigner creates a KMSCryptoSigner using the given kms and crypto implementations. -func NewKMSCryptoSigner(kms keyGetter, crypto signer) KMSCryptoSigner { - return &kmsCryptoSignerImpl{ - kms: kms, - crypto: crypto, - } -} diff --git a/wrapper/api/api.go b/wrapper/api/api.go new file mode 100644 index 0000000..9c13646 --- /dev/null +++ b/wrapper/api/api.go @@ -0,0 +1,100 @@ +/* +Copyright Gen Digital Inc. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ + +package api + +import ( + "errors" + + "github.com/trustbloc/kms-go/doc/jose/jwk" + kmsapi "github.com/trustbloc/kms-go/spi/kms" +) + +// Suite provides a suite of kms+crypto functions. +// +// Each suite method returns an implementation of a particular kms+crypto API, +// or ErrNotSupported if the given Suite does not support the requested API. +type Suite interface { + KMSCryptoVerifier() (KMSCryptoVerifier, error) + KeyCreator() (KeyCreator, error) + KMSCrypto() (KMSCrypto, error) + FixedKeyCrypto(pub *jwk.JWK) (FixedKeyCrypto, error) + RawKeyCreator() (RawKeyCreator, error) + KMSCryptoSigner() (KMSCryptoSigner, error) + FixedKeySigner(kid string) (FixedKeySigner, error) + KMSCryptoMultiSigner() (KMSCryptoMultiSigner, error) + FixedKeyMultiSigner(kid string) (FixedKeyMultiSigner, error) + EncrypterDecrypter() (EncrypterDecrypter, error) +} + +// ErrNotSupported is returned by a Suite method when said Suite does not +// support the requested behaviour. +var ErrNotSupported = errors.New("suite does not support requested behaviour") // nolint: gochecknoglobals + +// KMSCryptoVerifier provides a signature verification interface. +type KMSCryptoVerifier interface { + Verify(sig, msg []byte, pub *jwk.JWK) error +} + +// KeyCreator creates keypairs in the wrapped KMS, returning public keys in JWK format. +type KeyCreator interface { + Create(keyType kmsapi.KeyType) (*jwk.JWK, error) +} + +// KMSCrypto provides wrapped kms and crypto operations. +type KMSCrypto interface { + KeyCreator + + Sign(msg []byte, pub *jwk.JWK) ([]byte, error) + + KMSCryptoVerifier + + FixedKeyCrypto(pub *jwk.JWK) (FixedKeyCrypto, error) + FixedKeySigner(pub *jwk.JWK) (FixedKeySigner, error) +} + +// FixedKeyCrypto provides crypto operations using a fixed key. +type FixedKeyCrypto interface { + Sign(msg []byte) ([]byte, error) + Verify(sig, msg []byte) error +} + +// RawKeyCreator creates keypairs in the wrapped KMS, returning public keys as either JWK or the raw crypto key. +type RawKeyCreator interface { + KeyCreator + CreateRaw(keyType kmsapi.KeyType) (string, interface{}, error) +} + +// KMSCryptoSigner provides signing operations. +type KMSCryptoSigner interface { + Sign(msg []byte, pub *jwk.JWK) ([]byte, error) + FixedKeySigner(pub *jwk.JWK) (FixedKeySigner, error) +} + +// FixedKeySigner provides the common signer interface, using a fixed key for each signer instance. +type FixedKeySigner interface { + Sign(msg []byte) ([]byte, error) +} + +// KMSCryptoMultiSigner provides signing operations, including multi-signatures. +type KMSCryptoMultiSigner interface { + Sign(msg []byte, pub *jwk.JWK) ([]byte, error) + SignMulti(msgs [][]byte, pub *jwk.JWK) ([]byte, error) + FixedKeyMultiSigner(pub *jwk.JWK) (FixedKeyMultiSigner, error) + FixedMultiSignerGivenKID(kid string) (FixedKeyMultiSigner, error) +} + +// FixedKeyMultiSigner provides a signing interface for regular and +// multi-signatures using a fixed key for each signer instance. +type FixedKeyMultiSigner interface { + SignMulti(msgs [][]byte) ([]byte, error) + FixedKeySigner +} + +// EncrypterDecrypter provides encryption and decryption services. +type EncrypterDecrypter interface { + Encrypt(msg, aad []byte, kid string) (cipher, nonce []byte, err error) + Decrypt(cipher, aad, nonce []byte, kid string) (msg []byte, err error) +} diff --git a/wrapper/internal/params/params.go b/wrapper/internal/params/params.go new file mode 100644 index 0000000..c65a2d3 --- /dev/null +++ b/wrapper/internal/params/params.go @@ -0,0 +1,58 @@ +/* +Copyright Gen Digital Inc. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ + +package params + +import ( + kmsapi "github.com/trustbloc/kms-go/spi/kms" +) + +type Signer interface { + Sign(msg []byte, kh interface{}) ([]byte, error) +} + +type MultiSigner interface { + Signer + SignMulti(messages [][]byte, kh interface{}) ([]byte, error) +} + +type Verifier interface { + Verify(signature []byte, msg []byte, kh interface{}) error +} + +type SignerVerifier interface { + Signer + Verifier +} + +type KeyGetter interface { + Get(keyID string) (interface{}, error) +} + +type KeyHandleFetcher interface { + PubKeyBytesToHandle(pubKeyBytes []byte, keyType kmsapi.KeyType, opts ...kmsapi.KeyOpts) (interface{}, error) + ExportPubKeyBytes(keyID string) ([]byte, kmsapi.KeyType, error) + KeyGetter +} + +type KeyCreatorParam interface { + CreateAndExportPubKeyBytes(kt kmsapi.KeyType, opts ...kmsapi.KeyOpts) (string, []byte, error) +} + +type KeyManager interface { + KeyCreatorParam + KeyHandleFetcher +} + +type EncDecrypter interface { + Encrypt(msg, aad []byte, kh interface{}) ([]byte, []byte, error) + Decrypt(cipher, aad, nonce []byte, kh interface{}) ([]byte, error) +} + +type AllCrypto interface { + MultiSigner + Verifier + EncDecrypter +} diff --git a/wrapper/internal/wrapperimpl/creator.go b/wrapper/internal/wrapperimpl/creator.go new file mode 100644 index 0000000..6f7a28a --- /dev/null +++ b/wrapper/internal/wrapperimpl/creator.go @@ -0,0 +1,59 @@ +/* +Copyright Gen Digital Inc. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ + +package wrapperimpl + +import ( + "github.com/trustbloc/kms-go/doc/jose/jwk" + "github.com/trustbloc/kms-go/doc/jose/jwk/jwksupport" + "github.com/trustbloc/kms-go/spi/kms" + "github.com/trustbloc/kms-go/wrapper/api" + "github.com/trustbloc/kms-go/wrapper/internal/params" +) + +// NewKeyCreator returns a KeyCreator wrapping the given KMS. +func NewKeyCreator(kms params.KeyCreatorParam) api.RawKeyCreator { + return &keyCreatorImpl{kms: kms} +} + +type keyCreatorImpl struct { + kms params.KeyCreatorParam +} + +func (k *keyCreatorImpl) Create(keyType kms.KeyType) (*jwk.JWK, error) { + return createKey(k.kms, keyType) +} + +func (k *keyCreatorImpl) CreateRaw(keyType kms.KeyType) (string, interface{}, error) { + kid, pkBytes, err := k.kms.CreateAndExportPubKeyBytes(keyType) + if err != nil { + return "", nil, err + } + + raw, err := jwksupport.PubKeyBytesToKey(pkBytes, keyType) + if err != nil { + return "", nil, err + } + + return kid, raw, nil +} + +func createKey(creator params.KeyCreatorParam, keyType kms.KeyType) (*jwk.JWK, error) { + kid, pkBytes, err := creator.CreateAndExportPubKeyBytes(keyType) + if err != nil { + return nil, err + } + + pk, err := jwksupport.PubKeyBytesToJWK(pkBytes, keyType) + if err != nil { + return nil, err + } + + pk.KeyID = kid + + return pk, nil +} + +var _ api.KeyCreator = &keyCreatorImpl{} diff --git a/wrapper/internal/wrapperimpl/creator_test.go b/wrapper/internal/wrapperimpl/creator_test.go new file mode 100644 index 0000000..649fd00 --- /dev/null +++ b/wrapper/internal/wrapperimpl/creator_test.go @@ -0,0 +1,75 @@ +/* +Copyright Gen Digital Inc. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ + +package wrapperimpl + +import ( + "crypto/ed25519" + "crypto/rand" + "errors" + "testing" + + "github.com/stretchr/testify/require" + mockkms "github.com/trustbloc/kms-go/mock/kms" + kmsapi "github.com/trustbloc/kms-go/spi/kms" +) + +func TestKeyCreator(t *testing.T) { + + t.Run("success", func(t *testing.T) { + keyBytes, _, err := ed25519.GenerateKey(rand.Reader) + require.NoError(t, err) + + keyID := "foo" + + creator := NewKeyCreator(&mockkms.KeyManager{ + CrAndExportPubKeyValue: keyBytes, + CrAndExportPubKeyID: keyID, + }) + + pubJWK, err := creator.Create(kmsapi.ED25519Type) + require.NoError(t, err) + require.NotNil(t, pubJWK) + require.IsType(t, ed25519.PublicKey{}, pubJWK.Key) + + kid, pubRaw, err := creator.CreateRaw(kmsapi.ED25519Type) + require.NoError(t, err) + require.NotNil(t, pubRaw) + require.Equal(t, keyID, kid) + require.IsType(t, ed25519.PublicKey{}, pubRaw) + }) + + t.Run("kms create err", func(t *testing.T) { + errExpected := errors.New("expected error") + + creator := NewKeyCreator(&mockkms.KeyManager{ + CrAndExportPubKeyErr: errExpected, + }) + + pubJWK, err := creator.Create(kmsapi.ED25519Type) + require.ErrorIs(t, err, errExpected) + require.Nil(t, pubJWK) + + kid, pubRaw, err := creator.CreateRaw(kmsapi.ED25519Type) + require.ErrorIs(t, err, errExpected) + require.Nil(t, pubRaw) + require.Empty(t, kid) + }) + + t.Run("kms exports invalid key value", func(t *testing.T) { + creator := NewKeyCreator(&mockkms.KeyManager{ + CrAndExportPubKeyValue: []byte("foo"), + }) + + pubJWK, err := creator.Create(kmsapi.ECDSAP256DER) + require.Error(t, err) + require.Nil(t, pubJWK) + + kid, pubRaw, err := creator.CreateRaw(kmsapi.ECDSAP256DER) + require.Error(t, err) + require.Nil(t, pubRaw) + require.Empty(t, kid) + }) +} diff --git a/wrapper/internal/wrapperimpl/encrypter.go b/wrapper/internal/wrapperimpl/encrypter.go new file mode 100644 index 0000000..5568a57 --- /dev/null +++ b/wrapper/internal/wrapperimpl/encrypter.go @@ -0,0 +1,44 @@ +/* +Copyright Gen Digital Inc. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ + +package wrapperimpl + +import ( + "github.com/trustbloc/kms-go/wrapper/api" + "github.com/trustbloc/kms-go/wrapper/internal/params" +) + +// NewEncrypterDecrypter creates an api.EncrypterDecrypter using the given kms and crypto implementations. +func NewEncrypterDecrypter(kms params.KeyGetter, crypto params.EncDecrypter) api.EncrypterDecrypter { + return &crypterImpl{ + kms: kms, + crypto: crypto, + } +} + +type crypterImpl struct { + kms params.KeyGetter + crypto params.EncDecrypter +} + +func (c *crypterImpl) Encrypt(msg, aad []byte, kid string) (cipher, nonce []byte, err error) { + kh, err := c.kms.Get(kid) + if err != nil { + return nil, nil, err + } + + return c.crypto.Encrypt(msg, aad, kh) +} + +func (c *crypterImpl) Decrypt(cipher, aad, nonce []byte, kid string) (msg []byte, err error) { + kh, err := c.kms.Get(kid) + if err != nil { + return nil, err + } + + return c.crypto.Decrypt(cipher, aad, nonce, kh) +} + +var _ api.EncrypterDecrypter = &crypterImpl{} diff --git a/wrapper/internal/wrapperimpl/encrypter_test.go b/wrapper/internal/wrapperimpl/encrypter_test.go new file mode 100644 index 0000000..50c9a7f --- /dev/null +++ b/wrapper/internal/wrapperimpl/encrypter_test.go @@ -0,0 +1,79 @@ +/* +Copyright Gen Digital Inc. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ + +package wrapperimpl + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/require" + mockcrypto "github.com/trustbloc/kms-go/mock/crypto" + mockkms "github.com/trustbloc/kms-go/mock/kms" +) + +func TestEncrypterDecrypter(t *testing.T) { + t.Run("success", func(t *testing.T) { + cipher := []byte("nusutto ni torinokosareshi") + + msg := []byte("the thief left it behind") + aad := []byte("the moon, at my window") + kid := "foo" + nonce := []byte("e49tow4nho") + + crypter := NewEncrypterDecrypter(&mockkms.KeyManager{}, &mockcrypto.Crypto{ + EncryptValue: cipher, + EncryptNonceValue: nonce, + DecryptValue: msg, + }) + + encMessage, gotNonce, err := crypter.Encrypt(msg, aad, kid) + require.NoError(t, err) + require.Equal(t, cipher, encMessage) + require.Equal(t, nonce, gotNonce) + + gotMsg, err := crypter.Decrypt(encMessage, aad, nonce, kid) + require.NoError(t, err) + require.Equal(t, gotMsg, msg) + }) + + t.Run("kms get err", func(t *testing.T) { + errExpected := errors.New("expected error") + + crypter := NewEncrypterDecrypter(&mockkms.KeyManager{ + GetKeyErr: errExpected, + }, &mockcrypto.Crypto{}) + + enc, nonce, err := crypter.Encrypt(nil, nil, "") + require.ErrorIs(t, err, errExpected) + require.Nil(t, enc) + require.Nil(t, nonce) + + msg, err := crypter.Decrypt(nil, nil, nil, "") + require.ErrorIs(t, err, errExpected) + require.Nil(t, msg) + }) + + t.Run("crypto err", func(t *testing.T) { + errExpected := errors.New("expected error") + + crypter := NewEncrypterDecrypter( + &mockkms.KeyManager{}, + &mockcrypto.Crypto{ + EncryptErr: errExpected, + DecryptErr: errExpected, + }, + ) + + enc, nonce, err := crypter.Encrypt(nil, nil, "") + require.ErrorIs(t, err, errExpected) + require.Nil(t, enc) + require.Nil(t, nonce) + + msg, err := crypter.Decrypt(nil, nil, nil, "") + require.ErrorIs(t, err, errExpected) + require.Nil(t, msg) + }) +} diff --git a/wrapper/internal/wrapperimpl/multisigner.go b/wrapper/internal/wrapperimpl/multisigner.go new file mode 100644 index 0000000..daf9d92 --- /dev/null +++ b/wrapper/internal/wrapperimpl/multisigner.go @@ -0,0 +1,80 @@ +/* +Copyright Gen Digital Inc. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ + +package wrapperimpl + +import ( + "github.com/trustbloc/kms-go/doc/jose/jwk" + "github.com/trustbloc/kms-go/wrapper/api" + "github.com/trustbloc/kms-go/wrapper/internal/params" +) + +// NewKMSCryptoMultiSigner creates a KMSCryptoMultiSigner using the given kms and crypto implementations. +func NewKMSCryptoMultiSigner(kms params.KeyGetter, crypto params.MultiSigner) api.KMSCryptoMultiSigner { + return &multiSignerImpl{ + kms: kms, + crypto: crypto, + } +} + +type multiSignerImpl struct { + kms params.KeyGetter + crypto params.MultiSigner +} + +func (m *multiSignerImpl) Sign(msg []byte, pub *jwk.JWK) ([]byte, error) { + kh, err := m.kms.Get(pub.KeyID) + if err != nil { + return nil, err + } + + return m.crypto.Sign(msg, kh) +} + +func (m *multiSignerImpl) SignMulti(msgs [][]byte, pub *jwk.JWK) ([]byte, error) { + kh, err := m.kms.Get(pub.KeyID) + if err != nil { + return nil, err + } + + return m.crypto.SignMulti(msgs, kh) +} + +func (m *multiSignerImpl) FixedKeyMultiSigner(pub *jwk.JWK) (api.FixedKeyMultiSigner, error) { + return m.FixedMultiSignerGivenKID(pub.KeyID) +} + +func (m *multiSignerImpl) FixedMultiSignerGivenKID(kid string) (api.FixedKeyMultiSigner, error) { + return GetFixedMultiSigner(m.kms, m.crypto, kid) +} + +func GetFixedMultiSigner(kms params.KeyGetter, crypto params.MultiSigner, kid string) (api.FixedKeyMultiSigner, error) { + kh, err := kms.Get(kid) + if err != nil { + return nil, err + } + + return &fixedMultiSignerImpl{ + cr: crypto, + kh: kh, + }, nil +} + +var _ api.KMSCryptoMultiSigner = &multiSignerImpl{} + +type fixedMultiSignerImpl struct { + cr params.MultiSigner + kh interface{} +} + +func (f *fixedMultiSignerImpl) SignMulti(msgs [][]byte) ([]byte, error) { + return f.cr.SignMulti(msgs, f.kh) +} + +func (f *fixedMultiSignerImpl) Sign(msg []byte) ([]byte, error) { + return f.cr.Sign(msg, f.kh) +} + +var _ api.FixedKeyMultiSigner = &fixedMultiSignerImpl{} diff --git a/wrapper/internal/wrapperimpl/multisigner_test.go b/wrapper/internal/wrapperimpl/multisigner_test.go new file mode 100644 index 0000000..6e57c56 --- /dev/null +++ b/wrapper/internal/wrapperimpl/multisigner_test.go @@ -0,0 +1,128 @@ +/* +Copyright Gen Digital Inc. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ + +package wrapperimpl + +import ( + "errors" + "testing" + + "github.com/go-jose/go-jose/v3" + "github.com/stretchr/testify/require" + "github.com/trustbloc/kms-go/doc/jose/jwk" + mockcrypto "github.com/trustbloc/kms-go/mock/crypto" + mockkms "github.com/trustbloc/kms-go/mock/kms" +) + +func TestMultiSigner(t *testing.T) { + var ( + msgs = [][]byte{[]byte("foo"), []byte("qux")} + msg = []byte("foo bar") + pub = &jwk.JWK{JSONWebKey: jose.JSONWebKey{KeyID: "foo"}} + expSig = []byte("signature") + ) + + t.Run("sign success", func(t *testing.T) { + ms := NewKMSCryptoMultiSigner(&mockkms.KeyManager{}, &mockcrypto.Crypto{ + SignValue: expSig, + BBSSignValue: expSig, + }) + + sig, err := ms.Sign(msg, pub) + require.NoError(t, err) + require.Equal(t, expSig, sig) + + sig, err = ms.SignMulti(msgs, pub) + require.NoError(t, err) + require.Equal(t, expSig, sig) + }) + + t.Run("fixed key success", func(t *testing.T) { + ms := NewKMSCryptoMultiSigner(&mockkms.KeyManager{}, &mockcrypto.Crypto{ + SignValue: expSig, + BBSSignValue: expSig, + }) + + fkms, err := ms.FixedKeyMultiSigner(pub) + require.NoError(t, err) + + fkms, err = ms.FixedMultiSignerGivenKID(pub.KeyID) + require.NoError(t, err) + + sig, err := fkms.Sign(msg) + require.NoError(t, err) + require.Equal(t, expSig, sig) + + sig, err = fkms.SignMulti(msgs) + require.NoError(t, err) + require.Equal(t, expSig, sig) + }) + + errExpected := errors.New("expected error") + + t.Run("kms get error", func(t *testing.T) { + ms := NewKMSCryptoMultiSigner(&mockkms.KeyManager{ + GetKeyErr: errExpected, + }, &mockcrypto.Crypto{}) + + sig, err := ms.Sign(msg, pub) + require.ErrorIs(t, err, errExpected) + require.Nil(t, sig) + + sig, err = ms.SignMulti(msgs, pub) + require.ErrorIs(t, err, errExpected) + require.Nil(t, sig) + + fkms, err := ms.FixedKeyMultiSigner(pub) + require.ErrorIs(t, err, errExpected) + require.Nil(t, fkms) + + fkms, err = ms.FixedMultiSignerGivenKID(pub.KeyID) + require.ErrorIs(t, err, errExpected) + require.Nil(t, fkms) + }) + + t.Run("sign error", func(t *testing.T) { + ms := NewKMSCryptoMultiSigner( + &mockkms.KeyManager{}, + &mockcrypto.Crypto{ + SignErr: errExpected, + BBSSignErr: errExpected, + }, + ) + + sig, err := ms.Sign(msg, pub) + require.ErrorIs(t, err, errExpected) + require.Nil(t, sig) + + sig, err = ms.SignMulti(msgs, pub) + require.ErrorIs(t, err, errExpected) + require.Nil(t, sig) + }) + + t.Run("fixed key sign error", func(t *testing.T) { + ms := NewKMSCryptoMultiSigner( + &mockkms.KeyManager{}, + &mockcrypto.Crypto{ + SignErr: errExpected, + BBSSignErr: errExpected, + }, + ) + + fkms, err := ms.FixedKeyMultiSigner(pub) + require.NoError(t, err) + + fkms, err = ms.FixedMultiSignerGivenKID(pub.KeyID) + require.NoError(t, err) + + sig, err := fkms.Sign(msg) + require.ErrorIs(t, err, errExpected) + require.Nil(t, sig) + + sig, err = fkms.SignMulti(msgs) + require.ErrorIs(t, err, errExpected) + require.Nil(t, sig) + }) +} diff --git a/wrapper/signer.go b/wrapper/internal/wrapperimpl/signer.go similarity index 55% rename from wrapper/signer.go rename to wrapper/internal/wrapperimpl/signer.go index c4a0f44..aa8fabc 100644 --- a/wrapper/signer.go +++ b/wrapper/internal/wrapperimpl/signer.go @@ -3,15 +3,25 @@ Copyright Gen Digital Inc. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ -package wrapper +package wrapperimpl import ( "github.com/trustbloc/kms-go/doc/jose/jwk" + "github.com/trustbloc/kms-go/wrapper/api" + "github.com/trustbloc/kms-go/wrapper/internal/params" ) +// NewKMSCryptoSigner creates a KMSCryptoSigner using the given kms and crypto implementations. +func NewKMSCryptoSigner(kms params.KeyGetter, crypto params.Signer) api.KMSCryptoSigner { + return &kmsCryptoSignerImpl{ + kms: kms, + crypto: crypto, + } +} + type kmsCryptoSignerImpl struct { - kms keyGetter - crypto signer + kms params.KeyGetter + crypto params.Signer } func (k *kmsCryptoSignerImpl) Sign(msg []byte, pub *jwk.JWK) ([]byte, error) { @@ -23,7 +33,7 @@ func (k *kmsCryptoSignerImpl) Sign(msg []byte, pub *jwk.JWK) ([]byte, error) { return k.crypto.Sign(msg, kh) } -func (k *kmsCryptoSignerImpl) FixedKeySigner(pub *jwk.JWK) (FixedKeySigner, error) { +func (k *kmsCryptoSignerImpl) FixedKeySigner(pub *jwk.JWK) (api.FixedKeySigner, error) { kh, err := k.kms.Get(pub.KeyID) if err != nil { return nil, err @@ -36,7 +46,7 @@ func (k *kmsCryptoSignerImpl) FixedKeySigner(pub *jwk.JWK) (FixedKeySigner, erro } type fixedKeySignerImpl struct { - cr signer + cr params.Signer kh interface{} } diff --git a/wrapper/wrapper.go b/wrapper/internal/wrapperimpl/wrapper.go similarity index 57% rename from wrapper/wrapper.go rename to wrapper/internal/wrapperimpl/wrapper.go index 63427da..11c5c19 100644 --- a/wrapper/wrapper.go +++ b/wrapper/internal/wrapperimpl/wrapper.go @@ -3,34 +3,30 @@ Copyright Gen Digital Inc. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ -// Package wrapper provides wrappers that combine localkms and tinkcrypto together into a more streamlined API. -package wrapper +package wrapperimpl import ( "github.com/trustbloc/kms-go/doc/jose/jwk" - "github.com/trustbloc/kms-go/doc/jose/jwk/jwksupport" "github.com/trustbloc/kms-go/spi/kms" + "github.com/trustbloc/kms-go/wrapper/api" + "github.com/trustbloc/kms-go/wrapper/internal/params" ) +// NewKMSCrypto creates a KMSCrypto instance. +func NewKMSCrypto(kms params.KeyManager, crypto params.SignerVerifier) api.KMSCrypto { + return &kmsCryptoImpl{ + kms: kms, + cr: crypto, + } +} + type kmsCryptoImpl struct { - kms keyManager - cr signerVerifier + kms params.KeyManager + cr params.SignerVerifier } func (k *kmsCryptoImpl) Create(keyType kms.KeyType) (*jwk.JWK, error) { - kid, pkBytes, err := k.kms.CreateAndExportPubKeyBytes(keyType) - if err != nil { - return nil, err - } - - pk, err := jwksupport.PubKeyBytesToJWK(pkBytes, keyType) - if err != nil { - return nil, err - } - - pk.KeyID = kid - - return pk, nil + return createKey(k.kms, keyType) } func (k *kmsCryptoImpl) Sign(msg []byte, pub *jwk.JWK) ([]byte, error) { @@ -42,7 +38,7 @@ func (k *kmsCryptoImpl) Sign(msg []byte, pub *jwk.JWK) ([]byte, error) { return k.cr.Sign(msg, kh) } -func getKeyHandle(pub *jwk.JWK, keyManager keyHandleFetcher) (interface{}, error) { +func getKeyHandle(pub *jwk.JWK, keyManager params.KeyHandleFetcher) (interface{}, error) { var ( pkb []byte kt kms.KeyType @@ -79,38 +75,46 @@ func (k *kmsCryptoImpl) Verify(sig, msg []byte, pub *jwk.JWK) error { return k.cr.Verify(sig, msg, kh) } -func (k *kmsCryptoImpl) FixedKeyCrypto(pub *jwk.JWK) (FixedKeyCrypto, error) { - sigKH, err := k.kms.Get(pub.KeyID) +func (k *kmsCryptoImpl) FixedKeyCrypto(pub *jwk.JWK) (api.FixedKeyCrypto, error) { + return MakeFixedKeyCrypto(k.kms, k.cr, pub) +} + +func MakeFixedKeyCrypto(kms params.KeyManager, crypto params.SignerVerifier, pub *jwk.JWK) (api.FixedKeyCrypto, error) { + sigKH, err := kms.Get(pub.KeyID) if err != nil { return nil, err } - verKH, err := getKeyHandle(pub, k.kms) + verKH, err := getKeyHandle(pub, kms) if err != nil { return nil, err } return &fixedKeyImpl{ - cr: k.cr, + cr: crypto, sigKH: sigKH, verKH: verKH, }, nil } -func (k *kmsCryptoImpl) FixedKeySigner(pub *jwk.JWK) (FixedKeySigner, error) { - kh, err := k.kms.Get(pub.KeyID) +func (k *kmsCryptoImpl) FixedKeySigner(pub *jwk.JWK) (api.FixedKeySigner, error) { + return MakeFixedKeySigner(k.kms, k.cr, pub.KeyID) +} + +func MakeFixedKeySigner(kms params.KeyGetter, crypto params.Signer, kid string) (api.FixedKeySigner, error) { + kh, err := kms.Get(kid) if err != nil { return nil, err } return &fixedKeySignerImpl{ - cr: k.cr, + cr: crypto, kh: kh, }, nil } type fixedKeyImpl struct { - cr signerVerifier + cr params.SignerVerifier sigKH interface{} verKH interface{} } diff --git a/wrapper/wrapper_test.go b/wrapper/internal/wrapperimpl/wrapper_test.go similarity index 99% rename from wrapper/wrapper_test.go rename to wrapper/internal/wrapperimpl/wrapper_test.go index 6c2cfbd..7b3ea70 100644 --- a/wrapper/wrapper_test.go +++ b/wrapper/internal/wrapperimpl/wrapper_test.go @@ -3,7 +3,7 @@ Copyright Gen Digital Inc. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ -package wrapper +package wrapperimpl import ( "crypto/ed25519" diff --git a/wrapper/internal/wrapperimpl/wrapperimpl.go b/wrapper/internal/wrapperimpl/wrapperimpl.go new file mode 100644 index 0000000..c0d66bf --- /dev/null +++ b/wrapper/internal/wrapperimpl/wrapperimpl.go @@ -0,0 +1,8 @@ +/* +Copyright Gen Digital Inc. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ + +// Package wrapperimpl implements wrappers meant for localkms and tinkcrypto. +package wrapperimpl + diff --git a/wrapper/localsuite/localsuite.go b/wrapper/localsuite/localsuite.go new file mode 100644 index 0000000..125d298 --- /dev/null +++ b/wrapper/localsuite/localsuite.go @@ -0,0 +1,54 @@ +/* +Copyright Gen Digital Inc. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ + +// Package localsuite provides a wrapper.Suite using local kms and crypto implementations. +package localsuite + +import ( + "github.com/trustbloc/kms-go/crypto/tinkcrypto" + "github.com/trustbloc/kms-go/kms/localkms" + kmsapi "github.com/trustbloc/kms-go/spi/kms" + "github.com/trustbloc/kms-go/spi/secretlock" + "github.com/trustbloc/kms-go/wrapper/api" +) + +// NewLocalCryptoSuite initializes a wrapper.Suite using local kms and crypto +// implementations, supporting all Suite APIs. +func NewLocalCryptoSuite( + primaryKeyURI string, + keyStore kmsapi.Store, + secretLock secretlock.Service, +) (api.Suite, error) { + kms, err := localkms.New(primaryKeyURI, &kmsProv{ + store: keyStore, + lock: secretLock, + }) + if err != nil { + return nil, err + } + + crypto, err := tinkcrypto.New() + if err != nil { + return nil, err + } + + return &suiteImpl{ + kms: kms, + crypto: crypto, + }, nil +} + +type kmsProv struct { + store kmsapi.Store + lock secretlock.Service +} + +func (k *kmsProv) StorageProvider() kmsapi.Store { + return k.store +} + +func (k *kmsProv) SecretLock() secretlock.Service { + return k.lock +} diff --git a/wrapper/localsuite/suite.go b/wrapper/localsuite/suite.go new file mode 100644 index 0000000..c9a3565 --- /dev/null +++ b/wrapper/localsuite/suite.go @@ -0,0 +1,58 @@ +/* +Copyright Gen Digital Inc. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ + +package localsuite + +import ( + "github.com/trustbloc/kms-go/doc/jose/jwk" + wrapperapi "github.com/trustbloc/kms-go/wrapper/api" + "github.com/trustbloc/kms-go/wrapper/internal/params" + "github.com/trustbloc/kms-go/wrapper/internal/wrapperimpl" +) + +type suiteImpl struct { + kms params.KeyManager + crypto params.AllCrypto +} + +func (s *suiteImpl) KMSCryptoVerifier() (wrapperapi.KMSCryptoVerifier, error) { + return wrapperimpl.NewKMSCrypto(s.kms, s.crypto), nil +} + +func (s *suiteImpl) KeyCreator() (wrapperapi.KeyCreator, error) { + return wrapperimpl.NewKMSCrypto(s.kms, s.crypto), nil +} + +func (s *suiteImpl) KMSCrypto() (wrapperapi.KMSCrypto, error) { + return wrapperimpl.NewKMSCrypto(s.kms, s.crypto), nil +} + +func (s *suiteImpl) FixedKeyCrypto(pub *jwk.JWK) (wrapperapi.FixedKeyCrypto, error) { + return wrapperimpl.MakeFixedKeyCrypto(s.kms, s.crypto, pub) +} + +func (s *suiteImpl) RawKeyCreator() (wrapperapi.RawKeyCreator, error) { + return wrapperimpl.NewKeyCreator(s.kms), nil +} + +func (s *suiteImpl) KMSCryptoSigner() (wrapperapi.KMSCryptoSigner, error) { + return wrapperimpl.NewKMSCryptoSigner(s.kms, s.crypto), nil +} + +func (s *suiteImpl) FixedKeySigner(kid string) (wrapperapi.FixedKeySigner, error) { + return wrapperimpl.MakeFixedKeySigner(s.kms, s.crypto, kid) +} + +func (s *suiteImpl) KMSCryptoMultiSigner() (wrapperapi.KMSCryptoMultiSigner, error) { + return wrapperimpl.NewKMSCryptoMultiSigner(s.kms, s.crypto), nil +} + +func (s *suiteImpl) FixedKeyMultiSigner(kid string) (wrapperapi.FixedKeyMultiSigner, error) { + return wrapperimpl.GetFixedMultiSigner(s.kms, s.crypto, kid) +} + +func (s *suiteImpl) EncrypterDecrypter() (wrapperapi.EncrypterDecrypter, error) { + return wrapperimpl.NewEncrypterDecrypter(s.kms, s.crypto), nil +} diff --git a/wrapper/localsuite/suite_test.go b/wrapper/localsuite/suite_test.go new file mode 100644 index 0000000..7557bad --- /dev/null +++ b/wrapper/localsuite/suite_test.go @@ -0,0 +1,90 @@ +/* +Copyright Gen Digital Inc. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ + +package localsuite + +import ( + "testing" + + "github.com/stretchr/testify/require" + mockstorage "github.com/trustbloc/kms-go/internal/mock/storage" + "github.com/trustbloc/kms-go/kms" + "github.com/trustbloc/kms-go/secretlock/noop" + kmsapi "github.com/trustbloc/kms-go/spi/kms" +) + +func TestSuite(t *testing.T) { + store, e := kms.NewAriesProviderWrapper(mockstorage.NewMockStoreProvider()) + require.NoError(t, e) + + suite, e := NewLocalCryptoSuite("local-lock://custom/primary/key/", store, &noop.NoLock{}) + require.NoError(t, e) + + creator, e := suite.KeyCreator() + require.NoError(t, e) + + pub, e := creator.Create(kmsapi.BLS12381G2Type) + require.NoError(t, e) + + t.Run("KMSCryptoVerifier", func(t *testing.T) { + kcv, err := suite.KMSCryptoVerifier() + require.NoError(t, err) + require.NotNil(t, kcv) + }) + + t.Run("KeyCreator", func(t *testing.T) { + c, err := suite.KeyCreator() + require.NoError(t, err) + require.NotNil(t, c) + }) + + t.Run("KMSCrypto", func(t *testing.T) { + kc, err := suite.KMSCrypto() + require.NoError(t, err) + require.NotNil(t, kc) + }) + + t.Run("FixedKeyCrypto", func(t *testing.T) { + fkc, err := suite.FixedKeyCrypto(pub) + require.NoError(t, err) + require.NotNil(t, fkc) + }) + + t.Run("RawKeyCreator", func(t *testing.T) { + rc, err := suite.RawKeyCreator() + require.NoError(t, err) + require.NotNil(t, rc) + }) + + t.Run("KMSCryptoSigner", func(t *testing.T) { + kcs, err := suite.KMSCryptoSigner() + require.NoError(t, err) + require.NotNil(t, kcs) + }) + + t.Run("FixedKeySigner", func(t *testing.T) { + fks, err := suite.FixedKeySigner(pub.KeyID) + require.NoError(t, err) + require.NotNil(t, fks) + }) + + t.Run("KMSCryptoMultiSigner", func(t *testing.T) { + kcms, err := suite.KMSCryptoMultiSigner() + require.NoError(t, err) + require.NotNil(t, kcms) + }) + + t.Run("FixedKeyMultiSigner", func(t *testing.T) { + fkms, err := suite.FixedKeyMultiSigner(pub.KeyID) + require.NoError(t, err) + require.NotNil(t, fkms) + }) + + t.Run("EncrypterDecrypter", func(t *testing.T) { + enc, err := suite.EncrypterDecrypter() + require.NoError(t, err) + require.NotNil(t, enc) + }) +} diff --git a/wrapper/params.go b/wrapper/params.go deleted file mode 100644 index f16b120..0000000 --- a/wrapper/params.go +++ /dev/null @@ -1,42 +0,0 @@ -/* -Copyright Gen Digital Inc. All Rights Reserved. -SPDX-License-Identifier: Apache-2.0 -*/ - -package wrapper - -import ( - kmsapi "github.com/trustbloc/kms-go/spi/kms" -) - -type signer interface { - Sign(msg []byte, kh interface{}) ([]byte, error) -} - -type verifier interface { - Verify(signature []byte, msg []byte, kh interface{}) error -} - -type signerVerifier interface { - signer - verifier -} - -type keyGetter interface { - Get(keyID string) (interface{}, error) -} - -type keyHandleFetcher interface { - PubKeyBytesToHandle(pubKeyBytes []byte, keyType kmsapi.KeyType, opts ...kmsapi.KeyOpts) (interface{}, error) - ExportPubKeyBytes(keyID string) ([]byte, kmsapi.KeyType, error) - keyGetter -} - -type keyCreator interface { - CreateAndExportPubKeyBytes(kt kmsapi.KeyType, opts ...kmsapi.KeyOpts) (string, []byte, error) -} - -type keyManager interface { - keyCreator - keyHandleFetcher -} diff --git a/wrapper/websuite/fixedkey.go b/wrapper/websuite/fixedkey.go new file mode 100644 index 0000000..3f16028 --- /dev/null +++ b/wrapper/websuite/fixedkey.go @@ -0,0 +1,44 @@ +/* +Copyright Gen Digital Inc. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ + +package websuite + +import ( + webcrypto "github.com/trustbloc/kms-go/crypto/webkms" + "github.com/trustbloc/kms-go/kms/webkms" +) + +func makeFixedKey( + keyID string, + keyGetter *webkms.RemoteKMS, + crypto *webcrypto.RemoteCrypto, +) (*fixedKeyCrypto, error) { + keyURL, err := keyGetter.Get(keyID) + if err != nil { + return nil, err + } + + return &fixedKeyCrypto{ + keyURL: keyURL, + cr: crypto, + }, nil +} + +type fixedKeyCrypto struct { + keyURL interface{} + cr *webcrypto.RemoteCrypto +} + +func (f *fixedKeyCrypto) Sign(msg []byte) ([]byte, error) { + return f.cr.Sign(msg, f.keyURL) +} + +func (f *fixedKeyCrypto) SignMulti(msgs [][]byte) ([]byte, error) { + return f.cr.SignMulti(msgs, f.keyURL) +} + +func (f *fixedKeyCrypto) Verify(sig, msg []byte) error { + return f.cr.Verify(sig, msg, f.keyURL) +} diff --git a/wrapper/websuite/kmscrypto.go b/wrapper/websuite/kmscrypto.go new file mode 100644 index 0000000..c2e1ded --- /dev/null +++ b/wrapper/websuite/kmscrypto.go @@ -0,0 +1,111 @@ +/* +Copyright Gen Digital Inc. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ + +package websuite + +import ( + webcrypto "github.com/trustbloc/kms-go/crypto/webkms" + "github.com/trustbloc/kms-go/doc/jose/jwk" + "github.com/trustbloc/kms-go/doc/jose/jwk/jwksupport" + "github.com/trustbloc/kms-go/kms/webkms" + "github.com/trustbloc/kms-go/spi/kms" + wrapperapi "github.com/trustbloc/kms-go/wrapper/api" +) + +type kmsCrypto struct { + km *webkms.RemoteKMS + cr *webcrypto.RemoteCrypto +} + +func (k *kmsCrypto) Create(keyType kms.KeyType) (*jwk.JWK, error) { + kid, pkBytes, err := k.km.CreateAndExportPubKeyBytes(keyType) + if err != nil { + return nil, err + } + + pk, err := jwksupport.PubKeyBytesToJWK(pkBytes, keyType) + if err != nil { + return nil, err + } + + pk.KeyID = kid + + return pk, nil +} + +func (k *kmsCrypto) CreateRaw(keyType kms.KeyType) (string, interface{}, error) { + kid, pkBytes, err := k.km.CreateAndExportPubKeyBytes(keyType) + if err != nil { + return "", nil, err + } + + raw, err := jwksupport.PubKeyBytesToKey(pkBytes, keyType) + if err != nil { + return "", nil, err + } + + return kid, raw, nil +} + +func (k *kmsCrypto) Sign(msg []byte, pub *jwk.JWK) ([]byte, error) { + kh, err := k.km.Get(pub.KeyID) + if err != nil { + return nil, err + } + + return k.cr.Sign(msg, kh) +} + +func (k *kmsCrypto) SignMulti(msgs [][]byte, pub *jwk.JWK) ([]byte, error) { + kh, err := k.km.Get(pub.KeyID) + if err != nil { + return nil, err + } + + return k.cr.SignMulti(msgs, kh) +} + +func (k *kmsCrypto) Verify(sig, msg []byte, pub *jwk.JWK) error { + kh, err := k.km.Get(pub.KeyID) + if err != nil { + return err + } + + return k.cr.Verify(sig, msg, kh) +} + +func (k *kmsCrypto) Encrypt(msg, aad []byte, kid string) (cipher, nonce []byte, err error) { + kh, err := k.km.Get(kid) + if err != nil { + return nil, nil, err + } + + return k.cr.Encrypt(msg, aad, kh) +} + +func (k *kmsCrypto) Decrypt(cipher, aad, nonce []byte, kid string) (msg []byte, err error) { + kh, err := k.km.Get(kid) + if err != nil { + return nil, err + } + + return k.cr.Decrypt(cipher, aad, nonce, kh) +} + +func (k *kmsCrypto) FixedKeyCrypto(pub *jwk.JWK) (wrapperapi.FixedKeyCrypto, error) { + return makeFixedKey(pub.KeyID, k.km, k.cr) +} + +func (k *kmsCrypto) FixedKeySigner(pub *jwk.JWK) (wrapperapi.FixedKeySigner, error) { + return makeFixedKey(pub.KeyID, k.km, k.cr) +} + +func (k *kmsCrypto) FixedKeyMultiSigner(pub *jwk.JWK) (wrapperapi.FixedKeyMultiSigner, error) { + return makeFixedKey(pub.KeyID, k.km, k.cr) +} + +func (k *kmsCrypto) FixedMultiSignerGivenKID(kid string) (wrapperapi.FixedKeyMultiSigner, error) { + return makeFixedKey(kid, k.km, k.cr) +} diff --git a/wrapper/websuite/websuite.go b/wrapper/websuite/websuite.go new file mode 100644 index 0000000..8496dff --- /dev/null +++ b/wrapper/websuite/websuite.go @@ -0,0 +1,94 @@ +/* +Copyright Gen Digital Inc. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ + +// Package websuite provides a wrapper.Suite implemented using web kms and web crypto clients. +package websuite + +import ( + "net/http" + + webcrypto "github.com/trustbloc/kms-go/crypto/webkms" + "github.com/trustbloc/kms-go/doc/jose/jwk" + "github.com/trustbloc/kms-go/kms/webkms" + wrapperapi "github.com/trustbloc/kms-go/wrapper/api" +) + +// NewWebCryptoSuite initializes a wrapper.Suite using web kms and crypto +// clients, supporting all Suite APIs. +func NewWebCryptoSuite(endpoint string, httpClient *http.Client) wrapperapi.Suite { + km := webkms.New(endpoint, httpClient) + cr := webcrypto.New(endpoint, httpClient) + + return &suite{ + km: km, + cr: cr, + } +} + +type suite struct { + km *webkms.RemoteKMS + cr *webcrypto.RemoteCrypto +} + +func (s *suite) KMSCryptoVerifier() (wrapperapi.KMSCryptoVerifier, error) { + return &kmsCrypto{ + km: s.km, + cr: s.cr, + }, nil +} + +func (s *suite) KeyCreator() (wrapperapi.KeyCreator, error) { + return &kmsCrypto{ + km: s.km, + cr: s.cr, + }, nil +} + +func (s *suite) KMSCrypto() (wrapperapi.KMSCrypto, error) { + return &kmsCrypto{ + km: s.km, + cr: s.cr, + }, nil +} + +func (s *suite) FixedKeyCrypto(pub *jwk.JWK) (wrapperapi.FixedKeyCrypto, error) { + return makeFixedKey(pub.KeyID, s.km, s.cr) +} + +func (s *suite) RawKeyCreator() (wrapperapi.RawKeyCreator, error) { + return &kmsCrypto{ + km: s.km, + cr: s.cr, + }, nil +} + +func (s *suite) KMSCryptoSigner() (wrapperapi.KMSCryptoSigner, error) { + return &kmsCrypto{ + km: s.km, + cr: s.cr, + }, nil +} + +func (s *suite) FixedKeySigner(kid string) (wrapperapi.FixedKeySigner, error) { + return makeFixedKey(kid, s.km, s.cr) +} + +func (s *suite) KMSCryptoMultiSigner() (wrapperapi.KMSCryptoMultiSigner, error) { + return &kmsCrypto{ + km: s.km, + cr: s.cr, + }, nil +} + +func (s *suite) FixedKeyMultiSigner(kid string) (wrapperapi.FixedKeyMultiSigner, error) { + return makeFixedKey(kid, s.km, s.cr) +} + +func (s *suite) EncrypterDecrypter() (wrapperapi.EncrypterDecrypter, error) { + return &kmsCrypto{ + km: s.km, + cr: s.cr, + }, nil +}