]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/elliptic: add serialisation and key pair generation.
authorAdam Langley <agl@golang.org>
Thu, 16 Dec 2010 21:04:01 +0000 (16:04 -0500)
committerAdam Langley <agl@golang.org>
Thu, 16 Dec 2010 21:04:01 +0000 (16:04 -0500)
        This is a prerequisite to ECDHE support in crypto/tls.

R=r, rsc
CC=golang-dev
https://golang.org/cl/3685043

src/pkg/crypto/elliptic/elliptic.go
src/pkg/crypto/elliptic/elliptic_test.go

index 0f2277bfaf8c54dd7d43b6cbee5fd9950a796d28..beac45ca074489e641423491eac1f4fb8a2abbb8 100644 (file)
@@ -15,15 +15,18 @@ package elliptic
 
 import (
        "big"
+       "io"
+       "os"
        "sync"
 )
 
 // A Curve represents a short-form Weierstrass curve with a=-3.
 // See http://www.hyperelliptic.org/EFD/g1p/auto-shortw.html
 type Curve struct {
-       P      *big.Int // the order of the underlying field
-       B      *big.Int // the constant of the curve equation
-       Gx, Gy *big.Int // (x,y) of the base point
+       P       *big.Int // the order of the underlying field
+       B       *big.Int // the constant of the curve equation
+       Gx, Gy  *big.Int // (x,y) of the base point
+       BitSize int      // the size of the underlying field
 }
 
 // IsOnCurve returns true if the given (x,y) lies on the curve.
@@ -241,6 +244,60 @@ func (curve *Curve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) {
        return curve.ScalarMult(curve.Gx, curve.Gy, k)
 }
 
+var mask = []byte{0xff, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f}
+
+// GenerateKey returns a public/private key pair. The private key is generated
+// using the given reader, which must return random data.
+func (curve *Curve) GenerateKey(rand io.Reader) (priv []byte, x, y *big.Int, err os.Error) {
+       byteLen := (curve.BitSize + 7) >> 3
+       priv = make([]byte, byteLen)
+
+       for x == nil {
+               _, err = io.ReadFull(rand, priv)
+               if err != nil {
+                       return
+               }
+               // We have to mask off any excess bits in the case that the size of the
+               // underlying field is not a whole number of bytes.
+               priv[0] &= mask[curve.BitSize%8]
+               // This is because, in tests, rand will return all zeros and we don't
+               // want to get the point at infinity and loop forever.
+               priv[1] ^= 0x42
+               x, y = curve.ScalarBaseMult(priv)
+       }
+       return
+}
+
+// Marshal converts a point into the form specified in section 4.3.6 of ANSI
+// X9.62.
+func (curve *Curve) Marshal(x, y *big.Int) []byte {
+       byteLen := (curve.BitSize + 7) >> 3
+
+       ret := make([]byte, 1+2*byteLen)
+       ret[0] = 4 // uncompressed point
+
+       xBytes := x.Bytes()
+       copy(ret[1+byteLen-len(xBytes):], xBytes)
+       yBytes := y.Bytes()
+       copy(ret[1+2*byteLen-len(yBytes):], yBytes)
+       return ret
+}
+
+// Unmarshal converts a point, serialised by Marshal, into an x, y pair. On
+// error, x = nil.
+func (curve *Curve) Unmarshal(data []byte) (x, y *big.Int) {
+       byteLen := (curve.BitSize + 7) >> 3
+       if len(data) != 1+2*byteLen {
+               return
+       }
+       if data[0] != 4 { // uncompressed form
+               return
+       }
+       x = new(big.Int).SetBytes(data[1 : 1+byteLen])
+       y = new(big.Int).SetBytes(data[1+byteLen:])
+       return
+}
+
 var initonce sync.Once
 var p224 *Curve
 var p256 *Curve
@@ -261,6 +318,7 @@ func initP224() {
        p224.B, _ = new(big.Int).SetString("b4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4", 16)
        p224.Gx, _ = new(big.Int).SetString("b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21", 16)
        p224.Gy, _ = new(big.Int).SetString("bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34", 16)
+       p224.BitSize = 224
 }
 
 func initP256() {
@@ -270,6 +328,7 @@ func initP256() {
        p256.B, _ = new(big.Int).SetString("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16)
        p256.Gx, _ = new(big.Int).SetString("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16)
        p256.Gy, _ = new(big.Int).SetString("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16)
+       p256.BitSize = 256
 }
 
 func initP384() {
@@ -279,6 +338,7 @@ func initP384() {
        p384.B, _ = new(big.Int).SetString("b3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef", 16)
        p384.Gx, _ = new(big.Int).SetString("aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7", 16)
        p384.Gy, _ = new(big.Int).SetString("3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f", 16)
+       p384.BitSize = 384
 }
 
 func initP521() {
@@ -288,6 +348,7 @@ func initP521() {
        p521.B, _ = new(big.Int).SetString("051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00", 16)
        p521.Gx, _ = new(big.Int).SetString("c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66", 16)
        p521.Gy, _ = new(big.Int).SetString("11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650", 16)
+       p521.BitSize = 521
 }
 
 // P224 returns a Curve which implements P-224 (see FIPS 186-3, section D.2.2)
index 3707c429cbd841a9921392c31fcb9277afec6024..6ae6fb96d3ba6f259a2857a5fa72b5687058e2ba 100644 (file)
@@ -6,6 +6,7 @@ package elliptic
 
 import (
        "big"
+       "crypto/rand"
        "fmt"
        "testing"
 )
@@ -309,3 +310,22 @@ func BenchmarkBaseMult(b *testing.B) {
                p224.ScalarBaseMult(k.Bytes())
        }
 }
+
+func TestMarshal(t *testing.T) {
+       p224 := P224()
+       _, x, y, err := p224.GenerateKey(rand.Reader)
+       if err != nil {
+               t.Error(err)
+               return
+       }
+       serialised := p224.Marshal(x, y)
+       xx, yy := p224.Unmarshal(serialised)
+       if xx == nil {
+               t.Error("failed to unmarshal")
+               return
+       }
+       if xx.Cmp(x) != 0 || yy.Cmp(y) != 0 {
+               t.Error("unmarshal returned different values")
+               return
+       }
+}