Commit a8ba4082 authored by Adam Langley's avatar Adam Langley

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

R=rsc
CC=golang-dev
https://golang.org/cl/156051
parent 80b7f6a8
......@@ -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;
}
......@@ -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);
......
......@@ -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) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment