Use base64 encoded signing key format

This commit is contained in:
Herman Slatman 2024-01-11 17:04:08 +01:00
parent 1f5f756fce
commit 1bf807add3
No known key found for this signature in database
GPG Key ID: F4D8A44EA0A75A4F
6 changed files with 62 additions and 26 deletions

View File

@ -885,6 +885,10 @@ func TestHandler_NewOrder(t *testing.T) {
u := fmt.Sprintf("%s/acme/%s/order/ordID",
baseURL.String(), escProvName)
fakeWireSigningKey := `-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
-----END PUBLIC KEY-----`
type test struct {
ca acme.CertificateAuthority
db acme.DB
@ -1737,7 +1741,9 @@ func TestHandler_NewOrder(t *testing.T) {
Now: time.Now,
},
},
DPOP: &wire.DPOPOptions{},
DPOP: &wire.DPOPOptions{
SigningKey: []byte(fakeWireSigningKey),
},
},
})
acc := &acme.Account{ID: "accID"}

View File

@ -51,6 +51,9 @@ func newWireProvisionerWithOptions(t *testing.T, options *provisioner.Options) *
}
func TestWireIntegration(t *testing.T) {
fakeKey := `-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
-----END PUBLIC KEY-----`
prov := newWireProvisionerWithOptions(t, &provisioner.Options{
Wire: &wire.Options{
OIDC: &wire.OIDCOptions{
@ -72,7 +75,9 @@ func TestWireIntegration(t *testing.T) {
Now: time.Now,
},
},
DPOP: &wire.DPOPOptions{},
DPOP: &wire.DPOPOptions{
SigningKey: []byte(fakeKey),
},
},
})

View File

@ -490,10 +490,9 @@ func wireDPOP01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSO
return WrapErrorISE(err, "invalid Go template registered for 'target'")
}
key := dpopOptions.GetSigningKey()
params := verifyParams{
token: dpopPayload.AccessToken,
key: key,
key: dpopOptions.GetSigningKey(),
kid: kid,
issuer: issuer,
wireID: wireID,
@ -548,7 +547,7 @@ type wireDpopToken map[string]any
type verifyParams struct {
token string
key string
key crypto.PublicKey
issuer string
kid string
wireID wire.ID
@ -557,23 +556,13 @@ type verifyParams struct {
}
func parseAndVerifyWireAccessToken(v verifyParams) (*wireAccessToken, *wireDpopToken, error) {
k, err := pemutil.Parse([]byte(v.key)) // TODO(hs): move this to earlier in the configuration process? Do it once?
if err != nil {
return nil, nil, fmt.Errorf("failed parsing public key: %w", err)
}
pk, ok := k.(ed25519.PublicKey) // TODO(hs): allow more key types
if !ok {
return nil, nil, fmt.Errorf("unexpected type: %T", k)
}
jwt, err := jose.ParseSigned(v.token)
if err != nil {
return nil, nil, fmt.Errorf("failed parsing token: %w", err)
}
var accessToken wireAccessToken
if err = jwt.Claims(pk, &accessToken); err != nil {
if err = jwt.Claims(v.key, &accessToken); err != nil {
return nil, nil, fmt.Errorf("failed getting token claims: %w", err)
}

View File

@ -34,6 +34,7 @@ import (
"go.step.sm/crypto/jose"
"go.step.sm/crypto/keyutil"
"go.step.sm/crypto/minica"
"go.step.sm/crypto/pemutil"
"go.step.sm/crypto/x509util"
"github.com/smallstep/certificates/acme/wire"
@ -4305,10 +4306,11 @@ func createSubjectAltNameExtension(dnsNames, emailAddresses x509util.MultiString
}
func Test_parseAndVerifyWireAccessToken(t *testing.T) {
key := `
-----BEGIN PUBLIC KEY-----
key := `-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
-----END PUBLIC KEY-----` // TODO(hs): different format?
-----END PUBLIC KEY-----`
publicKey, err := pemutil.Parse([]byte(key))
require.NoError(t, err)
issuer := "https://wire.example.com/clients/314845990100130665/access-token"
kid := "QAv6C9q47Cyfd1u9z6uX3V_o-t11S8p81wLH-oTRlh0"
wireID := wire.ID{
@ -4327,7 +4329,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
at, dpop, err := parseAndVerifyWireAccessToken(verifyParams{
token: token,
key: key,
key: publicKey,
kid: kid,
issuer: issuer,
wireID: wireID,

View File

@ -2,23 +2,31 @@ package wire
import (
"bytes"
"crypto"
"errors"
"fmt"
"text/template"
"go.step.sm/crypto/pemutil"
)
type DPOPOptions struct {
// Public part of the signing key for DPoP access token
SigningKey string `json:"key"`
SigningKey []byte `json:"key"`
// URI template acme client must call to fetch the DPoP challenge proof (an access token from wire-server)
Target string `json:"target"`
}
func (o *DPOPOptions) GetSigningKey() string {
func (o *DPOPOptions) GetSigningKey() crypto.PublicKey {
if o == nil {
return ""
return nil
}
return o.SigningKey
k, err := pemutil.Parse(o.SigningKey) // TODO(hs): do this once?
if err != nil {
return nil
}
return k
}
func (o *DPOPOptions) GetTarget() string {
@ -44,6 +52,9 @@ func (o *DPOPOptions) EvaluateTarget(deviceID string) (string, error) {
}
func (o *DPOPOptions) validate() error {
if _, err := pemutil.Parse(o.SigningKey); err != nil {
return fmt.Errorf("failed parsing key: %w", err)
}
if _, err := template.New("DeviceID").Parse(o.GetTarget()); err != nil {
return fmt.Errorf("failed parsing template: %w", err)
}

View File

@ -8,6 +8,10 @@ import (
)
func TestOptions_Validate(t *testing.T) {
key := []byte(`-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
-----END PUBLIC KEY-----`)
type fields struct {
OIDC *OIDCOptions
DPOP *DPOPOptions
@ -26,7 +30,9 @@ func TestOptions_Validate(t *testing.T) {
},
Config: &Config{},
},
DPOP: &DPOPOptions{},
DPOP: &DPOPOptions{
SigningKey: key,
},
},
expectedErr: nil,
},
@ -90,6 +96,22 @@ func TestOptions_Validate(t *testing.T) {
},
expectedErr: errors.New("no DPoP options available"),
},
{
name: "fail/invalid-key",
fields: fields{
OIDC: &OIDCOptions{
Provider: &Provider{
IssuerURL: "https://example.com",
},
Config: &Config{},
},
DPOP: &DPOPOptions{
SigningKey: []byte{0x00},
Target: "",
},
},
expectedErr: errors.New(`failed validating DPoP options: failed parsing key: error decoding PEM: not a valid PEM encoded block`),
},
{
name: "fail/target-template",
fields: fields{
@ -100,7 +122,8 @@ func TestOptions_Validate(t *testing.T) {
Config: &Config{},
},
DPOP: &DPOPOptions{
Target: "{{}",
SigningKey: key,
Target: "{{}",
},
},
expectedErr: errors.New(`failed validating DPoP options: failed parsing template: template: DeviceID:1: unexpected "}" in command`),