From bb0e3f80d4032abcd9ea81c9e8e1c2c8461b52f2 Mon Sep 17 00:00:00 2001 From: Riobard Date: Sat, 25 Feb 2017 12:07:06 +0800 Subject: [PATCH] Initial commit --- README.md | 3 +++ x25519.go | 51 ++++++++++++++++++++++++++++++++++++++++ x25519_test.go | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 README.md create mode 100644 x25519.go create mode 100644 x25519_test.go diff --git a/README.md b/README.md new file mode 100644 index 0000000..216ca71 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +x25519 Elliptic curve Diffie-Hellman key exchange over Curve25519 + +Doc at https://godoc.org/github.com/riobard/go-x25519 \ No newline at end of file diff --git a/x25519.go b/x25519.go new file mode 100644 index 0000000..8a5d2fa --- /dev/null +++ b/x25519.go @@ -0,0 +1,51 @@ +// Package x25519 implements Elliptic Curve Diffie-Hellman (ECDH) function over Curve25519. +// Details at https://cr.yp.to/ecdh.html and https://tools.ietf.org/html/rfc7748 +package x25519 + +import ( + "crypto/rand" + "io" + + "golang.org/x/crypto/curve25519" +) + +// KeySize is the size of keys in bytes used in this package. +const KeySize = 32 + +// PublicKey is the type of Curve25519 public keys +type PublicKey []byte + +// SecretKey is the type of Curve25519 secret keys +type SecretKey []byte + +// Public returns the PublicKey corresponding to the SecretKey. +func (k SecretKey) Public() PublicKey { + var sk, pk [KeySize]byte + copy(sk[:], k) + curve25519.ScalarBaseMult(&pk, &sk) + return pk[:] +} + +// GenerateKey generates a public/secret key pair using entropy from random, or crypto/rand.Reader +// if random is nil. +func GenerateKey(random io.Reader) (PublicKey, SecretKey, error) { + var pk, sk [KeySize]byte + if random == nil { + random = rand.Reader + } + if _, err := io.ReadFull(random, sk[:]); err != nil { + return nil, nil, err + } + // NOTE: clamping is not necessary because curve25519.ScaleMult will do it anyway. + curve25519.ScalarBaseMult(&pk, &sk) + return pk[:], sk[:], nil +} + +// ComputeSecret computes the shared secret between our secret key and their public key. +func ComputeSecret(our SecretKey, their PublicKey) []byte { + var shared, sk, pk [KeySize]byte + copy(sk[:], our) + copy(pk[:], their) + curve25519.ScalarMult(&shared, &sk, &pk) + return shared[:] +} diff --git a/x25519_test.go b/x25519_test.go new file mode 100644 index 0000000..a64af2a --- /dev/null +++ b/x25519_test.go @@ -0,0 +1,64 @@ +package x25519 + +import ( + "bytes" + "encoding/hex" + "testing" +) + +var ( + // Test vector from https://tools.ietf.org/html/rfc7748#section-6.1 + aliceSK = "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a" + alicePK = "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a" + bobSK = "5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb" + bobPK = "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f" + sharedSecret = "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742" +) + +func TestGenerateKey(t *testing.T) { + ask, err := hex.DecodeString(aliceSK) + if err != nil { + t.Fatal(err) + } + + apk := SecretKey(ask).Public() + if alicePK != hex.EncodeToString(apk[:]) { + t.Fatal("public key failed") + } + + bsk, err := hex.DecodeString(bobSK) + if err != nil { + t.Fatal(err) + } + bpk := SecretKey(bsk).Public() + + if bobPK != hex.EncodeToString(bpk) { + t.Fatal("public key failed") + } + + s1 := hex.EncodeToString(ComputeSecret(ask, bpk)) + s2 := hex.EncodeToString(ComputeSecret(bsk, apk)) + if s1 != s2 { + t.Fatal("shared secret failed") + } + if s1 != sharedSecret { + t.Fatal("shared secret failed") + } +} + +func TestGenerateKey2(t *testing.T) { + ourPK, ourSK, _ := GenerateKey(nil) + theirPK, theirSK, _ := GenerateKey(nil) + + t.Logf("our secret = %0x", ourSK) + t.Logf("our public = %0x", ourPK) + t.Logf("their secret = %0x", theirSK) + t.Logf("their public = %0x", theirPK) + + s1 := ComputeSecret(ourSK, theirPK) + s2 := ComputeSecret(theirSK, ourPK) + if !bytes.Equal(s1, s2) { + t.Fatal("computed shared secrets differs") + } + t.Logf("shared secret = %0x", s1) +}