forked from TrueCloudLab/certificates
Add unit tests for azurekms.
This commit is contained in:
parent
97d08a1b61
commit
6389100325
7 changed files with 920 additions and 8 deletions
2
go.mod
2
go.mod
|
@ -16,7 +16,7 @@ require (
|
||||||
github.com/go-chi/chi v4.0.2+incompatible
|
github.com/go-chi/chi v4.0.2+incompatible
|
||||||
github.com/go-kit/kit v0.10.0 // indirect
|
github.com/go-kit/kit v0.10.0 // indirect
|
||||||
github.com/go-piv/piv-go v1.7.0
|
github.com/go-piv/piv-go v1.7.0
|
||||||
github.com/golang/mock v1.5.0
|
github.com/golang/mock v1.6.0
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5
|
github.com/googleapis/gax-go/v2 v2.0.5
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
||||||
|
|
3
go.sum
3
go.sum
|
@ -236,8 +236,9 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
|
||||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||||
github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=
|
|
||||||
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||||
|
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||||
|
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
|
80
kms/azurekms/internal/mock/key_vault_client.go
Normal file
80
kms/azurekms/internal/mock/key_vault_client.go
Normal file
|
@ -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)
|
||||||
|
}
|
|
@ -114,8 +114,7 @@ type KeyVault struct {
|
||||||
baseClient KeyVaultClient
|
baseClient KeyVaultClient
|
||||||
}
|
}
|
||||||
|
|
||||||
// New initializes a new KMS implemented using Azure Key Vault.
|
var createClient = func(ctx context.Context, opts apiv1.Options) (KeyVaultClient, error) {
|
||||||
func New(ctx context.Context, opts apiv1.Options) (*KeyVault, error) {
|
|
||||||
// Attempt to authorize with the following methods:
|
// Attempt to authorize with the following methods:
|
||||||
// 1. Environment variables.
|
// 1. Environment variables.
|
||||||
// - Client credentials
|
// - Client credentials
|
||||||
|
@ -133,9 +132,17 @@ func New(ctx context.Context, opts apiv1.Options) (*KeyVault, error) {
|
||||||
|
|
||||||
baseClient := keyvault.New()
|
baseClient := keyvault.New()
|
||||||
baseClient.Authorizer = authorizer
|
baseClient.Authorizer = authorizer
|
||||||
|
return &baseClient, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// New initializes a new KMS implemented using Azure Key Vault.
|
||||||
|
func New(ctx context.Context, opts apiv1.Options) (*KeyVault, error) {
|
||||||
|
baseClient, err := createClient(ctx, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return &KeyVault{
|
return &KeyVault{
|
||||||
baseClient: &baseClient,
|
baseClient: baseClient,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,6 +171,10 @@ func (k *KeyVault) GetPublicKey(req *apiv1.GetPublicKeyRequest) (crypto.PublicKe
|
||||||
|
|
||||||
// CreateKey creates a asymmetric key in Azure Key Vault.
|
// CreateKey creates a asymmetric key in Azure Key Vault.
|
||||||
func (k *KeyVault) CreateKey(req *apiv1.CreateKeyRequest) (*apiv1.CreateKeyResponse, error) {
|
func (k *KeyVault) CreateKey(req *apiv1.CreateKeyRequest) (*apiv1.CreateKeyResponse, error) {
|
||||||
|
if req.Name == "" {
|
||||||
|
return nil, errors.New("createKeyRequest 'name' cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
vault, name, _, err := parseKeyName(req.Name)
|
vault, name, _, err := parseKeyName(req.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
482
kms/azurekms/key_vault_test.go
Normal file
482
kms/azurekms/key_vault_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,7 +24,7 @@ type Signer struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSigner creates a new signer using a key in the AWS KMS.
|
// NewSigner creates a new signer using a key in the AWS KMS.
|
||||||
func NewSigner(client KeyVaultClient, signingKey string) (*Signer, error) {
|
func NewSigner(client KeyVaultClient, signingKey string) (crypto.Signer, error) {
|
||||||
vault, name, version, err := parseKeyName(signingKey)
|
vault, name, version, err := parseKeyName(signingKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -114,8 +114,17 @@ func (s *Signer) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]
|
||||||
func getSigningAlgorithm(key crypto.PublicKey, opts crypto.SignerOpts) (keyvault.JSONWebKeySignatureAlgorithm, error) {
|
func getSigningAlgorithm(key crypto.PublicKey, opts crypto.SignerOpts) (keyvault.JSONWebKeySignatureAlgorithm, error) {
|
||||||
switch key.(type) {
|
switch key.(type) {
|
||||||
case *rsa.PublicKey:
|
case *rsa.PublicKey:
|
||||||
_, isPSS := opts.(*rsa.PSSOptions)
|
hashFunc := opts.HashFunc()
|
||||||
switch h := opts.HashFunc(); h {
|
pss, isPSS := opts.(*rsa.PSSOptions)
|
||||||
|
// Random salt lengths are not supported
|
||||||
|
if isPSS &&
|
||||||
|
pss.SaltLength != rsa.PSSSaltLengthAuto &&
|
||||||
|
pss.SaltLength != rsa.PSSSaltLengthEqualsHash &&
|
||||||
|
pss.SaltLength != hashFunc.Size() {
|
||||||
|
return "", errors.Errorf("unsupported RSA-PSS salt length %d", pss.SaltLength)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch h := hashFunc; h {
|
||||||
case crypto.SHA256:
|
case crypto.SHA256:
|
||||||
if isPSS {
|
if isPSS {
|
||||||
return keyvault.PS256, nil
|
return keyvault.PS256, nil
|
||||||
|
|
329
kms/azurekms/signer_test.go
Normal file
329
kms/azurekms/signer_test.go
Normal file
|
@ -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 a new issue