diff --git a/xtypes/ecdsa_pub.go b/xtypes/ecdsa_pub.go index fe8b294..827f8e6 100644 --- a/xtypes/ecdsa_pub.go +++ b/xtypes/ecdsa_pub.go @@ -73,8 +73,22 @@ func (d *ECDSAPubKey) ValueValid(s string) error { // GetDefaultValue will be used to read the default value when showing usage // information. func (d *ECDSAPubKey) GetDefaultValue() (string, error) { - // TODO show the public key - return "", nil + if d.DefaultValue == nil { + return "", nil + } + der, err := x509.MarshalPKIXPublicKey(d.DefaultValue) + if err != nil { + return "", err + } + pemBlock := &pem.Block{ + Type: "PUBLIC KEY", + Bytes: der, + } + pemBytes := pem.EncodeToMemory(pemBlock) + if d.Base64Encoder != nil { + return d.Base64Encoder.EncodeToString(pemBytes), nil + } + return string(pemBytes), nil } func parseECPubKey(v string, base64Enc *base64.Encoding) (*ecdsa.PublicKey, error) { @@ -104,12 +118,12 @@ func parseECPubKey(v string, base64Enc *base64.Encoding) (*ecdsa.PublicKey, erro var err error pubK, err = x509.ParsePKIXPublicKey(pemBlock.Bytes) if err != nil { - return nil, fmt.Errorf("error decoding PEM block as ANS.1 public key: %w", err) + return nil, fmt.Errorf("error decoding PEM block as ASN.1 public key: %w", err) } ecpubK, ok := pubK.(*ecdsa.PublicKey) if !ok { - return nil, fmt.Errorf("expected key of type *ecdsa.pubateKey, but got type: %T", pubK) + return nil, fmt.Errorf("expected key of type *ecdsa.PublicKey, but got type: %T", pubK) } return ecpubK, nil diff --git a/xtypes/ecdsa_pub_test.go b/xtypes/ecdsa_pub_test.go index 69402a4..522aca8 100644 --- a/xtypes/ecdsa_pub_test.go +++ b/xtypes/ecdsa_pub_test.go @@ -5,6 +5,7 @@ import ( "crypto/elliptic" "crypto/rand" "crypto/x509" + "encoding/base64" "encoding/pem" "reflect" "testing" @@ -140,3 +141,31 @@ func generateTestECPubKey(t *testing.T) (*ecdsa.PublicKey, string) { } return &privateKey.PublicKey, string(pem.EncodeToMemory(pemBlock)) } + +func TestECDSAPubKey_GetDefaultValue(t *testing.T) { + pub, pubPEM := generateTestECPubKey(t) + + t.Run("nil default", func(t *testing.T) { + xt := &xtypes.ECDSAPubKey{DefaultValue: nil} + val, err := xt.GetDefaultValue() + assert.NoError(t, err) + assert.Equal(t, "", val) + }) + + t.Run("standard encoding", func(t *testing.T) { + xt := &xtypes.ECDSAPubKey{DefaultValue: pub} + val, err := xt.GetDefaultValue() + assert.NoError(t, err) + assert.Equal(t, pubPEM, val) + }) + + t.Run("with base64 encoder", func(t *testing.T) { + xt := &xtypes.ECDSAPubKey{ + DefaultValue: pub, + Base64Encoder: base64.StdEncoding, + } + val, err := xt.GetDefaultValue() + assert.NoError(t, err) + assert.Equal(t, base64.StdEncoding.EncodeToString([]byte(pubPEM)), val) + }) +} diff --git a/xtypes/x25519_pub.go b/xtypes/x25519_pub.go index db4e619..adcc999 100644 --- a/xtypes/x25519_pub.go +++ b/xtypes/x25519_pub.go @@ -12,10 +12,10 @@ import ( "github.com/simplesurance/proteus/types" ) -// X25519PublicKey is a xtype for *ecdh.PublicKey. The key format is expected +// X25519PubKey is a xtype for *ecdh.PublicKey. The key format is expected // to be on PKIX/PEM format, hex encoded (32 bytes), or raw bytes (optionally // base64 encoded). -type X25519PublicKey struct { +type X25519PubKey struct { DefaultValue *ecdh.PublicKey UpdateFn func(*ecdh.PublicKey) Base64Encoder *base64.Encoding @@ -25,10 +25,10 @@ type X25519PublicKey struct { } } -var _ types.XType = &X25519PublicKey{} +var _ types.XType = &X25519PubKey{} // UnmarshalParam parses the input as a string. -func (d *X25519PublicKey) UnmarshalParam(in *string) error { +func (d *X25519PubKey) UnmarshalParam(in *string) error { var pubK *ecdh.PublicKey if in != nil && *in != "" { var err error @@ -52,7 +52,7 @@ func (d *X25519PublicKey) UnmarshalParam(in *string) error { // Value reads the current updated value, taking the default value into // consideration. If the parameter is not marked as optional, this is // guaranteed to be not nil. -func (d *X25519PublicKey) Value() *ecdh.PublicKey { +func (d *X25519PubKey) Value() *ecdh.PublicKey { d.content.mutex.Lock() defer d.content.mutex.Unlock() @@ -65,7 +65,7 @@ func (d *X25519PublicKey) Value() *ecdh.PublicKey { // ValueValid test if the provided parameter value is valid. Has no side // effects. -func (d *X25519PublicKey) ValueValid(s string) error { +func (d *X25519PubKey) ValueValid(s string) error { if s == "" { return types.ErrNoValue } @@ -75,9 +75,14 @@ func (d *X25519PublicKey) ValueValid(s string) error { // GetDefaultValue will be used to read the default value when showing usage // information. -func (d *X25519PublicKey) GetDefaultValue() (string, error) { - // TODO show the public key - return "", nil +func (d *X25519PubKey) GetDefaultValue() (string, error) { + if d.DefaultValue == nil { + return "", nil + } + if d.Base64Encoder != nil { + return d.Base64Encoder.EncodeToString(d.DefaultValue.Bytes()), nil + } + return hex.EncodeToString(d.DefaultValue.Bytes()), nil } func parseX25519PublicKey(v string, base64Enc *base64.Encoding) (*ecdh.PublicKey, error) { @@ -103,7 +108,7 @@ func parseX25519PublicKey(v string, base64Enc *base64.Encoding) (*ecdh.PublicKey } pubK, err := x509.ParsePKIXPublicKey(pemBlock.Bytes) if err != nil { - return nil, fmt.Errorf("error decoding PEM block as ANS.1 public key: %w", err) + return nil, fmt.Errorf("error decoding PEM block as ASN.1 public key: %w", err) } xPubK, ok := pubK.(*ecdh.PublicKey) if !ok || xPubK.Curve() != ecdh.X25519() { diff --git a/xtypes/x25519_test.go b/xtypes/x25519_test.go index f201ab2..76eb98d 100644 --- a/xtypes/x25519_test.go +++ b/xtypes/x25519_test.go @@ -22,7 +22,7 @@ func TestX25519Keys(t *testing.T) { cfg := struct { Priv *xtypes.X25519PrivateKey - Pub *xtypes.X25519PublicKey + Pub *xtypes.X25519PubKey }{} testProvider := cfgtest.New(types.ParamValues{ @@ -49,7 +49,7 @@ func TestX25519KeysHex(t *testing.T) { cfg := struct { Priv *xtypes.X25519PrivateKey - Pub *xtypes.X25519PublicKey + Pub *xtypes.X25519PubKey }{} testProvider := cfgtest.New(types.ParamValues{ @@ -76,10 +76,10 @@ func TestX25519KeysBase64Raw(t *testing.T) { cfg := struct { Priv *xtypes.X25519PrivateKey - Pub *xtypes.X25519PublicKey + Pub *xtypes.X25519PubKey }{ Priv: &xtypes.X25519PrivateKey{Base64Encoder: base64.StdEncoding}, - Pub: &xtypes.X25519PublicKey{Base64Encoder: base64.StdEncoding}, + Pub: &xtypes.X25519PubKey{Base64Encoder: base64.StdEncoding}, } testProvider := cfgtest.New(types.ParamValues{ @@ -106,10 +106,10 @@ func TestX25519KeysBase64PEM(t *testing.T) { cfg := struct { Priv *xtypes.X25519PrivateKey - Pub *xtypes.X25519PublicKey + Pub *xtypes.X25519PubKey }{ Priv: &xtypes.X25519PrivateKey{Base64Encoder: base64.StdEncoding}, - Pub: &xtypes.X25519PublicKey{Base64Encoder: base64.StdEncoding}, + Pub: &xtypes.X25519PubKey{Base64Encoder: base64.StdEncoding}, } testProvider := cfgtest.New(types.ParamValues{ @@ -137,7 +137,7 @@ func TestX25519ValueValid(t *testing.T) { assert.Error(t, priv.ValueValid("")) _, pubPEM := generateTestX25519PubKey(t, nil) - pub := &xtypes.X25519PublicKey{} + pub := &xtypes.X25519PubKey{} assert.NoError(t, pub.ValueValid(pubPEM)) assert.NoError(t, pub.ValueValid(hex.EncodeToString(make([]byte, 32)))) @@ -168,7 +168,10 @@ func generateTestX25519PubKey(t *testing.T, priv *ecdh.PrivateKey) (*ecdh.Public if priv != nil { pub = priv.PublicKey() } else { - newPriv, _ := ecdh.X25519().GenerateKey(rand.Reader) + newPriv, err := ecdh.X25519().GenerateKey(rand.Reader) + if err != nil { + t.Fatalf("failed to generate X25519 private key: %v", err) + } pub = newPriv.PublicKey() } @@ -182,3 +185,32 @@ func generateTestX25519PubKey(t *testing.T, priv *ecdh.PrivateKey) (*ecdh.Public } return pub, string(pem.EncodeToMemory(pemBlock)) } + +func TestX25519PubKey_GetDefaultValue(t *testing.T) { + pub, _ := generateTestX25519PubKey(t, nil) + pubHex := hex.EncodeToString(pub.Bytes()) + + t.Run("nil default", func(t *testing.T) { + xt := &xtypes.X25519PubKey{DefaultValue: nil} + val, err := xt.GetDefaultValue() + assert.NoError(t, err) + assert.Equal(t, "", val) + }) + + t.Run("standard encoding (hex)", func(t *testing.T) { + xt := &xtypes.X25519PubKey{DefaultValue: pub} + val, err := xt.GetDefaultValue() + assert.NoError(t, err) + assert.Equal(t, pubHex, val) + }) + + t.Run("with base64 encoder", func(t *testing.T) { + xt := &xtypes.X25519PubKey{ + DefaultValue: pub, + Base64Encoder: base64.StdEncoding, + } + val, err := xt.GetDefaultValue() + assert.NoError(t, err) + assert.Equal(t, base64.StdEncoding.EncodeToString(pub.Bytes()), val) + }) +}