refactor: private key decoding and generators (#4116)

pull/4118/head^2
James Elliott 2022-10-03 11:52:29 +11:00 committed by GitHub
parent 32bd2eba60
commit 3f39914c8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 179 additions and 69 deletions

View File

@ -1,6 +1,8 @@
package main
import (
"crypto/ecdsa"
"crypto/rsa"
"encoding/json"
"fmt"
"io"
@ -181,6 +183,8 @@ var decodedTypes = []reflect.Type{
reflect.TypeOf(url.URL{}),
reflect.TypeOf(time.Duration(0)),
reflect.TypeOf(schema.Address{}),
reflect.TypeOf(rsa.PrivateKey{}),
reflect.TypeOf(ecdsa.PrivateKey{}),
}
func containsType(needle reflect.Type, haystack []reflect.Type) (contains bool) {

View File

@ -1,5 +1,5 @@
---
title: "Commit Message Guidelines"
title: "Commit Message"
description: "Authelia Development Commit Message Guidelines"
lead: "This section covers the git commit message guidelines we use for development."
date: 2021-01-30T19:29:07+11:00
@ -7,8 +7,8 @@ draft: false
images: []
menu:
contributing:
parent: "development"
weight: 231
parent: "guidelines"
weight: 320
toc: true
aliases:
- /docs/contributing/commitmsg-guidelines.html

View File

@ -2,7 +2,7 @@
title: "Documentation"
description: "Authelia Development Documentation Guidelines"
lead: "This section covers the guidelines we use when writing documentation."
date: 2021-01-30T19:29:07+11:00
date: 2022-10-02T14:32:16+11:00
draft: false
images: []
menu:

View File

@ -2,7 +2,7 @@
title: "Guidelines"
description: "An introduction into guidelines for contributing to the Authelia project."
lead: "An introduction into guidelines for contributing to the Authelia project."
date: 2022-06-15T17:51:47+10:00
date: 2022-10-02T14:32:16+11:00
draft: false
images: []
menu:

View File

@ -2,7 +2,7 @@
title: "Istio"
description: "A guide to integrating Authelia with the Istio Kubernetes Ingress."
lead: "A guide to integrating Authelia with the Istio Kubernetes Ingress."
date: 2022-06-15T17:51:47+10:00
date: 2022-10-02T13:59:09+11:00
draft: false
images: []
menu:

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,7 @@
package configuration
import (
"crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
"fmt"
@ -343,8 +344,8 @@ func StringToX509CertificateChainHookFunc() mapstructure.DecodeHookFuncType {
}
}
// StringToRSAPrivateKeyHookFunc decodes strings to rsa.PrivateKey's.
func StringToRSAPrivateKeyHookFunc() mapstructure.DecodeHookFuncType {
// StringToPrivateKeyHookFunc decodes strings to rsa.PrivateKey's.
func StringToPrivateKeyHookFunc() mapstructure.DecodeHookFuncType {
return func(f reflect.Type, t reflect.Type, data interface{}) (value interface{}, err error) {
if f.Kind() != reflect.String {
return data, nil
@ -354,21 +355,36 @@ func StringToRSAPrivateKeyHookFunc() mapstructure.DecodeHookFuncType {
return data, nil
}
expectedType := reflect.TypeOf(rsa.PrivateKey{})
expectedTypeRSA := reflect.TypeOf(rsa.PrivateKey{})
expectedTypeECDSA := reflect.TypeOf(ecdsa.PrivateKey{})
if t.Elem() != expectedType {
return data, nil
}
var (
i any
expectedType reflect.Type
)
dataStr := data.(string)
switch t.Elem() {
case expectedTypeRSA:
var result *rsa.PrivateKey
if dataStr == "" {
return result, nil
}
var i interface{}
expectedType = expectedTypeRSA
case expectedTypeECDSA:
var result *ecdsa.PrivateKey
if dataStr == "" {
return result, nil
}
expectedType = expectedTypeECDSA
default:
return data, nil
}
if i, err = utils.ParseX509FromPEM([]byte(dataStr)); err != nil {
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParseBasic, "*", expectedType, err)
@ -376,6 +392,16 @@ func StringToRSAPrivateKeyHookFunc() mapstructure.DecodeHookFuncType {
switch r := i.(type) {
case *rsa.PrivateKey:
if expectedType != expectedTypeRSA {
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParseBasic, "*", expectedType, fmt.Errorf("the data is for a %T not a *%s", r, expectedType))
}
return r, nil
case *ecdsa.PrivateKey:
if expectedType != expectedTypeECDSA {
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParseBasic, "*", expectedType, fmt.Errorf("the data is for a %T not a *%s", r, expectedType))
}
return r, nil
default:
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParseBasic, "*", expectedType, fmt.Errorf("the data is for a %T not a *%s", r, expectedType))

View File

@ -23,8 +23,8 @@ import (
func TestStringToMailAddressHookFunc(t *testing.T) {
testCases := []struct {
desc string
have interface{}
want interface{}
have any
want any
err string
decode bool
}{
@ -78,8 +78,8 @@ func TestStringToMailAddressHookFunc(t *testing.T) {
func TestStringToMailAddressHookFuncPointer(t *testing.T) {
testCases := []struct {
desc string
have interface{}
want interface{}
have any
want any
err string
decode bool
}{
@ -139,8 +139,8 @@ func TestStringToMailAddressHookFuncPointer(t *testing.T) {
func TestStringToURLHookFunc(t *testing.T) {
testCases := []struct {
desc string
have interface{}
want interface{}
have any
want any
err string
decode bool
}{
@ -212,8 +212,8 @@ func TestStringToURLHookFunc(t *testing.T) {
func TestStringToURLHookFuncPointer(t *testing.T) {
testCases := []struct {
desc string
have interface{}
want interface{}
have any
want any
err string
decode bool
}{
@ -285,8 +285,8 @@ func TestStringToURLHookFuncPointer(t *testing.T) {
func TestToTimeDurationHookFunc(t *testing.T) {
testCases := []struct {
desc string
have interface{}
want interface{}
have any
want any
err string
decode bool
}{
@ -405,8 +405,8 @@ func TestToTimeDurationHookFunc(t *testing.T) {
func TestToTimeDurationHookFuncPointer(t *testing.T) {
testCases := []struct {
desc string
have interface{}
want interface{}
have any
want any
err string
decode bool
}{
@ -526,8 +526,8 @@ func TestToTimeDurationHookFuncPointer(t *testing.T) {
func TestStringToRegexpFunc(t *testing.T) {
testCases := []struct {
desc string
have interface{}
want interface{}
have any
want any
err string
decode bool
wantGrps []string
@ -640,8 +640,8 @@ func TestStringToRegexpFunc(t *testing.T) {
func TestStringToRegexpFuncPointers(t *testing.T) {
testCases := []struct {
desc string
have interface{}
want interface{}
have any
want any
err string
decode bool
wantGrps []string
@ -775,8 +775,8 @@ func TestStringToAddressHookFunc(t *testing.T) {
testCases := []struct {
name string
have interface{}
expected interface{}
have any
expected any
err string
decode bool
}{
@ -867,51 +867,108 @@ func TestStringToAddressHookFunc(t *testing.T) {
}
}
func TestStringToRSAPrivateKeyHookFunc(t *testing.T) {
var nilkey *rsa.PrivateKey
func TestStringToPrivateKeyHookFunc(t *testing.T) {
var (
nilRSA *rsa.PrivateKey
nilECDSA *ecdsa.PrivateKey
nilCert *x509.Certificate
)
testCases := []struct {
desc string
have interface{}
want interface{}
have any
want any
err string
decode bool
}{
{
desc: "ShouldDecodeRSAPrivateKey",
have: x509PrivateKeyRSA1,
want: mustParseRSAPrivateKey(x509PrivateKeyRSA1),
want: MustParseRSAPrivateKey(x509PrivateKeyRSA1),
decode: true,
},
{
desc: "ShouldDecodeECDSAPrivateKey",
have: x509PrivateKeyEC1,
want: MustParseECDSAPrivateKey(x509PrivateKeyEC1),
decode: true,
},
{
desc: "ShouldNotDecodeToECDSAPrivateKey",
have: x509PrivateKeyRSA1,
want: &ecdsa.PrivateKey{},
decode: false,
decode: true,
err: "could not decode to a *ecdsa.PrivateKey: the data is for a *rsa.PrivateKey not a *ecdsa.PrivateKey",
},
{
desc: "ShouldNotDecodeEmptyKey",
desc: "ShouldNotDecodeEmptyRSAKey",
have: "",
want: nilkey,
want: nilRSA,
decode: true,
},
{
desc: "ShouldNotDecodeEmptyECDSAKey",
have: "",
want: nilECDSA,
decode: true,
},
{
desc: "ShouldNotDecodeECDSAKeyToRSAKey",
have: x509PrivateKeyEC1,
want: nilkey,
want: nilRSA,
decode: true,
err: "could not decode to a *rsa.PrivateKey: the data is for a *ecdsa.PrivateKey not a *rsa.PrivateKey",
},
{
desc: "ShouldNotDecodeRSAKeyToECDSAKey",
have: x509PrivateKeyRSA1,
want: nilECDSA,
decode: true,
err: "could not decode to a *ecdsa.PrivateKey: the data is for a *rsa.PrivateKey not a *ecdsa.PrivateKey",
},
{
desc: "ShouldNotDecodeBadRSAPrivateKey",
have: x509PrivateKeyRSA2,
want: nilkey,
want: nilRSA,
decode: true,
err: "could not decode to a *rsa.PrivateKey: failed to parse PEM block containing the key",
},
{
desc: "ShouldNotDecodeBadECDSAPrivateKey",
have: x509PrivateKeyEC2,
want: nilECDSA,
decode: true,
err: "could not decode to a *ecdsa.PrivateKey: failed to parse PEM block containing the key",
},
{
desc: "ShouldNotDecodeCertificateToRSAPrivateKey",
have: x509CertificateRSA1,
want: nilRSA,
decode: true,
err: "could not decode to a *rsa.PrivateKey: the data is for a *x509.Certificate not a *rsa.PrivateKey",
},
{
desc: "ShouldNotDecodeCertificateToECDSAPrivateKey",
have: x509CertificateRSA1,
want: nilECDSA,
decode: true,
err: "could not decode to a *ecdsa.PrivateKey: the data is for a *x509.Certificate not a *ecdsa.PrivateKey",
},
{
desc: "ShouldNotDecodeRSAKeyToCertificate",
have: x509PrivateKeyRSA1,
want: nilCert,
decode: false,
},
{
desc: "ShouldNotDecodeECDSAKeyToCertificate",
have: x509PrivateKeyEC1,
want: nilCert,
decode: false,
},
}
hook := configuration.StringToRSAPrivateKeyHookFunc()
hook := configuration.StringToPrivateKeyHookFunc()
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
@ -944,25 +1001,25 @@ func TestStringToX509CertificateHookFunc(t *testing.T) {
{
desc: "ShouldDecodeRSACertificate",
have: x509CertificateRSA1,
want: mustParseX509Certificate(x509CertificateRSA1),
want: MustParseX509Certificate(x509CertificateRSA1),
decode: true,
},
{
desc: "ShouldDecodeECDSACertificate",
have: x509CACertificateECDSA,
want: mustParseX509Certificate(x509CACertificateECDSA),
want: MustParseX509Certificate(x509CACertificateECDSA),
decode: true,
},
{
desc: "ShouldDecodeRSACACertificate",
have: x509CACertificateRSA,
want: mustParseX509Certificate(x509CACertificateRSA),
want: MustParseX509Certificate(x509CACertificateRSA),
decode: true,
},
{
desc: "ShouldDecodeECDSACACertificate",
have: x509CACertificateECDSA,
want: mustParseX509Certificate(x509CACertificateECDSA),
want: MustParseX509Certificate(x509CACertificateECDSA),
decode: true,
},
{
@ -1020,50 +1077,50 @@ func TestStringToX509CertificateChainHookFunc(t *testing.T) {
{
desc: "ShouldDecodeRSACertificate",
have: x509CertificateRSA1,
expected: mustParseX509CertificateChain(x509CertificateRSA1),
expected: MustParseX509CertificateChain(x509CertificateRSA1),
decode: true,
},
{
desc: "ShouldDecodeRSACertificateNoPtr",
have: x509CertificateRSA1,
expected: *mustParseX509CertificateChain(x509CertificateRSA1),
expected: *MustParseX509CertificateChain(x509CertificateRSA1),
decode: true,
},
{
desc: "ShouldDecodeRSACertificateChain",
have: buildChain(x509CertificateRSA1, x509CACertificateRSA),
expected: mustParseX509CertificateChain(x509CertificateRSA1, x509CACertificateRSA),
have: BuildChain(x509CertificateRSA1, x509CACertificateRSA),
expected: MustParseX509CertificateChain(x509CertificateRSA1, x509CACertificateRSA),
decode: true,
},
{
desc: "ShouldDecodeRSACertificateChainNoPtr",
have: buildChain(x509CertificateRSA1, x509CACertificateRSA),
expected: *mustParseX509CertificateChain(x509CertificateRSA1, x509CACertificateRSA),
have: BuildChain(x509CertificateRSA1, x509CACertificateRSA),
expected: *MustParseX509CertificateChain(x509CertificateRSA1, x509CACertificateRSA),
decode: true,
},
{
desc: "ShouldNotDecodeBadRSACertificateChain",
have: buildChain(x509CertificateRSA1, x509CACertificateECDSA),
expected: mustParseX509CertificateChain(x509CertificateRSA1, x509CACertificateECDSA),
have: BuildChain(x509CertificateRSA1, x509CACertificateECDSA),
expected: MustParseX509CertificateChain(x509CertificateRSA1, x509CACertificateECDSA),
verr: "certificate #1 in chain is not signed properly by certificate #2 in chain: x509: signature algorithm specifies an RSA public key, but have public key of type *ecdsa.PublicKey",
decode: true,
},
{
desc: "ShouldDecodeECDSACertificate",
have: x509CACertificateECDSA,
expected: mustParseX509CertificateChain(x509CACertificateECDSA),
expected: MustParseX509CertificateChain(x509CACertificateECDSA),
decode: true,
},
{
desc: "ShouldDecodeRSACACertificate",
have: x509CACertificateRSA,
expected: mustParseX509CertificateChain(x509CACertificateRSA),
expected: MustParseX509CertificateChain(x509CACertificateRSA),
decode: true,
},
{
desc: "ShouldDecodeECDSACACertificate",
have: x509CACertificateECDSA,
expected: mustParseX509CertificateChain(x509CACertificateECDSA),
expected: MustParseX509CertificateChain(x509CACertificateECDSA),
decode: true,
},
{
@ -1175,6 +1232,11 @@ bad key
MHcCAQEEIMn970LSn8aKVhBM4vyUmpZyEdCT4riN+Lp4QU04zUhYoAoGCCqGSM49
AwEHoUQDQgAEMD69n22nd78GmaRDzy/s7muqhbc/OEnFS2mNtiRAA5FaX+kbkCB5
8pu/k2jkaSVNZtBYKPVAibHkhvakjVb66A==
-----END EC PRIVATE KEY-----`
x509PrivateKeyEC2 = `
-----BEGIN EC PRIVATE KEY-----
bad key
-----END EC PRIVATE KEY-----`
x509CertificateRSA1 = `
@ -1232,14 +1294,14 @@ uDv6M2spMi0CIQC8uOSMcv11vp1ylsGg38N6XYA+GQa1BHRd79+91hC+7w==
-----END CERTIFICATE-----`
)
func mustParseRSAPrivateKey(data string) *rsa.PrivateKey {
func MustParseRSAPrivateKey(data string) *rsa.PrivateKey {
block, _ := pem.Decode([]byte(data))
if block == nil || block.Bytes == nil || len(block.Bytes) == 0 {
panic("not pem encoded")
}
if block.Type != "RSA PRIVATE KEY" {
panic("not private key")
panic("not rsa private key")
}
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
@ -1250,7 +1312,25 @@ func mustParseRSAPrivateKey(data string) *rsa.PrivateKey {
return key
}
func mustParseX509Certificate(data string) *x509.Certificate {
func MustParseECDSAPrivateKey(data string) *ecdsa.PrivateKey {
block, _ := pem.Decode([]byte(data))
if block == nil || block.Bytes == nil || len(block.Bytes) == 0 {
panic("not pem encoded")
}
if block.Type != "EC PRIVATE KEY" {
panic("not ecdsa private key")
}
key, err := x509.ParseECPrivateKey(block.Bytes)
if err != nil {
panic(err)
}
return key
}
func MustParseX509Certificate(data string) *x509.Certificate {
block, _ := pem.Decode([]byte(data))
if block == nil || len(block.Bytes) == 0 {
panic("not a PEM")
@ -1264,7 +1344,7 @@ func mustParseX509Certificate(data string) *x509.Certificate {
return cert
}
func buildChain(pems ...string) string {
func BuildChain(pems ...string) string {
buf := bytes.Buffer{}
for i, data := range pems {
@ -1278,8 +1358,8 @@ func buildChain(pems ...string) string {
return buf.String()
}
func mustParseX509CertificateChain(datas ...string) *schema.X509CertificateChain {
chain, err := schema.NewX509CertificateChain(buildChain(datas...))
func MustParseX509CertificateChain(datas ...string) *schema.X509CertificateChain {
chain, err := schema.NewX509CertificateChain(BuildChain(datas...))
if err != nil {
panic(err)
}

View File

@ -63,7 +63,7 @@ func unmarshal(ko *koanf.Koanf, val *schema.StructValidator, path string, o inte
StringToAddressHookFunc(),
StringToX509CertificateHookFunc(),
StringToX509CertificateChainHookFunc(),
StringToRSAPrivateKeyHookFunc(),
StringToPrivateKeyHookFunc(),
ToTimeDurationHookFunc(),
),
Metadata: nil,