diff --git a/x25519.go b/x25519.go index ef3010a..719bf71 100644 --- a/x25519.go +++ b/x25519.go @@ -13,59 +13,65 @@ import ( // KeySize is the size of keys in bytes used in this package. const KeySize = 32 -type PublicKey [32]byte - -func (pk *PublicKey) String() string { return string(pk[:]) } - -// SecretKey is the type of Curve25519 secret keys +// SecretKey is the type of Curve25519 secret keys. type SecretKey struct { - sk [32]byte - pk *PublicKey - ur *[32]byte + sk []byte + pk PublicKey + ur []byte // uniform representative of pk } -func NewSecretKey(sk []byte) *SecretKey { - k := new(SecretKey) - copy(k.sk[:], sk) - return k -} - -func (k *SecretKey) Bytes() []byte { return k.sk[:] } -func (k *SecretKey) String() string { return string(k.sk[:]) } +// Bytes returns the secret key as a byte slice. +func (k *SecretKey) Bytes() []byte { return k.sk } // Public returns the PublicKey corresponding to the secret key. -func (k *SecretKey) Public() *PublicKey { +func (k *SecretKey) Public() PublicKey { if k.pk == nil { - k.pk = new(PublicKey) - curve25519.ScalarBaseMult((*[32]byte)(k.pk), &k.sk) + var pk, sk [32]byte + copy(sk[:], k.sk) + curve25519.ScalarBaseMult(&pk, &sk) + k.pk = pk[:] } return k.pk } -// Uniform returns the uniform representative of the public key corresponding to the secret key, or nil +// PublicUniform returns the uniform representative of the public key corresponding to the secret key, or nil // if the public key does not have a uniform representative. -func (k *SecretKey) Uniform() *[32]byte { +func (k *SecretKey) PublicUniform() UniformRepresentative { if k.ur == nil { - pk := new(PublicKey) - ur := new([32]byte) - if extra25519.ScalarBaseMult((*[32]byte)(pk), ur, &k.sk) { - k.pk = pk - k.ur = ur + var sk, pk, ur [32]byte + copy(sk[:], k.sk) + if extra25519.ScalarBaseMult(&pk, &ur, &sk) { + k.pk = pk[:] + k.ur = ur[:] } } return k.ur } -// Shared computes the shared secret between our secret key and their public key. -func (k *SecretKey) Shared(shared *[32]byte, theirPublic *PublicKey) { - curve25519.ScalarMult(shared, &k.sk, (*[32]byte)(theirPublic)) +// Shared computes the shared secret between our secret key and peer's public key. +func (k *SecretKey) Shared(peer PublicKey) []byte { + var shared, sk, pk [32]byte + copy(sk[:], k.sk) + copy(pk[:], peer) + curve25519.ScalarMult(&shared, &sk, &pk) + return shared[:] } -// SharedUniform computes the shared secret between our secret key and their public key's uniform representative. -func (k *SecretKey) SharedUniform(shared, theirRepresentative *[32]byte) { - pk := new([32]byte) - extra25519.RepresentativeToPublicKey(pk, theirRepresentative) - curve25519.ScalarMult(shared, &k.sk, pk) +// SharedUniform computes the shared secret between our secret key and peer public key's uniform representative. +func (k *SecretKey) SharedUniform(peer UniformRepresentative) []byte { + var shared, pk, sk, ur [32]byte + copy(ur[:], peer) + copy(sk[:], k.sk) + extra25519.RepresentativeToPublicKey(&pk, &ur) + curve25519.ScalarMult(&shared, &sk, &pk) + return shared[:] +} + +// NewSecretKey creates a SecretKey from byte slice sk and len(sk) must be 32. +func NewSecretKey(sk []byte) *SecretKey { + k := new(SecretKey) + k.sk = sk + return k } // GenerateKey generates a secret key using entropy from random, or crypto/rand.Reader @@ -74,9 +80,10 @@ func GenerateKey(random io.Reader) (*SecretKey, error) { if random == nil { random = rand.Reader } - sk := new(SecretKey) - _, err := io.ReadFull(random, sk.sk[:]) - return sk, err + k := new(SecretKey) + k.sk = make([]byte, 32) + _, err := io.ReadFull(random, k.sk) + return k, err } // GenerateKeyUniform generates a secret key whose corresponding public key has a uniform representative @@ -85,16 +92,30 @@ func GenerateKeyUniform(random io.Reader) (*SecretKey, error) { if random == nil { random = rand.Reader } - sk := new(SecretKey) - for ok := false; !ok; ok = extra25519.ScalarBaseMult((*[32]byte)(sk.pk), sk.ur, &sk.sk) { - if _, err := io.ReadFull(random, sk.sk[:]); err != nil { + var pk, sk, ur [32]byte + for ok := false; !ok; ok = extra25519.ScalarBaseMult(&pk, &ur, &sk) { + if _, err := io.ReadFull(random, sk[:]); err != nil { return nil, err } } - return sk, nil + k := new(SecretKey) + k.sk = sk[:] + k.pk = pk[:] + k.ur = ur[:] + return k, nil } -// RepresentativeToPublicKey converts a uniform representative to a curve25519 public key. -func RepresentativeToPublicKey(publicKey, representative *[32]byte) { - extra25519.RepresentativeToPublicKey(publicKey, representative) +// UniformRepresentative is the type of Curve25519 public key uniform representatives. +// See https://www.imperialviolet.org/2013/12/25/elligator.html +type UniformRepresentative []byte + +// Public returns the curve25519 public key corresponding to the uniform presentative. +func (u UniformRepresentative) Public() PublicKey { + var pk, ur [32]byte + copy(ur[:], u) + extra25519.RepresentativeToPublicKey(&pk, &ur) + return pk[:] } + +// PublicKey is the type of Curve25519 public keys. +type PublicKey []byte diff --git a/x25519_test.go b/x25519_test.go index b3b8f13..bd9183b 100644 --- a/x25519_test.go +++ b/x25519_test.go @@ -15,7 +15,7 @@ var ( sharedSecret = "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742" ) -func TestGenerateKey(t *testing.T) { +func TestStardardKey(t *testing.T) { askhex, err := hex.DecodeString(aliceSK) if err != nil { t.Fatal(err) @@ -38,33 +38,40 @@ func TestGenerateKey(t *testing.T) { t.Fatal("public key failed") } - s1 := new([32]byte) - s2 := new([32]byte) - ask.Shared(s1, bpk) - bsk.Shared(s2, apk) - if !bytes.Equal(s1[:], s2[:]) { + s1 := ask.Shared(bpk) + s2 := bsk.Shared(apk) + if !bytes.Equal(s1, s2) { t.Fatal("shared secret failed") } - if hex.EncodeToString(s1[:]) != sharedSecret { + if hex.EncodeToString(s1) != sharedSecret { t.Fatal("shared secret failed") } } -func TestGenerateKey2(t *testing.T) { - ourSK, _ := GenerateKey(nil) - theirSK, _ := GenerateKey(nil) - - t.Logf("our secret = %0x", ourSK) - t.Logf("our public = %0x", ourSK.Public()) - t.Logf("their secret = %0x", theirSK) - t.Logf("their public = %0x", theirSK.Public()) - - s1 := new([32]byte) - s2 := new([32]byte) - ourSK.Shared(s1, theirSK.Public()) - theirSK.Shared(s2, ourSK.Public()) - if !bytes.Equal(s1[:], s2[:]) { - t.Fatal("computed shared secrets differs") +func TestGenerateKey(t *testing.T) { + for i := 0; i < 100; i++ { + ourSK, _ := GenerateKey(nil) + theirSK, _ := GenerateKey(nil) + s1 := ourSK.Shared(theirSK.Public()) + s2 := theirSK.Shared(ourSK.Public()) + if !bytes.Equal(s1, s2) { + t.Fatal("computed shared secrets differs") + } + } +} + +func TestUniformRepresentative(t *testing.T) { + + for i := 0; i < 1; i++ { + k, _ := GenerateKey(nil) + sk, _ := GenerateKeyUniform(nil) + pk := sk.Public() + ur := sk.PublicUniform() + if !bytes.Equal(ur.Public(), pk) { + t.Fatal("public key and its uniform representative do not match") + } + if !bytes.Equal(k.Shared(sk.Public()), k.SharedUniform(sk.PublicUniform())) { + t.Fatalf("shared secrets do not match") + } } - t.Logf("shared secret = %0x", s1) }