]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/rsa: add PKCS#1 v1.5 signature support. weekly.2009-11-17
authorAdam Langley <agl@golang.org>
Wed, 18 Nov 2009 02:21:47 +0000 (18:21 -0800)
committerAdam Langley <agl@golang.org>
Wed, 18 Nov 2009 02:21:47 +0000 (18:21 -0800)
R=rsc
CC=golang-dev
https://golang.org/cl/156051

src/pkg/crypto/rsa/pkcs1v15.go
src/pkg/crypto/rsa/pkcs1v15_test.go
src/pkg/crypto/rsa/rsa.go

index 96a8c6912da0e3c6a356732b6a4a06631d6106e8..f60d2b3970e750f601512481d51c1694a7ac817f 100644 (file)
@@ -136,3 +136,131 @@ func nonZeroRandomBytes(s []byte, rand io.Reader) (err os.Error) {
 
        return;
 }
+
+// Due to the design of PKCS#1 v1.5, we need to know the exact hash function in
+// use. A generic hash.Hash will not do.
+type PKCS1v15Hash int
+
+const (
+       HashMD5 PKCS1v15Hash    = iota;
+       HashSHA1;
+       HashSHA256;
+       HashSHA384;
+       HashSHA512;
+)
+
+// These are ASN1 DER structures:
+//   DigestInfo ::= SEQUENCE {
+//     digestAlgorithm AlgorithmIdentifier,
+//     digest OCTET STRING
+//   }
+// For performance, we don't use the generic ASN1 encoding. Rather, we
+// precompute a prefix of the digest value that makes a valid ASN1 DER string
+// with the correct contents.
+var hashPrefixes = [][]byte{
+       // HashMD5
+       []byte{0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10},
+       // HashSHA1
+       []byte{0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14},
+       // HashSHA256
+       []byte{0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20},
+       // HashSHA384
+       []byte{0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30},
+       // HashSHA512
+       []byte{0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40},
+}
+
+// SignPKCS1v15 calcuates the signature of hashed using RSASSA-PSS-SIGN from RSA PKCS#1 v1.5.
+// Note that hashed must be the result of hashing the input message using the
+// given hash function.
+func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash PKCS1v15Hash, hashed []byte) (s []byte, err os.Error) {
+       hashLen, prefix, err := pkcs1v15HashInfo(hash, len(hashed));
+       if err != nil {
+               return
+       }
+
+       tLen := len(prefix) + hashLen;
+       k := (priv.N.Len() + 7) / 8;
+       if k < tLen+11 {
+               return nil, MessageTooLongError{}
+       }
+
+       // EM = 0x00 || 0x01 || PS || 0x00 || T
+       em := make([]byte, k);
+       em[1] = 1;
+       for i := 2; i < k-tLen-1; i++ {
+               em[i] = 0xff
+       }
+       bytes.Copy(em[k-tLen:k-hashLen], prefix);
+       bytes.Copy(em[k-hashLen:k], hashed);
+
+       m := new(big.Int).SetBytes(em);
+       c, err := decrypt(rand, priv, m);
+       if err == nil {
+               s = c.Bytes()
+       }
+       return;
+}
+
+// VerifyPKCS1v15 verifies an RSA PKCS#1 v1.5 signature.
+// hashed is the result of hashing the input message using the given hash
+// function and sig is the signature. A valid signature is indicated by
+// returning a nil error.
+func VerifyPKCS1v15(pub *PublicKey, hash PKCS1v15Hash, hashed []byte, sig []byte) (err os.Error) {
+       hashLen, prefix, err := pkcs1v15HashInfo(hash, len(hashed));
+       if err != nil {
+               return
+       }
+
+       tLen := len(prefix) + hashLen;
+       k := (pub.N.Len() + 7) / 8;
+       if k < tLen+11 {
+               err = VerificationError{};
+               return;
+       }
+
+       c := new(big.Int).SetBytes(sig);
+       m := encrypt(new(big.Int), pub, c);
+       em := leftPad(m.Bytes(), k);
+       // EM = 0x00 || 0x01 || PS || 0x00 || T
+
+       ok := subtle.ConstantTimeByteEq(em[0], 0);
+       ok &= subtle.ConstantTimeByteEq(em[1], 1);
+       ok &= subtle.ConstantTimeCompare(em[k-hashLen:k], hashed);
+       ok &= subtle.ConstantTimeCompare(em[k-tLen:k-hashLen], prefix);
+       ok &= subtle.ConstantTimeByteEq(em[k-tLen-1], 0);
+
+       for i := 2; i < k-tLen-1; i++ {
+               ok &= subtle.ConstantTimeByteEq(em[i], 0xff)
+       }
+
+       if ok != 1 {
+               return VerificationError{}
+       }
+
+       return nil;
+}
+
+func pkcs1v15HashInfo(hash PKCS1v15Hash, inLen int) (hashLen int, prefix []byte, err os.Error) {
+       switch hash {
+       case HashMD5:
+               hashLen = 16
+       case HashSHA1:
+               hashLen = 20
+       case HashSHA256:
+               hashLen = 32
+       case HashSHA384:
+               hashLen = 48
+       case HashSHA512:
+               hashLen = 64
+       default:
+               return 0, nil, os.ErrorString("unknown hash function")
+       }
+
+       if inLen != hashLen {
+               return 0, nil, os.ErrorString("input must be hashed message")
+       }
+
+       prefix = hashPrefixes[int(hash)];
+       return;
+}
index dbfc64a9965bff07e6b8559e12265876ae919353..4d62deac1465e3285feeb59ccdfae6263d41d858 100644 (file)
@@ -7,7 +7,9 @@ package rsa
 import (
        "big";
        "bytes";
+       "crypto/sha1";
        "encoding/base64";
+       "encoding/hex";
        "os";
        "io";
        "strings";
@@ -154,6 +156,49 @@ func TestNonZeroRandomBytes(t *testing.T) {
        }
 }
 
