diff options
author | Adam Langley <agl@golang.org> | 2009-11-17 18:21:47 -0800 |
---|---|---|
committer | Adam Langley <agl@golang.org> | 2009-11-17 18:21:47 -0800 |
commit | a8ba40823c08508ca5f7562501a26bc2e85c88eb (patch) | |
tree | ce9a06150cbc5e86622fce7a280d4c87af08c69f | |
parent | 80b7f6a8d4b9e3ee650f95fbd070d32b73ac6376 (diff) | |
download | go-weekly.2009-11-17.tar.gz go-weekly.2009-11-17.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.go | 128 | ||||
-rw-r--r-- | src/pkg/crypto/rsa/pkcs1v15_test.go | 45 | ||||
-rw-r--r-- | src/pkg/crypto/rsa/rsa.go | 6 |
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) { |