From 6f272f9b0eab71a91bca4a81032aef83810a1feb Mon Sep 17 00:00:00 2001 From: John Maguire Date: Fri, 22 Nov 2024 16:50:53 -0500 Subject: [PATCH] Update to match api changes --- client.go | 13 ++++++++-- client_test.go | 3 ++- crypto.go | 59 ++++++++++++++++++++-------------------------- crypto_test.go | 20 ++++++++++++++++ message/message.go | 8 +++---- 5 files changed, 63 insertions(+), 40 deletions(-) create mode 100644 crypto_test.go diff --git a/client.go b/client.go index 96eb8d2..03cb718 100644 --- a/client.go +++ b/client.go @@ -102,13 +102,18 @@ func (c *Client) Enroll(ctx context.Context, logger logrus.FieldLogger, code str return nil, nil, nil, nil, err } + hostPubkeyP256, err := MarshalECDSAP256PublicKey(keys.ecdsaP256PublicKey) + if err != nil { + return nil, nil, nil, nil, err + } + // Make a request to the API with the enrollment code jv, err := json.Marshal(message.EnrollRequest{ Code: code, DHPubkey: keys.x25519PublicKeyPEM, EdPubkey: cert.MarshalEd25519PublicKey(ed25519.PublicKey(keys.ed25519PublicKey)), P256ECDHPubkey: keys.ecdhP256PublicKeyPEM, - P256ECDSAPubkey: MarshalECDSAP256PublicKey(keys.ecdsaP256PublicKey), + P256ECDSAPubkey: hostPubkeyP256, Timestamp: time.Now(), }) if err != nil { @@ -246,7 +251,11 @@ func (c *Client) DoUpdate(ctx context.Context, creds Credentials) ([]byte, []byt privkeyPEM = newKeys.x25519PrivateKeyPEM privkey = Ed25519PrivateKey{newKeys.ed25519PrivateKey} case P256: - updateKeys.P256ECDSAPubkey = MarshalECDSAP256PublicKey(newKeys.ecdsaP256PublicKey) + b, err := MarshalECDSAP256PublicKey(newKeys.ecdsaP256PublicKey) + if err != nil { + return nil, nil, nil, fmt.Errorf("failed to marshal ECDSA P256 public key: %s", err) + } + updateKeys.P256ECDSAPubkey = b updateKeys.P256ECDHPubkey = newKeys.ecdhP256PublicKeyPEM privkeyPEM = newKeys.ecdhP256PrivateKeyPEM privkey = P256PrivateKey{newKeys.ecdsaP256PrivateKey} diff --git a/client_test.go b/client_test.go index ebdfd3a..7906cb4 100644 --- a/client_test.go +++ b/client_test.go @@ -195,7 +195,8 @@ func TestDoUpdate(t *testing.T) { config, pkey, creds, _, err := c.Enroll(ctx, testutil.NewTestLogger(), "foobar") require.NoError(t, err) - pubkey := cert.MarshalEd25519PublicKey(ed25519.PublicKey(creds.PrivateKey.PublicKey())) + // convert privkey to private key + pubkey := cert.MarshalEd25519PublicKey(creds.PrivateKey.Unwrap().(ed25519.PrivateKey).Public().(ed25519.PublicKey)) // make sure all credential values were set assert.NotEmpty(t, creds.HostID) diff --git a/crypto.go b/crypto.go index db03c85..031ec55 100644 --- a/crypto.go +++ b/crypto.go @@ -6,6 +6,7 @@ import ( "crypto/elliptic" "crypto/rand" "crypto/sha256" + "crypto/x509" "encoding/pem" "fmt" "io" @@ -17,15 +18,22 @@ import ( const ECDSAP256PublicKeyBanner = "NEBULA ECDSA P256 PUBLIC KEY" // MarshalECDSAP256PublicKey is a simple helper to PEM encode an ECDSA P256 public key -func MarshalECDSAP256PublicKey(key []byte) []byte { - return pem.EncodeToMemory(&pem.Block{Type: ECDSAP256PublicKeyBanner, Bytes: key}) +func MarshalECDSAP256PublicKey(k *ecdsa.PublicKey) ([]byte, error) { + b, err := x509.MarshalPKIXPublicKey(k) + if err != nil { + return nil, err + } + return pem.EncodeToMemory(&pem.Block{ + Type: ECDSAP256PublicKeyBanner, + Bytes: b, + }), nil } // PrivateKey is used to sign messages. type PrivateKey interface { Type() PrivateKeyType - PublicKey() PublicKey Sign(msg []byte) ([]byte, error) + Unwrap() interface{} } type PrivateKeyType int @@ -43,14 +51,14 @@ func (k Ed25519PrivateKey) Type() PrivateKeyType { return Ed25519 } -func (k Ed25519PrivateKey) PublicKey() PublicKey { - return PublicKey(k.Public().(ed25519.PublicKey)) -} - func (k Ed25519PrivateKey) Sign(msg []byte) ([]byte, error) { return ed25519.Sign(k.PrivateKey, msg), nil } +func (k Ed25519PrivateKey) Unwrap() interface{} { + return k.PrivateKey +} + type P256PrivateKey struct { *ecdsa.PrivateKey } @@ -59,34 +67,26 @@ func (k P256PrivateKey) Type() PrivateKeyType { return P256 } -func (k P256PrivateKey) PublicKey() PublicKey { - pkey, err := k.ECDH() - if err != nil { - panic(err) - } - - return pkey.PublicKey().Bytes() -} - func (k P256PrivateKey) Sign(msg []byte) ([]byte, error) { hashed := sha256.Sum256(msg) return ecdsa.SignASN1(rand.Reader, k.PrivateKey, hashed[:]) } -// PublicKey is used to carry the public key of a PrivateKey. -type PublicKey []byte +func (k P256PrivateKey) Unwrap() interface{} { + return k.PrivateKey +} type initialKeys struct { // 25519 Curve x25519PublicKeyPEM []byte // ECDH x25519PrivateKeyPEM []byte // ECDH - ed25519PublicKey PublicKey // EdDSA + ed25519PublicKey []byte // EdDSA ed25519PrivateKey ed25519.PrivateKey // EdDSA // P256 Curve ecdhP256PublicKeyPEM []byte // ECDH ecdhP256PrivateKeyPEM []byte // ECDH - ecdsaP256PublicKey PublicKey // ECDSA + ecdsaP256PublicKey *ecdsa.PublicKey // ECDSA ecdsaP256PrivateKey *ecdsa.PrivateKey // ECDSA, in spite of its type } @@ -104,7 +104,7 @@ func newKeys() (*initialKeys, error) { return &initialKeys{ x25519PublicKeyPEM: x25519PublicKeyPEM, x25519PrivateKeyPEM: x25519PrivateKeyPEM, - ed25519PublicKey: PublicKey(ed25519PublicKey), + ed25519PublicKey: ed25519PublicKey, ed25519PrivateKey: ed25519PrivateKey, ecdhP256PublicKeyPEM: ecdhP256PublicKeyPEM, ecdhP256PrivateKeyPEM: ecdhP256PrivateKeyPEM, @@ -129,7 +129,7 @@ func new25519Keys() ([]byte, []byte, ed25519.PublicKey, ed25519.PrivateKey, erro } // newP256Keys returns a new set of Nebula (Diffie-Hellman) ECDH P256 keys and a new set of ECDSA (request signing) keys. -func newP256Keys() ([]byte, []byte, []byte, *ecdsa.PrivateKey, error) { +func newP256Keys() ([]byte, []byte, *ecdsa.PublicKey, *ecdsa.PrivateKey, error) { ecdhPubkeyPEM, ecdhPrivkeyPEM, err := newNebulaP256Keypair() if err != nil { return nil, nil, nil, nil, fmt.Errorf("failed to generate Nebula keypair: %s", err) @@ -180,9 +180,9 @@ func newEd25519Keypair() (ed25519.PublicKey, ed25519.PrivateKey, error) { // newNebulaP256Keypair returns a new Nebula keypair (P256) in PEM format. func newNebulaP256Keypair() ([]byte, []byte, error) { - _, rawPrivkey, err := newP256Keypair() + rawPrivkey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("error while generating ecdsa keys: %s", err) } ecdhPrivkey, err := rawPrivkey.ECDH() @@ -197,20 +197,13 @@ func newNebulaP256Keypair() ([]byte, []byte, error) { } // newP256Keypair create a pair of P256 public key and private key and returns them, in that order. -func newP256Keypair() ([]byte, *ecdsa.PrivateKey, error) { +func newP256Keypair() (*ecdsa.PublicKey, *ecdsa.PrivateKey, error) { privkey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return nil, nil, fmt.Errorf("error while generating ecdsa keys: %s", err) } - // ecdh.PrivateKey lets us get at the encoded bytes, even though - // we aren't using ECDH here. - pubkey, err := privkey.ECDH() - if err != nil { - return nil, nil, err - } - - return pubkey.PublicKey().Bytes(), privkey, nil + return privkey.Public().(*ecdsa.PublicKey), privkey, nil } func nonce() []byte { diff --git a/crypto_test.go b/crypto_test.go new file mode 100644 index 0000000..bfa1ee1 --- /dev/null +++ b/crypto_test.go @@ -0,0 +1,20 @@ +package dnapi + +import ( + "crypto/ed25519" + "testing" + + "github.com/slackhq/nebula/cert" + "github.com/stretchr/testify/require" +) + +func TestNewKeys(t *testing.T) { + // TestNewKeys tests the creation of new keys + t.Parallel() + ik, err := newKeys() + require.NoError(t, err) + b, err := MarshalECDSAP256PublicKey(ik.ecdsaP256PublicKey) + require.NoError(t, err) + + t.Logf("ecdhP256PublicKeyPEM: %s, ecdsaP256PublicKey: %s, ed25519PublicKey: %s", ik.ecdhP256PublicKeyPEM, b, cert.MarshalEd25519PublicKey(ed25519.PublicKey(ik.ed25519PublicKey))) +} diff --git a/message/message.go b/message/message.go index 2f340d8..f9b103a 100644 --- a/message/message.go +++ b/message/message.go @@ -65,8 +65,8 @@ type CheckForUpdateResponse struct { type DoUpdateRequest struct { EdPubkeyPEM []byte `json:"edPubkeyPEM"` // X25519 (used for key exchange) DHPubkeyPEM []byte `json:"dhPubkeyPEM"` // Ed25519 (used for signing) - P256ECDHPubkey []byte `json:"p256ECDHPubkeyPEM"` // P256 (used for key exchange) - P256ECDSAPubkey []byte `json:"p256ECDSAPubkeyPEM"` // P256 (used for signing) + P256ECDHPubkey []byte `json:"ecdhP256PubkeyPEM"` // P256 (used for key exchange) + P256ECDSAPubkey []byte `json:"ecdsaP256PubkeyPEM"` // P256 (used for signing) Nonce []byte `json:"nonce"` } @@ -123,8 +123,8 @@ type EnrollRequest struct { Code string `json:"code"` DHPubkey []byte `json:"dhPubkey"` // X25519 (used for key exchange) EdPubkey []byte `json:"edPubkey"` // Ed25519 (used for signing) - P256ECDHPubkey []byte `json:"p256ECDHPubkey"` // P256 (used for key exchange) - P256ECDSAPubkey []byte `json:"p256ECDSAPubkey"` // P256 (used for signing) + P256ECDHPubkey []byte `json:"ecdhP256Pubkey"` // P256 (used for key exchange) + P256ECDSAPubkey []byte `json:"ecdsaP256Pubkey"` // P256 (used for signing) Timestamp time.Time `json:"timestamp"` }