aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Langley <agl@golang.org>2009-11-17 18:21:47 -0800
committerAdam Langley <agl@golang.org>2009-11-17 18:21:47 -0800
commita8ba40823c08508ca5f7562501a26bc2e85c88eb (patch)
treece9a06150cbc5e86622fce7a280d4c87af08c69f
parent80b7f6a8d4b9e3ee650f95fbd070d32b73ac6376 (diff)
downloadgo-a8ba40823c08508ca5f7562501a26bc2e85c88eb.tar.gz
go-a8ba40823c08508ca5f7562501a26bc2e85c88eb.zip
crypto/rsa: add PKCS#1 v1.5 signature support.weekly.2009-11-17
R=rsc CC=golang-dev https://golang.org/cl/156051
-rw-r--r--src/pkg/crypto/rsa/pkcs1v15.go128
-rw-r--r--src/pkg/crypto/rsa/pkcs1v15_test.go45
-rw-r--r--src/pkg/crypto/rsa/rsa.go6
3 files changed, 179 insertions, 0 deletions
diff --git a/src/pkg/crypto/rsa/pkcs1v15.go b/src/pkg/crypto/rsa/pkcs1v15.go
index 96a8c6912d..f60d2b3970 100644
--- a/src/pkg/crypto/rsa/pkcs1v15.go
+++ b/src/pkg/crypto/rsa/pkcs1v15.go
@@ -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;
+}
diff --git a/src/pkg/crypto/rsa/pkcs1v15_test.go b/src/pkg/crypto/rsa/pkcs1v15_test.go
index dbfc64a996..4d62deac14 100644
--- a/src/pkg/crypto/rsa/pkcs1v15_test.go
+++ b/src/pkg/crypto/rsa/pkcs1v15_test.go
@@ -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);
diff --git a/src/pkg/crypto/rsa/rsa.go b/src/pkg/crypto/rsa/rsa.go
index 163e412c0f..e73aaf1e6f 100644
--- a/src/pkg/crypto/rsa/rsa.go
+++ b/src/pkg/crypto/rsa/rsa.go
@@ -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) {