|
|
|
@ -1,47 +1,124 @@
|
|
|
|
|
package config
|
|
|
|
|
package config_test
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"crypto/elliptic"
|
|
|
|
|
"crypto/x509"
|
|
|
|
|
"crypto/x509/pkix"
|
|
|
|
|
"math/big"
|
|
|
|
|
"strings"
|
|
|
|
|
"testing"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
"gopkg.in/yaml.v3"
|
|
|
|
|
|
|
|
|
|
"git.cacert.org/cacert-gosigner/pkg/config"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type TestCurve struct {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t TestCurve) Params() *elliptic.CurveParams {
|
|
|
|
|
panic("not needed")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t TestCurve) IsOnCurve(_, _ *big.Int) bool {
|
|
|
|
|
panic("not needed")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t TestCurve) Add(_, _, _, _ *big.Int) (x, y *big.Int) {
|
|
|
|
|
panic("not needed")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t TestCurve) Double(_, _ *big.Int) (x, y *big.Int) {
|
|
|
|
|
panic("not needed")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t TestCurve) ScalarMult(_, _ *big.Int, _ []byte) (x, y *big.Int) {
|
|
|
|
|
panic("not needed")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t TestCurve) ScalarBaseMult(_ []byte) (x, y *big.Int) {
|
|
|
|
|
panic("not needed")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestPrivateKeyInfo_MarshalYAML(t *testing.T) {
|
|
|
|
|
testCurve := TestCurve{}
|
|
|
|
|
testData := []struct {
|
|
|
|
|
name string
|
|
|
|
|
pkInfo *PrivateKeyInfo
|
|
|
|
|
pkInfo *config.PrivateKeyInfo
|
|
|
|
|
expected string
|
|
|
|
|
expectErr bool
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
"RSA",
|
|
|
|
|
&PrivateKeyInfo{
|
|
|
|
|
name: "RSA",
|
|
|
|
|
pkInfo: &config.PrivateKeyInfo{
|
|
|
|
|
Algorithm: x509.RSA,
|
|
|
|
|
RSABits: 3072,
|
|
|
|
|
},
|
|
|
|
|
`algorithm: RSA
|
|
|
|
|
expected: `algorithm: RSA
|
|
|
|
|
rsa-bits: 3072
|
|
|
|
|
`,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"ECDSA",
|
|
|
|
|
&PrivateKeyInfo{
|
|
|
|
|
name: "ECDSA P-224",
|
|
|
|
|
pkInfo: &config.PrivateKeyInfo{
|
|
|
|
|
Algorithm: x509.ECDSA,
|
|
|
|
|
EccCurve: elliptic.P224(),
|
|
|
|
|
},
|
|
|
|
|
`algorithm: EC
|
|
|
|
|
expected: `algorithm: EC
|
|
|
|
|
ecc-curve: P-224
|
|
|
|
|
`,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "ECDSA P-256",
|
|
|
|
|
pkInfo: &config.PrivateKeyInfo{
|
|
|
|
|
Algorithm: x509.ECDSA,
|
|
|
|
|
EccCurve: elliptic.P256(),
|
|
|
|
|
},
|
|
|
|
|
expected: `algorithm: EC
|
|
|
|
|
ecc-curve: P-256
|
|
|
|
|
`,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "ECDSA P-384",
|
|
|
|
|
pkInfo: &config.PrivateKeyInfo{
|
|
|
|
|
Algorithm: x509.ECDSA,
|
|
|
|
|
EccCurve: elliptic.P384(),
|
|
|
|
|
},
|
|
|
|
|
expected: `algorithm: EC
|
|
|
|
|
ecc-curve: P-384
|
|
|
|
|
`,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "ECDSA P-521",
|
|
|
|
|
pkInfo: &config.PrivateKeyInfo{
|
|
|
|
|
Algorithm: x509.ECDSA,
|
|
|
|
|
EccCurve: elliptic.P521(),
|
|
|
|
|
},
|
|
|
|
|
expected: `algorithm: EC
|
|
|
|
|
ecc-curve: P-521
|
|
|
|
|
`,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "ECDSA unsupported curve",
|
|
|
|
|
pkInfo: &config.PrivateKeyInfo{
|
|
|
|
|
Algorithm: x509.ECDSA,
|
|
|
|
|
EccCurve: testCurve,
|
|
|
|
|
},
|
|
|
|
|
expectErr: true,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, item := range testData {
|
|
|
|
|
t.Run(item.name, func(t *testing.T) {
|
|
|
|
|
data, err := yaml.Marshal(item.pkInfo)
|
|
|
|
|
if item.expectErr {
|
|
|
|
|
require.Error(t, err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
assert.YAMLEq(t, item.expected, string(data))
|
|
|
|
@ -53,56 +130,99 @@ func TestPrivateKeyInfo_UnmarshalYAML(t *testing.T) {
|
|
|
|
|
testData := []struct {
|
|
|
|
|
name string
|
|
|
|
|
yaml string
|
|
|
|
|
expected *PrivateKeyInfo
|
|
|
|
|
expected *config.PrivateKeyInfo
|
|
|
|
|
expectErr bool
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
"RSA",
|
|
|
|
|
`label: "mykey"
|
|
|
|
|
name: "malformed",
|
|
|
|
|
yaml: `noyaml`,
|
|
|
|
|
expectErr: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "RSA",
|
|
|
|
|
yaml: `label: "mykey"
|
|
|
|
|
algorithm: "RSA"
|
|
|
|
|
rsa-bits: 2048`,
|
|
|
|
|
&PrivateKeyInfo{
|
|
|
|
|
expected: &config.PrivateKeyInfo{
|
|
|
|
|
Algorithm: x509.RSA,
|
|
|
|
|
RSABits: 2048,
|
|
|
|
|
},
|
|
|
|
|
false,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"ECDSA",
|
|
|
|
|
`label: "mykey"
|
|
|
|
|
name: "ECDSA P-224",
|
|
|
|
|
yaml: `label: "mykey"
|
|
|
|
|
algorithm: "EC"
|
|
|
|
|
ecc-curve: "P-224"`,
|
|
|
|
|
expected: &config.PrivateKeyInfo{
|
|
|
|
|
Algorithm: x509.ECDSA,
|
|
|
|
|
EccCurve: elliptic.P224(),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "ECDSA P-256",
|
|
|
|
|
yaml: `label: "mykey"
|
|
|
|
|
algorithm: "EC"
|
|
|
|
|
ecc-curve: "P-256"`,
|
|
|
|
|
expected: &config.PrivateKeyInfo{
|
|
|
|
|
Algorithm: x509.ECDSA,
|
|
|
|
|
EccCurve: elliptic.P256(),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "ECDSA P-384",
|
|
|
|
|
yaml: `label: "mykey"
|
|
|
|
|
algorithm: "EC"
|
|
|
|
|
ecc-curve: "P-384"`,
|
|
|
|
|
expected: &config.PrivateKeyInfo{
|
|
|
|
|
Algorithm: x509.ECDSA,
|
|
|
|
|
EccCurve: elliptic.P384(),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "ECDSA P-521",
|
|
|
|
|
yaml: `label: "mykey"
|
|
|
|
|
algorithm: "EC"
|
|
|
|
|
ecc-curve: "P-521"`,
|
|
|
|
|
&PrivateKeyInfo{
|
|
|
|
|
expected: &config.PrivateKeyInfo{
|
|
|
|
|
Algorithm: x509.ECDSA,
|
|
|
|
|
EccCurve: elliptic.P521(),
|
|
|
|
|
},
|
|
|
|
|
false,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"no-algorithm",
|
|
|
|
|
`label: "mykey"`,
|
|
|
|
|
nil,
|
|
|
|
|
true,
|
|
|
|
|
name: "no-algorithm",
|
|
|
|
|
yaml: `label: "mykey"`,
|
|
|
|
|
expectErr: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"RSA-no-rsa-bits",
|
|
|
|
|
`label: "mykey"
|
|
|
|
|
name: "RSA-no-rsa-bits",
|
|
|
|
|
yaml: `label: "mykey"
|
|
|
|
|
algorithm: "RSA"`,
|
|
|
|
|
nil,
|
|
|
|
|
true,
|
|
|
|
|
expectErr: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"ECDSA-no-curve",
|
|
|
|
|
`label: "mykey"
|
|
|
|
|
name: "ECDSA-no-curve",
|
|
|
|
|
yaml: `label: "mykey"
|
|
|
|
|
algorithm: "EC"`,
|
|
|
|
|
nil,
|
|
|
|
|
true,
|
|
|
|
|
expectErr: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "ECDSA-invalid-curve",
|
|
|
|
|
yaml: `label: "mykey"
|
|
|
|
|
algorithm: "EC"
|
|
|
|
|
ecc-curve: "fancy-but-wrong"`,
|
|
|
|
|
expectErr: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "wrong-key-algorithm",
|
|
|
|
|
yaml: `label: "mykey"
|
|
|
|
|
algorithm: "ChaCha"`,
|
|
|
|
|
expectErr: true,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, item := range testData {
|
|
|
|
|
t.Run(item.name, func(t *testing.T) {
|
|
|
|
|
pkInfo := &PrivateKeyInfo{}
|
|
|
|
|
pkInfo := &config.PrivateKeyInfo{}
|
|
|
|
|
err := yaml.Unmarshal([]byte(item.yaml), pkInfo)
|
|
|
|
|
if err != nil && !item.expectErr {
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
@ -120,47 +240,172 @@ algorithm: "EC"`,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestCaCertificateEntry_UnmarshalYAML(t *testing.T) {
|
|
|
|
|
data := `{
|
|
|
|
|
"key-info": {
|
|
|
|
|
"algorithm":"EC",
|
|
|
|
|
"ecc-curve":"P-521"
|
|
|
|
|
testData := []struct {
|
|
|
|
|
name, yaml string
|
|
|
|
|
expected config.CaCertificateEntry
|
|
|
|
|
expectErr bool
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "no yaml",
|
|
|
|
|
yaml: "no yaml",
|
|
|
|
|
expectErr: true,
|
|
|
|
|
},
|
|
|
|
|
"certificate-file":"test.crt",
|
|
|
|
|
"common-name":"My Little Test Root CA"
|
|
|
|
|
}`
|
|
|
|
|
|
|
|
|
|
entry := CaCertificateEntry{}
|
|
|
|
|
|
|
|
|
|
err := yaml.Unmarshal([]byte(data), &entry)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
assert.Equal(t, CaCertificateEntry{
|
|
|
|
|
KeyInfo: &PrivateKeyInfo{
|
|
|
|
|
{
|
|
|
|
|
name: "no common name",
|
|
|
|
|
yaml: `
|
|
|
|
|
key-info:
|
|
|
|
|
algorithm: EC
|
|
|
|
|
ecc-curve: P-521
|
|
|
|
|
`,
|
|
|
|
|
expectErr: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "no key info",
|
|
|
|
|
yaml: `
|
|
|
|
|
common-name: My Little Test Root CA
|
|
|
|
|
`,
|
|
|
|
|
expectErr: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "EC P-521",
|
|
|
|
|
yaml: `
|
|
|
|
|
key-info:
|
|
|
|
|
algorithm: EC
|
|
|
|
|
ecc-curve: P-521
|
|
|
|
|
common-name: My Little Test Root CA
|
|
|
|
|
ext-key-usages:
|
|
|
|
|
- client
|
|
|
|
|
- server
|
|
|
|
|
`,
|
|
|
|
|
expectErr: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "EC root P-521",
|
|
|
|
|
yaml: `
|
|
|
|
|
key-info:
|
|
|
|
|
algorithm: EC
|
|
|
|
|
ecc-curve: P-521
|
|
|
|
|
common-name: My Little Test Root CA
|
|
|
|
|
storage: root
|
|
|
|
|
`,
|
|
|
|
|
expected: config.CaCertificateEntry{
|
|
|
|
|
KeyInfo: &config.PrivateKeyInfo{
|
|
|
|
|
Algorithm: x509.ECDSA,
|
|
|
|
|
EccCurve: elliptic.P521(),
|
|
|
|
|
},
|
|
|
|
|
CommonName: "My Little Test Root CA",
|
|
|
|
|
Storage: "root",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "EC sub P-256 client server",
|
|
|
|
|
yaml: `
|
|
|
|
|
key-info:
|
|
|
|
|
algorithm: EC
|
|
|
|
|
ecc-curve: P-256
|
|
|
|
|
common-name: My Little Test Sub CA
|
|
|
|
|
parent: root
|
|
|
|
|
ext-key-usages:
|
|
|
|
|
- client
|
|
|
|
|
- server
|
|
|
|
|
`,
|
|
|
|
|
expected: config.CaCertificateEntry{
|
|
|
|
|
KeyInfo: &config.PrivateKeyInfo{
|
|
|
|
|
Algorithm: x509.ECDSA,
|
|
|
|
|
EccCurve: elliptic.P256(),
|
|
|
|
|
},
|
|
|
|
|
Parent: "root",
|
|
|
|
|
CommonName: "My Little Test Sub CA",
|
|
|
|
|
Storage: "default",
|
|
|
|
|
}, entry)
|
|
|
|
|
ExtKeyUsage: []x509.ExtKeyUsage{
|
|
|
|
|
x509.ExtKeyUsageClientAuth,
|
|
|
|
|
x509.ExtKeyUsageServerAuth,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "EC sub P-256 person",
|
|
|
|
|
yaml: `
|
|
|
|
|
key-info:
|
|
|
|
|
algorithm: EC
|
|
|
|
|
ecc-curve: P-256
|
|
|
|
|
common-name: My Little Test Sub CA
|
|
|
|
|
parent: root
|
|
|
|
|
ext-key-usages:
|
|
|
|
|
- client
|
|
|
|
|
- code
|
|
|
|
|
- email
|
|
|
|
|
- ocsp
|
|
|
|
|
`,
|
|
|
|
|
expected: config.CaCertificateEntry{
|
|
|
|
|
KeyInfo: &config.PrivateKeyInfo{
|
|
|
|
|
Algorithm: x509.ECDSA,
|
|
|
|
|
EccCurve: elliptic.P256(),
|
|
|
|
|
},
|
|
|
|
|
CommonName: "My Little Test Sub CA",
|
|
|
|
|
Storage: "default",
|
|
|
|
|
Parent: "root",
|
|
|
|
|
ExtKeyUsage: []x509.ExtKeyUsage{
|
|
|
|
|
x509.ExtKeyUsageClientAuth,
|
|
|
|
|
x509.ExtKeyUsageCodeSigning,
|
|
|
|
|
x509.ExtKeyUsageEmailProtection,
|
|
|
|
|
x509.ExtKeyUsageOCSPSigning,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "invalid ext-key-usages",
|
|
|
|
|
yaml: `
|
|
|
|
|
key-info:
|
|
|
|
|
algorithm: EC
|
|
|
|
|
ecc-curve: P-256
|
|
|
|
|
common-name: My Little Test Sub CA
|
|
|
|
|
parent: root
|
|
|
|
|
ext-key-usages:
|
|
|
|
|
- wrong_one
|
|
|
|
|
`,
|
|
|
|
|
expectErr: true,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, item := range testData {
|
|
|
|
|
t.Run(item.name, func(t *testing.T) {
|
|
|
|
|
entry := config.CaCertificateEntry{}
|
|
|
|
|
|
|
|
|
|
err := yaml.Unmarshal([]byte(item.yaml), &entry)
|
|
|
|
|
if item.expectErr {
|
|
|
|
|
require.Error(t, err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
assert.Equal(t, item.expected, entry)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLoadConfiguration(t *testing.T) {
|
|
|
|
|
testData := []struct {
|
|
|
|
|
name, yaml string
|
|
|
|
|
err bool
|
|
|
|
|
testData := map[string]struct {
|
|
|
|
|
yaml string
|
|
|
|
|
errMsg string
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "Good",
|
|
|
|
|
"Good": {
|
|
|
|
|
yaml: `---
|
|
|
|
|
Settings:
|
|
|
|
|
organization:
|
|
|
|
|
organization: ["Acme CAs Ltd."]
|
|
|
|
|
validity-years:
|
|
|
|
|
root: 20
|
|
|
|
|
intermediary: 5
|
|
|
|
|
url-patterns:
|
|
|
|
|
ocsp: http://ocsp.example.org/
|
|
|
|
|
crl: http://crl.example.org/%s
|
|
|
|
|
issuer: http://%s.cas.example.org/
|
|
|
|
|
KeyStorage:
|
|
|
|
|
default:
|
|
|
|
|
type: softhsm
|
|
|
|
|
label: ca-storage
|
|
|
|
|
CAs:
|
|
|
|
|
root:
|
|
|
|
|
common-name: "Root CA"
|
|
|
|
@ -180,22 +425,75 @@ CAs:
|
|
|
|
|
ecc-curve: P-256
|
|
|
|
|
parent: root
|
|
|
|
|
`,
|
|
|
|
|
err: false,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Bad",
|
|
|
|
|
yaml: `noyamlforyou: ]`,
|
|
|
|
|
err: true,
|
|
|
|
|
"Bad": {
|
|
|
|
|
yaml: `no yaml for you: ]`,
|
|
|
|
|
errMsg: "could not parse YAML configuration",
|
|
|
|
|
},
|
|
|
|
|
"NoGlobal": {
|
|
|
|
|
yaml: `---
|
|
|
|
|
KeyStorage:
|
|
|
|
|
default:
|
|
|
|
|
label: default
|
|
|
|
|
type: softhsm
|
|
|
|
|
CAs:
|
|
|
|
|
root:
|
|
|
|
|
common-name: "Root CA"
|
|
|
|
|
key-info:
|
|
|
|
|
algorithm: EC
|
|
|
|
|
ecc-curve: P-384
|
|
|
|
|
`,
|
|
|
|
|
errMsg: "configuration entry 'Settings' is missing or empty",
|
|
|
|
|
},
|
|
|
|
|
"NoKeyStorage": {
|
|
|
|
|
yaml: `
|
|
|
|
|
Settings:
|
|
|
|
|
organization:
|
|
|
|
|
organization: ["Acme CAs Ltd."]
|
|
|
|
|
validity-years:
|
|
|
|
|
root: 20
|
|
|
|
|
intermediary: 5
|
|
|
|
|
url-patterns:
|
|
|
|
|
ocsp: http://ocsp.example.org/
|
|
|
|
|
crl: http://crl.example.org/%s.crl
|
|
|
|
|
issuer: http://%s.cas.example.org/
|
|
|
|
|
CAs:
|
|
|
|
|
root:
|
|
|
|
|
common-name: "Root CA"
|
|
|
|
|
key-info:
|
|
|
|
|
algorithm: EC
|
|
|
|
|
ecc-curve: P-384
|
|
|
|
|
`,
|
|
|
|
|
errMsg: "configuration entry 'KeyStorage' is missing or empty",
|
|
|
|
|
},
|
|
|
|
|
"NoCAs": {
|
|
|
|
|
yaml: `
|
|
|
|
|
Settings:
|
|
|
|
|
organization:
|
|
|
|
|
organization: ["Acme CAs Ltd."]
|
|
|
|
|
validity-years:
|
|
|
|
|
root: 20
|
|
|
|
|
intermediary: 5
|
|
|
|
|
url-patterns:
|
|
|
|
|
ocsp: http://ocsp.example.org/
|
|
|
|
|
crl: http://crl.example.org/%s.crl
|
|
|
|
|
issuer: http://%s.cas.example.org/
|
|
|
|
|
KeyStorage:
|
|
|
|
|
default:
|
|
|
|
|
label: default
|
|
|
|
|
type: softhsm
|
|
|
|
|
`,
|
|
|
|
|
errMsg: "configuration entry 'CAs' is missing or empty",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, item := range testData {
|
|
|
|
|
t.Run(item.name, func(t *testing.T) {
|
|
|
|
|
for name, item := range testData {
|
|
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
|
r := strings.NewReader(item.yaml)
|
|
|
|
|
sc, err := LoadConfiguration(r)
|
|
|
|
|
sc, err := config.LoadConfiguration(r)
|
|
|
|
|
|
|
|
|
|
if item.err {
|
|
|
|
|
assert.Error(t, err)
|
|
|
|
|
if item.errMsg != "" {
|
|
|
|
|
assert.ErrorContains(t, err, item.errMsg)
|
|
|
|
|
assert.Nil(t, sc)
|
|
|
|
|
} else {
|
|
|
|
|
assert.NoError(t, err)
|
|
|
|
@ -205,17 +503,21 @@ CAs:
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSignerConfig_RootCAs(t *testing.T) {
|
|
|
|
|
yamlData := `---
|
|
|
|
|
const sampleConfig = `---
|
|
|
|
|
Settings:
|
|
|
|
|
organization:
|
|
|
|
|
organization: ["Acme CAs Ltd."]
|
|
|
|
|
validity-years:
|
|
|
|
|
root: 20
|
|
|
|
|
intermediary: 5
|
|
|
|
|
url-patterns:
|
|
|
|
|
ocsp: http://ocsp.example.org/
|
|
|
|
|
crl: http://crl.example.org/%s.crl
|
|
|
|
|
issuer: http://%s.cas.example.org/
|
|
|
|
|
KeyStorage:
|
|
|
|
|
default:
|
|
|
|
|
type: softhsm
|
|
|
|
|
label: ca-storage
|
|
|
|
|
CAs:
|
|
|
|
|
root:
|
|
|
|
|
common-name: "Root CA"
|
|
|
|
@ -235,12 +537,399 @@ CAs:
|
|
|
|
|
ecc-curve: P-256
|
|
|
|
|
parent: root
|
|
|
|
|
`
|
|
|
|
|
r := strings.NewReader(yamlData)
|
|
|
|
|
sc, err := LoadConfiguration(r)
|
|
|
|
|
|
|
|
|
|
func loadSignerConfig(t *testing.T) *config.SignerConfig {
|
|
|
|
|
r := strings.NewReader(sampleConfig)
|
|
|
|
|
sc, err := config.LoadConfiguration(r)
|
|
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.NotNil(t, sc)
|
|
|
|
|
return sc
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSignerConfig_RootCAs(t *testing.T) {
|
|
|
|
|
sc := loadSignerConfig(t)
|
|
|
|
|
|
|
|
|
|
roots := sc.RootCAs()
|
|
|
|
|
assert.Equal(t, roots, []string{"root"})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSignerConfig_IntermediaryCAs(t *testing.T) {
|
|
|
|
|
sc := loadSignerConfig(t)
|
|
|
|
|
|
|
|
|
|
cAs := sc.IntermediaryCAs()
|
|
|
|
|
assert.ElementsMatch(t, cAs, []string{"sub1", "sub2"})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSignerConfig_GetParentCA(t *testing.T) {
|
|
|
|
|
testData := map[string]struct{ errMsg string }{
|
|
|
|
|
"root": {
|
|
|
|
|
errMsg: "CA root is a root CA and has no parent",
|
|
|
|
|
},
|
|
|
|
|
"unknown": {
|
|
|
|
|
errMsg: "no CA definition for unknown",
|
|
|
|
|
},
|
|
|
|
|
"sub1": {},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sc := loadSignerConfig(t)
|
|
|
|
|
|
|
|
|
|
for name, item := range testData {
|
|
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
|
ca, err := sc.GetParentCA(name)
|
|
|
|
|
|
|
|
|
|
if item.errMsg != "" {
|
|
|
|
|
assert.ErrorContains(t, err, item.errMsg)
|
|
|
|
|
} else {
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.Equal(t, "Root CA", ca.CommonName)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSignerConfig_CertificateFileName(t *testing.T) {
|
|
|
|
|
sc := loadSignerConfig(t)
|
|
|
|
|
|
|
|
|
|
assert.Equal(t, "root.crt", sc.CertificateFileName("root"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSignerConfig_BuildCRLUrl(t *testing.T) {
|
|
|
|
|
sc := loadSignerConfig(t)
|
|
|
|
|
|
|
|
|
|
ca, err := sc.GetCADefinition("sub1")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
url := sc.BuildCRLUrl(ca)
|
|
|
|
|
assert.Equal(t, "http://crl.example.org/root.crl", url)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSignerConfig_BuildIssuerUrl(t *testing.T) {
|
|
|
|
|
sc := loadSignerConfig(t)
|
|
|
|
|
|
|
|
|
|
ca, err := sc.GetCADefinition("sub1")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
url := sc.BuildIssuerURL(ca)
|
|
|
|
|
assert.Equal(t, "http://root.cas.example.org/", url)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSignerConfig_BuildOCSPURL(t *testing.T) {
|
|
|
|
|
sc := loadSignerConfig(t)
|
|
|
|
|
|
|
|
|
|
ca, err := sc.GetCADefinition("sub1")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
url := sc.BuildOCSPURL(ca)
|
|
|
|
|
assert.Equal(t, "http://ocsp.example.org/", url)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSignerConfig_BuildOCSPURL2(t *testing.T) {
|
|
|
|
|
r := strings.NewReader(`---
|
|
|
|
|
Settings:
|
|
|
|
|
organization:
|
|
|
|
|
organization: ["Acme CAs Ltd."]
|
|
|
|
|
validity-years:
|
|
|
|
|
root: 20
|
|
|
|
|
intermediary: 5
|
|
|
|
|
url-patterns:
|
|
|
|
|
ocsp: http://ocsp.example.org/%s
|
|
|
|
|
crl: http://crl.example.org/%s.crl
|
|
|
|
|
issuer: http://%s.cas.example.org/
|
|
|
|
|
KeyStorage:
|
|
|
|
|
default:
|
|
|
|
|
type: softhsm
|
|
|
|
|
label: ca-storage
|
|
|
|
|
CAs:
|
|
|
|
|
root:
|
|
|
|
|
common-name: "Root CA"
|
|
|
|
|
key-info:
|
|
|
|
|
algorithm: EC
|
|
|
|
|
ecc-curve: P-384
|
|
|
|
|
sub1:
|
|
|
|
|
common-name: "Sub CA 1"
|
|
|
|
|
key-info:
|
|
|
|
|
algorithm: EC
|
|
|
|
|
ecc-curve: P-256
|
|
|
|
|
parent: root
|
|
|
|
|
sub2:
|
|
|
|
|
common-name: "Sub CA 2"
|
|
|
|
|
key-info:
|
|
|
|
|
algorithm: EC
|
|
|
|
|
ecc-curve: P-256
|
|
|
|
|
parent: root
|
|
|
|
|
`)
|
|
|
|
|
sc, err := config.LoadConfiguration(r)
|
|
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.NotNil(t, sc)
|
|
|
|
|
|
|
|
|
|
ca, err := sc.GetCADefinition("sub1")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
url := sc.BuildOCSPURL(ca)
|
|
|
|
|
assert.Equal(t, "http://ocsp.example.org/root", url)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSettings_UnmarshalYAML(t *testing.T) {
|
|
|
|
|
testData := map[string]struct {
|
|
|
|
|
yaml string
|
|
|
|
|
expected config.Settings
|
|
|
|
|
errMsg string
|
|
|
|
|
}{
|
|
|
|
|
"no yaml": {
|
|
|
|
|
yaml: "no yaml",
|
|
|
|
|
errMsg: "could not decode YAML",
|
|
|
|
|
},
|
|
|
|
|
"missing organization": {
|
|
|
|
|
yaml: `
|
|
|
|
|
validity-years:
|
|
|
|
|
root: 10
|
|
|
|
|
intermediary: 5
|
|
|
|
|
url-patterns:
|
|
|
|
|
ocsp: http://ocsp.example.org/
|
|
|
|
|
crl: http://crl.example.org/%s.crl
|
|
|
|
|
issuer: http://issuer.example.org/%s.crt
|
|
|
|
|
`,
|
|
|
|
|
errMsg: "invalid Settings you need to specify 'organization'",
|
|
|
|
|
},
|
|
|
|
|
"missing validity-years": {
|
|
|
|
|
yaml: `
|
|
|
|
|
organization:
|
|
|
|
|
organization: ["Acme CAs Ltd."]
|
|
|
|
|
url-patterns:
|
|
|
|
|
ocsp: http://ocsp.example.org/
|
|
|
|
|
crl: http://crl.example.org/%s.crl
|
|
|
|
|
issuer: http://issuer.example.org/%s.crt
|
|
|
|
|
`,
|
|
|
|
|
errMsg: "invalid Settings you must specify validity years for 'root' and 'intermediary'",
|
|
|
|
|
},
|
|
|
|
|
"missing url-patterns": {
|
|
|
|
|
yaml: `
|
|
|
|
|
organization:
|
|
|
|
|
organization: ["Acme CAs Ltd."]
|
|
|
|
|
validity-years:
|
|
|
|
|
root: 10
|
|
|
|
|
intermediary: 5
|
|
|
|
|
`,
|
|
|
|
|
errMsg: "invalid Settings",
|
|
|
|
|
},
|
|
|
|
|
"invalid validity-years": {
|
|
|
|
|
yaml: `
|
|
|
|
|
organization:
|
|
|
|
|
organization: ["Acme CAs Ltd."]
|
|
|
|
|
validity-years:
|
|
|
|
|
root: 5
|
|
|
|
|
intermediary: 10
|
|
|
|
|
url-patterns:
|
|
|
|
|
ocsp: http://ocsp.example.org/
|
|
|
|
|
crl: http://crl.example.org/%s.crl
|
|
|
|
|
issuer: http://issuer.example.org/%s.crt
|
|
|
|
|
`,
|
|
|
|
|
errMsg: "invalid Settings validity of root CA certificates must be equal or greater than those if intermediary CA certificates",
|
|
|
|
|
},
|
|
|
|
|
"no OCSP pattern": {
|
|
|
|
|
yaml: `
|
|
|
|
|
organization:
|
|
|
|
|
organization: ["Acme CAs Ltd."]
|
|
|
|
|
validity-years:
|
|
|
|
|
root: 10
|
|
|
|
|
intermediary: 5
|
|
|
|
|
url-patterns:
|
|
|
|
|
crl: http://crl.example.org/%s.crl
|
|
|
|
|
issuer: http://issuer.example.org/%s.crt
|
|
|
|
|
`,
|
|
|
|
|
errMsg: "invalid Settings you must specify an 'ocsp' URL pattern",
|
|
|
|
|
},
|
|
|
|
|
"invalid OCSP pattern": {
|
|
|
|
|
yaml: `
|
|
|
|
|
organization:
|
|
|
|
|
organization: ["Acme CAs Ltd."]
|
|
|
|
|
validity-years:
|
|
|
|
|
root: 10
|
|
|
|
|
intermediary: 5
|
|
|
|
|
url-patterns:
|
|
|
|
|
ocsp: http://ocsp.example.org/%s_%s
|
|
|
|
|
crl: http://crl.example.org/%s.crl
|
|
|
|
|
issuer: http://issuer.example.org/%s.crt
|
|
|
|
|
`,
|
|
|
|
|
errMsg: "invalid Settings url-pattern 'ocsp' must contain zero or one '%s' placeholder",
|
|
|
|
|
},
|
|
|
|
|
"no CRL pattern": {
|
|
|
|
|
yaml: `
|
|
|
|
|
organization:
|
|
|
|
|
organization: ["Acme CAs Ltd."]
|
|
|
|
|
validity-years:
|
|
|
|
|
root: 10
|
|
|
|
|
intermediary: 5
|
|
|
|
|
url-patterns:
|
|
|
|
|
ocsp: http://ocsp.example.org/
|
|
|
|
|
issuer: http://issuer.example.org/%s.crt
|
|
|
|
|
`,
|
|
|
|
|
errMsg: "invalid Settings you must specify an 'crl' URL pattern",
|
|
|
|
|
},
|
|
|
|
|
"invalid CRL pattern": {
|
|
|
|
|
yaml: `
|
|
|
|
|
organization:
|
|
|
|
|
organization: ["Acme CAs Ltd."]
|
|
|
|
|
validity-years:
|
|
|
|
|
root: 10
|
|
|
|
|
intermediary: 5
|
|
|
|
|
url-patterns:
|
|
|
|
|
ocsp: http://ocsp.example.org/
|
|
|
|
|
crl: http://crl.example.org/
|
|
|
|
|
issuer: http://issuer.example.org/%s.crt
|
|
|
|
|
`,
|
|
|
|
|
errMsg: "invalid Settings url-pattern 'crl' must contain one '%s' placeholder",
|
|
|
|
|
},
|
|
|
|
|
"no issuer pattern": {
|
|
|
|
|
yaml: `
|
|
|
|
|
organization:
|
|
|
|
|
organization: ["Acme CAs Ltd."]
|
|
|
|
|
validity-years:
|
|
|
|
|
root: 10
|
|
|
|
|
intermediary: 5
|
|
|
|
|
url-patterns:
|
|
|
|
|
crl: http://crl.example.org/%s.crl
|
|
|
|
|
ocsp: http://ocsp.example.org/
|
|
|
|
|
`,
|
|
|
|
|
errMsg: "invalid Settings you must specify an 'issuer' URL pattern",
|
|
|
|
|
},
|
|
|
|
|
"invalid issuer pattern": {
|
|
|
|
|
yaml: `
|
|
|
|
|
organization:
|
|
|
|
|
organization: ["Acme CAs Ltd."]
|
|
|
|
|
validity-years:
|
|
|
|
|
root: 10
|
|
|
|
|
intermediary: 5
|
|
|
|
|
url-patterns:
|
|
|
|
|
ocsp: http://ocsp.example.org/
|
|
|
|
|
crl: http://crl.example.org/%s.crl
|
|
|
|
|
issuer: http://issuer.example.org/
|
|
|
|
|
`,
|
|
|
|
|
errMsg: "invalid Settings url-pattern 'issuer' must contain one '%s' placeholder",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for name, item := range testData {
|
|
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
|
var s config.Settings
|
|
|
|
|
err := yaml.Unmarshal([]byte(item.yaml), &s)
|
|
|
|
|
|
|
|
|
|
if item.errMsg != "" {
|
|
|
|
|
assert.ErrorContains(t, err, item.errMsg)
|
|
|
|
|
} else {
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.NotNil(t, s)
|
|
|
|
|
assert.Equal(t, item.expected, s)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestKeyStorage_UnmarshalYAML(t *testing.T) {
|
|
|
|
|
testData := map[string]struct {
|
|
|
|
|
yaml string
|
|
|
|
|
expected config.KeyStorage
|
|
|
|
|
errMsg string
|
|
|
|
|
}{
|
|
|
|
|
"no yaml": {
|
|
|
|
|
yaml: `no yaml`,
|
|
|
|
|
errMsg: "could not decode YAML:",
|
|
|
|
|
},
|
|
|
|
|
"softhsm": {
|
|
|
|
|
yaml: `
|
|
|
|
|
label: softhsm
|
|
|
|
|
type: softhsm`,
|
|
|
|
|
expected: config.KeyStorage{
|
|
|
|
|
Label: "softhsm", Module: config.SoftHsmModule,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
"custom": {
|
|
|
|
|
yaml: `
|
|
|
|
|
label: custom
|
|
|
|
|
type: p11module
|
|
|
|
|
module: /usr/lib/mytokenp11.so`,
|
|
|
|
|
expected: config.KeyStorage{
|
|
|
|
|
Label: "custom", Module: "/usr/lib/mytokenp11.so",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
"missing module": {
|
|
|
|
|
yaml: `
|
|
|
|
|
label: custom
|
|
|
|
|
type: p11module`,
|
|
|
|
|
errMsg: "specify a 'module' field when using the 'p11module' type",
|
|
|
|
|
},
|
|
|
|
|
"unsupported type": {
|
|
|
|
|
yaml: `
|
|
|
|
|
label: something
|
|
|
|
|
type: something`,
|
|
|
|
|
errMsg: "unsupported KeyStorage type 'something'",
|
|
|
|
|
},
|
|
|
|
|
"missing label": {
|
|
|
|
|
yaml: `
|
|
|
|
|
type: softhsm`,
|
|
|
|
|
errMsg: "element 'label' must be specified",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for name, item := range testData {
|
|
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
|
var ks config.KeyStorage
|
|
|
|
|
err := yaml.Unmarshal([]byte(item.yaml), &ks)
|
|
|
|
|
|
|
|
|
|
if item.errMsg != "" {
|
|
|
|
|
assert.ErrorContains(t, err, item.errMsg)
|
|
|
|
|
} else {
|
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
assert.Equal(t, item.expected, ks)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSignerConfig_GetCADefinition(t *testing.T) {
|
|
|
|
|
sc := loadSignerConfig(t)
|
|
|
|
|
|
|
|
|
|
def, err := sc.GetCADefinition("unknown")
|
|
|
|
|
assert.ErrorContains(t, err, "no CA definition found for label unknown")
|
|
|
|
|
assert.Nil(t, def)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSignerConfig_CalculateValidity(t *testing.T) {
|
|
|
|
|
sc := loadSignerConfig(t)
|
|
|
|
|
|
|
|
|
|
for _, ca := range []string{"root", "sub1"} {
|
|
|
|
|
t.Run(ca, func(t *testing.T) {
|
|
|
|
|
def, err := sc.GetCADefinition(ca)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
now := time.Now()
|
|
|
|
|
notBefore, notAfter := sc.CalculateValidity(def, now)
|
|
|
|
|
|
|
|
|
|
assert.True(t, now.Equal(notBefore))
|
|
|
|
|
assert.True(t, now.Before(notAfter))
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSignerConfig_CalculateSubject(t *testing.T) {
|
|
|
|
|
testData := map[string]struct {
|
|
|
|
|
cn string
|
|
|
|
|
}{
|
|
|
|
|
"root": {cn: "Root CA"},
|
|
|
|
|
"sub1": {cn: "Sub CA 1"},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sc := loadSignerConfig(t)
|
|
|
|
|
|
|
|
|
|
for name, item := range testData {
|
|
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
|
def, err := sc.GetCADefinition(name)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
subject := sc.CalculateSubject(def)
|
|
|
|
|
assert.Equal(t, pkix.Name{Organization: []string{"Acme CAs Ltd."}, CommonName: item.cn}, subject)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|