]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/dsa: add support for DSA
authorAdam Langley <agl@golang.org>
Tue, 25 Jan 2011 17:25:53 +0000 (12:25 -0500)
committerAdam Langley <agl@golang.org>
Tue, 25 Jan 2011 17:25:53 +0000 (12:25 -0500)
R=bradfitzgo, r, bradfitzwork, nsz, rsc
CC=golang-dev
https://golang.org/cl/3990043

src/pkg/Makefile
src/pkg/crypto/dsa/Makefile [new file with mode: 0644]
src/pkg/crypto/dsa/dsa.go [new file with mode: 0644]
src/pkg/crypto/dsa/dsa_test.go [new file with mode: 0644]

index 6e3c7f05f7c6fd6c510a863968a4a0b203a7001b..619e8aa26125eb83c751e38b6dde71df0595445d 100644 (file)
@@ -33,6 +33,7 @@ DIRS=\
        crypto/blowfish\
        crypto/cast5\
        crypto/cipher\
+       crypto/dsa\
        crypto/elliptic\
        crypto/hmac\
        crypto/md4\
diff --git a/src/pkg/crypto/dsa/Makefile b/src/pkg/crypto/dsa/Makefile
new file mode 100644 (file)
index 0000000..fa89d4a
--- /dev/null
@@ -0,0 +1,11 @@
+# Copyright 2011 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+include ../../../Make.inc
+
+TARG=crypto/dsa
+GOFILES=\
+       dsa.go\
+
+include ../../../Make.pkg
diff --git a/src/pkg/crypto/dsa/dsa.go b/src/pkg/crypto/dsa/dsa.go
new file mode 100644 (file)
index 0000000..f0af8bb
--- /dev/null
@@ -0,0 +1,276 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package dsa implements the Digital Signature Algorithm, as defined in FIPS 186-3
+package dsa
+
+import (
+       "big"
+       "io"
+       "os"
+)
+
+// Parameters represents the domain parameters for a key. These parameters can
+// be shared across many keys. The bit length of Q must be a multiple of 8.
+type Parameters struct {
+       P, Q, G *big.Int
+}
+
+// PublicKey represents a DSA public key.
+type PublicKey struct {
+       Parameters
+       Y *big.Int
+}
+
+// PrivateKey represents a DSA private key.
+type PrivateKey struct {
+       PublicKey
+       X *big.Int
+}
+
+type invalidPublicKeyError int
+
+func (invalidPublicKeyError) String() string {
+       return "crypto/dsa: invalid public key"
+}
+
+// InvalidPublicKeyError results when a public key is not usable by this code.
+// FIPS is quite strict about the format of DSA keys, but other code may be
+// less so. Thus, when using keys which may have been generated by other code,
+// this error must be handled.
+var InvalidPublicKeyError = invalidPublicKeyError(0)
+
+// ParameterSizes is a enumeration of the acceptable bit lengths of the primes
+// in a set of DSA parameters. See FIPS 186-3, section 4.2.
+type ParameterSizes int
+
+const (
+       L1024N160 ParameterSizes = iota
+       L2048N224
+       L2048N256
+       L3072N256
+)
+
+// numMRTests is the number of Miller-Rabin primality tests that we perform. We
+// pick the largest recommended number from table C.1 of FIPS 186-3.
+const numMRTests = 64
+
+// GenerateParameters puts a random, valid set of DSA parameters into params.
+// This function takes many seconds, even on fast machines.
+func GenerateParameters(params *Parameters, rand io.Reader, sizes ParameterSizes) (err os.Error) {
+       // This function doesn't follow FIPS 186-3 exactly in that it doesn't
+       // use a verification seed to generate the primes. The verification
+       // seed doesn't appear to be exported or used by other code and
+       // omitting it makes the code cleaner.
+
+       var L, N int
+       switch sizes {
+       case L1024N160:
+               L = 1024
+               N = 160
+       case L2048N224:
+               L = 2048
+               N = 224
+       case L2048N256:
+               L = 2048
+               N = 256
+       case L3072N256:
+               L = 3072
+               N = 256
+       default:
+               return os.ErrorString("crypto/dsa: invalid ParameterSizes")
+       }
+
+       qBytes := make([]byte, N/8)
+       pBytes := make([]byte, L/8)
+
+       q := new(big.Int)
+       p := new(big.Int)
+       rem := new(big.Int)
+       one := new(big.Int)
+       one.SetInt64(1)
+
+GeneratePrimes:
+       for {
+               _, err = io.ReadFull(rand, qBytes)
+               if err != nil {
+                       return
+               }
+
+               qBytes[len(qBytes)-1] |= 1
+               qBytes[0] |= 0x80
+               q.SetBytes(qBytes)
+
+               if !big.ProbablyPrime(q, numMRTests) {
+                       continue
+               }
+
+               for i := 0; i < 4*L; i++ {
+                       _, err = io.ReadFull(rand, pBytes)
+                       if err != nil {
+                               return
+                       }
+
+                       pBytes[len(pBytes)-1] |= 1
+                       pBytes[0] |= 0x80
+
+                       p.SetBytes(pBytes)
+                       rem.Mod(p, q)
+                       rem.Sub(rem, one)
+                       p.Sub(p, rem)
+                       if p.BitLen() < L {
+                               continue
+                       }
+
+                       if !big.ProbablyPrime(p, numMRTests) {
+                               continue
+                       }
+
+                       params.P = p
+                       params.Q = q
+                       break GeneratePrimes
+               }
+       }
+
+       h := new(big.Int)
+       h.SetInt64(2)
+       g := new(big.Int)
+
+       pm1 := new(big.Int).Sub(p, one)
+       e := new(big.Int).Div(pm1, q)
+
+       for {
+               g.Exp(h, e, p)
+               if g.Cmp(one) == 0 {
+                       h.Add(h, one)
+                       continue
+               }
+
+               params.G = g
+               return
+       }
+
+       panic("unreachable")
+}
+
+// GenerateKey generates a public&private key pair. The Parameters of the
+// PrivateKey must already be valid (see GenerateParameters).
+func GenerateKey(priv *PrivateKey, rand io.Reader) os.Error {
+       if priv.P == nil || priv.Q == nil || priv.G == nil {
+               return os.ErrorString("crypto/dsa: parameters not set up before generating key")
+       }
+
+       x := new(big.Int)
+       xBytes := make([]byte, priv.Q.BitLen()/8)
+
+       for {
+               _, err := io.ReadFull(rand, xBytes)
+               if err != nil {
+                       return err
+               }
+               x.SetBytes(xBytes)
+               if x.Sign() != 0 && x.Cmp(priv.Q) < 0 {
+                       break
+               }
+       }
+
+       priv.X = x
+       priv.Y = new(big.Int)
+       priv.Y.Exp(priv.G, x, priv.P)
+       return nil
+}
+
+// Sign signs an arbitrary length hash (which should be the result of hashing a
+// larger message) using the private key, priv. It returns the signature as a
+// pair of integers. The security of the private key depends on the entropy of
+// rand.
+func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err os.Error) {
+       // FIPS 186-3, section 4.6
+
+       n := priv.Q.BitLen()
+       if n&7 != 0 {
+               err = InvalidPublicKeyError
+               return
+       }
+       n >>= 3
+
+       for {
+               k := new(big.Int)
+               buf := make([]byte, n)
+               for {
+                       _, err = io.ReadFull(rand, buf)
+                       if err != nil {
+                               return
+                       }
+                       k.SetBytes(buf)
+                       if k.Sign() > 0 && k.Cmp(priv.Q) < 0 {
+                               break
+                       }
+               }
+
+               kInv := new(big.Int).ModInverse(k, priv.Q)
+
+               r = new(big.Int).Exp(priv.G, k, priv.P)
+               r.Mod(r, priv.Q)
+
+               if r.Sign() == 0 {
+                       continue
+               }
+
+               if n > len(hash) {
+                       n = len(hash)
+               }
+               z := k.SetBytes(hash[:n])
+
+               s = new(big.Int).Mul(priv.X, r)
+               s.Add(s, z)
+               s.Mod(s, priv.Q)
+               s.Mul(s, kInv)
+               s.Mod(s, priv.Q)
+
+               if s.Sign() != 0 {
+                       break
+               }
+       }
+
+       return
+}
+
+// Verify verifies the signature in r, s of hash using the public key, pub. It
+// returns true iff the signature is valid.
+func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool {
+       // FIPS 186-3, section 4.7
+
+       if r.Sign() < 1 || r.Cmp(pub.Q) >= 0 {
+               return false
+       }
+       if s.Sign() < 1 || s.Cmp(pub.Q) >= 0 {
+               return false
+       }
+
+       w := new(big.Int).ModInverse(s, pub.Q)
+
+       n := pub.Q.BitLen()
+       if n&7 != 0 {
+               return false
+       }
+       n >>= 3
+
+       if n > len(hash) {
+               n = len(hash)
+       }
+       z := new(big.Int).SetBytes(hash[:n])
+
+       u1 := new(big.Int).Mul(z, w)
+       u1.Mod(u1, pub.Q)
+       u2 := w.Mul(r, w)
+       u2.Mod(u2, pub.Q)
+       v := u1.Exp(pub.G, u1, pub.P)
+       u2.Exp(pub.Y, u2, pub.P)
+       v.Mul(v, u2)
+       v.Mod(v, pub.P)
+       v.Mod(v, pub.Q)
+
+       return v.Cmp(r) == 0
+}
diff --git a/src/pkg/crypto/dsa/dsa_test.go b/src/pkg/crypto/dsa/dsa_test.go
new file mode 100644 (file)
index 0000000..deec08d
--- /dev/null
@@ -0,0 +1,84 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package dsa
+
+import (
+       "big"
+       "crypto/rand"
+       "testing"
+)
+
+func testSignAndVerify(t *testing.T, i int, priv *PrivateKey) {
+       hashed := []byte("testing")
+       r, s, err := Sign(rand.Reader, priv, hashed)
+       if err != nil {
+               t.Errorf("%d: error signing: %s", i, err)
+               return
+       }
+
+       if !Verify(&priv.PublicKey, hashed, r, s) {
+               t.Errorf("%d: Verify failed", i)
+       }
+}
+
+func testParameterGeneration(t *testing.T, sizes ParameterSizes, L, N int) {
+       var priv PrivateKey
+       params := &priv.Parameters
+
+       err := GenerateParameters(params, rand.Reader, sizes)
+       if err != nil {
+               t.Errorf("%d: %s", int(sizes), err)
+               return
+       }
+
+       if params.P.BitLen() != L {
+               t.Errorf("%d: params.BitLen got:%d want:%d", int(sizes), params.P.BitLen(), L)
+       }
+
+       if params.Q.BitLen() != N {
+               t.Errorf("%d: q.BitLen got:%d want:%d", int(sizes), params.Q.BitLen(), L)
+       }
+
+       one := new(big.Int)
+       one.SetInt64(1)
+       pm1 := new(big.Int).Sub(params.P, one)
+       quo, rem := new(big.Int).DivMod(pm1, params.Q, new(big.Int))
+       if rem.Sign() != 0 {
+               t.Errorf("%d: p-1 mod q != 0", int(sizes))
+       }
+       x := new(big.Int).Exp(params.G, quo, params.P)
+       if x.Cmp(one) == 0 {
+               t.Errorf("%d: invalid generator", int(sizes))
+       }
+
+       err = GenerateKey(&priv, rand.Reader)
+       if err != nil {
+               t.Errorf("error generating key: %s", err)
+               return
+       }
+
+       testSignAndVerify(t, int(sizes), &priv)
+}
+
+func TestParameterGeneration(t *testing.T) {
+       // This test is too slow to run all the time.
+       return
+
+       testParameterGeneration(t, L1024N160, 1024, 160)
+       testParameterGeneration(t, L2048N224, 2048, 224)
+       testParameterGeneration(t, L2048N256, 2048, 256)
+       testParameterGeneration(t, L3072N256, 3072, 256)
+}
+
+func TestSignAndVerify(t *testing.T) {
+       var priv PrivateKey
+       priv.P, _ = new(big.Int).SetString("A9B5B793FB4785793D246BAE77E8FF63CA52F442DA763C440259919FE1BC1D6065A9350637A04F75A2F039401D49F08E066C4D275A5A65DA5684BC563C14289D7AB8A67163BFBF79D85972619AD2CFF55AB0EE77A9002B0EF96293BDD0F42685EBB2C66C327079F6C98000FBCB79AACDE1BC6F9D5C7B1A97E3D9D54ED7951FEF", 16)
+       priv.Q, _ = new(big.Int).SetString("E1D3391245933D68A0714ED34BBCB7A1F422B9C1", 16)
+       priv.G, _ = new(big.Int).SetString("634364FC25248933D01D1993ECABD0657CC0CB2CEED7ED2E3E8AECDFCDC4A25C3B15E9E3B163ACA2984B5539181F3EFF1A5E8903D71D5B95DA4F27202B77D2C44B430BB53741A8D59A8F86887525C9F2A6A5980A195EAA7F2FF910064301DEF89D3AA213E1FAC7768D89365318E370AF54A112EFBA9246D9158386BA1B4EEFDA", 16)
+       priv.Y, _ = new(big.Int).SetString("32969E5780CFE1C849A1C276D7AEB4F38A23B591739AA2FE197349AEEBD31366AEE5EB7E6C6DDB7C57D02432B30DB5AA66D9884299FAA72568944E4EEDC92EA3FBC6F39F53412FBCC563208F7C15B737AC8910DBC2D9C9B8C001E72FDC40EB694AB1F06A5A2DBD18D9E36C66F31F566742F11EC0A52E9F7B89355C02FB5D32D2", 16)
+       priv.X, _ = new(big.Int).SetString("5078D4D29795CBE76D3AACFE48C9AF0BCDBEE91A", 16)
+
+       testSignAndVerify(t, 0, &priv)
+}