Add most of cloudcas unit tests and minor fixes.
This commit is contained in:
parent
8eff4e77a8
commit
01e6495f43
5 changed files with 902 additions and 23 deletions
56
cas/apiv1/extension_test.go
Normal file
56
cas/apiv1/extension_test.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
package apiv1
|
||||
|
||||
import (
|
||||
"crypto/x509/pkix"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCreateCertificateAuthorityExtension(t *testing.T) {
|
||||
type args struct {
|
||||
typ Type
|
||||
certificateID string
|
||||
keyValuePairs []string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want pkix.Extension
|
||||
wantErr bool
|
||||
}{
|
||||
{"ok", args{Type(CloudCAS), "1ac75689-cd3f-482e-a695-8a13daf39dc4", nil}, pkix.Extension{
|
||||
Id: oidStepCertificateAuthority,
|
||||
Critical: false,
|
||||
Value: []byte{
|
||||
0x30, 0x30, 0x13, 0x08, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x63, 0x61, 0x73, 0x13, 0x24, 0x31, 0x61,
|
||||
0x63, 0x37, 0x35, 0x36, 0x38, 0x39, 0x2d, 0x63, 0x64, 0x33, 0x66, 0x2d, 0x34, 0x38, 0x32, 0x65,
|
||||
0x2d, 0x61, 0x36, 0x39, 0x35, 0x2d, 0x38, 0x61, 0x31, 0x33, 0x64, 0x61, 0x66, 0x33, 0x39, 0x64,
|
||||
0x63, 0x34,
|
||||
},
|
||||
}, false},
|
||||
{"ok", args{Type(CloudCAS), "1ac75689-cd3f-482e-a695-8a13daf39dc4", []string{"foo", "bar"}}, pkix.Extension{
|
||||
Id: oidStepCertificateAuthority,
|
||||
Critical: false,
|
||||
Value: []byte{
|
||||
0x30, 0x3c, 0x13, 0x08, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x63, 0x61, 0x73, 0x13, 0x24, 0x31, 0x61,
|
||||
0x63, 0x37, 0x35, 0x36, 0x38, 0x39, 0x2d, 0x63, 0x64, 0x33, 0x66, 0x2d, 0x34, 0x38, 0x32, 0x65,
|
||||
0x2d, 0x61, 0x36, 0x39, 0x35, 0x2d, 0x38, 0x61, 0x31, 0x33, 0x64, 0x61, 0x66, 0x33, 0x39, 0x64,
|
||||
0x63, 0x34, 0x30, 0x0a, 0x13, 0x03, 0x66, 0x6f, 0x6f, 0x13, 0x03, 0x62, 0x61, 0x72,
|
||||
},
|
||||
}, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := CreateCertificateAuthorityExtension(tt.args.typ, tt.args.certificateID, tt.args.keyValuePairs...)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("CreateCertificateAuthorityExtension() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("CreateCertificateAuthorityExtension() = %v, want %v", got, tt.want)
|
||||
fmt.Printf("%x\n", got.Value)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -70,7 +70,7 @@ func createPublicKey(key crypto.PublicKey) (*pb.PublicKey, error) {
|
|||
return nil, errors.Wrap(err, "error marshaling public key")
|
||||
}
|
||||
return &pb.PublicKey{
|
||||
Type: pb.PublicKey_PEM_RSA_KEY,
|
||||
Type: pb.PublicKey_PEM_EC_KEY,
|
||||
Key: pem.EncodeToMemory(&pem.Block{
|
||||
Type: "PUBLIC KEY",
|
||||
Bytes: asn1Bytes,
|
||||
|
@ -215,9 +215,9 @@ func createReusableConfig(cert *x509.Certificate) *pb.ReusableConfigWrapper {
|
|||
unknownEKUs = append(unknownEKUs, createObjectID(oid))
|
||||
}
|
||||
|
||||
policyIDs := make([]*pb.ObjectId, len(cert.PolicyIdentifiers))
|
||||
for i, oid := range cert.PolicyIdentifiers {
|
||||
policyIDs[i] = createObjectID(oid)
|
||||
var policyIDs []*pb.ObjectId
|
||||
for _, oid := range cert.PolicyIdentifiers {
|
||||
policyIDs = append(policyIDs, createObjectID(oid))
|
||||
}
|
||||
|
||||
var caOptions *pb.ReusableConfigValues_CaOptions
|
||||
|
|
213
cas/cloudcas/certificate_test.go
Normal file
213
cas/cloudcas/certificate_test.go
Normal file
|
@ -0,0 +1,213 @@
|
|||
package cloudcas
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
pb "google.golang.org/genproto/googleapis/cloud/security/privateca/v1beta1"
|
||||
)
|
||||
|
||||
var (
|
||||
testLeafPrivateKey = `-----BEGIN PUBLIC KEY-----
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAdUSRBrpgHFilN4eaGlNnX2+xfjX
|
||||
a1Iwk2/+AensjFTXJi1UAIB0e+4pqi7Sen5E2QVBhntEHCrA3xOf7czgPw==
|
||||
-----END PUBLIC KEY-----
|
||||
`
|
||||
testRSACertificate = `-----BEGIN CERTIFICATE-----
|
||||
MIICozCCAkmgAwIBAgIRANNhMpODj7ThgviZCoF6kj8wCgYIKoZIzj0EAwIwKjEo
|
||||
MCYGA1UEAxMfR29vZ2xlIENBUyBUZXN0IEludGVybWVkaWF0ZSBDQTAeFw0yMDA5
|
||||
MTUwMTUxMDdaFw0zMDA5MTMwMTUxMDNaMB0xGzAZBgNVBAMTEnRlc3Quc21hbGxz
|
||||
dGVwLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANPRjuIlsP5Z
|
||||
672syAsHlbILFabG/xmrlsO0UdcLo4Yjf9WPAFA+7q+CsVDFh4dQbMv96fsHtdYP
|
||||
E9wlWyMqYG+5E8QT2i0WNFEoYcXOGZuXdyD/TA5Aucu1RuYLrZXQrXWDnvaWOgvr
|
||||
EZ6s9VsPCzzkL8KBejIMQIMY0KXEJfB/HgXZNn8V2trZkWT5CzxbcOF3s3UC1Z6F
|
||||
Ja6zjpxhSyRkqgknJxv6yK4t7HEwdhrDI8uyxJYHPQWKNRjWecHWE9E+MtoS7D08
|
||||
mTh8qlAKoBbkGolR2nJSXffU09F3vSg+MIfjPiRqjf6394cQ3T9D5yZK//rCrxWU
|
||||
8KKBQMEmdKcCAwEAAaOBkTCBjjAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYI
|
||||
KwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBQffuoYvH1+IF1cipl35gXJxSJE
|
||||
SjAfBgNVHSMEGDAWgBRIOVqyLDSlErJLuWWEvRm5UU1r1TAdBgNVHREEFjAUghJ0
|
||||
ZXN0LnNtYWxsc3RlcC5jb20wCgYIKoZIzj0EAwIDSAAwRQIhAL9AAw/LVLvvxBkM
|
||||
sJnHd+RIk7ZblkgcArwpIS2+Z5xNAiBtUED4zyimz9b4aQiXdw4IMd2CKxVyW8eE
|
||||
6x1vSZMvzQ==
|
||||
-----END CERTIFICATE-----`
|
||||
testRSAPublicKey = `-----BEGIN RSA PUBLIC KEY-----
|
||||
MIIBCgKCAQEA09GO4iWw/lnrvazICweVsgsVpsb/GauWw7RR1wujhiN/1Y8AUD7u
|
||||
r4KxUMWHh1Bsy/3p+we11g8T3CVbIypgb7kTxBPaLRY0UShhxc4Zm5d3IP9MDkC5
|
||||
y7VG5gutldCtdYOe9pY6C+sRnqz1Ww8LPOQvwoF6MgxAgxjQpcQl8H8eBdk2fxXa
|
||||
2tmRZPkLPFtw4XezdQLVnoUlrrOOnGFLJGSqCScnG/rIri3scTB2GsMjy7LElgc9
|
||||
BYo1GNZ5wdYT0T4y2hLsPTyZOHyqUAqgFuQaiVHaclJd99TT0Xe9KD4wh+M+JGqN
|
||||
/rf3hxDdP0PnJkr/+sKvFZTwooFAwSZ0pwIDAQAB
|
||||
-----END RSA PUBLIC KEY-----
|
||||
`
|
||||
)
|
||||
|
||||
func Test_createCertificateConfig(t *testing.T) {
|
||||
cert := mustParseCertificate(t, testLeafCertificate)
|
||||
type args struct {
|
||||
tpl *x509.Certificate
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *pb.Certificate_Config
|
||||
wantErr bool
|
||||
}{
|
||||
{"ok", args{cert}, &pb.Certificate_Config{
|
||||
Config: &pb.CertificateConfig{
|
||||
SubjectConfig: &pb.CertificateConfig_SubjectConfig{
|
||||
Subject: &pb.Subject{},
|
||||
CommonName: "test.smallstep.com",
|
||||
SubjectAltName: &pb.SubjectAltNames{
|
||||
DnsNames: []string{"test.smallstep.com"},
|
||||
},
|
||||
},
|
||||
ReusableConfig: &pb.ReusableConfigWrapper{
|
||||
ConfigValues: &pb.ReusableConfigWrapper_ReusableConfigValues{
|
||||
ReusableConfigValues: &pb.ReusableConfigValues{
|
||||
KeyUsage: &pb.KeyUsage{
|
||||
BaseKeyUsage: &pb.KeyUsage_KeyUsageOptions{
|
||||
DigitalSignature: true,
|
||||
},
|
||||
ExtendedKeyUsage: &pb.KeyUsage_ExtendedKeyUsageOptions{
|
||||
ClientAuth: true,
|
||||
ServerAuth: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
PublicKey: &pb.PublicKey{
|
||||
Type: pb.PublicKey_PEM_EC_KEY,
|
||||
Key: []byte(testLeafPrivateKey),
|
||||
},
|
||||
},
|
||||
}, false},
|
||||
{"fail", args{&x509.Certificate{}}, nil, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := createCertificateConfig(tt.args.tpl)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("createCertificateConfig() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("createCertificateConfig() = %v, want %v", got.Config.ReusableConfig, tt.want.Config.ReusableConfig)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_createPublicKey(t *testing.T) {
|
||||
edpub, _, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ecCert := mustParseCertificate(t, testLeafCertificate)
|
||||
rsaCert := mustParseCertificate(t, testRSACertificate)
|
||||
type args struct {
|
||||
key crypto.PublicKey
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *pb.PublicKey
|
||||
wantErr bool
|
||||
}{
|
||||
{"ok ec", args{ecCert.PublicKey}, &pb.PublicKey{
|
||||
Type: pb.PublicKey_PEM_EC_KEY,
|
||||
Key: []byte(testLeafPrivateKey),
|
||||
}, false},
|
||||
{"ok rsa", args{rsaCert.PublicKey}, &pb.PublicKey{
|
||||
Type: pb.PublicKey_PEM_RSA_KEY,
|
||||
Key: []byte(testRSAPublicKey),
|
||||
}, false},
|
||||
{"fail ed25519", args{edpub}, nil, true},
|
||||
{"fail ec marshal", args{&ecdsa.PublicKey{
|
||||
Curve: &elliptic.CurveParams{Name: "FOO", BitSize: 256},
|
||||
X: ecCert.PublicKey.(*ecdsa.PublicKey).X,
|
||||
Y: ecCert.PublicKey.(*ecdsa.PublicKey).Y,
|
||||
}}, nil, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := createPublicKey(tt.args.key)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("createPublicKey() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("createPublicKey() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_createSubject(t *testing.T) {
|
||||
type args struct {
|
||||
cert *x509.Certificate
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *pb.Subject
|
||||
}{
|
||||
{"ok empty", args{&x509.Certificate{}}, &pb.Subject{}},
|
||||
{"ok all", args{&x509.Certificate{
|
||||
Subject: pkix.Name{
|
||||
Country: []string{"US"},
|
||||
Organization: []string{"Smallstep Labs"},
|
||||
OrganizationalUnit: []string{"Engineering"},
|
||||
Locality: []string{"San Francisco"},
|
||||
Province: []string{"California"},
|
||||
StreetAddress: []string{"1 A St."},
|
||||
PostalCode: []string{"12345"},
|
||||
SerialNumber: "1234567890",
|
||||
CommonName: "test.smallstep.com",
|
||||
},
|
||||
}}, &pb.Subject{
|
||||
CountryCode: "US",
|
||||
Organization: "Smallstep Labs",
|
||||
OrganizationalUnit: "Engineering",
|
||||
Locality: "San Francisco",
|
||||
Province: "California",
|
||||
StreetAddress: "1 A St.",
|
||||
PostalCode: "12345",
|
||||
}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := createSubject(tt.args.cert); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("createSubject() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_createSubjectAlternativeNames(t *testing.T) {
|
||||
type args struct {
|
||||
cert *x509.Certificate
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *pb.SubjectAltNames
|
||||
}{
|
||||
{"ok empty", args{&x509.Certificate{}}, &pb.SubjectAltNames{}},
|
||||
// TODO
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := createSubjectAlternativeNames(tt.args.cert); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("createSubjectAlternativeNames() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -2,15 +2,15 @@ package cloudcas
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
privateca "cloud.google.com/go/security/privateca/apiv1beta1"
|
||||
"github.com/google/uuid"
|
||||
gax "github.com/googleapis/gax-go/v2"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/smallstep/certificates/cas/apiv1"
|
||||
"google.golang.org/api/option"
|
||||
|
@ -24,9 +24,11 @@ func init() {
|
|||
})
|
||||
}
|
||||
|
||||
func debug(v interface{}) {
|
||||
b, _ := json.MarshalIndent(v, "", " ")
|
||||
fmt.Println(string(b))
|
||||
// CertificateAuthorityClient is the interface implemented by the Google CAS
|
||||
// client.
|
||||
type CertificateAuthorityClient interface {
|
||||
CreateCertificate(ctx context.Context, req *pb.CreateCertificateRequest, opts ...gax.CallOption) (*pb.Certificate, error)
|
||||
RevokeCertificate(ctx context.Context, req *pb.RevokeCertificateRequest, opts ...gax.CallOption) (*pb.Certificate, error)
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -34,13 +36,40 @@ var (
|
|||
stepOIDCertificateAuthority = append(asn1.ObjectIdentifier(nil), append(stepOIDRoot, 2)...)
|
||||
)
|
||||
|
||||
// recocationCodeMap maps revocation reason codes from RFC 5280, to Google CAS
|
||||
// revocation reasons. Revocation reason 7 is not used, and revocation reason 8
|
||||
// (removeFromCRL) is not supported by Google CAS.
|
||||
var revocationCodeMap = map[int]pb.RevocationReason{
|
||||
0: pb.RevocationReason_REVOCATION_REASON_UNSPECIFIED,
|
||||
1: pb.RevocationReason_KEY_COMPROMISE,
|
||||
2: pb.RevocationReason_CERTIFICATE_AUTHORITY_COMPROMISE,
|
||||
3: pb.RevocationReason_AFFILIATION_CHANGED,
|
||||
4: pb.RevocationReason_SUPERSEDED,
|
||||
5: pb.RevocationReason_CESSATION_OF_OPERATION,
|
||||
6: pb.RevocationReason_CERTIFICATE_HOLD,
|
||||
9: pb.RevocationReason_PRIVILEGE_WITHDRAWN,
|
||||
10: pb.RevocationReason_ATTRIBUTE_AUTHORITY_COMPROMISE,
|
||||
}
|
||||
|
||||
// CloudCAS implements a Certificate Authority Service using Google Cloud CAS.
|
||||
type CloudCAS struct {
|
||||
client *privateca.CertificateAuthorityClient
|
||||
client CertificateAuthorityClient
|
||||
certificateAuthority string
|
||||
}
|
||||
|
||||
type caClient interface{}
|
||||
// newCertificateAuthorityClient creates the certificate authority client. This
|
||||
// function is used for testing purposes.
|
||||
var newCertificateAuthorityClient = func(ctx context.Context, credentialsFile string) (CertificateAuthorityClient, error) {
|
||||
var cloudOpts []option.ClientOption
|
||||
if credentialsFile != "" {
|
||||
cloudOpts = append(cloudOpts, option.WithCredentialsFile(credentialsFile))
|
||||
}
|
||||
client, err := privateca.NewCertificateAuthorityClient(ctx, cloudOpts...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error creating client")
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// New creates a new CertificateAuthorityService implementation using Google
|
||||
// Cloud CAS.
|
||||
|
@ -49,14 +78,9 @@ func New(ctx context.Context, opts apiv1.Options) (*CloudCAS, error) {
|
|||
return nil, errors.New("cloudCAS 'certificateAuthority' cannot be empty")
|
||||
}
|
||||
|
||||
var cloudOpts []option.ClientOption
|
||||
if opts.CredentialsFile != "" {
|
||||
cloudOpts = append(cloudOpts, option.WithCredentialsFile(opts.CredentialsFile))
|
||||
}
|
||||
|
||||
client, err := privateca.NewCertificateAuthorityClient(ctx, cloudOpts...)
|
||||
client, err := newCertificateAuthorityClient(ctx, opts.CredentialsFile)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error creating client")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &CloudCAS{
|
||||
|
@ -109,7 +133,11 @@ func (c *CloudCAS) RenewCertificate(req *apiv1.RenewCertificateRequest) (*apiv1.
|
|||
|
||||
// RevokeCertificate a certificate using Google Cloud CAS.
|
||||
func (c *CloudCAS) RevokeCertificate(req *apiv1.RevokeCertificateRequest) (*apiv1.RevokeCertificateResponse, error) {
|
||||
if req.Certificate == nil {
|
||||
reason, ok := revocationCodeMap[req.ReasonCode]
|
||||
switch {
|
||||
case !ok:
|
||||
return nil, errors.Errorf("revokeCertificate 'reasonCode=%d' is invalid or not supported", req.ReasonCode)
|
||||
case req.Certificate == nil:
|
||||
return nil, errors.New("revokeCertificateRequest `certificate` cannot be nil")
|
||||
}
|
||||
|
||||
|
@ -119,7 +147,7 @@ func (c *CloudCAS) RevokeCertificate(req *apiv1.RevokeCertificateRequest) (*apiv
|
|||
}
|
||||
|
||||
var cae apiv1.CertificateAuthorityExtension
|
||||
if _, err := asn1.Unmarshal(ext.Value, &ext); err != nil {
|
||||
if _, err := asn1.Unmarshal(ext.Value, &cae); err != nil {
|
||||
return nil, errors.Wrap(err, "error unmarshaling certificate authority extension")
|
||||
}
|
||||
|
||||
|
@ -128,7 +156,8 @@ func (c *CloudCAS) RevokeCertificate(req *apiv1.RevokeCertificateRequest) (*apiv
|
|||
|
||||
certpb, err := c.client.RevokeCertificate(ctx, &pb.RevokeCertificateRequest{
|
||||
Name: c.certificateAuthority + "/certificates/" + cae.CertificateID,
|
||||
Reason: pb.RevocationReason_REVOCATION_REASON_UNSPECIFIED,
|
||||
Reason: reason,
|
||||
RequestId: req.RequestID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cloudCAS RevokeCertificate failed")
|
||||
|
@ -192,7 +221,7 @@ func defaultContext() (context.Context, context.CancelFunc) {
|
|||
}
|
||||
|
||||
func createCertificateID() (string, error) {
|
||||
id, err := uuid.NewRandom()
|
||||
id, err := uuid.NewRandomFromReader(rand.Reader)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "error creating certificate id")
|
||||
}
|
||||
|
|
581
cas/cloudcas/cloudcas_test.go
Normal file
581
cas/cloudcas/cloudcas_test.go
Normal file
|
@ -0,0 +1,581 @@
|
|||
package cloudcas
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"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
|
||||
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},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
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},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
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 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_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
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{"fail default credentials", args{context.Background(), apiv1.Options{Certificateauthority: testAuthorityName}}, true},
|
||||
{"fail certificate authority", args{context.Background(), apiv1.Options{}}, true},
|
||||
{"fail with credentials", args{context.Background(), apiv1.Options{
|
||||
Certificateauthority: testAuthorityName, CredentialsFile: "testdata/missing.json",
|
||||
}}, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, 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_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-calulate 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue