From dff498f17f22efe020679740bc4969ffd1fb48ec Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 15 Jan 2020 19:32:26 -0800 Subject: [PATCH] Add tests for cloudkms. --- kms/cloudkms/cloudkms.go | 11 +- kms/cloudkms/cloudkms_test.go | 350 ++++++++++++++++++++++++++++++++++ kms/cloudkms/mock_test.go | 46 +++++ kms/cloudkms/signer.go | 1 - kms/cloudkms/signer_test.go | 148 ++++++++++++++ kms/cloudkms/testdata/pub.pem | 4 + 6 files changed, 555 insertions(+), 5 deletions(-) create mode 100644 kms/cloudkms/cloudkms_test.go create mode 100644 kms/cloudkms/mock_test.go create mode 100644 kms/cloudkms/signer_test.go create mode 100644 kms/cloudkms/testdata/pub.pem diff --git a/kms/cloudkms/cloudkms.go b/kms/cloudkms/cloudkms.go index 5118d4ae..afd0f493 100644 --- a/kms/cloudkms/cloudkms.go +++ b/kms/cloudkms/cloudkms.go @@ -107,8 +107,7 @@ func (k *CloudKMS) CreateSigner(req *apiv1.CreateSignerRequest) (crypto.Signer, // CreateKey creates in Google's Cloud KMS a new asymmetric key for signing. func (k *CloudKMS) CreateKey(req *apiv1.CreateKeyRequest) (*apiv1.CreateKeyResponse, error) { - switch { - case req.Name == "": + if req.Name == "" { return nil, errors.New("createKeyRequest 'name' cannot be empty") } @@ -137,7 +136,7 @@ func (k *CloudKMS) CreateKey(req *apiv1.CreateKeyRequest) (*apiv1.CreateKeyRespo // Split `projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID` // to `projects/PROJECT_ID/locations/global/keyRings/RING_ID` and `KEY_ID`. - keyRing, keyId := Parent(req.Name) + keyRing, keyID := Parent(req.Name) if err := k.createKeyRingIfNeeded(keyRing); err != nil { return nil, err } @@ -148,7 +147,7 @@ func (k *CloudKMS) CreateKey(req *apiv1.CreateKeyRequest) (*apiv1.CreateKeyRespo // Create private key in CloudKMS. response, err := k.client.CreateCryptoKey(ctx, &kmspb.CreateCryptoKeyRequest{ Parent: keyRing, - CryptoKeyId: keyId, + CryptoKeyId: keyID, CryptoKey: &kmspb.CryptoKey{ Purpose: kmspb.CryptoKey_ASYMMETRIC_SIGN, VersionTemplate: &kmspb.CryptoKeyVersionTemplate{ @@ -224,6 +223,10 @@ func (k *CloudKMS) createKeyRingIfNeeded(name string) error { // follow the pattern: // projects/([^/]+)/locations/([a-zA-Z0-9_-]{1,63})/keyRings/([a-zA-Z0-9_-]{1,63})/cryptoKeys/([a-zA-Z0-9_-]{1,63})/cryptoKeyVersions/([a-zA-Z0-9_-]{1,63}) func (k *CloudKMS) GetPublicKey(req *apiv1.GetPublicKeyRequest) (crypto.PublicKey, error) { + if req.Name == "" { + return nil, errors.New("createKeyRequest 'name' cannot be empty") + } + ctx, cancel := defaultContext() defer cancel() diff --git a/kms/cloudkms/cloudkms_test.go b/kms/cloudkms/cloudkms_test.go new file mode 100644 index 00000000..00863b85 --- /dev/null +++ b/kms/cloudkms/cloudkms_test.go @@ -0,0 +1,350 @@ +package cloudkms + +import ( + "context" + "crypto" + "fmt" + "io/ioutil" + "reflect" + "testing" + + gax "github.com/googleapis/gax-go/v2" + "github.com/smallstep/certificates/kms/apiv1" + "github.com/smallstep/cli/crypto/pemutil" + kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func TestParent(t *testing.T) { + type args struct { + name string + } + tests := []struct { + name string + args args + want string + want1 string + }{ + {"zero", args{"child"}, "", "child"}, + {"one", args{"parent/child"}, "", "child"}, + {"two", args{"grandparent/parent/child"}, "grandparent", "child"}, + {"three", args{"great-grandparent/grandparent/parent/child"}, "great-grandparent/grandparent", "child"}, + {"empty", args{""}, "", ""}, + {"root", args{"/"}, "", ""}, + {"child", args{"/child"}, "", "child"}, + {"parent", args{"parent/"}, "", ""}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, got1 := Parent(tt.args.name) + if got != tt.want { + t.Errorf("Parent() got = %v, want %v", got, tt.want) + } + if got1 != tt.want1 { + t.Errorf("Parent() got1 = %v, want %v", got1, tt.want1) + } + }) + } +} + +func TestNew(t *testing.T) { + type args struct { + ctx context.Context + opts apiv1.Options + } + tests := []struct { + name string + args args + want *CloudKMS + wantErr bool + }{ + {"fail authentication", args{context.Background(), apiv1.Options{}}, nil, true}, + {"fail credentials", args{context.Background(), apiv1.Options{CredentialsFile: "testdata/missing"}}, nil, true}, + } + 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 TestCloudKMS_Close(t *testing.T) { + type fields struct { + client keyManagementClient + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + {"ok", fields{&MockClient{close: func() error { return nil }}}, false}, + {"fail", fields{&MockClient{close: func() error { return fmt.Errorf("an error") }}}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + k := &CloudKMS{ + client: tt.fields.client, + } + if err := k.Close(); (err != nil) != tt.wantErr { + t.Errorf("CloudKMS.Close() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestCloudKMS_CreateSigner(t *testing.T) { + keyName := "projects/p/locations/l/keyRings/k/cryptoKeys/c/cryptoKeyVersions/1" + type fields struct { + client keyManagementClient + } + type args struct { + req *apiv1.CreateSignerRequest + } + tests := []struct { + name string + fields fields + args args + want crypto.Signer + wantErr bool + }{ + {"ok", fields{&MockClient{}}, args{&apiv1.CreateSignerRequest{SigningKey: keyName}}, &signer{client: &MockClient{}, signingKey: keyName}, false}, + {"fail", fields{&MockClient{}}, args{&apiv1.CreateSignerRequest{SigningKey: ""}}, nil, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + k := &CloudKMS{ + client: tt.fields.client, + } + got, err := k.CreateSigner(tt.args.req) + if (err != nil) != tt.wantErr { + t.Errorf("CloudKMS.CreateSigner() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("CloudKMS.CreateSigner() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCloudKMS_CreateKey(t *testing.T) { + keyName := "projects/p/locations/l/keyRings/k/cryptoKeys/c" + testError := fmt.Errorf("an error") + alreadyExists := status.Error(codes.AlreadyExists, "already exists") + + pemBytes, err := ioutil.ReadFile("testdata/pub.pem") + if err != nil { + t.Fatal(err) + } + pk, err := pemutil.ParseKey(pemBytes) + if err != nil { + t.Fatal(err) + } + + type fields struct { + client keyManagementClient + } + type args struct { + req *apiv1.CreateKeyRequest + } + tests := []struct { + name string + fields fields + args args + want *apiv1.CreateKeyResponse + wantErr bool + }{ + {"ok", fields{ + &MockClient{ + getKeyRing: func(_ context.Context, _ *kmspb.GetKeyRingRequest, _ ...gax.CallOption) (*kmspb.KeyRing, error) { + return &kmspb.KeyRing{}, nil + }, + createCryptoKey: func(_ context.Context, _ *kmspb.CreateCryptoKeyRequest, _ ...gax.CallOption) (*kmspb.CryptoKey, error) { + return &kmspb.CryptoKey{Name: keyName}, nil + }, + getPublicKey: func(_ context.Context, _ *kmspb.GetPublicKeyRequest, _ ...gax.CallOption) (*kmspb.PublicKey, error) { + return &kmspb.PublicKey{Pem: string(pemBytes)}, nil + }, + }}, + args{&apiv1.CreateKeyRequest{Name: keyName, ProtectionLevel: apiv1.HSM, SignatureAlgorithm: apiv1.ECDSAWithSHA256}}, + &apiv1.CreateKeyResponse{Name: keyName + "/cryptoKeyVersions/1", PublicKey: pk, CreateSignerRequest: apiv1.CreateSignerRequest{SigningKey: keyName + "/cryptoKeyVersions/1"}}, false}, + {"ok new key ring", fields{ + &MockClient{ + getKeyRing: func(_ context.Context, _ *kmspb.GetKeyRingRequest, _ ...gax.CallOption) (*kmspb.KeyRing, error) { + return nil, testError + }, + createKeyRing: func(_ context.Context, _ *kmspb.CreateKeyRingRequest, _ ...gax.CallOption) (*kmspb.KeyRing, error) { + return nil, alreadyExists + }, + createCryptoKey: func(_ context.Context, _ *kmspb.CreateCryptoKeyRequest, _ ...gax.CallOption) (*kmspb.CryptoKey, error) { + return &kmspb.CryptoKey{Name: keyName}, nil + }, + getPublicKey: func(_ context.Context, _ *kmspb.GetPublicKeyRequest, _ ...gax.CallOption) (*kmspb.PublicKey, error) { + return &kmspb.PublicKey{Pem: string(pemBytes)}, nil + }, + }}, + args{&apiv1.CreateKeyRequest{Name: keyName, ProtectionLevel: apiv1.Software, SignatureAlgorithm: apiv1.SHA256WithRSA, Bits: 3072}}, + &apiv1.CreateKeyResponse{Name: keyName + "/cryptoKeyVersions/1", PublicKey: pk, CreateSignerRequest: apiv1.CreateSignerRequest{SigningKey: keyName + "/cryptoKeyVersions/1"}}, false}, + {"ok new key version", fields{ + &MockClient{ + getKeyRing: func(_ context.Context, _ *kmspb.GetKeyRingRequest, _ ...gax.CallOption) (*kmspb.KeyRing, error) { + return &kmspb.KeyRing{}, nil + }, + createCryptoKey: func(_ context.Context, _ *kmspb.CreateCryptoKeyRequest, _ ...gax.CallOption) (*kmspb.CryptoKey, error) { + return nil, alreadyExists + }, + createCryptoKeyVersion: func(_ context.Context, _ *kmspb.CreateCryptoKeyVersionRequest, _ ...gax.CallOption) (*kmspb.CryptoKeyVersion, error) { + return &kmspb.CryptoKeyVersion{Name: keyName + "/cryptoKeyVersions/2"}, nil + }, + getPublicKey: func(_ context.Context, _ *kmspb.GetPublicKeyRequest, _ ...gax.CallOption) (*kmspb.PublicKey, error) { + return &kmspb.PublicKey{Pem: string(pemBytes)}, nil + }, + }}, + args{&apiv1.CreateKeyRequest{Name: keyName, ProtectionLevel: apiv1.HSM, SignatureAlgorithm: apiv1.ECDSAWithSHA256}}, + &apiv1.CreateKeyResponse{Name: keyName + "/cryptoKeyVersions/2", PublicKey: pk, CreateSignerRequest: apiv1.CreateSignerRequest{SigningKey: keyName + "/cryptoKeyVersions/2"}}, false}, + {"fail name", fields{&MockClient{}}, args{&apiv1.CreateKeyRequest{}}, nil, true}, + {"fail protection level", fields{&MockClient{}}, args{&apiv1.CreateKeyRequest{Name: keyName, ProtectionLevel: apiv1.ProtectionLevel(100)}}, nil, true}, + {"fail signature algorithm", fields{&MockClient{}}, args{&apiv1.CreateKeyRequest{Name: keyName, ProtectionLevel: apiv1.Software, SignatureAlgorithm: apiv1.SignatureAlgorithm(100)}}, nil, true}, + {"fail number of bits", fields{&MockClient{}}, args{&apiv1.CreateKeyRequest{Name: keyName, ProtectionLevel: apiv1.Software, SignatureAlgorithm: apiv1.SHA256WithRSA, Bits: 1024}}, + nil, true}, + {"fail create key ring", fields{ + &MockClient{ + getKeyRing: func(_ context.Context, _ *kmspb.GetKeyRingRequest, _ ...gax.CallOption) (*kmspb.KeyRing, error) { + return nil, testError + }, + createKeyRing: func(_ context.Context, _ *kmspb.CreateKeyRingRequest, _ ...gax.CallOption) (*kmspb.KeyRing, error) { + return nil, testError + }, + }}, + args{&apiv1.CreateKeyRequest{Name: keyName, ProtectionLevel: apiv1.HSM, SignatureAlgorithm: apiv1.ECDSAWithSHA256}}, + nil, true}, + {"fail create key", fields{ + &MockClient{ + getKeyRing: func(_ context.Context, _ *kmspb.GetKeyRingRequest, _ ...gax.CallOption) (*kmspb.KeyRing, error) { + return &kmspb.KeyRing{}, nil + }, + createCryptoKey: func(_ context.Context, _ *kmspb.CreateCryptoKeyRequest, _ ...gax.CallOption) (*kmspb.CryptoKey, error) { + return nil, testError + }, + }}, + args{&apiv1.CreateKeyRequest{Name: keyName, ProtectionLevel: apiv1.HSM, SignatureAlgorithm: apiv1.ECDSAWithSHA256}}, + nil, true}, + {"fail create key version", fields{ + &MockClient{ + getKeyRing: func(_ context.Context, _ *kmspb.GetKeyRingRequest, _ ...gax.CallOption) (*kmspb.KeyRing, error) { + return &kmspb.KeyRing{}, nil + }, + createCryptoKey: func(_ context.Context, _ *kmspb.CreateCryptoKeyRequest, _ ...gax.CallOption) (*kmspb.CryptoKey, error) { + return nil, alreadyExists + }, + createCryptoKeyVersion: func(_ context.Context, _ *kmspb.CreateCryptoKeyVersionRequest, _ ...gax.CallOption) (*kmspb.CryptoKeyVersion, error) { + return nil, testError + }, + }}, + args{&apiv1.CreateKeyRequest{Name: keyName, ProtectionLevel: apiv1.HSM, SignatureAlgorithm: apiv1.ECDSAWithSHA256}}, + nil, true}, + {"fail get public key", fields{ + &MockClient{ + getKeyRing: func(_ context.Context, _ *kmspb.GetKeyRingRequest, _ ...gax.CallOption) (*kmspb.KeyRing, error) { + return &kmspb.KeyRing{}, nil + }, + createCryptoKey: func(_ context.Context, _ *kmspb.CreateCryptoKeyRequest, _ ...gax.CallOption) (*kmspb.CryptoKey, error) { + return &kmspb.CryptoKey{Name: keyName}, nil + }, + getPublicKey: func(_ context.Context, _ *kmspb.GetPublicKeyRequest, _ ...gax.CallOption) (*kmspb.PublicKey, error) { + return nil, testError + }, + }}, + args{&apiv1.CreateKeyRequest{Name: keyName, ProtectionLevel: apiv1.HSM, SignatureAlgorithm: apiv1.ECDSAWithSHA256}}, + nil, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + k := &CloudKMS{ + client: tt.fields.client, + } + got, err := k.CreateKey(tt.args.req) + if (err != nil) != tt.wantErr { + t.Errorf("CloudKMS.CreateKey() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("CloudKMS.CreateKey() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCloudKMS_GetPublicKey(t *testing.T) { + keyName := "projects/p/locations/l/keyRings/k/cryptoKeys/c/cryptoKeyVersions/1" + testError := fmt.Errorf("an error") + + pemBytes, err := ioutil.ReadFile("testdata/pub.pem") + if err != nil { + t.Fatal(err) + } + pk, err := pemutil.ParseKey(pemBytes) + if err != nil { + t.Fatal(err) + } + + type fields struct { + client keyManagementClient + } + type args struct { + req *apiv1.GetPublicKeyRequest + } + tests := []struct { + name string + fields fields + args args + want crypto.PublicKey + wantErr bool + }{ + {"ok", fields{ + &MockClient{ + getPublicKey: func(_ context.Context, _ *kmspb.GetPublicKeyRequest, _ ...gax.CallOption) (*kmspb.PublicKey, error) { + return &kmspb.PublicKey{Pem: string(pemBytes)}, nil + }, + }}, + args{&apiv1.GetPublicKeyRequest{Name: keyName}}, pk, false}, + {"fail name", fields{&MockClient{}}, args{&apiv1.GetPublicKeyRequest{}}, nil, true}, + {"fail get public key", fields{ + &MockClient{ + getPublicKey: func(_ context.Context, _ *kmspb.GetPublicKeyRequest, _ ...gax.CallOption) (*kmspb.PublicKey, error) { + return nil, testError + }, + }}, + args{&apiv1.GetPublicKeyRequest{Name: keyName}}, nil, true}, + {"fail parse pem", fields{ + &MockClient{ + getPublicKey: func(_ context.Context, _ *kmspb.GetPublicKeyRequest, _ ...gax.CallOption) (*kmspb.PublicKey, error) { + return &kmspb.PublicKey{Pem: string("bad pem")}, nil + }, + }}, + args{&apiv1.GetPublicKeyRequest{Name: keyName}}, nil, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + k := &CloudKMS{ + client: tt.fields.client, + } + got, err := k.GetPublicKey(tt.args.req) + if (err != nil) != tt.wantErr { + t.Errorf("CloudKMS.GetPublicKey() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("CloudKMS.GetPublicKey() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/kms/cloudkms/mock_test.go b/kms/cloudkms/mock_test.go new file mode 100644 index 00000000..7617bd85 --- /dev/null +++ b/kms/cloudkms/mock_test.go @@ -0,0 +1,46 @@ +package cloudkms + +import ( + "context" + + gax "github.com/googleapis/gax-go/v2" + kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1" +) + +type MockClient struct { + close func() error + getPublicKey func(context.Context, *kmspb.GetPublicKeyRequest, ...gax.CallOption) (*kmspb.PublicKey, error) + asymmetricSign func(context.Context, *kmspb.AsymmetricSignRequest, ...gax.CallOption) (*kmspb.AsymmetricSignResponse, error) + createCryptoKey func(context.Context, *kmspb.CreateCryptoKeyRequest, ...gax.CallOption) (*kmspb.CryptoKey, error) + getKeyRing func(context.Context, *kmspb.GetKeyRingRequest, ...gax.CallOption) (*kmspb.KeyRing, error) + createKeyRing func(context.Context, *kmspb.CreateKeyRingRequest, ...gax.CallOption) (*kmspb.KeyRing, error) + createCryptoKeyVersion func(context.Context, *kmspb.CreateCryptoKeyVersionRequest, ...gax.CallOption) (*kmspb.CryptoKeyVersion, error) +} + +func (m *MockClient) Close() error { + return m.close() +} + +func (m *MockClient) GetPublicKey(ctx context.Context, req *kmspb.GetPublicKeyRequest, opts ...gax.CallOption) (*kmspb.PublicKey, error) { + return m.getPublicKey(ctx, req, opts...) +} + +func (m *MockClient) AsymmetricSign(ctx context.Context, req *kmspb.AsymmetricSignRequest, opts ...gax.CallOption) (*kmspb.AsymmetricSignResponse, error) { + return m.asymmetricSign(ctx, req, opts...) +} + +func (m *MockClient) CreateCryptoKey(ctx context.Context, req *kmspb.CreateCryptoKeyRequest, opts ...gax.CallOption) (*kmspb.CryptoKey, error) { + return m.createCryptoKey(ctx, req, opts...) +} + +func (m *MockClient) GetKeyRing(ctx context.Context, req *kmspb.GetKeyRingRequest, opts ...gax.CallOption) (*kmspb.KeyRing, error) { + return m.getKeyRing(ctx, req, opts...) +} + +func (m *MockClient) CreateKeyRing(ctx context.Context, req *kmspb.CreateKeyRingRequest, opts ...gax.CallOption) (*kmspb.KeyRing, error) { + return m.createKeyRing(ctx, req, opts...) +} + +func (m *MockClient) CreateCryptoKeyVersion(ctx context.Context, req *kmspb.CreateCryptoKeyVersionRequest, opts ...gax.CallOption) (*kmspb.CryptoKeyVersion, error) { + return m.createCryptoKeyVersion(ctx, req, opts...) +} diff --git a/kms/cloudkms/signer.go b/kms/cloudkms/signer.go index 515e15dc..be1162ef 100644 --- a/kms/cloudkms/signer.go +++ b/kms/cloudkms/signer.go @@ -36,7 +36,6 @@ func (s *signer) Public() crypto.PublicKey { pk, err := pemutil.ParseKey([]byte(response.Pem)) if err != nil { - println(2, err.Error()) return err } diff --git a/kms/cloudkms/signer_test.go b/kms/cloudkms/signer_test.go new file mode 100644 index 00000000..c8c2fe3f --- /dev/null +++ b/kms/cloudkms/signer_test.go @@ -0,0 +1,148 @@ +package cloudkms + +import ( + "context" + "crypto" + "crypto/rand" + "fmt" + "io" + "io/ioutil" + "reflect" + "testing" + + gax "github.com/googleapis/gax-go/v2" + "github.com/smallstep/cli/crypto/pemutil" + kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1" +) + +func Test_newSigner(t *testing.T) { + type args struct { + c keyManagementClient + signingKey string + } + tests := []struct { + name string + args args + want *signer + }{ + {"ok", args{&MockClient{}, "signingKey"}, &signer{client: &MockClient{}, signingKey: "signingKey"}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := newSigner(tt.args.c, tt.args.signingKey); !reflect.DeepEqual(got, tt.want) { + t.Errorf("newSigner() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_signer_Public(t *testing.T) { + keyName := "projects/p/locations/l/keyRings/k/cryptoKeys/c/cryptoKeyVersions/1" + testError := fmt.Errorf("an error") + + pemBytes, err := ioutil.ReadFile("testdata/pub.pem") + if err != nil { + t.Fatal(err) + } + pk, err := pemutil.ParseKey(pemBytes) + if err != nil { + t.Fatal(err) + } + + type fields struct { + client keyManagementClient + signingKey string + } + tests := []struct { + name string + fields fields + want crypto.PublicKey + wantErr bool + }{ + {"ok", fields{&MockClient{ + getPublicKey: func(_ context.Context, _ *kmspb.GetPublicKeyRequest, _ ...gax.CallOption) (*kmspb.PublicKey, error) { + return &kmspb.PublicKey{Pem: string(pemBytes)}, nil + }, + }, keyName}, pk, false}, + {"fail get public key", fields{&MockClient{ + getPublicKey: func(_ context.Context, _ *kmspb.GetPublicKeyRequest, _ ...gax.CallOption) (*kmspb.PublicKey, error) { + return nil, testError + }, + }, keyName}, nil, true}, + {"fail parse pem", fields{ + &MockClient{ + getPublicKey: func(_ context.Context, _ *kmspb.GetPublicKeyRequest, _ ...gax.CallOption) (*kmspb.PublicKey, error) { + return &kmspb.PublicKey{Pem: string("bad pem")}, nil + }, + }, keyName}, nil, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := &signer{ + client: tt.fields.client, + signingKey: tt.fields.signingKey, + } + got := s.Public() + if _, ok := got.(error); ok != tt.wantErr { + t.Errorf("signer.Public() error = %v, wantErr %v", got, tt.wantErr) + return + } + if !tt.wantErr && !reflect.DeepEqual(got, tt.want) { + t.Errorf("signer.Public() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_signer_Sign(t *testing.T) { + keyName := "projects/p/locations/l/keyRings/k/cryptoKeys/c/cryptoKeyVersions/1" + okClient := &MockClient{ + asymmetricSign: func(_ context.Context, _ *kmspb.AsymmetricSignRequest, _ ...gax.CallOption) (*kmspb.AsymmetricSignResponse, error) { + return &kmspb.AsymmetricSignResponse{Signature: []byte("ok signature")}, nil + }, + } + failClient := &MockClient{ + asymmetricSign: func(_ context.Context, _ *kmspb.AsymmetricSignRequest, _ ...gax.CallOption) (*kmspb.AsymmetricSignResponse, error) { + return nil, fmt.Errorf("an error") + }, + } + + type fields struct { + client keyManagementClient + signingKey string + } + type args struct { + rand io.Reader + digest []byte + opts crypto.SignerOpts + } + tests := []struct { + name string + fields fields + args args + want []byte + wantErr bool + }{ + {"ok sha256", fields{okClient, keyName}, args{rand.Reader, []byte("digest"), crypto.SHA256}, []byte("ok signature"), false}, + {"ok sha384", fields{okClient, keyName}, args{rand.Reader, []byte("digest"), crypto.SHA384}, []byte("ok signature"), false}, + {"ok sha512", fields{okClient, keyName}, args{rand.Reader, []byte("digest"), crypto.SHA512}, []byte("ok signature"), false}, + {"fail MD5", fields{okClient, keyName}, args{rand.Reader, []byte("digest"), crypto.MD5}, nil, true}, + {"fail asymmetric sign", fields{failClient, keyName}, args{rand.Reader, []byte("digest"), crypto.SHA256}, nil, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := &signer{ + client: tt.fields.client, + signingKey: tt.fields.signingKey, + } + 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) + } + }) + } +} diff --git a/kms/cloudkms/testdata/pub.pem b/kms/cloudkms/testdata/pub.pem new file mode 100644 index 00000000..e31e583e --- /dev/null +++ b/kms/cloudkms/testdata/pub.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5VPD/W5RXn0lrs2MdoNteTSZ+sh1 +veT13hakPZF9YzaNVZgujqK3d1nt+4jPECU+ED/WQ1GgFZiVGUo3flvB/w== +-----END PUBLIC KEY-----