certificates/api/api_test.go
Herman Slatman 2215a05c28
Add tests for ACME EAB Admin
Refactored some of the existing bits for testing the Authority
API by creation of a new LinkedAuthority interface and changing
visibility of the MockAuthority to be usable by other packages.

At this time, not all of the functions of MockAuthority it usable
yet. Will refactor when needed or requested.
2021-12-08 15:19:38 +01:00

1159 lines
38 KiB
Go

package api
import (
"bytes"
"context"
"crypto/dsa" //nolint
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/json"
"encoding/pem"
"fmt"
"io"
"math/big"
"net/http"
"net/http/httptest"
"reflect"
"strings"
"testing"
"time"
"github.com/go-chi/chi"
"github.com/pkg/errors"
"github.com/smallstep/assert"
"github.com/smallstep/certificates/authority"
"github.com/smallstep/certificates/authority/provisioner"
"github.com/smallstep/certificates/errs"
"github.com/smallstep/certificates/logging"
"go.step.sm/crypto/jose"
"golang.org/x/crypto/ssh"
)
const (
rootPEM = `-----BEGIN CERTIFICATE-----
MIIEBDCCAuygAwIBAgIDAjppMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
YWwgQ0EwHhcNMTMwNDA1MTUxNTU1WhcNMTUwNDA0MTUxNTU1WjBJMQswCQYDVQQG
EwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzElMCMGA1UEAxMcR29vZ2xlIEludGVy
bmV0IEF1dGhvcml0eSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AJwqBHdc2FCROgajguDYUEi8iT/xGXAaiEZ+4I/F8YnOIe5a/mENtzJEiaB0C1NP
VaTOgmKV7utZX8bhBYASxF6UP7xbSDj0U/ck5vuR6RXEz/RTDfRK/J9U3n2+oGtv
h8DQUB8oMANA2ghzUWx//zo8pzcGjr1LEQTrfSTe5vn8MXH7lNVg8y5Kr0LSy+rE
ahqyzFPdFUuLH8gZYR/Nnag+YyuENWllhMgZxUYi+FOVvuOAShDGKuy6lyARxzmZ
EASg8GF6lSWMTlJ14rbtCMoU/M4iarNOz0YDl5cDfsCx3nuvRTPPuj5xt970JSXC
DTWJnZ37DhF5iR43xa+OcmkCAwEAAaOB+zCB+DAfBgNVHSMEGDAWgBTAephojYn7
qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUSt0GFhu89mi1dvWBtrtiGrpagS8wEgYD
VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwOgYDVR0fBDMwMTAvoC2g
K4YpaHR0cDovL2NybC5nZW90cnVzdC5jb20vY3Jscy9ndGdsb2JhbC5jcmwwPQYI
KwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwOi8vZ3RnbG9iYWwtb2NzcC5n
ZW90cnVzdC5jb20wFwYDVR0gBBAwDjAMBgorBgEEAdZ5AgUBMA0GCSqGSIb3DQEB
BQUAA4IBAQA21waAESetKhSbOHezI6B1WLuxfoNCunLaHtiONgaX4PCVOzf9G0JY
/iLIa704XtE7JW4S615ndkZAkNoUyHgN7ZVm2o6Gb4ChulYylYbc3GrKBIxbf/a/
zG+FA1jDaFETzf3I93k9mTXwVqO94FntT0QJo544evZG0R0SnU++0ED8Vf4GXjza
HFa9llF7b1cq26KqltyMdMKVvvBulRP/F/A8rLIQjcxz++iPAsbw+zOzlTvjwsto
WHPbqCRiOwY1nQ2pM714A5AuTHhdUDqB1O6gyHA43LL5Z/qHQF1hwFGPa4NrzQU6
yuGnBXj8ytqU0CwIPX4WecigUCAkVDNx
-----END CERTIFICATE-----`
certPEM = `-----BEGIN CERTIFICATE-----
MIIDujCCAqKgAwIBAgIIE31FZVaPXTUwDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE
BhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMTHEdvb2dsZSBJbnRl
cm5ldCBBdXRob3JpdHkgRzIwHhcNMTQwMTI5MTMyNzQzWhcNMTQwNTI5MDAwMDAw
WjBpMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwN
TW91bnRhaW4gVmlldzETMBEGA1UECgwKR29vZ2xlIEluYzEYMBYGA1UEAwwPbWFp
bC5nb29nbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfRrObuSW5T7q
5CnSEqefEmtH4CCv6+5EckuriNr1CjfVvqzwfAhopXkLrq45EQm8vkmf7W96XJhC
7ZM0dYi1/qOCAU8wggFLMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAa
BgNVHREEEzARgg9tYWlsLmdvb2dsZS5jb20wCwYDVR0PBAQDAgeAMGgGCCsGAQUF
BwEBBFwwWjArBggrBgEFBQcwAoYfaHR0cDovL3BraS5nb29nbGUuY29tL0dJQUcy
LmNydDArBggrBgEFBQcwAYYfaHR0cDovL2NsaWVudHMxLmdvb2dsZS5jb20vb2Nz
cDAdBgNVHQ4EFgQUiJxtimAuTfwb+aUtBn5UYKreKvMwDAYDVR0TAQH/BAIwADAf
BgNVHSMEGDAWgBRK3QYWG7z2aLV29YG2u2IaulqBLzAXBgNVHSAEEDAOMAwGCisG
AQQB1nkCBQEwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDovL3BraS5nb29nbGUuY29t
L0dJQUcyLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAH6RYHxHdcGpMpFE3oxDoFnP+
gtuBCHan2yE2GRbJ2Cw8Lw0MmuKqHlf9RSeYfd3BXeKkj1qO6TVKwCh+0HdZk283
TZZyzmEOyclm3UGFYe82P/iDFt+CeQ3NpmBg+GoaVCuWAARJN/KfglbLyyYygcQq
0SgeDh8dRKUiaW3HQSoYvTvdTuqzwK4CXsr3b5/dAOY8uMuG/IAR3FgwTbZ1dtoW
RvOTa8hYiU6A475WuZKyEHcwnGYe57u2I2KbMgcKjPniocj4QzgYsVAVKW3IwaOh
yE+vPxsiUkvQHdO2fojCkY8jg70jxM+gu59tPDNbw3Uh/2Ij310FgTHsnGQMyA==
-----END CERTIFICATE-----`
csrPEM = `-----BEGIN CERTIFICATE REQUEST-----
MIIEYjCCAkoCAQAwHTEbMBkGA1UEAxMSdGVzdC5zbWFsbHN0ZXAuY29tMIICIjAN
BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuCpifZfoZhYNywfpnPa21NezXgtn
wrWBFE6xhVzE7YDSIqtIsj8aR7R8zwEymxfv5j5298LUy/XSmItVH31CsKyfcGqN
QM0PZr9XY3z5V6qchGMqjzt/jqlYMBHujcxIFBfz4HATxSgKyvHqvw14ESsS2huu
7jowx+XTKbFYgKcXrjBkvOej5FXD3ehkg0jDA2UAJNdfKmrc1BBEaaqOtfh7eyU2
HU7+5gxH8C27IiCAmNj719E0B99Nu2MUw6aLFIM4xAcRga33Avevx6UuXZZIEepe
V1sihrkcnDK9Vsxkme5erXzvAoOiRusiC2iIomJHJrdRM5ReEU+N+Tl1Kxq+rk7H
/qAq78wVm07M1/GGi9SUMObZS4WuJpM6whlikIAEbv9iV+CK0sv/Jr/AADdGMmQU
lwk+Q0ZNE8p4ZuWILv/dtLDtDVBpnrrJ9e8duBtB0lGcG8MdaUCQ346EI4T0Sgx0
hJ+wMq8zYYFfPIZEHC8o9p1ywWN9ySpJ8Zj/5ubmx9v2bY67GbuVFEa8iAp+S00x
/Z8nD6/JsoKtexuHyGr3ixWFzlBqXDuugukIDFUOVDCbuGw4Io4/hEMu4Zz0TIFk
Uu/wf2z75Tt8EkosKLu2wieKcY7n7Vhog/0tqexqWlWtJH0tvq4djsGoSvA62WPs
0iXXj+aZIARPNhECAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4ICAQA0vyHIndAkIs/I
Nnz5yZWCokRjokoKv3Aj4VilyjncL+W0UIPULLU/47ZyoHVSUj2t8gknr9xu/Kd+
g/2z0RiF3CIp8IUH49w/HYWaR95glzVNAAzr8qD9UbUqloLVQW3lObSRGtezhdZO
sspw5dC+inhAb1LZhx8PVxB3SAeJ8h11IEBr0s2Hxt9viKKd7YPtIFZkZdOkVx4R
if1DMawj1P6fEomf8z7m+dmbUYTqqosbCbRL01mzEga/kF6JyH/OzpNlcsAiyM8e
BxPWH6TtPqwmyy4y7j1outmM0RnyUw5A0HmIbWh+rHpXiHVsnNqse0XfzmaxM8+z
dxYeDax8aMWZKfvY1Zew+xIxl7DtEy1BpxrZcawumJYt5+LL+bwF/OtL0inQLnw8
zyqydsXNdrpIQJnfmWPld7ThWbQw2FBE70+nFSxHeG2ULnpF3M9xf6ZNAF4gqaNE
Q7vMNPBWrJWu+A++vHY61WGET+h4lY3GFr2I8OE4IiHPQi1D7Y0+fwOmStwuRPM4
2rARcJChNdiYBkkuvs4kixKTTjdXhB8RQtuBSrJ0M1tzq2qMbm7F8G01rOg4KlXU
58jHzJwr1K7cx0lpWfGTtc5bseCGtTKmDBXTziw04yl8eE1+ZFOganixGwCtl4Tt
DCbKzWTW8lqVdp9Kyf7XEhhc2R8C5w==
-----END CERTIFICATE REQUEST-----`
stepCertPEM = `-----BEGIN CERTIFICATE-----
MIIChTCCAiugAwIBAgIRAJ3O5T28Rdj2lr/UPjf+GAUwCgYIKoZIzj0EAwIwJDEi
MCAGA1UEAxMZU21hbGxzdGVwIEludGVybWVkaWF0ZSBDQTAeFw0xOTAyMjAyMDE1
NDNaFw0xOTAyMjEyMDE1NDNaMHExCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEW
MBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEcMBoGA1UEChMTU21hbGxzdGVwIExhYnMg
SW5jLjEfMB0GA1UEAxMWaW50ZXJuYWwuc21hbGxzdGVwLmNvbTBZMBMGByqGSM49
AgEGCCqGSM49AwEHA0IABC0aKrTNl+gXFuNkXisqX4/foLO3VMt+Kphngziim+fz
aJhiS9JU+oFYLTNW6HWGUD8CNzfwrmWlVsAmiJwHKlKjgfAwge0wDgYDVR0PAQH/
BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQU
JheKvlZqNv1IcgaC8WOS1Zg0i1QwHwYDVR0jBBgwFoAUu97PaFQPfuyKOeew7Hg4
5WFIAVMwIQYDVR0RBBowGIIWaW50ZXJuYWwuc21hbGxzdGVwLmNvbTBZBgwrBgEE
AYKkZMYoQAEESTBHAgEBBBVtYXJpYW5vQHNtYWxsc3RlcC5jb20EK2pPMzdkdERi
a3UtUW5hYnM1VlIwWXc2WUZGdjl3ZUExOGRwM2h0dmRFanMwCgYIKoZIzj0EAwID
SAAwRQIhAIrn17fP5CBrGtKuhyPiq6eSwryBCf8ki+k17u5a+E/LAiB24Y2E0Put
nIHOI54lAqDeF7A0y73fPRVCiJEWmuxz0g==
-----END CERTIFICATE-----`
pubKey = `{
"use": "sig",
"kty": "EC",
"kid": "oV1p0MJeGQ7qBlK6B-oyfVdBRjh_e7VSK_YSEEqgW00",
"crv": "P-256",
"alg": "ES256",
"x": "p9QX4tzjxUrB0fgqRWLKUuPolDtBW681f2Qyh-uVNhk",
"y": "CNSEloc4oLDFTX0Vywj0WiqOlh516sFQwCj6WtM8LT8"
}`
privKey = "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjEwMDAwMCwicDJzIjoiNEhBYjE0WDQ5OFM4LWxSb29JTnpqZyJ9.RbkJXGzI3kOsaP20KmZs0ELFLgpRddAE49AJHlEblw-uH_gg6SV3QA.M3MArEpHgI171lhm.gBlFySpzK9F7riBJbtLSNkb4nAw_gWokqs1jS-ZK1qxuqTK-9mtX5yILjRnftx9P9uFp5xt7rvv4Mgom1Ed4V9WtIyfNP_Cz3Pme1Eanp5nY68WCe_yG6iSB1RJdMDBUb2qBDZiBdhJim1DRXsOfgedOrNi7GGbppMlD77DEpId118owR5izA-c6Q_hg08hIE3tnMAnebDNQoF9jfEY99_AReVRH8G4hgwZEPCfXMTb3J-lowKGG4vXIbK5knFLh47SgOqG4M2M51SMS-XJ7oBz1Vjoamc90QIqKV51rvZ5m0N_sPFtxzcfV4E9yYH3XVd4O-CG4ydVKfKVyMtQ.mcKFZqBHp_n7Ytj2jz9rvw"
)
func parseCertificate(data string) *x509.Certificate {
block, _ := pem.Decode([]byte(data))
if block == nil {
panic("failed to parse certificate PEM")
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
panic("failed to parse certificate: " + err.Error())
}
return cert
}
func parseCertificateRequest(data string) *x509.CertificateRequest {
block, _ := pem.Decode([]byte(data))
if block == nil {
panic("failed to parse certificate request PEM")
}
csr, err := x509.ParseCertificateRequest(block.Bytes)
if err != nil {
panic("failed to parse certificate request: " + err.Error())
}
return csr
}
func TestNewCertificate(t *testing.T) {
cert := parseCertificate(rootPEM)
if !reflect.DeepEqual(Certificate{Certificate: cert}, NewCertificate(cert)) {
t.Errorf("NewCertificate failed, got %v, wants %v", NewCertificate(cert), Certificate{Certificate: cert})
}
}
func TestCertificate_MarshalJSON(t *testing.T) {
type fields struct {
Certificate *x509.Certificate
}
tests := []struct {
name string
fields fields
want []byte
wantErr bool
}{
{"nil", fields{Certificate: nil}, []byte("null"), false},
{"empty", fields{Certificate: &x509.Certificate{Raw: nil}}, []byte(`"-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----\n"`), false},
{"root", fields{Certificate: parseCertificate(rootPEM)}, []byte(`"` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `\n"`), false},
{"cert", fields{Certificate: parseCertificate(certPEM)}, []byte(`"` + strings.ReplaceAll(certPEM, "\n", `\n`) + `\n"`), false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := Certificate{
Certificate: tt.fields.Certificate,
}
got, err := c.MarshalJSON()
if (err != nil) != tt.wantErr {
t.Errorf("Certificate.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Certificate.MarshalJSON() = %s, want %s", got, tt.want)
}
})
}
}
func TestCertificate_UnmarshalJSON(t *testing.T) {
tests := []struct {
name string
data []byte
wantCert bool
wantErr bool
}{
{"no data", nil, false, true},
{"incomplete string 1", []byte(`"foobar`), false, true}, {"incomplete string 2", []byte(`foobar"`), false, true},
{"invalid string", []byte(`"foobar"`), false, true},
{"invalid bytes 0", []byte{}, false, true}, {"invalid bytes 1", []byte{1}, false, true},
{"empty csr", []byte(`"-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE----\n"`), false, true},
{"invalid type", []byte(`"` + strings.ReplaceAll(csrPEM, "\n", `\n`) + `"`), false, true},
{"empty string", []byte(`""`), false, false},
{"json null", []byte(`null`), false, false},
{"valid root", []byte(`"` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `"`), true, false},
{"valid cert", []byte(`"` + strings.ReplaceAll(certPEM, "\n", `\n`) + `"`), true, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var c Certificate
if err := c.UnmarshalJSON(tt.data); (err != nil) != tt.wantErr {
t.Errorf("Certificate.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
}
if tt.wantCert && c.Certificate == nil {
t.Error("Certificate.UnmarshalJSON() failed, Certificate is nil")
}
})
}
}
func TestCertificate_UnmarshalJSON_json(t *testing.T) {
tests := []struct {
name string
data string
wantCert bool
wantErr bool
}{
{"invalid type (bool)", `{"crt":true}`, false, true},
{"invalid type (number)", `{"crt":123}`, false, true},
{"invalid type (object)", `{"crt":{}}`, false, true},
{"empty crt (null)", `{"crt":null}`, false, false},
{"empty crt (string)", `{"crt":""}`, false, false},
{"empty crt", `{"crt":"-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE----\n"}`, false, true},
{"valid crt", `{"crt":"` + strings.ReplaceAll(certPEM, "\n", `\n`) + `"}`, true, false},
}
type request struct {
Cert Certificate `json:"crt"`
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var body request
if err := json.Unmarshal([]byte(tt.data), &body); (err != nil) != tt.wantErr {
t.Errorf("json.Unmarshal() error = %v, wantErr %v", err, tt.wantErr)
}
switch tt.wantCert {
case true:
if body.Cert.Certificate == nil {
t.Error("json.Unmarshal() failed, Certificate is nil")
}
case false:
if body.Cert.Certificate != nil {
t.Error("json.Unmarshal() failed, Certificate is not nil")
}
}
})
}
}
func TestNewCertificateRequest(t *testing.T) {
csr := parseCertificateRequest(csrPEM)
if !reflect.DeepEqual(CertificateRequest{CertificateRequest: csr}, NewCertificateRequest(csr)) {
t.Errorf("NewCertificateRequest failed, got %v, wants %v", NewCertificateRequest(csr), CertificateRequest{CertificateRequest: csr})
}
}
func TestCertificateRequest_MarshalJSON(t *testing.T) {
type fields struct {
CertificateRequest *x509.CertificateRequest
}
tests := []struct {
name string
fields fields
want []byte
wantErr bool
}{
{"nil", fields{CertificateRequest: nil}, []byte("null"), false},
{"empty", fields{CertificateRequest: &x509.CertificateRequest{}}, []byte(`"-----BEGIN CERTIFICATE REQUEST-----\n-----END CERTIFICATE REQUEST-----\n"`), false},
{"csr", fields{CertificateRequest: parseCertificateRequest(csrPEM)}, []byte(`"` + strings.ReplaceAll(csrPEM, "\n", `\n`) + `\n"`), false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := CertificateRequest{
CertificateRequest: tt.fields.CertificateRequest,
}
got, err := c.MarshalJSON()
if (err != nil) != tt.wantErr {
t.Errorf("CertificateRequest.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("CertificateRequest.MarshalJSON() = %s, want %s", got, tt.want)
}
})
}
}
func TestCertificateRequest_UnmarshalJSON(t *testing.T) {
tests := []struct {
name string
data []byte
wantCert bool
wantErr bool
}{
{"no data", nil, false, true},
{"incomplete string 1", []byte(`"foobar`), false, true}, {"incomplete string 2", []byte(`foobar"`), false, true},
{"invalid string", []byte(`"foobar"`), false, true},
{"invalid bytes 0", []byte{}, false, true}, {"invalid bytes 1", []byte{1}, false, true},
{"empty csr", []byte(`"-----BEGIN CERTIFICATE REQUEST-----\n-----END CERTIFICATE REQUEST----\n"`), false, true},
{"invalid type", []byte(`"` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `"`), false, true},
{"empty string", []byte(`""`), false, false},
{"json null", []byte(`null`), false, false},
{"valid csr", []byte(`"` + strings.ReplaceAll(csrPEM, "\n", `\n`) + `"`), true, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var c CertificateRequest
if err := c.UnmarshalJSON(tt.data); (err != nil) != tt.wantErr {
t.Errorf("CertificateRequest.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
}
if tt.wantCert && c.CertificateRequest == nil {
t.Error("CertificateRequest.UnmarshalJSON() failed, CertificateRequet is nil")
}
})
}
}
func TestCertificateRequest_UnmarshalJSON_json(t *testing.T) {
tests := []struct {
name string
data string
wantCert bool
wantErr bool
}{
{"invalid type (bool)", `{"csr":true}`, false, true},
{"invalid type (number)", `{"csr":123}`, false, true},
{"invalid type (object)", `{"csr":{}}`, false, true},
{"empty csr (null)", `{"csr":null}`, false, false},
{"empty csr (string)", `{"csr":""}`, false, false},
{"empty csr", `{"csr":"-----BEGIN CERTIFICATE REQUEST-----\n-----END CERTIFICATE REQUEST----\n"}`, false, true},
{"valid csr", `{"csr":"` + strings.ReplaceAll(csrPEM, "\n", `\n`) + `"}`, true, false},
}
type request struct {
CSR CertificateRequest `json:"csr"`
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var body request
if err := json.Unmarshal([]byte(tt.data), &body); (err != nil) != tt.wantErr {
t.Errorf("json.Unmarshal() error = %v, wantErr %v", err, tt.wantErr)
}
switch tt.wantCert {
case true:
if body.CSR.CertificateRequest == nil {
t.Error("json.Unmarshal() failed, CertificateRequest is nil")
}
case false:
if body.CSR.CertificateRequest != nil {
t.Error("json.Unmarshal() failed, CertificateRequest is not nil")
}
}
})
}
}
func TestSignRequest_Validate(t *testing.T) {
csr := parseCertificateRequest(csrPEM)
bad := parseCertificateRequest(csrPEM)
bad.Signature[0]++
type fields struct {
CsrPEM CertificateRequest
OTT string
NotBefore time.Time
NotAfter time.Time
}
tests := []struct {
name string
fields fields
err error
}{
{"missing csr", fields{CertificateRequest{}, "foobarzar", time.Time{}, time.Time{}}, errors.New("missing csr")},
{"invalid csr", fields{CertificateRequest{bad}, "foobarzar", time.Time{}, time.Time{}}, errors.New("invalid csr")},
{"missing ott", fields{CertificateRequest{csr}, "", time.Time{}, time.Time{}}, errors.New("missing ott")},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := &SignRequest{
CsrPEM: tt.fields.CsrPEM,
OTT: tt.fields.OTT,
NotAfter: NewTimeDuration(tt.fields.NotAfter),
NotBefore: NewTimeDuration(tt.fields.NotBefore),
}
if err := s.Validate(); err != nil {
if assert.NotNil(t, tt.err) {
assert.HasPrefix(t, err.Error(), tt.err.Error())
}
} else {
assert.Nil(t, tt.err)
}
})
}
}
type mockProvisioner struct {
ret1, ret2, ret3 interface{}
err error
getID func() string
getIDForToken func() string
getTokenID func(string) (string, error)
getName func() string
getType func() provisioner.Type
getEncryptedKey func() (string, string, bool)
init func(provisioner.Config) error
authorizeRenew func(ctx context.Context, cert *x509.Certificate) error
authorizeRevoke func(ctx context.Context, token string) error
authorizeSign func(ctx context.Context, ott string) ([]provisioner.SignOption, error)
authorizeRenewal func(*x509.Certificate) error
authorizeSSHSign func(ctx context.Context, token string) ([]provisioner.SignOption, error)
authorizeSSHRevoke func(ctx context.Context, token string) error
authorizeSSHRenew func(ctx context.Context, token string) (*ssh.Certificate, error)
authorizeSSHRekey func(ctx context.Context, token string) (*ssh.Certificate, []provisioner.SignOption, error)
}
func (m *mockProvisioner) GetID() string {
if m.getID != nil {
return m.getID()
}
return m.ret1.(string)
}
func (m *mockProvisioner) GetIDForToken() string {
if m.getIDForToken != nil {
return m.getIDForToken()
}
return m.ret1.(string)
}
func (m *mockProvisioner) GetTokenID(token string) (string, error) {
if m.getTokenID != nil {
return m.getTokenID(token)
}
if m.ret1 == nil {
return "", m.err
}
return m.ret1.(string), m.err
}
func (m *mockProvisioner) GetName() string {
if m.getName != nil {
return m.getName()
}
return m.ret1.(string)
}
func (m *mockProvisioner) GetType() provisioner.Type {
if m.getType != nil {
return m.getType()
}
return m.ret1.(provisioner.Type)
}
func (m *mockProvisioner) GetEncryptedKey() (string, string, bool) {
if m.getEncryptedKey != nil {
return m.getEncryptedKey()
}
return m.ret1.(string), m.ret2.(string), m.ret3.(bool)
}
func (m *mockProvisioner) Init(c provisioner.Config) error {
if m.init != nil {
return m.init(c)
}
return m.err
}
func (m *mockProvisioner) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error {
if m.authorizeRenew != nil {
return m.authorizeRenew(ctx, cert)
}
return m.err
}
func (m *mockProvisioner) AuthorizeRevoke(ctx context.Context, token string) error {
if m.authorizeRevoke != nil {
return m.authorizeRevoke(ctx, token)
}
return m.err
}
func (m *mockProvisioner) AuthorizeSign(ctx context.Context, ott string) ([]provisioner.SignOption, error) {
if m.authorizeSign != nil {
return m.authorizeSign(ctx, ott)
}
return m.ret1.([]provisioner.SignOption), m.err
}
func (m *mockProvisioner) AuthorizeRenewal(c *x509.Certificate) error {
if m.authorizeRenewal != nil {
return m.authorizeRenewal(c)
}
return m.err
}
func (m *mockProvisioner) AuthorizeSSHSign(ctx context.Context, token string) ([]provisioner.SignOption, error) {
if m.authorizeSSHSign != nil {
return m.authorizeSSHSign(ctx, token)
}
return m.ret1.([]provisioner.SignOption), m.err
}
func (m *mockProvisioner) AuthorizeSSHRevoke(ctx context.Context, token string) error {
if m.authorizeSSHRevoke != nil {
return m.authorizeSSHRevoke(ctx, token)
}
return m.err
}
func (m *mockProvisioner) AuthorizeSSHRenew(ctx context.Context, token string) (*ssh.Certificate, error) {
if m.authorizeSSHRenew != nil {
return m.authorizeSSHRenew(ctx, token)
}
return m.ret1.(*ssh.Certificate), m.err
}
func (m *mockProvisioner) AuthorizeSSHRekey(ctx context.Context, token string) (*ssh.Certificate, []provisioner.SignOption, error) {
if m.authorizeSSHRekey != nil {
return m.authorizeSSHRekey(ctx, token)
}
return m.ret1.(*ssh.Certificate), m.ret2.([]provisioner.SignOption), m.err
}
func Test_caHandler_Route(t *testing.T) {
type fields struct {
Authority Authority
}
type args struct {
r Router
}
tests := []struct {
name string
fields fields
args args
}{
{"ok", fields{&MockAuthority{}}, args{chi.NewRouter()}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
h := &caHandler{
Authority: tt.fields.Authority,
}
h.Route(tt.args.r)
})
}
}
func Test_caHandler_Health(t *testing.T) {
req := httptest.NewRequest("GET", "http://example.com/health", nil)
w := httptest.NewRecorder()
h := New(&MockAuthority{}).(*caHandler)
h.Health(w, req)
res := w.Result()
if res.StatusCode != 200 {
t.Errorf("caHandler.Health StatusCode = %d, wants 200", res.StatusCode)
}
body, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Errorf("caHandler.Health unexpected error = %v", err)
}
expected := []byte("{\"status\":\"ok\"}\n")
if !bytes.Equal(body, expected) {
t.Errorf("caHandler.Health Body = %s, wants %s", body, expected)
}
}
func Test_caHandler_Root(t *testing.T) {
tests := []struct {
name string
root *x509.Certificate
err error
statusCode int
}{
{"ok", parseCertificate(rootPEM), nil, 200},
{"fail", nil, fmt.Errorf("not found"), 404},
}
// Request with chi context
chiCtx := chi.NewRouteContext()
chiCtx.URLParams.Add("sha", "efc7d6b475a56fe587650bcdb999a4a308f815ba44db4bf0371ea68a786ccd36")
req := httptest.NewRequest("GET", "http://example.com/root/efc7d6b475a56fe587650bcdb999a4a308f815ba44db4bf0371ea68a786ccd36", nil)
req = req.WithContext(context.WithValue(context.Background(), chi.RouteCtxKey, chiCtx))
expected := []byte(`{"ca":"` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `\n"}`)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
h := New(&MockAuthority{ret1: tt.root, err: tt.err}).(*caHandler)
w := httptest.NewRecorder()
h.Root(w, req)
res := w.Result()
if res.StatusCode != tt.statusCode {
t.Errorf("caHandler.Root StatusCode = %d, wants %d", res.StatusCode, tt.statusCode)
}
body, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Errorf("caHandler.Root unexpected error = %v", err)
}
if tt.statusCode == 200 {
if !bytes.Equal(bytes.TrimSpace(body), expected) {
t.Errorf("caHandler.Root Body = %s, wants %s", body, expected)
}
}
})
}
}
func Test_caHandler_Sign(t *testing.T) {
csr := parseCertificateRequest(csrPEM)
valid, err := json.Marshal(SignRequest{
CsrPEM: CertificateRequest{csr},
OTT: "foobarzar",
})
if err != nil {
t.Fatal(err)
}
invalid, err := json.Marshal(SignRequest{
CsrPEM: CertificateRequest{csr},
OTT: "",
})
if err != nil {
t.Fatal(err)
}
expected1 := []byte(`{"crt":"` + strings.ReplaceAll(certPEM, "\n", `\n`) + `\n","ca":"` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `\n","certChain":["` + strings.ReplaceAll(certPEM, "\n", `\n`) + `\n","` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `\n"]}`)
expected2 := []byte(`{"crt":"` + strings.ReplaceAll(stepCertPEM, "\n", `\n`) + `\n","ca":"` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `\n","certChain":["` + strings.ReplaceAll(stepCertPEM, "\n", `\n`) + `\n","` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `\n"]}`)
tests := []struct {
name string
input string
certAttrOpts []provisioner.SignOption
autherr error
cert *x509.Certificate
root *x509.Certificate
signErr error
statusCode int
expected []byte
}{
{"ok", string(valid), nil, nil, parseCertificate(certPEM), parseCertificate(rootPEM), nil, http.StatusCreated, expected1},
{"ok with Provisioner", string(valid), nil, nil, parseCertificate(stepCertPEM), parseCertificate(rootPEM), nil, http.StatusCreated, expected2},
{"json read error", "{", nil, nil, nil, nil, nil, http.StatusBadRequest, nil},
{"validate error", string(invalid), nil, nil, nil, nil, nil, http.StatusBadRequest, nil},
{"authorize error", string(valid), nil, fmt.Errorf("an error"), nil, nil, nil, http.StatusUnauthorized, nil},
{"sign error", string(valid), nil, nil, nil, nil, fmt.Errorf("an error"), http.StatusForbidden, nil},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
h := New(&MockAuthority{
ret1: tt.cert, ret2: tt.root, err: tt.signErr,
authorizeSign: func(ott string) ([]provisioner.SignOption, error) {
return tt.certAttrOpts, tt.autherr
},
getTLSOptions: func() *authority.TLSOptions {
return nil
},
}).(*caHandler)
req := httptest.NewRequest("POST", "http://example.com/sign", strings.NewReader(tt.input))
w := httptest.NewRecorder()
h.Sign(logging.NewResponseLogger(w), req)
res := w.Result()
if res.StatusCode != tt.statusCode {
t.Errorf("caHandler.Root StatusCode = %d, wants %d", res.StatusCode, tt.statusCode)
}
body, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Errorf("caHandler.Root unexpected error = %v", err)
}
if tt.statusCode < http.StatusBadRequest {
if !bytes.Equal(bytes.TrimSpace(body), tt.expected) {
t.Errorf("caHandler.Root Body = %s, wants %s", body, tt.expected)
}
}
})
}
}
func Test_caHandler_Renew(t *testing.T) {
cs := &tls.ConnectionState{
PeerCertificates: []*x509.Certificate{parseCertificate(certPEM)},
}
tests := []struct {
name string
tls *tls.ConnectionState
cert *x509.Certificate
root *x509.Certificate
err error
statusCode int
}{
{"ok", cs, parseCertificate(certPEM), parseCertificate(rootPEM), nil, http.StatusCreated},
{"no tls", nil, nil, nil, nil, http.StatusBadRequest},
{"no peer certificates", &tls.ConnectionState{}, nil, nil, nil, http.StatusBadRequest},
{"renew error", cs, nil, nil, errs.Forbidden("an error"), http.StatusForbidden},
}
expected := []byte(`{"crt":"` + strings.ReplaceAll(certPEM, "\n", `\n`) + `\n","ca":"` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `\n","certChain":["` + strings.ReplaceAll(certPEM, "\n", `\n`) + `\n","` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `\n"]}`)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
h := New(&MockAuthority{
ret1: tt.cert, ret2: tt.root, err: tt.err,
getTLSOptions: func() *authority.TLSOptions {
return nil
},
}).(*caHandler)
req := httptest.NewRequest("POST", "http://example.com/renew", nil)
req.TLS = tt.tls
w := httptest.NewRecorder()
h.Renew(logging.NewResponseLogger(w), req)
res := w.Result()
if res.StatusCode != tt.statusCode {
t.Errorf("caHandler.Renew StatusCode = %d, wants %d", res.StatusCode, tt.statusCode)
}
body, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Errorf("caHandler.Renew unexpected error = %v", err)
}
if tt.statusCode < http.StatusBadRequest {
if !bytes.Equal(bytes.TrimSpace(body), expected) {
t.Errorf("caHandler.Root Body = %s, wants %s", body, expected)
}
}
})
}
}
func Test_caHandler_Rekey(t *testing.T) {
cs := &tls.ConnectionState{
PeerCertificates: []*x509.Certificate{parseCertificate(certPEM)},
}
csr := parseCertificateRequest(csrPEM)
valid, err := json.Marshal(RekeyRequest{
CsrPEM: CertificateRequest{csr},
})
if err != nil {
t.Fatal(err)
}
tests := []struct {
name string
input string
tls *tls.ConnectionState
cert *x509.Certificate
root *x509.Certificate
err error
statusCode int
}{
{"ok", string(valid), cs, parseCertificate(certPEM), parseCertificate(rootPEM), nil, http.StatusCreated},
{"no tls", string(valid), nil, nil, nil, nil, http.StatusBadRequest},
{"no peer certificates", string(valid), &tls.ConnectionState{}, nil, nil, nil, http.StatusBadRequest},
{"rekey error", string(valid), cs, nil, nil, errs.Forbidden("an error"), http.StatusForbidden},
{"json read error", "{", cs, nil, nil, nil, http.StatusBadRequest},
}
expected := []byte(`{"crt":"` + strings.ReplaceAll(certPEM, "\n", `\n`) + `\n","ca":"` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `\n","certChain":["` + strings.ReplaceAll(certPEM, "\n", `\n`) + `\n","` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `\n"]}`)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
h := New(&MockAuthority{
ret1: tt.cert, ret2: tt.root, err: tt.err,
getTLSOptions: func() *authority.TLSOptions {
return nil
},
}).(*caHandler)
req := httptest.NewRequest("POST", "http://example.com/rekey", strings.NewReader(tt.input))
req.TLS = tt.tls
w := httptest.NewRecorder()
h.Rekey(logging.NewResponseLogger(w), req)
res := w.Result()
if res.StatusCode != tt.statusCode {
t.Errorf("caHandler.Rekey StatusCode = %d, wants %d", res.StatusCode, tt.statusCode)
}
body, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Errorf("caHandler.Rekey unexpected error = %v", err)
}
if tt.statusCode < http.StatusBadRequest {
if !bytes.Equal(bytes.TrimSpace(body), expected) {
t.Errorf("caHandler.Rekey Body = %s, wants %s", body, expected)
}
}
})
}
}
func Test_caHandler_Provisioners(t *testing.T) {
type fields struct {
Authority Authority
}
type args struct {
w http.ResponseWriter
r *http.Request
}
req, err := http.NewRequest("GET", "http://example.com/provisioners?cursor=foo&limit=20", http.NoBody)
if err != nil {
t.Fatal(err)
}
reqLimitFail, err := http.NewRequest("GET", "http://example.com/provisioners?limit=abc", http.NoBody)
if err != nil {
t.Fatal(err)
}
var key jose.JSONWebKey
if err := json.Unmarshal([]byte(pubKey), &key); err != nil {
t.Fatal(err)
}
p := provisioner.List{
&provisioner.JWK{
Type: "JWK",
Name: "max",
EncryptedKey: "abc",
Key: &key,
},
&provisioner.JWK{
Type: "JWK",
Name: "mariano",
EncryptedKey: "def",
Key: &key,
},
}
pr := ProvisionersResponse{
Provisioners: p,
}
tests := []struct {
name string
fields fields
args args
statusCode int
}{
{"ok", fields{&MockAuthority{ret1: p, ret2: ""}}, args{httptest.NewRecorder(), req}, 200},
{"fail", fields{&MockAuthority{ret1: p, ret2: "", err: fmt.Errorf("the error")}}, args{httptest.NewRecorder(), req}, 500},
{"limit fail", fields{&MockAuthority{ret1: p, ret2: ""}}, args{httptest.NewRecorder(), reqLimitFail}, 400},
}
expected, err := json.Marshal(pr)
if err != nil {
t.Fatal(err)
}
expectedError400 := errs.BadRequest("limit 'abc' is not an integer")
expectedError400Bytes, err := json.Marshal(expectedError400)
assert.FatalError(t, err)
expectedError500 := errs.InternalServer("force")
expectedError500Bytes, err := json.Marshal(expectedError500)
assert.FatalError(t, err)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
h := &caHandler{
Authority: tt.fields.Authority,
}
h.Provisioners(tt.args.w, tt.args.r)
rec := tt.args.w.(*httptest.ResponseRecorder)
res := rec.Result()
if res.StatusCode != tt.statusCode {
t.Errorf("caHandler.Provisioners StatusCode = %d, wants %d", res.StatusCode, tt.statusCode)
}
body, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Errorf("caHandler.Provisioners unexpected error = %v", err)
}
if tt.statusCode < http.StatusBadRequest {
if !bytes.Equal(bytes.TrimSpace(body), expected) {
t.Errorf("caHandler.Provisioners Body = %s, wants %s", body, expected)
}
} else {
switch tt.statusCode {
case 400:
if !bytes.Equal(bytes.TrimSpace(body), expectedError400Bytes) {
t.Errorf("caHandler.Provisioners Body = %s, wants %s", body, expectedError400Bytes)
}
case 500:
if !bytes.Equal(bytes.TrimSpace(body), expectedError500Bytes) {
t.Errorf("caHandler.Provisioners Body = %s, wants %s", body, expectedError500Bytes)
}
default:
t.Errorf("caHandler.Provisioner unexpected status code = %d", tt.statusCode)
}
}
})
}
}
func Test_caHandler_ProvisionerKey(t *testing.T) {
type fields struct {
Authority Authority
}
type args struct {
w http.ResponseWriter
r *http.Request
}
// Request with chi context
chiCtx := chi.NewRouteContext()
chiCtx.URLParams.Add("kid", "oV1p0MJeGQ7qBlK6B-oyfVdBRjh_e7VSK_YSEEqgW00")
req := httptest.NewRequest("GET", "http://example.com/provisioners/oV1p0MJeGQ7qBlK6B-oyfVdBRjh_e7VSK_YSEEqgW00/encrypted-key", nil)
req = req.WithContext(context.WithValue(context.Background(), chi.RouteCtxKey, chiCtx))
tests := []struct {
name string
fields fields
args args
statusCode int
}{
{"ok", fields{&MockAuthority{ret1: privKey}}, args{httptest.NewRecorder(), req}, 200},
{"fail", fields{&MockAuthority{ret1: "", err: fmt.Errorf("not found")}}, args{httptest.NewRecorder(), req}, 404},
}
expected := []byte(`{"key":"` + privKey + `"}`)
expectedError404 := errs.NotFound("force")
expectedError404Bytes, err := json.Marshal(expectedError404)
assert.FatalError(t, err)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
h := &caHandler{
Authority: tt.fields.Authority,
}
h.ProvisionerKey(tt.args.w, tt.args.r)
rec := tt.args.w.(*httptest.ResponseRecorder)
res := rec.Result()
if res.StatusCode != tt.statusCode {
t.Errorf("caHandler.Provisioners StatusCode = %d, wants %d", res.StatusCode, tt.statusCode)
}
body, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Errorf("caHandler.Provisioners unexpected error = %v", err)
}
if tt.statusCode < http.StatusBadRequest {
if !bytes.Equal(bytes.TrimSpace(body), expected) {
t.Errorf("caHandler.Provisioners Body = %s, wants %s", body, expected)
}
} else {
if !bytes.Equal(bytes.TrimSpace(body), expectedError404Bytes) {
t.Errorf("caHandler.Provisioners Body = %s, wants %s", body, expectedError404Bytes)
}
}
})
}
}
func Test_caHandler_Roots(t *testing.T) {
cs := &tls.ConnectionState{
PeerCertificates: []*x509.Certificate{parseCertificate(certPEM)},
}
tests := []struct {
name string
tls *tls.ConnectionState
cert *x509.Certificate
root *x509.Certificate
err error
statusCode int
}{
{"ok", cs, parseCertificate(certPEM), parseCertificate(rootPEM), nil, http.StatusCreated},
{"no peer certificates", &tls.ConnectionState{}, parseCertificate(certPEM), parseCertificate(rootPEM), nil, http.StatusCreated},
{"fail", cs, nil, nil, fmt.Errorf("an error"), http.StatusForbidden},
}
expected := []byte(`{"crts":["` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `\n"]}`)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
h := New(&MockAuthority{ret1: []*x509.Certificate{tt.root}, err: tt.err}).(*caHandler)
req := httptest.NewRequest("GET", "http://example.com/roots", nil)
req.TLS = tt.tls
w := httptest.NewRecorder()
h.Roots(w, req)
res := w.Result()
if res.StatusCode != tt.statusCode {
t.Errorf("caHandler.Roots StatusCode = %d, wants %d", res.StatusCode, tt.statusCode)
}
body, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Errorf("caHandler.Roots unexpected error = %v", err)
}
if tt.statusCode < http.StatusBadRequest {
if !bytes.Equal(bytes.TrimSpace(body), expected) {
t.Errorf("caHandler.Roots Body = %s, wants %s", body, expected)
}
}
})
}
}
func Test_caHandler_Federation(t *testing.T) {
cs := &tls.ConnectionState{
PeerCertificates: []*x509.Certificate{parseCertificate(certPEM)},
}
tests := []struct {
name string
tls *tls.ConnectionState
cert *x509.Certificate
root *x509.Certificate
err error
statusCode int
}{
{"ok", cs, parseCertificate(certPEM), parseCertificate(rootPEM), nil, http.StatusCreated},
{"no peer certificates", &tls.ConnectionState{}, parseCertificate(certPEM), parseCertificate(rootPEM), nil, http.StatusCreated},
{"fail", cs, nil, nil, fmt.Errorf("an error"), http.StatusForbidden},
}
expected := []byte(`{"crts":["` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `\n"]}`)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
h := New(&MockAuthority{ret1: []*x509.Certificate{tt.root}, err: tt.err}).(*caHandler)
req := httptest.NewRequest("GET", "http://example.com/federation", nil)
req.TLS = tt.tls
w := httptest.NewRecorder()
h.Federation(w, req)
res := w.Result()
if res.StatusCode != tt.statusCode {
t.Errorf("caHandler.Federation StatusCode = %d, wants %d", res.StatusCode, tt.statusCode)
}
body, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Errorf("caHandler.Federation unexpected error = %v", err)
}
if tt.statusCode < http.StatusBadRequest {
if !bytes.Equal(bytes.TrimSpace(body), expected) {
t.Errorf("caHandler.Federation Body = %s, wants %s", body, expected)
}
}
})
}
}
func Test_fmtPublicKey(t *testing.T) {
p256, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatal(err)
}
rsa1024, err := rsa.GenerateKey(rand.Reader, 1024)
if err != nil {
t.Fatal(err)
}
edPub, edPriv, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
var dsa2048 dsa.PrivateKey
if err := dsa.GenerateParameters(&dsa2048.Parameters, rand.Reader, dsa.L2048N256); err != nil {
t.Fatal(err)
}
if err := dsa.GenerateKey(&dsa2048, rand.Reader); err != nil {
t.Fatal(err)
}
type args struct {
pub, priv interface{}
cert *x509.Certificate
}
tests := []struct {
name string
args args
want string
}{
{"p256", args{p256.Public(), p256, nil}, "ECDSA P-256"},
{"rsa1024", args{rsa1024.Public(), rsa1024, nil}, "RSA 1024"},
{"ed25519", args{edPub, edPriv, nil}, "Ed25519"},
{"dsa2048", args{cert: &x509.Certificate{PublicKeyAlgorithm: x509.DSA, PublicKey: &dsa2048.PublicKey}}, "DSA 2048"},
{"unknown", args{cert: &x509.Certificate{PublicKeyAlgorithm: x509.ECDSA, PublicKey: []byte("12345678")}}, "ECDSA unknown"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var cert *x509.Certificate
if tt.args.cert != nil {
cert = tt.args.cert
} else {
cert = mustCertificate(t, tt.args.pub, tt.args.priv)
}
if got := fmtPublicKey(cert); got != tt.want {
t.Errorf("fmtPublicKey() = %v, want %v", got, tt.want)
}
})
}
}
func mustCertificate(t *testing.T, pub, priv interface{}) *x509.Certificate {
template := x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
Organization: []string{"Acme Co"},
},
NotBefore: time.Now(),
NotAfter: time.Now().Add(24 * time.Hour),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
der, err := x509.CreateCertificate(rand.Reader, &template, &template, pub, priv)
if err != nil {
t.Fatal(err)
}
cert, err := x509.ParseCertificate(der)
if err != nil {
t.Fatal(err)
}
return cert
}