2b4b902975
Fixes smallstep/cli#363
735 lines
25 KiB
Go
735 lines
25 KiB
Go
package cloudcas
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto/rand"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/asn1"
|
|
"io"
|
|
"os"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
gax "github.com/googleapis/gax-go/v2"
|
|
"github.com/pkg/errors"
|
|
"github.com/smallstep/certificates/cas/apiv1"
|
|
pb "google.golang.org/genproto/googleapis/cloud/security/privateca/v1beta1"
|
|
)
|
|
|
|
var (
|
|
errTest = errors.New("test error")
|
|
testAuthorityName = "projects/test-project/locations/us-west1/certificateAuthorities/test-ca"
|
|
testCertificateName = "projects/test-project/locations/us-west1/certificateAuthorities/test-ca/certificates/test-certificate"
|
|
testRootCertificate = `-----BEGIN CERTIFICATE-----
|
|
MIIBhjCCAS2gAwIBAgIQLbKTuXau4+t3KFbGpJJAADAKBggqhkjOPQQDAjAiMSAw
|
|
HgYDVQQDExdHb29nbGUgQ0FTIFRlc3QgUm9vdCBDQTAeFw0yMDA5MTQyMjQ4NDla
|
|
Fw0zMDA5MTIyMjQ4NDlaMCIxIDAeBgNVBAMTF0dvb2dsZSBDQVMgVGVzdCBSb290
|
|
IENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYKGgQ3/0D7+oBTc0CXoYfSC6
|
|
M8hOqLsmzBapPZSYpfwjgEsjdNU84jdrYmW1zF1+p+MrL4c7qJv9NLo/picCuqNF
|
|
MEMwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYE
|
|
FFVn9V7Qymd7cUJh9KAhnUDAQL5YMAoGCCqGSM49BAMCA0cAMEQCIA4LzttYoT3u
|
|
8TYgSrvFT+Z+cklfi4UrPBU6aSbcUaW2AiAPfaqbyccQT3CxMVyHg+xZZjAirZp8
|
|
lAeA/T4FxAonHA==
|
|
-----END CERTIFICATE-----`
|
|
testIntermediateCertificate = `-----BEGIN CERTIFICATE-----
|
|
MIIBsDCCAVagAwIBAgIQOb91kHxWKVzSJ9ESW1ViVzAKBggqhkjOPQQDAjAiMSAw
|
|
HgYDVQQDExdHb29nbGUgQ0FTIFRlc3QgUm9vdCBDQTAeFw0yMDA5MTQyMjQ4NDla
|
|
Fw0zMDA5MTIyMjQ4NDlaMCoxKDAmBgNVBAMTH0dvb2dsZSBDQVMgVGVzdCBJbnRl
|
|
cm1lZGlhdGUgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASUHN1cNyId4Ei/
|
|
4MxD5VrZFc51P50caMUdDZVrPveidChBYCU/9IM6vnRlZHx2HLjQ0qAvqHwY3rT0
|
|
xc7n+PfCo2YwZDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAd
|
|
BgNVHQ4EFgQUSDlasiw0pRKyS7llhL0ZuVFNa9UwHwYDVR0jBBgwFoAUVWf1XtDK
|
|
Z3txQmH0oCGdQMBAvlgwCgYIKoZIzj0EAwIDSAAwRQIgMmsLcoC4KriXw+s+cZx2
|
|
bJMf6Mx/WESj31buJJhpzY0CIQCBUa/JtvS3nyce/4DF5tK2v49/NWHREgqAaZ57
|
|
DcYyHQ==
|
|
-----END CERTIFICATE-----`
|
|
testLeafCertificate = `-----BEGIN CERTIFICATE-----
|
|
MIIB1jCCAX2gAwIBAgIQQfOn+COMeuD8VYF1TiDkEzAKBggqhkjOPQQDAjAqMSgw
|
|
JgYDVQQDEx9Hb29nbGUgQ0FTIFRlc3QgSW50ZXJtZWRpYXRlIENBMB4XDTIwMDkx
|
|
NDIyNTE1NVoXDTMwMDkxMjIyNTE1MlowHTEbMBkGA1UEAxMSdGVzdC5zbWFsbHN0
|
|
ZXAuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAdUSRBrpgHFilN4eaGlN
|
|
nX2+xfjXa1Iwk2/+AensjFTXJi1UAIB0e+4pqi7Sen5E2QVBhntEHCrA3xOf7czg
|
|
P6OBkTCBjjAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsG
|
|
AQUFBwMCMB0GA1UdDgQWBBSYPbu4Tmm7Zze/hCePeZH1Avoj+jAfBgNVHSMEGDAW
|
|
gBRIOVqyLDSlErJLuWWEvRm5UU1r1TAdBgNVHREEFjAUghJ0ZXN0LnNtYWxsc3Rl
|
|
cC5jb20wCgYIKoZIzj0EAwIDRwAwRAIgY+nTc+RHn31/BOhht4JpxCmJPHxqFT3S
|
|
ojnictBudV0CIB87ipY5HV3c8FLVEzTA0wFwdDZvQraQYsthwbg2kQFb
|
|
-----END CERTIFICATE-----`
|
|
testSignedCertificate = `-----BEGIN CERTIFICATE-----
|
|
MIIB/DCCAaKgAwIBAgIQHHFuGMz0cClfde5kqP5prTAKBggqhkjOPQQDAjAqMSgw
|
|
JgYDVQQDEx9Hb29nbGUgQ0FTIFRlc3QgSW50ZXJtZWRpYXRlIENBMB4XDTIwMDkx
|
|
NTAwMDQ0M1oXDTMwMDkxMzAwMDQ0MFowHTEbMBkGA1UEAxMSdGVzdC5zbWFsbHN0
|
|
ZXAuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMqNCiXMvbn74LsHzRv+8
|
|
17m9vEzH6RHrg3m82e0uEc36+fZWV/zJ9SKuONmnl5VP79LsjL5SVH0RDj73U2XO
|
|
DKOBtjCBszAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsG
|
|
AQUFBwMCMB0GA1UdDgQWBBRTA2cTs7PCNjnps/+T0dS8diqv0DAfBgNVHSMEGDAW
|
|
gBRIOVqyLDSlErJLuWWEvRm5UU1r1TBCBgwrBgEEAYKkZMYoQAIEMjAwEwhjbG91
|
|
ZGNhcxMkZDhkMThhNjgtNTI5Ni00YWYzLWFlNGItMmY4NzdkYTNmYmQ5MAoGCCqG
|
|
SM49BAMCA0gAMEUCIGxl+pqJ50WYWUqK2l4V1FHoXSi0Nht5kwTxFxnWZu1xAiEA
|
|
zemu3bhWLFaGg3s8i+HTEhw4RqkHP74vF7AVYp88bAw=
|
|
-----END CERTIFICATE-----`
|
|
)
|
|
|
|
type testClient struct {
|
|
credentialsFile string
|
|
certificate *pb.Certificate
|
|
certificateAuthority *pb.CertificateAuthority
|
|
err error
|
|
}
|
|
|
|
func newTestClient(credentialsFile string) (CertificateAuthorityClient, error) {
|
|
if credentialsFile == "testdata/error.json" {
|
|
return nil, errTest
|
|
}
|
|
return &testClient{
|
|
credentialsFile: credentialsFile,
|
|
}, nil
|
|
}
|
|
|
|
func okTestClient() *testClient {
|
|
return &testClient{
|
|
credentialsFile: "testdata/credentials.json",
|
|
certificate: &pb.Certificate{
|
|
Name: testCertificateName,
|
|
PemCertificate: testSignedCertificate,
|
|
PemCertificateChain: []string{testIntermediateCertificate, testRootCertificate},
|
|
},
|
|
certificateAuthority: &pb.CertificateAuthority{
|
|
PemCaCertificates: []string{testIntermediateCertificate, testRootCertificate},
|
|
},
|
|
}
|
|
}
|
|
|
|
func failTestClient() *testClient {
|
|
return &testClient{
|
|
credentialsFile: "testdata/credentials.json",
|
|
err: errTest,
|
|
}
|
|
}
|
|
|
|
func badTestClient() *testClient {
|
|
return &testClient{
|
|
credentialsFile: "testdata/credentials.json",
|
|
certificate: &pb.Certificate{
|
|
Name: testCertificateName,
|
|
PemCertificate: "not a pem cert",
|
|
PemCertificateChain: []string{testIntermediateCertificate, testRootCertificate},
|
|
},
|
|
certificateAuthority: &pb.CertificateAuthority{
|
|
PemCaCertificates: []string{testIntermediateCertificate, "not a pem cert"},
|
|
},
|
|
}
|
|
}
|
|
|
|
func setTeeReader(t *testing.T, w *bytes.Buffer) {
|
|
t.Helper()
|
|
reader := rand.Reader
|
|
t.Cleanup(func() {
|
|
rand.Reader = reader
|
|
})
|
|
rand.Reader = io.TeeReader(reader, w)
|
|
}
|
|
|
|
func (c *testClient) CreateCertificate(ctx context.Context, req *pb.CreateCertificateRequest, opts ...gax.CallOption) (*pb.Certificate, error) {
|
|
return c.certificate, c.err
|
|
}
|
|
|
|
func (c *testClient) RevokeCertificate(ctx context.Context, req *pb.RevokeCertificateRequest, opts ...gax.CallOption) (*pb.Certificate, error) {
|
|
return c.certificate, c.err
|
|
}
|
|
|
|
func (c *testClient) GetCertificateAuthority(ctx context.Context, req *pb.GetCertificateAuthorityRequest, opts ...gax.CallOption) (*pb.CertificateAuthority, error) {
|
|
return c.certificateAuthority, c.err
|
|
}
|
|
|
|
func mustParseCertificate(t *testing.T, pemCert string) *x509.Certificate {
|
|
t.Helper()
|
|
crt, err := parseCertificate(pemCert)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return crt
|
|
}
|
|
|
|
func TestNew(t *testing.T) {
|
|
tmp := newCertificateAuthorityClient
|
|
newCertificateAuthorityClient = func(ctx context.Context, credentialsFile string) (CertificateAuthorityClient, error) {
|
|
return newTestClient(credentialsFile)
|
|
}
|
|
t.Cleanup(func() {
|
|
newCertificateAuthorityClient = tmp
|
|
})
|
|
|
|
type args struct {
|
|
ctx context.Context
|
|
opts apiv1.Options
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want *CloudCAS
|
|
wantErr bool
|
|
}{
|
|
{"ok", args{context.Background(), apiv1.Options{
|
|
CertificateAuthority: testAuthorityName,
|
|
}}, &CloudCAS{
|
|
client: &testClient{},
|
|
certificateAuthority: testAuthorityName,
|
|
}, false},
|
|
{"ok with credentials", args{context.Background(), apiv1.Options{
|
|
CertificateAuthority: testAuthorityName, CredentialsFile: "testdata/credentials.json",
|
|
}}, &CloudCAS{
|
|
client: &testClient{credentialsFile: "testdata/credentials.json"},
|
|
certificateAuthority: testAuthorityName,
|
|
}, false},
|
|
{"fail certificate authority", args{context.Background(), apiv1.Options{}}, nil, true},
|
|
{"fail with credentials", args{context.Background(), apiv1.Options{
|
|
CertificateAuthority: testAuthorityName, CredentialsFile: "testdata/error.json",
|
|
}}, 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 TestNew_register(t *testing.T) {
|
|
tmp := newCertificateAuthorityClient
|
|
newCertificateAuthorityClient = func(ctx context.Context, credentialsFile string) (CertificateAuthorityClient, error) {
|
|
return newTestClient(credentialsFile)
|
|
}
|
|
t.Cleanup(func() {
|
|
newCertificateAuthorityClient = tmp
|
|
})
|
|
|
|
want := &CloudCAS{
|
|
client: &testClient{credentialsFile: "testdata/credentials.json"},
|
|
certificateAuthority: testAuthorityName,
|
|
}
|
|
|
|
newFn, ok := apiv1.LoadCertificateAuthorityServiceNewFunc(apiv1.CloudCAS)
|
|
if !ok {
|
|
t.Error("apiv1.LoadCertificateAuthorityServiceNewFunc(apiv1.CloudCAS) was not found")
|
|
return
|
|
}
|
|
|
|
got, err := newFn(context.Background(), apiv1.Options{
|
|
CertificateAuthority: testAuthorityName, CredentialsFile: "testdata/credentials.json",
|
|
})
|
|
if err != nil {
|
|
t.Errorf("New() error = %v", err)
|
|
return
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Errorf("New() = %v, want %v", got, want)
|
|
}
|
|
|
|
}
|
|
|
|
func TestNew_real(t *testing.T) {
|
|
if v, ok := os.LookupEnv("GOOGLE_APPLICATION_CREDENTIALS"); ok {
|
|
os.Unsetenv("GOOGLE_APPLICATION_CREDENTIALS")
|
|
t.Cleanup(func() {
|
|
os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", v)
|
|
})
|
|
}
|
|
|
|
type args struct {
|
|
ctx context.Context
|
|
opts apiv1.Options
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
skipOnCI bool
|
|
args args
|
|
wantErr bool
|
|
}{
|
|
{"fail default credentials", true, args{context.Background(), apiv1.Options{CertificateAuthority: testAuthorityName}}, true},
|
|
{"fail certificate authority", false, args{context.Background(), apiv1.Options{}}, true},
|
|
{"fail with credentials", false, args{context.Background(), apiv1.Options{
|
|
CertificateAuthority: testAuthorityName, CredentialsFile: "testdata/missing.json",
|
|
}}, true},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.skipOnCI && os.Getenv("CI") == "true" {
|
|
t.SkipNow()
|
|
}
|
|
_, err := New(tt.args.ctx, tt.args.opts)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCloudCAS_GetCertificateAuthority(t *testing.T) {
|
|
root := mustParseCertificate(t, testRootCertificate)
|
|
type fields struct {
|
|
client CertificateAuthorityClient
|
|
certificateAuthority string
|
|
}
|
|
type args struct {
|
|
req *apiv1.GetCertificateAuthorityRequest
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
want *apiv1.GetCertificateAuthorityResponse
|
|
wantErr bool
|
|
}{
|
|
{"ok", fields{okTestClient(), testCertificateName}, args{&apiv1.GetCertificateAuthorityRequest{}}, &apiv1.GetCertificateAuthorityResponse{
|
|
RootCertificate: root,
|
|
}, false},
|
|
{"ok with name", fields{okTestClient(), testCertificateName}, args{&apiv1.GetCertificateAuthorityRequest{
|
|
Name: testCertificateName,
|
|
}}, &apiv1.GetCertificateAuthorityResponse{
|
|
RootCertificate: root,
|
|
}, false},
|
|
{"fail GetCertificateAuthority", fields{failTestClient(), testCertificateName}, args{&apiv1.GetCertificateAuthorityRequest{}}, nil, true},
|
|
{"fail bad root", fields{badTestClient(), testCertificateName}, args{&apiv1.GetCertificateAuthorityRequest{}}, nil, true},
|
|
{"fail no pems", fields{&testClient{certificateAuthority: &pb.CertificateAuthority{}}, testCertificateName}, args{&apiv1.GetCertificateAuthorityRequest{}}, nil, true},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
c := &CloudCAS{
|
|
client: tt.fields.client,
|
|
certificateAuthority: tt.fields.certificateAuthority,
|
|
}
|
|
got, err := c.GetCertificateAuthority(tt.args.req)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("CloudCAS.GetCertificateAuthority() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
t.Errorf("CloudCAS.GetCertificateAuthority() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCloudCAS_CreateCertificate(t *testing.T) {
|
|
type fields struct {
|
|
client CertificateAuthorityClient
|
|
certificateAuthority string
|
|
}
|
|
type args struct {
|
|
req *apiv1.CreateCertificateRequest
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
want *apiv1.CreateCertificateResponse
|
|
wantErr bool
|
|
}{
|
|
{"ok", fields{okTestClient(), testCertificateName}, args{&apiv1.CreateCertificateRequest{
|
|
Template: mustParseCertificate(t, testLeafCertificate),
|
|
Lifetime: 24 * time.Hour,
|
|
}}, &apiv1.CreateCertificateResponse{
|
|
Certificate: mustParseCertificate(t, testSignedCertificate),
|
|
CertificateChain: []*x509.Certificate{mustParseCertificate(t, testIntermediateCertificate)},
|
|
}, false},
|
|
{"fail Template", fields{okTestClient(), testCertificateName}, args{&apiv1.CreateCertificateRequest{
|
|
Lifetime: 24 * time.Hour,
|
|
}}, nil, true},
|
|
{"fail Lifetime", fields{okTestClient(), testCertificateName}, args{&apiv1.CreateCertificateRequest{
|
|
Template: mustParseCertificate(t, testLeafCertificate),
|
|
}}, nil, true},
|
|
{"fail CreateCertificate", fields{failTestClient(), testCertificateName}, args{&apiv1.CreateCertificateRequest{
|
|
Template: mustParseCertificate(t, testLeafCertificate),
|
|
Lifetime: 24 * time.Hour,
|
|
}}, nil, true},
|
|
{"fail Certificate", fields{badTestClient(), testCertificateName}, args{&apiv1.CreateCertificateRequest{
|
|
Template: mustParseCertificate(t, testLeafCertificate),
|
|
Lifetime: 24 * time.Hour,
|
|
}}, nil, true},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
c := &CloudCAS{
|
|
client: tt.fields.client,
|
|
certificateAuthority: tt.fields.certificateAuthority,
|
|
}
|
|
got, err := c.CreateCertificate(tt.args.req)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("CloudCAS.CreateCertificate() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
t.Errorf("CloudCAS.CreateCertificate() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCloudCAS_createCertificate(t *testing.T) {
|
|
leaf := mustParseCertificate(t, testLeafCertificate)
|
|
signed := mustParseCertificate(t, testSignedCertificate)
|
|
chain := []*x509.Certificate{mustParseCertificate(t, testIntermediateCertificate)}
|
|
|
|
type fields struct {
|
|
client CertificateAuthorityClient
|
|
certificateAuthority string
|
|
}
|
|
type args struct {
|
|
tpl *x509.Certificate
|
|
lifetime time.Duration
|
|
requestID string
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
want *x509.Certificate
|
|
want1 []*x509.Certificate
|
|
wantErr bool
|
|
}{
|
|
{"ok", fields{okTestClient(), testAuthorityName}, args{leaf, 24 * time.Hour, "request-id"}, signed, chain, false},
|
|
{"fail CertificateConfig", fields{okTestClient(), testAuthorityName}, args{&x509.Certificate{}, 24 * time.Hour, "request-id"}, nil, nil, true},
|
|
{"fail CreateCertificate", fields{failTestClient(), testAuthorityName}, args{leaf, 24 * time.Hour, "request-id"}, nil, nil, true},
|
|
{"fail ParseCertificates", fields{badTestClient(), testAuthorityName}, args{leaf, 24 * time.Hour, "request-id"}, nil, nil, true},
|
|
{"fail create id", fields{okTestClient(), testAuthorityName}, args{leaf, 24 * time.Hour, "request-id"}, nil, nil, true},
|
|
}
|
|
|
|
// Pre-calculate rand.Random
|
|
buf := new(bytes.Buffer)
|
|
setTeeReader(t, buf)
|
|
for i := 0; i < len(tests)-1; i++ {
|
|
_, err := uuid.NewRandomFromReader(rand.Reader)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
rand.Reader = buf
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
c := &CloudCAS{
|
|
client: tt.fields.client,
|
|
certificateAuthority: tt.fields.certificateAuthority,
|
|
}
|
|
got, got1, err := c.createCertificate(tt.args.tpl, tt.args.lifetime, tt.args.requestID)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("CloudCAS.createCertificate() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
t.Errorf("CloudCAS.createCertificate() got = %v, want %v", got, tt.want)
|
|
}
|
|
if !reflect.DeepEqual(got1, tt.want1) {
|
|
t.Errorf("CloudCAS.createCertificate() got1 = %v, want %v", got1, tt.want1)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCloudCAS_RenewCertificate(t *testing.T) {
|
|
type fields struct {
|
|
client CertificateAuthorityClient
|
|
certificateAuthority string
|
|
}
|
|
type args struct {
|
|
req *apiv1.RenewCertificateRequest
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
want *apiv1.RenewCertificateResponse
|
|
wantErr bool
|
|
}{
|
|
{"ok", fields{okTestClient(), testCertificateName}, args{&apiv1.RenewCertificateRequest{
|
|
Template: mustParseCertificate(t, testLeafCertificate),
|
|
Lifetime: 24 * time.Hour,
|
|
}}, &apiv1.RenewCertificateResponse{
|
|
Certificate: mustParseCertificate(t, testSignedCertificate),
|
|
CertificateChain: []*x509.Certificate{mustParseCertificate(t, testIntermediateCertificate)},
|
|
}, false},
|
|
{"fail Template", fields{okTestClient(), testCertificateName}, args{&apiv1.RenewCertificateRequest{
|
|
Lifetime: 24 * time.Hour,
|
|
}}, nil, true},
|
|
{"fail Lifetime", fields{okTestClient(), testCertificateName}, args{&apiv1.RenewCertificateRequest{
|
|
Template: mustParseCertificate(t, testLeafCertificate),
|
|
}}, nil, true},
|
|
{"fail CreateCertificate", fields{failTestClient(), testCertificateName}, args{&apiv1.RenewCertificateRequest{
|
|
Template: mustParseCertificate(t, testLeafCertificate),
|
|
Lifetime: 24 * time.Hour,
|
|
}}, nil, true},
|
|
{"fail Certificate", fields{badTestClient(), testCertificateName}, args{&apiv1.RenewCertificateRequest{
|
|
Template: mustParseCertificate(t, testLeafCertificate),
|
|
Lifetime: 24 * time.Hour,
|
|
}}, nil, true},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
c := &CloudCAS{
|
|
client: tt.fields.client,
|
|
certificateAuthority: tt.fields.certificateAuthority,
|
|
}
|
|
got, err := c.RenewCertificate(tt.args.req)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("CloudCAS.RenewCertificate() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
t.Errorf("CloudCAS.RenewCertificate() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCloudCAS_RevokeCertificate(t *testing.T) {
|
|
badExtensionCert := mustParseCertificate(t, testSignedCertificate)
|
|
for i, ext := range badExtensionCert.Extensions {
|
|
if ext.Id.Equal(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 37476, 9000, 64, 2}) {
|
|
badExtensionCert.Extensions[i].Value = []byte("bad-data")
|
|
}
|
|
}
|
|
|
|
type fields struct {
|
|
client CertificateAuthorityClient
|
|
certificateAuthority string
|
|
}
|
|
type args struct {
|
|
req *apiv1.RevokeCertificateRequest
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
want *apiv1.RevokeCertificateResponse
|
|
wantErr bool
|
|
}{
|
|
{"ok", fields{okTestClient(), testCertificateName}, args{&apiv1.RevokeCertificateRequest{
|
|
Certificate: mustParseCertificate(t, testSignedCertificate),
|
|
ReasonCode: 1,
|
|
}}, &apiv1.RevokeCertificateResponse{
|
|
Certificate: mustParseCertificate(t, testSignedCertificate),
|
|
CertificateChain: []*x509.Certificate{mustParseCertificate(t, testIntermediateCertificate)},
|
|
}, false},
|
|
{"fail Extension", fields{okTestClient(), testCertificateName}, args{&apiv1.RevokeCertificateRequest{
|
|
Certificate: mustParseCertificate(t, testLeafCertificate),
|
|
ReasonCode: 1,
|
|
}}, nil, true},
|
|
{"fail Extension Value", fields{okTestClient(), testCertificateName}, args{&apiv1.RevokeCertificateRequest{
|
|
Certificate: badExtensionCert,
|
|
ReasonCode: 1,
|
|
}}, nil, true},
|
|
{"fail Certificate", fields{okTestClient(), testCertificateName}, args{&apiv1.RevokeCertificateRequest{
|
|
ReasonCode: 2,
|
|
}}, nil, true},
|
|
{"fail ReasonCode", fields{okTestClient(), testCertificateName}, args{&apiv1.RevokeCertificateRequest{
|
|
Certificate: mustParseCertificate(t, testSignedCertificate),
|
|
ReasonCode: 100,
|
|
}}, nil, true},
|
|
{"fail ReasonCode 7", fields{okTestClient(), testCertificateName}, args{&apiv1.RevokeCertificateRequest{
|
|
Certificate: mustParseCertificate(t, testSignedCertificate),
|
|
ReasonCode: 7,
|
|
}}, nil, true},
|
|
{"fail ReasonCode 8", fields{okTestClient(), testCertificateName}, args{&apiv1.RevokeCertificateRequest{
|
|
Certificate: mustParseCertificate(t, testSignedCertificate),
|
|
ReasonCode: 8,
|
|
}}, nil, true},
|
|
{"fail RevokeCertificate", fields{failTestClient(), testCertificateName}, args{&apiv1.RevokeCertificateRequest{
|
|
Certificate: mustParseCertificate(t, testSignedCertificate),
|
|
ReasonCode: 1,
|
|
}}, nil, true},
|
|
{"fail ParseCertificate", fields{badTestClient(), testCertificateName}, args{&apiv1.RevokeCertificateRequest{
|
|
Certificate: mustParseCertificate(t, testSignedCertificate),
|
|
ReasonCode: 1,
|
|
}}, nil, true},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
c := &CloudCAS{
|
|
client: tt.fields.client,
|
|
certificateAuthority: tt.fields.certificateAuthority,
|
|
}
|
|
got, err := c.RevokeCertificate(tt.args.req)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("CloudCAS.RevokeCertificate() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
t.Errorf("CloudCAS.RevokeCertificate() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_createCertificateID(t *testing.T) {
|
|
buf := new(bytes.Buffer)
|
|
setTeeReader(t, buf)
|
|
uuid, err := uuid.NewRandomFromReader(rand.Reader)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
rand.Reader = buf
|
|
|
|
tests := []struct {
|
|
name string
|
|
want string
|
|
wantErr bool
|
|
}{
|
|
{"ok", uuid.String(), false},
|
|
{"fail", "", true},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := createCertificateID()
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("createCertificateID() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if got != tt.want {
|
|
t.Errorf("createCertificateID() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_parseCertificate(t *testing.T) {
|
|
type args struct {
|
|
pemCert string
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want *x509.Certificate
|
|
wantErr bool
|
|
}{
|
|
{"ok", args{testLeafCertificate}, mustParseCertificate(t, testLeafCertificate), false},
|
|
{"ok intermediate", args{testIntermediateCertificate}, mustParseCertificate(t, testIntermediateCertificate), false},
|
|
{"fail pem", args{"not pem"}, nil, true},
|
|
{"fail parseCertificate", args{"-----BEGIN CERTIFICATE-----\nZm9vYmFyCg==\n-----END CERTIFICATE-----\n"}, nil, true},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := parseCertificate(tt.args.pemCert)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("parseCertificate() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
t.Errorf("parseCertificate() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_getCertificateAndChain(t *testing.T) {
|
|
type args struct {
|
|
certpb *pb.Certificate
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want *x509.Certificate
|
|
want1 []*x509.Certificate
|
|
wantErr bool
|
|
}{
|
|
{"ok", args{&pb.Certificate{
|
|
Name: testCertificateName,
|
|
PemCertificate: testSignedCertificate,
|
|
PemCertificateChain: []string{testIntermediateCertificate, testRootCertificate},
|
|
}}, mustParseCertificate(t, testSignedCertificate), []*x509.Certificate{mustParseCertificate(t, testIntermediateCertificate)}, false},
|
|
{"fail PemCertificate", args{&pb.Certificate{
|
|
Name: testCertificateName,
|
|
PemCertificate: "foobar",
|
|
PemCertificateChain: []string{testIntermediateCertificate, testRootCertificate},
|
|
}}, nil, nil, true},
|
|
{"fail PemCertificateChain", args{&pb.Certificate{
|
|
Name: testCertificateName,
|
|
PemCertificate: testSignedCertificate,
|
|
PemCertificateChain: []string{"foobar", testRootCertificate},
|
|
}}, nil, nil, true},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, got1, err := getCertificateAndChain(tt.args.certpb)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("getCertificateAndChain() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
t.Errorf("getCertificateAndChain() got = %v, want %v", got, tt.want)
|
|
}
|
|
if !reflect.DeepEqual(got1, tt.want1) {
|
|
t.Errorf("getCertificateAndChain() got1 = %v, want %v", got1, tt.want1)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCloudCAS(t *testing.T) {
|
|
cas, err := New(context.Background(), apiv1.Options{
|
|
Type: "cloudCAS",
|
|
CertificateAuthority: "projects/smallstep-cas-test/locations/us-west1",
|
|
CredentialsFile: "/Users/mariano/smallstep-cas-test-8a068f3e4540.json",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// resp, err := cas.CreateCertificateAuthority(&apiv1.CreateCertificateAuthorityRequest{
|
|
// Type: apiv1.RootCA,
|
|
// Template: &x509.Certificate{
|
|
// Subject: pkix.Name{
|
|
// CommonName: "Test Mariano Root CA",
|
|
// },
|
|
// BasicConstraintsValid: true,
|
|
// IsCA: true,
|
|
// MaxPathLen: 1,
|
|
// MaxPathLenZero: false,
|
|
// KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
|
// },
|
|
// Lifetime: time.Duration(30 * 24 * time.Hour),
|
|
// Project: "smallstep-cas-test",
|
|
// Location: "us-west1",
|
|
// })
|
|
// if err != nil {
|
|
// t.Fatal(err)
|
|
// }
|
|
// debug(resp)
|
|
resp := &apiv1.CreateCertificateAuthorityResponse{
|
|
Name: "projects/smallstep-cas-test/locations/us-west1/certificateAuthorities/9a593da4-61af-4426-a2f8-0650373b9c8e",
|
|
}
|
|
|
|
resp, err = cas.CreateCertificateAuthority(&apiv1.CreateCertificateAuthorityRequest{
|
|
Type: apiv1.IntermediateCA,
|
|
Template: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
Country: []string{"US"},
|
|
CommonName: "Test Mariano Intermediate CA",
|
|
},
|
|
BasicConstraintsValid: true,
|
|
IsCA: true,
|
|
MaxPathLen: 0,
|
|
MaxPathLenZero: true,
|
|
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
|
},
|
|
Lifetime: time.Duration(24 * time.Hour),
|
|
Parent: resp,
|
|
Project: "smallstep-cas-test",
|
|
Location: "us-west1",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// debug(resp)
|
|
t.Error("foo")
|
|
}
|