+type signPKCS1v15Test struct {
+       in, out string;
+}
+
+// These vectors have been tested with
+//   `openssl rsautl -verify -inkey pk -in signature | hexdump -C`
+var signPKCS1v15Tests = []signPKCS1v15Test{
+       signPKCS1v15Test{"Test.\n", "a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e336ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362ae"},
+}
+
+func TestSignPKCS1v15(t *testing.T) {
+       for i, test := range signPKCS1v15Tests {
+               h := sha1.New();
+               h.Write(strings.Bytes(test.in));
+               digest := h.Sum();
+
+               s, err := SignPKCS1v15(nil, rsaPrivateKey, HashSHA1, digest);
+               if err != nil {
+                       t.Errorf("#%d %s", i, err)
+               }
+
+               expected, _ := hex.DecodeString(test.out);
+               if bytes.Compare(s, expected) != 0 {
+                       t.Errorf("#%d got: %x want: %x", i, s, expected)
+               }
+       }
+}
+
+func TestVerifyPKCS1v15(t *testing.T) {
+       for i, test := range signPKCS1v15Tests {
+               h := sha1.New();
+               h.Write(strings.Bytes(test.in));
+               digest := h.Sum();
+
+               sig, _ := hex.DecodeString(test.out);
+
+               err := VerifyPKCS1v15(&rsaPrivateKey.PublicKey, HashSHA1, digest, sig);
+               if err != nil {
+                       t.Errorf("#%d %s", i, err)
+               }
+       }
+}
+
 func bigFromString(s string) *big.Int {
        ret := new(big.Int);
        ret.SetString(s, 10);
index 163e412c0f3e7c8fa01902bbfb84865952f123f0..e73aaf1e6f08d666bdc3b39f1f83cac80ec4ce38 100644 (file)
@@ -288,6 +288,12 @@ type DecryptionError struct{}
 
 func (DecryptionError) String() string { return "RSA decryption error" }
 
+// A VerificationError represents a failure to verify a signature.
+// It is deliberately vague to avoid adaptive attacks.
+type VerificationError struct{}
+
+func (VerificationError) String() string       { return "RSA verification error" }
+
 // modInverse returns ia, the inverse of a in the multiplicative group of prime
 // order n. It requires that a be a member of the group (i.e. less than n).
 func modInverse(a, n *big.Int) (ia *big.Int, ok bool) {