Commits

Adam Langley  committed d1b7541

crypto/rsa: add PKCS#1 v1.5 signature support.

R=rsc
CC=golang-dev
http://codereview.appspot.com/156051

  • Participants
  • Parent commits aeb5dd2
  • Branches default
  • Tags weekly.2009-11-17

Comments (0)

Files changed (3)

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

 
 	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;
+}

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

 import (
 	"big";
 	"bytes";
+	"crypto/sha1";
 	"encoding/base64";
+	"encoding/hex";
 	"os";
 	"io";
 	"strings";
 	}
 }
 
+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);

File src/pkg/crypto/rsa/rsa.go

 
 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) {