Add unit tests for azurekms.
parent
97d08a1b61
commit
6389100325
@ -0,0 +1,80 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/smallstep/certificates/kms/azurekms (interfaces: KeyVaultClient)
|
||||
|
||||
// Package mock is a generated GoMock package.
|
||||
package mock
|
||||
|
||||
import (
|
||||
context "context"
|
||||
keyvault "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// KeyVaultClient is a mock of KeyVaultClient interface
|
||||
type KeyVaultClient struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *KeyVaultClientMockRecorder
|
||||
}
|
||||
|
||||
// KeyVaultClientMockRecorder is the mock recorder for KeyVaultClient
|
||||
type KeyVaultClientMockRecorder struct {
|
||||
mock *KeyVaultClient
|
||||
}
|
||||
|
||||
// NewKeyVaultClient creates a new mock instance
|
||||
func NewKeyVaultClient(ctrl *gomock.Controller) *KeyVaultClient {
|
||||
mock := &KeyVaultClient{ctrl: ctrl}
|
||||
mock.recorder = &KeyVaultClientMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *KeyVaultClient) EXPECT() *KeyVaultClientMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// CreateKey mocks base method
|
||||
func (m *KeyVaultClient) CreateKey(arg0 context.Context, arg1, arg2 string, arg3 keyvault.KeyCreateParameters) (keyvault.KeyBundle, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "CreateKey", arg0, arg1, arg2, arg3)
|
||||
ret0, _ := ret[0].(keyvault.KeyBundle)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// CreateKey indicates an expected call of CreateKey
|
||||
func (mr *KeyVaultClientMockRecorder) CreateKey(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateKey", reflect.TypeOf((*KeyVaultClient)(nil).CreateKey), arg0, arg1, arg2, arg3)
|
||||
}
|
||||
|
||||
// GetKey mocks base method
|
||||
func (m *KeyVaultClient) GetKey(arg0 context.Context, arg1, arg2, arg3 string) (keyvault.KeyBundle, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetKey", arg0, arg1, arg2, arg3)
|
||||
ret0, _ := ret[0].(keyvault.KeyBundle)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetKey indicates an expected call of GetKey
|
||||
func (mr *KeyVaultClientMockRecorder) GetKey(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetKey", reflect.TypeOf((*KeyVaultClient)(nil).GetKey), arg0, arg1, arg2, arg3)
|
||||
}
|
||||
|
||||
// Sign mocks base method
|
||||
func (m *KeyVaultClient) Sign(arg0 context.Context, arg1, arg2, arg3 string, arg4 keyvault.KeySignParameters) (keyvault.KeyOperationResult, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Sign", arg0, arg1, arg2, arg3, arg4)
|
||||
ret0, _ := ret[0].(keyvault.KeyOperationResult)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Sign indicates an expected call of Sign
|
||||
func (mr *KeyVaultClientMockRecorder) Sign(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sign", reflect.TypeOf((*KeyVaultClient)(nil).Sign), arg0, arg1, arg2, arg3, arg4)
|
||||
}
|
@ -0,0 +1,482 @@
|
||||
//go:generate mockgen -package mock -mock_names=KeyVaultClient=KeyVaultClient -destination internal/mock/key_vault_client.go github.com/smallstep/certificates/kms/azurekms KeyVaultClient
|
||||
package azurekms
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault"
|
||||
"github.com/Azure/go-autorest/autorest/date"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/smallstep/certificates/kms/apiv1"
|
||||
"github.com/smallstep/certificates/kms/azurekms/internal/mock"
|
||||
"go.step.sm/crypto/keyutil"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
)
|
||||
|
||||
var errTest = fmt.Errorf("test error")
|
||||
|
||||
func mockNow(t *testing.T) time.Time {
|
||||
old := now
|
||||
t0 := time.Unix(1234567890, 123).UTC()
|
||||
now = func() time.Time {
|
||||
return t0
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
now = old
|
||||
})
|
||||
return t0
|
||||
}
|
||||
|
||||
func mockClient(t *testing.T) *mock.KeyVaultClient {
|
||||
t.Helper()
|
||||
ctrl := gomock.NewController(t)
|
||||
t.Cleanup(func() {
|
||||
ctrl.Finish()
|
||||
})
|
||||
return mock.NewKeyVaultClient(ctrl)
|
||||
}
|
||||
|
||||
func mockCreateClient(t *testing.T, ctrl *gomock.Controller) {
|
||||
t.Helper()
|
||||
old := createClient
|
||||
|
||||
createClient = func(ctx context.Context, opts apiv1.Options) (KeyVaultClient, error) {
|
||||
return mock.NewKeyVaultClient(ctrl), nil
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
createClient = old
|
||||
})
|
||||
}
|
||||
|
||||
func createJWK(t *testing.T, pub crypto.PublicKey) *keyvault.JSONWebKey {
|
||||
t.Helper()
|
||||
b, err := json.Marshal(&jose.JSONWebKey{
|
||||
Key: pub,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
key := new(keyvault.JSONWebKey)
|
||||
if err := json.Unmarshal(b, key); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
mockCreateClient(t, ctrl)
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
opts apiv1.Options
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *KeyVault
|
||||
wantErr bool
|
||||
}{
|
||||
{"ok", args{context.Background(), apiv1.Options{}}, &KeyVault{
|
||||
baseClient: mock.NewKeyVaultClient(ctrl),
|
||||
}, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := New(tt.args.ctx, tt.args.opts)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("New() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyVault_GetPublicKey(t *testing.T) {
|
||||
key, err := keyutil.GenerateDefaultSigner()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pub := key.Public()
|
||||
jwk := createJWK(t, pub)
|
||||
|
||||
client := mockClient(t)
|
||||
client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", "").Return(keyvault.KeyBundle{
|
||||
Key: jwk,
|
||||
}, nil)
|
||||
client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", "my-version").Return(keyvault.KeyBundle{
|
||||
Key: jwk,
|
||||
}, nil)
|
||||
client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "not-found", "my-version").Return(keyvault.KeyBundle{}, errTest)
|
||||
|
||||
type fields struct {
|
||||
baseClient KeyVaultClient
|
||||
}
|
||||
type args struct {
|
||||
req *apiv1.GetPublicKeyRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want crypto.PublicKey
|
||||
wantErr bool
|
||||
}{
|
||||
{"ok", fields{client}, args{&apiv1.GetPublicKeyRequest{
|
||||
Name: "azurekms:vault=my-vault;id=my-key",
|
||||
}}, pub, false},
|
||||
{"ok with version", fields{client}, args{&apiv1.GetPublicKeyRequest{
|
||||
Name: "azurekms:vault=my-vault;id=my-key?version=my-version",
|
||||
}}, pub, false},
|
||||
{"fail GetKey", fields{client}, args{&apiv1.GetPublicKeyRequest{
|
||||
Name: "azurekms:vault=my-vault;id=not-found?version=my-version",
|
||||
}}, nil, true},
|
||||
{"fail vault", fields{client}, args{&apiv1.GetPublicKeyRequest{
|
||||
Name: "azurekms:vault=;id=not-found?version=my-version",
|
||||
}}, nil, true},
|
||||
{"fail id", fields{client}, args{&apiv1.GetPublicKeyRequest{
|
||||
Name: "azurekms:vault=;id=?version=my-version",
|
||||
}}, nil, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
k := &KeyVault{
|
||||
baseClient: tt.fields.baseClient,
|
||||
}
|
||||
got, err := k.GetPublicKey(tt.args.req)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("KeyVault.GetPublicKey() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("KeyVault.GetPublicKey() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyVault_CreateKey(t *testing.T) {
|
||||
ecKey, err := keyutil.GenerateDefaultSigner()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rsaKey, err := keyutil.GenerateSigner("RSA", "", 2048)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ecPub := ecKey.Public()
|
||||
rsaPub := rsaKey.Public()
|
||||
ecJWK := createJWK(t, ecPub)
|
||||
rsaJWK := createJWK(t, rsaPub)
|
||||
|
||||
t0 := date.UnixTime(mockNow(t))
|
||||
client := mockClient(t)
|
||||
|
||||
expects := []struct {
|
||||
Name string
|
||||
Kty keyvault.JSONWebKeyType
|
||||
KeySize *int32
|
||||
Curve keyvault.JSONWebKeyCurveName
|
||||
Key *keyvault.JSONWebKey
|
||||
}{
|
||||
{"P-256", keyvault.EC, nil, keyvault.P256, ecJWK},
|
||||
{"P-256 HSM", keyvault.ECHSM, nil, keyvault.P256, ecJWK},
|
||||
{"P-256 Default", keyvault.EC, nil, keyvault.P256, ecJWK},
|
||||
{"P-384", keyvault.EC, nil, keyvault.P384, ecJWK},
|
||||
{"P-521", keyvault.EC, nil, keyvault.P521, ecJWK},
|
||||
{"RSA 0", keyvault.RSA, &value3072, "", rsaJWK},
|
||||
{"RSA 0 HSM", keyvault.RSAHSM, &value3072, "", rsaJWK},
|
||||
{"RSA 2048", keyvault.RSA, &value2048, "", rsaJWK},
|
||||
{"RSA 3072", keyvault.RSA, &value3072, "", rsaJWK},
|
||||
{"RSA 4096", keyvault.RSA, &value4096, "", rsaJWK},
|
||||
}
|
||||
|
||||
for _, e := range expects {
|
||||
client.EXPECT().CreateKey(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", keyvault.KeyCreateParameters{
|
||||
Kty: e.Kty,
|
||||
KeySize: e.KeySize,
|
||||
Curve: e.Curve,
|
||||
KeyOps: &[]keyvault.JSONWebKeyOperation{
|
||||
keyvault.Sign, keyvault.Verify,
|
||||
},
|
||||
KeyAttributes: &keyvault.KeyAttributes{
|
||||
Enabled: &valueTrue,
|
||||
Created: &t0,
|
||||
NotBefore: &t0,
|
||||
},
|
||||
}).Return(keyvault.KeyBundle{
|
||||
Key: e.Key,
|
||||
}, nil)
|
||||
}
|
||||
client.EXPECT().CreateKey(gomock.Any(), "https://my-vault.vault.azure.net/", "not-found", gomock.Any()).Return(keyvault.KeyBundle{}, errTest)
|
||||
client.EXPECT().CreateKey(gomock.Any(), "https://my-vault.vault.azure.net/", "not-found", gomock.Any()).Return(keyvault.KeyBundle{
|
||||
Key: nil,
|
||||
}, nil)
|
||||
|
||||
type fields struct {
|
||||
baseClient KeyVaultClient
|
||||
}
|
||||
type args struct {
|
||||
req *apiv1.CreateKeyRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want *apiv1.CreateKeyResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{"ok P-256", fields{client}, args{&apiv1.CreateKeyRequest{
|
||||
Name: "azurekms:vault=my-vault;id=my-key",
|
||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
||||
ProtectionLevel: apiv1.Software,
|
||||
}}, &apiv1.CreateKeyResponse{
|
||||
Name: "azurekms:id=my-key;vault=my-vault",
|
||||
PublicKey: ecPub,
|
||||
CreateSignerRequest: apiv1.CreateSignerRequest{
|
||||
SigningKey: "azurekms:id=my-key;vault=my-vault",
|
||||
},
|
||||
}, false},
|
||||
{"ok P-256 HSM", fields{client}, args{&apiv1.CreateKeyRequest{
|
||||
Name: "azurekms:vault=my-vault;id=my-key",
|
||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
||||
ProtectionLevel: apiv1.HSM,
|
||||
}}, &apiv1.CreateKeyResponse{
|
||||
Name: "azurekms:id=my-key;vault=my-vault",
|
||||
PublicKey: ecPub,
|
||||
CreateSignerRequest: apiv1.CreateSignerRequest{
|
||||
SigningKey: "azurekms:id=my-key;vault=my-vault",
|
||||
},
|
||||
}, false},
|
||||
{"ok P-256 Default", fields{client}, args{&apiv1.CreateKeyRequest{
|
||||
Name: "azurekms:vault=my-vault;id=my-key",
|
||||
}}, &apiv1.CreateKeyResponse{
|
||||
Name: "azurekms:id=my-key;vault=my-vault",
|
||||
PublicKey: ecPub,
|
||||
CreateSignerRequest: apiv1.CreateSignerRequest{
|
||||
SigningKey: "azurekms:id=my-key;vault=my-vault",
|
||||
},
|
||||
}, false},
|
||||
{"ok P-384", fields{client}, args{&apiv1.CreateKeyRequest{
|
||||
Name: "azurekms:vault=my-vault;id=my-key",
|
||||
SignatureAlgorithm: apiv1.ECDSAWithSHA384,
|
||||
}}, &apiv1.CreateKeyResponse{
|
||||
Name: "azurekms:id=my-key;vault=my-vault",
|
||||
PublicKey: ecPub,
|
||||
CreateSignerRequest: apiv1.CreateSignerRequest{
|
||||
SigningKey: "azurekms:id=my-key;vault=my-vault",
|
||||
},
|
||||
}, false},
|
||||
{"ok P-521", fields{client}, args{&apiv1.CreateKeyRequest{
|
||||
Name: "azurekms:vault=my-vault;id=my-key",
|
||||
SignatureAlgorithm: apiv1.ECDSAWithSHA512,
|
||||
}}, &apiv1.CreateKeyResponse{
|
||||
Name: "azurekms:id=my-key;vault=my-vault",
|
||||
PublicKey: ecPub,
|
||||
CreateSignerRequest: apiv1.CreateSignerRequest{
|
||||
SigningKey: "azurekms:id=my-key;vault=my-vault",
|
||||
},
|
||||
}, false},
|
||||
{"ok RSA 0", fields{client}, args{&apiv1.CreateKeyRequest{
|
||||
Name: "azurekms:vault=my-vault;id=my-key",
|
||||
Bits: 0,
|
||||
SignatureAlgorithm: apiv1.SHA256WithRSA,
|
||||
ProtectionLevel: apiv1.Software,
|
||||
}}, &apiv1.CreateKeyResponse{
|
||||
Name: "azurekms:id=my-key;vault=my-vault",
|
||||
PublicKey: rsaPub,
|
||||
CreateSignerRequest: apiv1.CreateSignerRequest{
|
||||
SigningKey: "azurekms:id=my-key;vault=my-vault",
|
||||
},
|
||||
}, false},
|
||||
{"ok RSA 0 HSM", fields{client}, args{&apiv1.CreateKeyRequest{
|
||||
Name: "azurekms:vault=my-vault;id=my-key",
|
||||
Bits: 0,
|
||||
SignatureAlgorithm: apiv1.SHA256WithRSAPSS,
|
||||
ProtectionLevel: apiv1.HSM,
|
||||
}}, &apiv1.CreateKeyResponse{
|
||||
Name: "azurekms:id=my-key;vault=my-vault",
|
||||
PublicKey: rsaPub,
|
||||
CreateSignerRequest: apiv1.CreateSignerRequest{
|
||||
SigningKey: "azurekms:id=my-key;vault=my-vault",
|
||||
},
|
||||
}, false},
|
||||
{"ok RSA 2048", fields{client}, args{&apiv1.CreateKeyRequest{
|
||||
Name: "azurekms:vault=my-vault;id=my-key",
|
||||
Bits: 2048,
|
||||
SignatureAlgorithm: apiv1.SHA384WithRSA,
|
||||
}}, &apiv1.CreateKeyResponse{
|
||||
Name: "azurekms:id=my-key;vault=my-vault",
|
||||
PublicKey: rsaPub,
|
||||
CreateSignerRequest: apiv1.CreateSignerRequest{
|
||||
SigningKey: "azurekms:id=my-key;vault=my-vault",
|
||||
},
|
||||
}, false},
|
||||
{"ok RSA 3072", fields{client}, args{&apiv1.CreateKeyRequest{
|
||||
Name: "azurekms:vault=my-vault;id=my-key",
|
||||
Bits: 3072,
|
||||
SignatureAlgorithm: apiv1.SHA512WithRSA,
|
||||
}}, &apiv1.CreateKeyResponse{
|
||||
Name: "azurekms:id=my-key;vault=my-vault",
|
||||
PublicKey: rsaPub,
|
||||
CreateSignerRequest: apiv1.CreateSignerRequest{
|
||||
SigningKey: "azurekms:id=my-key;vault=my-vault",
|
||||
},
|
||||
}, false},
|
||||
{"ok RSA 4096", fields{client}, args{&apiv1.CreateKeyRequest{
|
||||
Name: "azurekms:vault=my-vault;id=my-key",
|
||||
Bits: 4096,
|
||||
SignatureAlgorithm: apiv1.SHA512WithRSAPSS,
|
||||
}}, &apiv1.CreateKeyResponse{
|
||||
Name: "azurekms:id=my-key;vault=my-vault",
|
||||
PublicKey: rsaPub,
|
||||
CreateSignerRequest: apiv1.CreateSignerRequest{
|
||||
SigningKey: "azurekms:id=my-key;vault=my-vault",
|
||||
},
|
||||
}, false},
|
||||
{"fail createKey", fields{client}, args{&apiv1.CreateKeyRequest{
|
||||
Name: "azurekms:vault=my-vault;id=not-found",
|
||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
||||
}}, nil, true},
|
||||
{"fail convertKey", fields{client}, args{&apiv1.CreateKeyRequest{
|
||||
Name: "azurekms:vault=my-vault;id=not-found",
|
||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
||||
}}, nil, true},
|
||||
{"fail name", fields{client}, args{&apiv1.CreateKeyRequest{
|
||||
Name: "",
|
||||
}}, nil, true},
|
||||
{"fail vault", fields{client}, args{&apiv1.CreateKeyRequest{
|
||||
Name: "azurekms:vault=;id=not-found?version=my-version",
|
||||
}}, nil, true},
|
||||
{"fail id", fields{client}, args{&apiv1.CreateKeyRequest{
|
||||
Name: "azurekms:vault=my-vault;id=?version=my-version",
|
||||
}}, nil, true},
|
||||
{"fail SignatureAlgorithm", fields{client}, args{&apiv1.CreateKeyRequest{
|
||||
Name: "azurekms:vault=my-vault;id=not-found",
|
||||
SignatureAlgorithm: apiv1.PureEd25519,
|
||||
}}, nil, true},
|
||||
{"fail bit size", fields{client}, args{&apiv1.CreateKeyRequest{
|
||||
Name: "azurekms:vault=my-vault;id=not-found",
|
||||
SignatureAlgorithm: apiv1.SHA384WithRSAPSS,
|
||||
Bits: 1024,
|
||||
}}, nil, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
k := &KeyVault{
|
||||
baseClient: tt.fields.baseClient,
|
||||
}
|
||||
got, err := k.CreateKey(tt.args.req)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("KeyVault.CreateKey() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("KeyVault.CreateKey() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyVault_CreateSigner(t *testing.T) {
|
||||
key, err := keyutil.GenerateDefaultSigner()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pub := key.Public()
|
||||
jwk := createJWK(t, pub)
|
||||
|
||||
client := mockClient(t)
|
||||
client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", "").Return(keyvault.KeyBundle{
|
||||
Key: jwk,
|
||||
}, nil)
|
||||
client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", "my-version").Return(keyvault.KeyBundle{
|
||||
Key: jwk,
|
||||
}, nil)
|
||||
client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "not-found", "my-version").Return(keyvault.KeyBundle{}, errTest)
|
||||
|
||||
type fields struct {
|
||||
baseClient KeyVaultClient
|
||||
}
|
||||
type args struct {
|
||||
req *apiv1.CreateSignerRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want crypto.Signer
|
||||
wantErr bool
|
||||
}{
|
||||
{"ok", fields{client}, args{&apiv1.CreateSignerRequest{
|
||||
SigningKey: "azurekms:vault=my-vault;id=my-key",
|
||||
}}, &Signer{
|
||||
client: client,
|
||||
vaultBaseURL: "https://my-vault.vault.azure.net/",
|
||||
name: "my-key",
|
||||
version: "",
|
||||
publicKey: pub,
|
||||
}, false},
|
||||
{"ok with version", fields{client}, args{&apiv1.CreateSignerRequest{
|
||||
SigningKey: "azurekms:vault=my-vault;id=my-key;version=my-version",
|
||||
}}, &Signer{
|
||||
client: client,
|
||||
vaultBaseURL: "https://my-vault.vault.azure.net/",
|
||||
name: "my-key",
|
||||
version: "my-version",
|
||||
publicKey: pub,
|
||||
}, false},
|
||||
{"fail GetKey", fields{client}, args{&apiv1.CreateSignerRequest{
|
||||
SigningKey: "azurekms:vault=my-vault;id=not-found;version=my-version",
|
||||
}}, nil, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
k := &KeyVault{
|
||||
baseClient: tt.fields.baseClient,
|
||||
}
|
||||
got, err := k.CreateSigner(tt.args.req)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("KeyVault.CreateSigner() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("KeyVault.CreateSigner() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyVault_Close(t *testing.T) {
|
||||
client := mockClient(t)
|
||||
type fields struct {
|
||||
baseClient KeyVaultClient
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
wantErr bool
|
||||
}{
|
||||
{"ok", fields{client}, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
k := &KeyVault{
|
||||
baseClient: tt.fields.baseClient,
|
||||
}
|
||||
if err := k.Close(); (err != nil) != tt.wantErr {
|
||||
t.Errorf("KeyVault.Close() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,329 @@
|
||||
package azurekms
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault"
|
||||
"github.com/golang/mock/gomock"
|
||||
"go.step.sm/crypto/keyutil"
|
||||
"golang.org/x/crypto/cryptobyte"
|
||||
"golang.org/x/crypto/cryptobyte/asn1"
|
||||
)
|
||||
|
||||
func TestNewSigner(t *testing.T) {
|
||||
key, err := keyutil.GenerateDefaultSigner()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pub := key.Public()
|
||||
jwk := createJWK(t, pub)
|
||||
|
||||
client := mockClient(t)
|
||||
client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", "").Return(keyvault.KeyBundle{
|
||||
Key: jwk,
|
||||
}, nil)
|
||||
client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", "my-version").Return(keyvault.KeyBundle{
|
||||
Key: jwk,
|
||||
}, nil)
|
||||
client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "not-found", "my-version").Return(keyvault.KeyBundle{}, errTest)
|
||||
|
||||
type args struct {
|
||||
client KeyVaultClient
|
||||
signingKey string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want crypto.Signer
|
||||
wantErr bool
|
||||
}{
|
||||
{"ok", args{client, "azurekms:vault=my-vault;id=my-key"}, &Signer{
|
||||
client: client,
|
||||
vaultBaseURL: "https://my-vault.vault.azure.net/",
|
||||
name: "my-key",
|
||||
version: "",
|
||||
publicKey: pub,
|
||||
}, false},
|
||||
{"ok with version", args{client, "azurekms:id=my-key;vault=my-vault?version=my-version"}, &Signer{
|
||||
client: client,
|
||||
vaultBaseURL: "https://my-vault.vault.azure.net/",
|
||||
name: "my-key",
|
||||
version: "my-version",
|
||||
publicKey: pub,
|
||||
}, false},
|
||||
{"fail GetKey", args{client, "azurekms:id=not-found;vault=my-vault?version=my-version"}, nil, true},
|
||||
{"fail vault", args{client, "azurekms:id=not-found;vault="}, nil, true},
|
||||
{"fail id", args{client, "azurekms:id=;vault=my-vault?version=my-version"}, nil, true},
|
||||
{"fail scheme", args{client, "kms:id=not-found;vault=my-vault?version=my-version"}, nil, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := NewSigner(tt.args.client, tt.args.signingKey)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("NewSigner() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("NewSigner() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSigner_Public(t *testing.T) {
|
||||
key, err := keyutil.GenerateDefaultSigner()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pub := key.Public()
|
||||
|
||||
type fields struct {
|
||||
publicKey crypto.PublicKey
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want crypto.PublicKey
|
||||
}{
|
||||
{"ok", fields{pub}, pub},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := &Signer{
|
||||
publicKey: tt.fields.publicKey,
|
||||
}
|
||||
if got := s.Public(); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Signer.Public() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSigner_Sign(t *testing.T) {
|
||||
sign := func(kty, crv string, bits int, opts crypto.SignerOpts) (crypto.PublicKey, []byte, string, []byte) {
|
||||
key, err := keyutil.GenerateSigner(kty, crv, bits)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
h := opts.HashFunc().New()
|
||||
h.Write([]byte("random-data"))
|
||||
sum := h.Sum(nil)
|
||||
|
||||
var sig, resultSig []byte
|
||||
if priv, ok := key.(*ecdsa.PrivateKey); ok {
|
||||
r, s, err := ecdsa.Sign(rand.Reader, priv, sum)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
curveBits := priv.Params().BitSize
|
||||
keyBytes := curveBits / 8
|
||||
if curveBits%8 > 0 {
|
||||
keyBytes++
|
||||
}
|
||||
rBytes := r.Bytes()
|
||||
rBytesPadded := make([]byte, keyBytes)
|
||||
copy(rBytesPadded[keyBytes-len(rBytes):], rBytes)
|
||||
|
||||
sBytes := s.Bytes()
|
||||
sBytesPadded := make([]byte, keyBytes)
|
||||
copy(sBytesPadded[keyBytes-len(sBytes):], sBytes)
|
||||
resultSig = append(rBytesPadded, sBytesPadded...)
|
||||
|
||||
var b cryptobyte.Builder
|
||||
b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) {
|
||||
b.AddASN1BigInt(r)
|
||||
b.AddASN1BigInt(s)
|
||||
})
|
||||
sig, err = b.Bytes()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
sig, err = key.Sign(rand.Reader, sum, opts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
resultSig = sig
|
||||
}
|
||||
|
||||
return key.Public(), h.Sum(nil), base64.RawURLEncoding.EncodeToString(resultSig), sig
|
||||
}
|
||||
|
||||
p256, p256Digest, p256ResultSig, p256Sig := sign("EC", "P-256", 0, crypto.SHA256)
|
||||
p384, p384Digest, p386ResultSig, p384Sig := sign("EC", "P-384", 0, crypto.SHA384)
|
||||
p521, p521Digest, p521ResultSig, p521Sig := sign("EC", "P-521", 0, crypto.SHA512)
|
||||
rsaSHA256, rsaSHA256Digest, rsaSHA256ResultSig, rsaSHA256Sig := sign("RSA", "", 2048, crypto.SHA256)
|
||||
rsaSHA384, rsaSHA384Digest, rsaSHA384ResultSig, rsaSHA384Sig := sign("RSA", "", 2048, crypto.SHA384)
|
||||
rsaSHA512, rsaSHA512Digest, rsaSHA512ResultSig, rsaSHA512Sig := sign("RSA", "", 2048, crypto.SHA512)
|
||||
rsaPSSSHA256, rsaPSSSHA256Digest, rsaPSSSHA256ResultSig, rsaPSSSHA256Sig := sign("RSA", "", 2048, &rsa.PSSOptions{
|
||||
SaltLength: rsa.PSSSaltLengthAuto,
|
||||
Hash: crypto.SHA256,
|
||||
})
|
||||
rsaPSSSHA384, rsaPSSSHA384Digest, rsaPSSSHA384ResultSig, rsaPSSSHA384Sig := sign("RSA", "", 2048, &rsa.PSSOptions{
|
||||
SaltLength: rsa.PSSSaltLengthAuto,
|
||||
Hash: crypto.SHA512,
|
||||
})
|
||||
rsaPSSSHA512, rsaPSSSHA512Digest, rsaPSSSHA512ResultSig, rsaPSSSHA512Sig := sign("RSA", "", 2048, &rsa.PSSOptions{
|
||||
SaltLength: rsa.PSSSaltLengthAuto,
|
||||
Hash: crypto.SHA512,
|
||||
})
|
||||
|
||||
ed25519Key, err := keyutil.GenerateSigner("OKP", "Ed25519", 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
client := mockClient(t)
|
||||
expects := []struct {
|
||||
name string
|
||||
keyVersion string
|
||||
alg keyvault.JSONWebKeySignatureAlgorithm
|
||||
digest []byte
|
||||
result keyvault.KeyOperationResult
|
||||
err error
|
||||
}{
|
||||
{"P-256", "", keyvault.ES256, p256Digest, keyvault.KeyOperationResult{
|
||||
Result: &p256ResultSig,
|
||||
}, nil},
|
||||
{"P-384", "my-version", keyvault.ES384, p384Digest, keyvault.KeyOperationResult{
|
||||
Result: &p386ResultSig,
|
||||
}, nil},
|
||||
{"P-521", "my-version", keyvault.ES512, p521Digest, keyvault.KeyOperationResult{
|
||||
Result: &p521ResultSig,
|
||||
}, nil},
|
||||
{"RSA SHA256", "", keyvault.RS256, rsaSHA256Digest, keyvault.KeyOperationResult{
|
||||
Result: &rsaSHA256ResultSig,
|
||||
}, nil},
|
||||
{"RSA SHA384", "", keyvault.RS384, rsaSHA384Digest, keyvault.KeyOperationResult{
|
||||
Result: &rsaSHA384ResultSig,
|
||||
}, nil},
|
||||
{"RSA SHA512", "", keyvault.RS512, rsaSHA512Digest, keyvault.KeyOperationResult{
|
||||
Result: &rsaSHA512ResultSig,
|
||||
}, nil},
|
||||
{"RSA-PSS SHA256", "", keyvault.PS256, rsaPSSSHA256Digest, keyvault.KeyOperationResult{
|
||||
Result: &rsaPSSSHA256ResultSig,
|
||||
}, nil},
|
||||
{"RSA-PSS SHA384", "", keyvault.PS384, rsaPSSSHA384Digest, keyvault.KeyOperationResult{
|
||||
Result: &rsaPSSSHA384ResultSig,
|
||||
}, nil},
|
||||
{"RSA-PSS SHA512", "", keyvault.PS512, rsaPSSSHA512Digest, keyvault.KeyOperationResult{
|
||||
Result: &rsaPSSSHA512ResultSig,
|
||||
}, nil},
|
||||
// Errors
|
||||
{"fail Sign", "", keyvault.RS256, rsaSHA256Digest, keyvault.KeyOperationResult{}, errTest},
|
||||
{"fail sign length", "", keyvault.ES256, p256Digest, keyvault.KeyOperationResult{
|
||||
Result: &rsaSHA256ResultSig,
|
||||
}, nil},
|
||||
}
|
||||
for _, e := range expects {
|
||||
value := base64.RawURLEncoding.EncodeToString(e.digest)
|
||||
client.EXPECT().Sign(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", e.keyVersion, keyvault.KeySignParameters{
|
||||
Algorithm: e.alg,
|
||||
Value: &value,
|
||||
}).Return(e.result, e.err)
|
||||
}
|
||||
|
||||
type fields struct {
|
||||
client KeyVaultClient
|
||||
vaultBaseURL string
|
||||
name string
|
||||
version string
|
||||
publicKey crypto.PublicKey
|
||||
}
|
||||
type args struct {
|
||||
rand io.Reader
|
||||
digest []byte
|
||||
opts crypto.SignerOpts
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want []byte
|
||||
wantErr bool
|
||||
}{
|
||||
{"ok P-256", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", p256}, args{
|
||||
rand.Reader, p256Digest[:], crypto.SHA256,
|
||||
}, p256Sig, false},
|
||||
{"ok P-384", fields{client, "https://my-vault.vault.azure.net/", "my-key", "my-version", p384}, args{
|
||||
rand.Reader, p384Digest[:], crypto.SHA384,
|
||||
}, p384Sig, false},
|
||||
{"ok P-521", fields{client, "https://my-vault.vault.azure.net/", "my-key", "my-version", p521}, args{
|
||||
rand.Reader, p521Digest[:], crypto.SHA512,
|
||||
}, p521Sig, false},
|
||||
{"ok RSA SHA256", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaSHA256}, args{
|
||||
rand.Reader, rsaSHA256Digest[:], crypto.SHA256,
|
||||
}, rsaSHA256Sig, false},
|
||||
{"ok RSA SHA384", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaSHA384}, args{
|
||||
rand.Reader, rsaSHA384Digest[:], crypto.SHA384,
|
||||
}, rsaSHA384Sig, false},
|
||||
{"ok RSA SHA512", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaSHA512}, args{
|
||||
rand.Reader, rsaSHA512Digest[:], crypto.SHA512,
|
||||
}, rsaSHA512Sig, false},
|
||||
{"ok RSA-PSS SHA256", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaPSSSHA256}, args{
|
||||
rand.Reader, rsaPSSSHA256Digest[:], &rsa.PSSOptions{
|
||||
SaltLength: rsa.PSSSaltLengthAuto,
|
||||
Hash: crypto.SHA256,
|
||||
},
|
||||
}, rsaPSSSHA256Sig, false},
|
||||
{"ok RSA-PSS SHA384", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaPSSSHA384}, args{
|
||||
rand.Reader, rsaPSSSHA384Digest[:], &rsa.PSSOptions{
|
||||
SaltLength: rsa.PSSSaltLengthEqualsHash,
|
||||
Hash: crypto.SHA384,
|
||||
},
|
||||
}, rsaPSSSHA384Sig, false},
|
||||
{"ok RSA-PSS SHA512", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaPSSSHA512}, args{
|
||||
rand.Reader, rsaPSSSHA512Digest[:], &rsa.PSSOptions{
|
||||
SaltLength: 64,
|
||||
Hash: crypto.SHA512,
|
||||
},
|
||||
}, rsaPSSSHA512Sig, false},
|
||||
{"fail Sign", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaSHA256}, args{
|
||||
rand.Reader, rsaSHA256Digest[:], crypto.SHA256,
|
||||
}, nil, true},
|
||||
{"fail sign length", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", p256}, args{
|
||||
rand.Reader, p256Digest[:], crypto.SHA256,
|
||||
}, nil, true},
|
||||
{"fail RSA-PSS salt length", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaPSSSHA256}, args{
|
||||
rand.Reader, rsaPSSSHA256Digest[:], &rsa.PSSOptions{
|
||||
SaltLength: 64,
|
||||
Hash: crypto.SHA256,
|
||||
},
|
||||
}, nil, true},
|
||||
{"fail RSA Hash", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaSHA256}, args{
|
||||
rand.Reader, rsaSHA256Digest[:], crypto.SHA1,
|
||||
}, nil, true},
|
||||
{"fail ECDSA Hash", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", p256}, args{
|
||||
rand.Reader, p256Digest[:], crypto.MD5,
|
||||
}, nil, true},
|
||||
{"fail Ed25519", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", ed25519Key}, args{
|
||||
rand.Reader, []byte("message"), crypto.Hash(0),
|
||||
}, nil, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := &Signer{
|
||||
client: tt.fields.client,
|
||||
vaultBaseURL: tt.fields.vaultBaseURL,
|
||||
name: tt.fields.name,
|
||||
version: tt.fields.version,
|
||||
publicKey: tt.fields.publicKey,
|
||||
}
|
||||
got, err := s.Sign(tt.args.rand, tt.args.digest, tt.args.opts)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Signer.Sign() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Signer.Sign() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue