Add tests for stepcas.

This commit is contained in:
Mariano Cano 2021-03-18 18:01:38 -07:00
parent bcf70206ac
commit dbb48ecf8d
3 changed files with 890 additions and 4 deletions

668
cas/stepcas/stepcas_test.go Normal file
View file

@ -0,0 +1,668 @@
package stepcas
import (
"bytes"
"context"
"crypto"
"crypto/ed25519"
"crypto/rand"
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"reflect"
"testing"
"time"
"github.com/smallstep/certificates/api"
"github.com/smallstep/certificates/ca"
"github.com/smallstep/certificates/cas/apiv1"
"go.step.sm/crypto/x509util"
)
var (
testRootCrt *x509.Certificate
testRootKey crypto.Signer
testRootPath, testRootKeyPath string
testRootFingerprint string
testIssCrt *x509.Certificate
testIssKey crypto.Signer
testIssPath, testIssKeyPath string
testX5CCrt *x509.Certificate
testX5CKey crypto.Signer
testX5CPath, testX5CKeyPath string
testCR *x509.CertificateRequest
testCrt *x509.Certificate
testKey crypto.Signer
testFailCR *x509.CertificateRequest
)
func mustSignCertificate(subject string, sans []string, template string, parent *x509.Certificate, signer crypto.Signer) (*x509.Certificate, crypto.Signer) {
pub, priv, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
panic(err)
}
cr, err := x509util.CreateCertificateRequest(subject, sans, priv)
if err != nil {
panic(err)
}
cert, err := x509util.NewCertificate(cr, x509util.WithTemplate(template, x509util.CreateTemplateData(subject, sans)))
if err != nil {
panic(err)
}
crt := cert.GetCertificate()
crt.NotBefore = time.Now()
crt.NotAfter = crt.NotBefore.Add(time.Hour)
if parent == nil {
parent = crt
}
if signer == nil {
signer = priv
}
if crt, err = x509util.CreateCertificate(crt, parent, pub, signer); err != nil {
panic(err)
}
return crt, priv
}
func mustSerializeCrt(filename string, certs ...*x509.Certificate) {
buf := new(bytes.Buffer)
for _, c := range certs {
if err := pem.Encode(buf, &pem.Block{
Type: "CERTIFICATE",
Bytes: c.Raw,
}); err != nil {
panic(err)
}
}
if err := ioutil.WriteFile(filename, buf.Bytes(), 0600); err != nil {
panic(err)
}
}
func mustSerializeKey(filename string, key crypto.Signer) {
b, err := x509.MarshalPKCS8PrivateKey(key)
if err != nil {
panic(err)
}
b = pem.EncodeToMemory(&pem.Block{
Type: "PRIVATE KEY",
Bytes: b,
})
if err := ioutil.WriteFile(filename, b, 0600); err != nil {
panic(err)
}
}
func testCAHelper(t *testing.T) (*url.URL, *ca.Client) {
t.Helper()
writeJSON := func(w http.ResponseWriter, v interface{}) {
_ = json.NewEncoder(w).Encode(v)
}
parseJSON := func(r *http.Request, v interface{}) {
_ = json.NewDecoder(r.Body).Decode(v)
}
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case r.RequestURI == "/root/"+testRootFingerprint:
w.WriteHeader(http.StatusOK)
writeJSON(w, api.RootResponse{
RootPEM: api.NewCertificate(testRootCrt),
})
case r.RequestURI == "/sign":
var msg api.SignRequest
parseJSON(r, &msg)
if msg.CsrPEM.DNSNames[0] == "fail.doe.org" {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, `{"error":"fail","message":"fail"}`)
return
}
w.WriteHeader(http.StatusOK)
writeJSON(w, api.SignResponse{
CertChainPEM: []api.Certificate{api.NewCertificate(testCrt), api.NewCertificate(testIssCrt)},
})
case r.RequestURI == "/revoke":
var msg api.RevokeRequest
parseJSON(r, &msg)
if msg.Serial == "fail" {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, `{"error":"fail","message":"fail"}`)
return
}
w.WriteHeader(http.StatusOK)
writeJSON(w, api.RevokeResponse{
Status: "ok",
})
default:
w.WriteHeader(http.StatusNotFound)
fmt.Fprintf(w, `{"error":"not found"}`)
}
}))
t.Cleanup(func() {
srv.Close()
})
u, err := url.Parse(srv.URL)
if err != nil {
srv.Close()
t.Fatal(err)
}
client, err := ca.NewClient(srv.URL, ca.WithTransport(http.DefaultTransport))
if err != nil {
srv.Close()
t.Fatal(err)
}
return u, client
}
func TestMain(m *testing.M) {
testRootCrt, testRootKey = mustSignCertificate("Test Root Certificate", nil, x509util.DefaultRootTemplate, nil, nil)
testIssCrt, testIssKey = mustSignCertificate("Test Intermediate Certificate", nil, x509util.DefaultIntermediateTemplate, testRootCrt, testRootKey)
testX5CCrt, testX5CKey = mustSignCertificate("Test X5C Certificate", nil, x509util.DefaultLeafTemplate, testIssCrt, testIssKey)
// Final certificate.
var err error
testCrt, testKey = mustSignCertificate("Test Certificate", []string{"doe.org"}, x509util.DefaultLeafTemplate, testIssCrt, testIssKey)
testCR, err = x509util.CreateCertificateRequest("Test Certificate", []string{"doe.org"}, testKey)
if err != nil {
panic(err)
}
// CR used in errors.
testFailCR, err = x509util.CreateCertificateRequest("Test Certificate", []string{"fail.doe.org"}, testKey)
if err != nil {
panic(err)
}
testRootFingerprint = x509util.Fingerprint(testRootCrt)
path, err := os.MkdirTemp(os.TempDir(), "stepcas")
if err != nil {
panic(err)
}
testRootPath = filepath.Join(path, "root_ca.crt")
testRootKeyPath = filepath.Join(path, "root_ca.key")
mustSerializeCrt(testRootPath, testRootCrt)
mustSerializeKey(testRootKeyPath, testRootKey)
testIssPath = filepath.Join(path, "intermediate_ca.crt")
testIssKeyPath = filepath.Join(path, "intermediate_ca.key")
mustSerializeCrt(testIssPath, testIssCrt)
mustSerializeKey(testIssKeyPath, testIssKey)
testX5CPath = filepath.Join(path, "x5c.crt")
testX5CKeyPath = filepath.Join(path, "x5c.key")
mustSerializeCrt(testX5CPath, testX5CCrt, testIssCrt)
mustSerializeKey(testX5CKeyPath, testX5CKey)
code := m.Run()
if err := os.RemoveAll(path); err != nil {
panic(err)
}
os.Exit(code)
}
func Test_init(t *testing.T) {
caURL, _ := testCAHelper(t)
fn, ok := apiv1.LoadCertificateAuthorityServiceNewFunc(apiv1.StepCAS)
if !ok {
t.Errorf("apiv1.Register() ok = %v, want true", ok)
return
}
fn(context.Background(), apiv1.Options{
CertificateAuthority: caURL.String(),
CertificateAuthorityFingerprint: testRootFingerprint,
CertificateIssuer: &apiv1.CertificateIssuer{
Type: "x5c",
Provisioner: "X5C",
Certificate: testX5CPath,
Key: testX5CKeyPath,
},
})
}
func TestNew(t *testing.T) {
caURL, client := testCAHelper(t)
type args struct {
ctx context.Context
opts apiv1.Options
}
tests := []struct {
name string
args args
want *StepCAS
wantErr bool
}{
{"ok", args{context.TODO(), apiv1.Options{
CertificateAuthority: caURL.String(),
CertificateAuthorityFingerprint: testRootFingerprint,
CertificateIssuer: &apiv1.CertificateIssuer{
Type: "x5c",
Provisioner: "X5C",
Certificate: testX5CPath,
Key: testX5CKeyPath,
},
}}, &StepCAS{
x5c: &x5cIssuer{
caURL: caURL,
certFile: testX5CPath,
keyFile: testX5CKeyPath,
issuer: "X5C",
},
client: client,
fingerprint: testRootFingerprint,
}, false},
{"fail authority", args{context.TODO(), apiv1.Options{
CertificateAuthority: "",
CertificateAuthorityFingerprint: testRootFingerprint,
CertificateIssuer: &apiv1.CertificateIssuer{
Type: "x5c",
Provisioner: "X5C",
Certificate: testX5CPath,
Key: testX5CKeyPath,
},
}}, nil, true},
{"fail fingerprint", args{context.TODO(), apiv1.Options{
CertificateAuthority: caURL.String(),
CertificateAuthorityFingerprint: "",
CertificateIssuer: &apiv1.CertificateIssuer{
Type: "x5c",
Provisioner: "X5C",
Certificate: testX5CPath,
Key: testX5CKeyPath,
},
}}, nil, true},
{"fail type", args{context.TODO(), apiv1.Options{
CertificateAuthority: caURL.String(),
CertificateAuthorityFingerprint: testRootFingerprint,
CertificateIssuer: &apiv1.CertificateIssuer{
Type: "",
Provisioner: "X5C",
Certificate: testX5CPath,
Key: testX5CKeyPath,
},
}}, nil, true},
{"fail provisioner", args{context.TODO(), apiv1.Options{
CertificateAuthority: caURL.String(),
CertificateAuthorityFingerprint: testRootFingerprint,
CertificateIssuer: &apiv1.CertificateIssuer{
Type: "x5c",
Provisioner: "",
Certificate: testX5CPath,
Key: testX5CKeyPath,
},
}}, nil, true},
{"fail certificate", args{context.TODO(), apiv1.Options{
CertificateAuthority: caURL.String(),
CertificateAuthorityFingerprint: testRootFingerprint,
CertificateIssuer: &apiv1.CertificateIssuer{
Type: "x5c",
Provisioner: "X5C",
Certificate: "",
Key: testX5CKeyPath,
},
}}, nil, true},
{"fail key", args{context.TODO(), apiv1.Options{
CertificateAuthority: caURL.String(),
CertificateAuthorityFingerprint: testRootFingerprint,
CertificateIssuer: &apiv1.CertificateIssuer{
Type: "x5c",
Provisioner: "X5C",
Certificate: testX5CPath,
Key: "",
},
}}, nil, true},
{"bad authority", args{context.TODO(), apiv1.Options{
CertificateAuthority: "https://foobar",
CertificateAuthorityFingerprint: testRootFingerprint,
CertificateIssuer: &apiv1.CertificateIssuer{
Type: "x5c",
Provisioner: "X5C",
Certificate: testX5CPath,
Key: testX5CKeyPath,
},
}}, nil, true},
{"fail parse url", args{context.TODO(), apiv1.Options{
CertificateAuthority: "::failparse",
CertificateAuthorityFingerprint: testRootFingerprint,
CertificateIssuer: &apiv1.CertificateIssuer{
Type: "x5c",
Provisioner: "X5C",
Certificate: testX5CPath,
Key: testX5CKeyPath,
},
}}, nil, true},
{"fail new client", args{context.TODO(), apiv1.Options{
CertificateAuthority: caURL.String(),
CertificateAuthorityFingerprint: "foobar",
CertificateIssuer: &apiv1.CertificateIssuer{
Type: "x5c",
Provisioner: "X5C",
Certificate: testX5CPath,
Key: testX5CKeyPath,
},
}}, nil, true},
{"fail new x5c issuer", args{context.TODO(), apiv1.Options{
CertificateAuthority: caURL.String(),
CertificateAuthorityFingerprint: testRootFingerprint,
CertificateIssuer: &apiv1.CertificateIssuer{
Type: "x5c",
Provisioner: "X5C",
Certificate: testX5CPath + ".missing",
Key: testX5CKeyPath,
},
}}, nil, true},
{"bad issuer", args{context.TODO(), apiv1.Options{
CertificateAuthority: caURL.String(),
CertificateAuthorityFingerprint: testRootFingerprint,
CertificateIssuer: nil}}, nil, true},
{"bad issuer type", args{context.TODO(), apiv1.Options{
CertificateAuthority: caURL.String(),
CertificateAuthorityFingerprint: testRootFingerprint,
CertificateIssuer: &apiv1.CertificateIssuer{
Type: "fail",
Provisioner: "X5C",
Certificate: testX5CPath,
Key: testX5CKeyPath,
},
}}, 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
}
// We cannot compare client
if got != nil && tt.want != nil {
got.client = tt.want.client
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("New() = %v, want %v", got, tt.want)
}
})
}
}
func TestStepCAS_CreateCertificate(t *testing.T) {
caURL, client := testCAHelper(t)
x5c, err := newX5CIssuer(caURL, &apiv1.CertificateIssuer{
Type: "x5c",
Provisioner: "X5C",
Certificate: testX5CPath,
Key: testX5CKeyPath,
})
if err != nil {
t.Fatal(err)
}
type fields struct {
x5c *x5cIssuer
client *ca.Client
fingerprint string
}
type args struct {
req *apiv1.CreateCertificateRequest
}
tests := []struct {
name string
fields fields
args args
want *apiv1.CreateCertificateResponse
wantErr bool
}{
{"ok", fields{x5c, client, testRootFingerprint}, args{&apiv1.CreateCertificateRequest{
CSR: testCR,
Lifetime: time.Hour,
}}, &apiv1.CreateCertificateResponse{
Certificate: testCrt,
CertificateChain: []*x509.Certificate{testIssCrt},
}, false},
{"fail CSR", fields{x5c, client, testRootFingerprint}, args{&apiv1.CreateCertificateRequest{
CSR: nil,
Lifetime: time.Hour,
}}, nil, true},
{"fail lifetime", fields{x5c, client, testRootFingerprint}, args{&apiv1.CreateCertificateRequest{
CSR: testCR,
Lifetime: 0,
}}, nil, true},
{"fail sign token", fields{nil, client, testRootFingerprint}, args{&apiv1.CreateCertificateRequest{
CSR: testCR,
Lifetime: time.Hour,
}}, nil, true},
{"fail client sign", fields{x5c, client, testRootFingerprint}, args{&apiv1.CreateCertificateRequest{
CSR: testFailCR,
Lifetime: time.Hour,
}}, nil, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := &StepCAS{
x5c: tt.fields.x5c,
client: tt.fields.client,
fingerprint: tt.fields.fingerprint,
}
got, err := s.CreateCertificate(tt.args.req)
if (err != nil) != tt.wantErr {
t.Errorf("StepCAS.CreateCertificate() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("StepCAS.CreateCertificate() = %v, want %v", got, tt.want)
}
})
}
}
func TestStepCAS_RenewCertificate(t *testing.T) {
caURL, client := testCAHelper(t)
x5c, err := newX5CIssuer(caURL, &apiv1.CertificateIssuer{
Type: "x5c",
Provisioner: "X5C",
Certificate: testX5CPath,
Key: testX5CKeyPath,
})
if err != nil {
t.Fatal(err)
}
type fields struct {
x5c *x5cIssuer
client *ca.Client
fingerprint string
}
type args struct {
req *apiv1.RenewCertificateRequest
}
tests := []struct {
name string
fields fields
args args
want *apiv1.RenewCertificateResponse
wantErr bool
}{
{"ok", fields{x5c, client, testRootFingerprint}, args{&apiv1.RenewCertificateRequest{
CSR: testCR,
Lifetime: time.Hour,
}}, &apiv1.RenewCertificateResponse{
Certificate: testCrt,
CertificateChain: []*x509.Certificate{testIssCrt},
}, false},
{"fail CSR", fields{x5c, client, testRootFingerprint}, args{&apiv1.RenewCertificateRequest{
CSR: nil,
Lifetime: time.Hour,
}}, nil, true},
{"fail lifetime", fields{x5c, client, testRootFingerprint}, args{&apiv1.RenewCertificateRequest{
CSR: testCR,
Lifetime: 0,
}}, nil, true},
{"fail sign token", fields{nil, client, testRootFingerprint}, args{&apiv1.RenewCertificateRequest{
CSR: testCR,
Lifetime: time.Hour,
}}, nil, true},
{"fail client sign", fields{x5c, client, testRootFingerprint}, args{&apiv1.RenewCertificateRequest{
CSR: testFailCR,
Lifetime: time.Hour,
}}, nil, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := &StepCAS{
x5c: tt.fields.x5c,
client: tt.fields.client,
fingerprint: tt.fields.fingerprint,
}
got, err := s.RenewCertificate(tt.args.req)
if (err != nil) != tt.wantErr {
t.Errorf("StepCAS.RenewCertificate() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("StepCAS.RenewCertificate() = %v, want %v", got, tt.want)
}
})
}
}
func TestStepCAS_RevokeCertificate(t *testing.T) {
caURL, client := testCAHelper(t)
x5c, err := newX5CIssuer(caURL, &apiv1.CertificateIssuer{
Type: "x5c",
Provisioner: "X5C",
Certificate: testX5CPath,
Key: testX5CKeyPath,
})
if err != nil {
t.Fatal(err)
}
type fields struct {
x5c *x5cIssuer
client *ca.Client
fingerprint string
}
type args struct {
req *apiv1.RevokeCertificateRequest
}
tests := []struct {
name string
fields fields
args args
want *apiv1.RevokeCertificateResponse
wantErr bool
}{
{"ok serial number", fields{x5c, client, testRootFingerprint}, args{&apiv1.RevokeCertificateRequest{
SerialNumber: "ok",
Certificate: nil,
}}, &apiv1.RevokeCertificateResponse{}, false},
{"ok certificate", fields{x5c, client, testRootFingerprint}, args{&apiv1.RevokeCertificateRequest{
SerialNumber: "",
Certificate: testCrt,
}}, &apiv1.RevokeCertificateResponse{
Certificate: testCrt,
}, false},
{"ok both", fields{x5c, client, testRootFingerprint}, args{&apiv1.RevokeCertificateRequest{
SerialNumber: "ok",
Certificate: testCrt,
}}, &apiv1.RevokeCertificateResponse{
Certificate: testCrt,
}, false},
{"fail request", fields{x5c, client, testRootFingerprint}, args{&apiv1.RevokeCertificateRequest{
SerialNumber: "",
Certificate: nil,
}}, nil, true},
{"fail revoke token", fields{nil, client, testRootFingerprint}, args{&apiv1.RevokeCertificateRequest{
SerialNumber: "ok",
}}, nil, true},
{"fail client revoke", fields{x5c, client, testRootFingerprint}, args{&apiv1.RevokeCertificateRequest{
SerialNumber: "fail",
}}, nil, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := &StepCAS{
x5c: tt.fields.x5c,
client: tt.fields.client,
fingerprint: tt.fields.fingerprint,
}
got, err := s.RevokeCertificate(tt.args.req)
if (err != nil) != tt.wantErr {
t.Errorf("StepCAS.RevokeCertificate() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("StepCAS.RevokeCertificate() = %v, want %v", got, tt.want)
}
})
}
}
func TestStepCAS_GetCertificateAuthority(t *testing.T) {
caURL, client := testCAHelper(t)
x5c, err := newX5CIssuer(caURL, &apiv1.CertificateIssuer{
Type: "x5c",
Provisioner: "X5C",
Certificate: testX5CPath,
Key: testX5CKeyPath,
})
if err != nil {
t.Fatal(err)
}
type fields struct {
x5c *x5cIssuer
client *ca.Client
fingerprint string
}
type args struct {
req *apiv1.GetCertificateAuthorityRequest
}
tests := []struct {
name string
fields fields
args args
want *apiv1.GetCertificateAuthorityResponse
wantErr bool
}{
{"ok", fields{x5c, client, testRootFingerprint}, args{&apiv1.GetCertificateAuthorityRequest{
Name: caURL.String(),
}}, &apiv1.GetCertificateAuthorityResponse{
RootCertificate: testRootCrt,
}, false},
{"fail fingerprint", fields{x5c, client, "fail"}, args{&apiv1.GetCertificateAuthorityRequest{
Name: caURL.String(),
}}, nil, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := &StepCAS{
x5c: tt.fields.x5c,
client: tt.fields.client,
fingerprint: tt.fields.fingerprint,
}
got, err := s.GetCertificateAuthority(tt.args.req)
if (err != nil) != tt.wantErr {
t.Errorf("StepCAS.GetCertificateAuthority() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("StepCAS.GetCertificateAuthority() = %v, want %v", got, tt.want)
}
})
}
}

View file

@ -125,8 +125,8 @@ func newX5CSigner(certFile, keyFile string) (jose.Signer, error) {
func newJoseSigner(key crypto.Signer, so *jose.SignerOptions) (jose.Signer, error) { func newJoseSigner(key crypto.Signer, so *jose.SignerOptions) (jose.Signer, error) {
var alg jose.SignatureAlgorithm var alg jose.SignatureAlgorithm
switch k := key.(type) { switch k := key.Public().(type) {
case *ecdsa.PrivateKey: case *ecdsa.PublicKey:
switch k.Curve.Params().Name { switch k.Curve.Params().Name {
case "P-256": case "P-256":
alg = jose.ES256 alg = jose.ES256
@ -137,9 +137,9 @@ func newJoseSigner(key crypto.Signer, so *jose.SignerOptions) (jose.Signer, erro
default: default:
return nil, errors.Errorf("unsupported elliptic curve %s", k.Curve.Params().Name) return nil, errors.Errorf("unsupported elliptic curve %s", k.Curve.Params().Name)
} }
case ed25519.PrivateKey: case ed25519.PublicKey:
alg = jose.EdDSA alg = jose.EdDSA
case *rsa.PrivateKey: case *rsa.PublicKey:
alg = jose.DefaultRSASigAlgorithm alg = jose.DefaultRSASigAlgorithm
default: default:
return nil, errors.Errorf("unsupported key type %T", k) return nil, errors.Errorf("unsupported key type %T", k)

View file

@ -0,0 +1,218 @@
package stepcas
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"io"
"net/url"
"reflect"
"testing"
"go.step.sm/crypto/jose"
)
type noneSigner []byte
func (b noneSigner) Public() crypto.PublicKey {
return []byte(b)
}
func (b noneSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) {
return digest, nil
}
func Test_x5cIssuer_SignToken(t *testing.T) {
caURL, err := url.Parse("https://ca.smallstep.com")
if err != nil {
t.Fatal(err)
}
type fields struct {
caURL *url.URL
certFile string
keyFile string
issuer string
}
type args struct {
subject string
sans []string
}
type claims struct {
Aud []string `json:"aud"`
Sub string `json:"sub"`
Sans []string `json:"sans"`
}
tests := []struct {
name string
fields fields
args args
wantErr bool
}{
{"ok", fields{caURL, testX5CPath, testX5CKeyPath, "X5C"}, args{"doe", []string{"doe.org"}}, false},
{"fail crt", fields{caURL, "", testX5CKeyPath, "X5C"}, args{"doe", []string{"doe.org"}}, true},
{"fail key", fields{caURL, testX5CPath, "", "X5C"}, args{"doe", []string{"doe.org"}}, true},
{"fail no signer", fields{caURL, testIssKeyPath, testIssPath, "X5C"}, args{"doe", []string{"doe.org"}}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
i := &x5cIssuer{
caURL: tt.fields.caURL,
certFile: tt.fields.certFile,
keyFile: tt.fields.keyFile,
issuer: tt.fields.issuer,
}
got, err := i.SignToken(tt.args.subject, tt.args.sans)
if (err != nil) != tt.wantErr {
t.Errorf("x5cIssuer.SignToken() error = %v, wantErr %v", err, tt.wantErr)
}
if !tt.wantErr {
jwt, err := jose.ParseSigned(got)
if err != nil {
t.Errorf("jose.ParseSigned() error = %v", err)
}
var c claims
want := claims{
Aud: []string{tt.fields.caURL.String() + "/1.0/sign#x5c/X5C"},
Sub: tt.args.subject,
Sans: tt.args.sans,
}
if err := jwt.Claims(testX5CKey.Public(), &c); err != nil {
t.Errorf("jwt.Claims() error = %v", err)
}
if !reflect.DeepEqual(c, want) {
t.Errorf("jwt.Claims() claims = %#v, want %#v", c, want)
}
}
})
}
}
func Test_x5cIssuer_RevokeToken(t *testing.T) {
caURL, err := url.Parse("https://ca.smallstep.com")
if err != nil {
t.Fatal(err)
}
type fields struct {
caURL *url.URL
certFile string
keyFile string
issuer string
}
type args struct {
subject string
}
type claims struct {
Aud []string `json:"aud"`
Sub string `json:"sub"`
Sans []string `json:"sans"`
}
tests := []struct {
name string
fields fields
args args
wantErr bool
}{
{"ok", fields{caURL, testX5CPath, testX5CKeyPath, "X5C"}, args{"doe"}, false},
{"fail crt", fields{caURL, "", testX5CKeyPath, "X5C"}, args{"doe"}, true},
{"fail key", fields{caURL, testX5CPath, "", "X5C"}, args{"doe"}, true},
{"fail no signer", fields{caURL, testIssKeyPath, testIssPath, "X5C"}, args{"doe"}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
i := &x5cIssuer{
caURL: tt.fields.caURL,
certFile: tt.fields.certFile,
keyFile: tt.fields.keyFile,
issuer: tt.fields.issuer,
}
got, err := i.RevokeToken(tt.args.subject)
if (err != nil) != tt.wantErr {
t.Errorf("x5cIssuer.RevokeToken() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr {
jwt, err := jose.ParseSigned(got)
if err != nil {
t.Errorf("jose.ParseSigned() error = %v", err)
}
var c claims
want := claims{
Aud: []string{tt.fields.caURL.String() + "/1.0/revoke#x5c/X5C"},
Sub: tt.args.subject,
}
if err := jwt.Claims(testX5CKey.Public(), &c); err != nil {
t.Errorf("jwt.Claims() error = %v", err)
}
if !reflect.DeepEqual(c, want) {
t.Errorf("jwt.Claims() claims = %#v, want %#v", c, want)
}
}
})
}
}
func Test_newJoseSigner(t *testing.T) {
mustSigner := func(args ...interface{}) crypto.Signer {
if err := args[len(args)-1]; err != nil {
t.Fatal(err)
}
for _, a := range args {
if s, ok := a.(crypto.Signer); ok {
return s
}
}
t.Fatal("signer not found")
return nil
}
p224 := mustSigner(ecdsa.GenerateKey(elliptic.P224(), rand.Reader))
p256 := mustSigner(ecdsa.GenerateKey(elliptic.P256(), rand.Reader))
p384 := mustSigner(ecdsa.GenerateKey(elliptic.P384(), rand.Reader))
p521 := mustSigner(ecdsa.GenerateKey(elliptic.P521(), rand.Reader))
edKey := mustSigner(ed25519.GenerateKey(rand.Reader))
rsaKey := mustSigner(rsa.GenerateKey(rand.Reader, 2048))
type args struct {
key crypto.Signer
so *jose.SignerOptions
}
tests := []struct {
name string
args args
want []jose.Header
wantErr bool
}{
{"p256", args{p256, nil}, []jose.Header{{Algorithm: "ES256"}}, false},
{"p384", args{p384, new(jose.SignerOptions).WithType("JWT")}, []jose.Header{{Algorithm: "ES384", ExtraHeaders: map[jose.HeaderKey]interface{}{"typ": "JWT"}}}, false},
{"p521", args{p521, new(jose.SignerOptions).WithHeader("kid", "the-kid")}, []jose.Header{{Algorithm: "ES512", KeyID: "the-kid"}}, false},
{"ed25519", args{edKey, nil}, []jose.Header{{Algorithm: "EdDSA"}}, false},
{"rsa", args{rsaKey, nil}, []jose.Header{{Algorithm: "RS256"}}, false},
{"fail p224", args{p224, nil}, nil, true},
{"fail signer", args{noneSigner{1, 2, 3}, nil}, nil, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := newJoseSigner(tt.args.key, tt.args.so)
if (err != nil) != tt.wantErr {
t.Errorf("newJoseSigner() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr {
jws, err := got.Sign([]byte("{}"))
if err != nil {
t.Errorf("jose.Signer.Sign() err = %v", err)
}
jwt, err := jose.ParseSigned(jws.FullSerialize())
if err != nil {
t.Errorf("jose.ParseSigned() err = %v", err)
}
if !reflect.DeepEqual(jwt.Headers, tt.want) {
t.Errorf("jose.Header got = %v, want = %v", jwt.Headers, tt.want)
}
}
})
}
}