From 0a976c6c037b042875cdeb0f41f138a878c4604f Mon Sep 17 00:00:00 2001 From: Danny Thuering <655937+dthuering@users.noreply.github.com> Date: Fri, 16 Jun 2023 07:12:08 +0200 Subject: [PATCH] add properties to key creation request allow per key type setting for HSM backed storage in Azure --- CHANGELOG.md | 4 +++ src/stores/api/http/keys.go | 3 +- src/stores/api/types/keys.go | 1 + src/stores/entities/attributes.go | 3 ++ src/stores/store/keys/akv/akv.go | 10 +++++++ src/stores/store/keys/akv/akv_test.go | 43 ++++++++++++++++++++++++++- src/stores/store/keys/akv/parser.go | 4 +-- 7 files changed, 64 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index caec119b4..d7a645d8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Quorum Key Manager Release Notes +## v21.12.6 (2023-6-16) +### 🆕 Features +* Add AKV HSM support for Keys. + ## v21.12.5 (2022-6-13) ### 🛠 Bug fixes * Fix panic `d.nx != 0` caused by concurrency issue on hashing credentials. diff --git a/src/stores/api/http/keys.go b/src/stores/api/http/keys.go index b0a8bdc53..14ce20266 100644 --- a/src/stores/api/http/keys.go +++ b/src/stores/api/http/keys.go @@ -81,7 +81,8 @@ func (h *KeysHandler) create(rw http.ResponseWriter, request *http.Request) { EllipticCurve: entities2.Curve(createKeyRequest.Curve), }, &entities.Attributes{ - Tags: createKeyRequest.Tags, + Tags: createKeyRequest.Tags, + Properties: createKeyRequest.Properties, }) if err != nil { infrahttp.WriteHTTPErrorResponse(rw, err) diff --git a/src/stores/api/types/keys.go b/src/stores/api/types/keys.go index 077fe4fe0..1525bf746 100644 --- a/src/stores/api/types/keys.go +++ b/src/stores/api/types/keys.go @@ -10,6 +10,7 @@ type CreateKeyRequest struct { Curve string `json:"curve" validate:"required,isCurve" example:"secp256k1" enums:"babyjubjub,secp256k1"` SigningAlgorithm string `json:"signingAlgorithm" validate:"required,isSigningAlgorithm" example:"ecdsa" enums:"ecdsa,eddsa"` Tags map[string]string `json:"tags,omitempty"` + Properties map[string]string `json:"properties,omitempty" example:"EC-HSM"` } type ImportKeyRequest struct { diff --git a/src/stores/entities/attributes.go b/src/stores/entities/attributes.go index 87aa6df4d..011fbb447 100644 --- a/src/stores/entities/attributes.go +++ b/src/stores/entities/attributes.go @@ -29,6 +29,9 @@ type Attributes struct { // Tags attached to a stored item Tags map[string]string + + // Properties for further configuration options + Properties map[string]string } type Recovery struct { diff --git a/src/stores/store/keys/akv/akv.go b/src/stores/store/keys/akv/akv.go index 8c3540711..25a051374 100644 --- a/src/stores/store/keys/akv/akv.go +++ b/src/stores/store/keys/akv/akv.go @@ -3,6 +3,7 @@ package akv import ( "context" "encoding/base64" + "strings" "time" entities2 "github.com/consensys/quorum-key-manager/src/entities" @@ -18,6 +19,10 @@ import ( "github.com/consensys/quorum-key-manager/src/stores/entities" ) +const ( + AZURE_KEY_VAULT_TYPE = "AZURE_KEY_VAULT_TYPE" +) + type Store struct { client akv.KeysClient logger log.Logger @@ -45,6 +50,11 @@ func (s *Store) Create(ctx context.Context, id string, alg *entities2.Algorithm, case alg.Type == entities2.Ecdsa && alg.EllipticCurve == entities2.Secp256k1: kty = keyvault.EC crv = keyvault.P256K + if keyVaultType, ok := attr.Properties[AZURE_KEY_VAULT_TYPE]; ok { + if string(keyvault.ECHSM) == strings.ToUpper(keyVaultType) { + kty = keyvault.ECHSM + } + } default: errMessage := "not supported elliptic curve and signing algorithm in AKV for creation" logger.Error(errMessage) diff --git a/src/stores/store/keys/akv/akv_test.go b/src/stores/store/keys/akv/akv_test.go index 845c2733f..5712072e2 100644 --- a/src/stores/store/keys/akv/akv_test.go +++ b/src/stores/store/keys/akv/akv_test.go @@ -43,7 +43,7 @@ type akvKeyStoreTestSuite struct { keyStore stores.KeyStore } -func TestHashicorpKeyStore(t *testing.T) { +func TestAkvKeyStore(t *testing.T) { s := new(akvKeyStoreTestSuite) suite.Run(t, s) } @@ -82,6 +82,47 @@ func (s *akvKeyStoreTestSuite) TestCreate() { s.mockVault.EXPECT().CreateKey(gomock.Any(), id, akv.EC, akv.P256K, gomock.Any(), gomock.Any(), gomock.Any()). Return(akvKey, nil) + key, err := s.keyStore.Create(ctx, id, algorithm, attributes) + fmt.Printf("\n\n%s\n\n", hexutil.Encode(key.PublicKey)) + assert.NoError(s.T(), err) + assert.Equal(s.T(), publicKey, hexutil.Encode(key.PublicKey)) + assert.Equal(s.T(), id, key.ID) + assert.Equal(s.T(), entities.Ecdsa, key.Algo.Type) + assert.Equal(s.T(), entities.Secp256k1, key.Algo.EllipticCurve) + assert.False(s.T(), key.Metadata.Disabled) + assert.Equal(s.T(), version, key.Metadata.Version) + }) +} + +func (s *akvKeyStoreTestSuite) TestCreateHsm() { + ctx := context.Background() + attributes := testutils.FakeAttributes() + algorithm := testutils.FakeAlgorithm() + attributes.Properties = map[string]string{ + AZURE_KEY_VAULT_TYPE: "ec-hsm", + } + version := "1234" + + akvKeyID := fmt.Sprintf("keyvault.com/keys/%s/%s", id, version) + akvKey := akv.KeyBundle{ + Attributes: &akv.KeyAttributes{ + Enabled: common.ToPtr(true).(*bool), + Created: common.ToPtr(date.NewUnixTimeFromNanoseconds(time.Now().UnixNano())).(*date.UnixTime), + Updated: common.ToPtr(date.NewUnixTimeFromNanoseconds(time.Now().UnixNano())).(*date.UnixTime), + }, + Key: &akv.JSONWebKey{ + Kid: &akvKeyID, + Crv: akv.P256K, + Kty: akv.ECHSM, + X: &base64PubKeyX, + Y: &base64PubKeyY, + }, + } + + s.Run("should create a new key successfully", func() { + s.mockVault.EXPECT().CreateKey(gomock.Any(), id, akv.ECHSM, akv.P256K, gomock.Any(), gomock.Any(), gomock.Any()). + Return(akvKey, nil) + key, err := s.keyStore.Create(ctx, id, algorithm, attributes) assert.NoError(s.T(), err) diff --git a/src/stores/store/keys/akv/parser.go b/src/stores/store/keys/akv/parser.go index d303f2abe..80997450f 100644 --- a/src/stores/store/keys/akv/parser.go +++ b/src/stores/store/keys/akv/parser.go @@ -42,7 +42,7 @@ func convertToAKVKeyAttr(attr *entities.Attributes) *keyvault.KeyAttributes { func algoFromAKVKeyTypeCrv(kty keyvault.JSONWebKeyType, crv keyvault.JSONWebKeyCurveName) *entities2.Algorithm { algo := &entities2.Algorithm{} - if kty == keyvault.EC { + if kty == keyvault.EC || kty == keyvault.ECHSM { algo.Type = entities2.Ecdsa } @@ -55,7 +55,7 @@ func algoFromAKVKeyTypeCrv(kty keyvault.JSONWebKeyType, crv keyvault.JSONWebKeyC func pubKeyBytes(key *keyvault.JSONWebKey) []byte { switch { - case key.Kty == keyvault.EC && key.Crv == keyvault.P256K: + case (key.Kty == keyvault.EC || key.Kty == keyvault.ECHSM) && key.Crv == keyvault.P256K: xBytes, _ := decodePubKeyBase64(*key.X) yBytes, _ := decodePubKeyBase64(*key.Y) pKey := ecdsa.PublicKey{X: new(big.Int).SetBytes(xBytes), Y: new(big.Int).SetBytes(yBytes)}