// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // This package implements RSA encryption as specified in PKCS#1. package rsa // TODO(agl): Add support for PSS padding. import ( "big"; "bytes"; "crypto/subtle"; "hash"; "io"; "os"; ) var bigZero = big.NewInt(0) var bigOne = big.NewInt(1) /* TODO(agl): Enable once big implements ProbablyPrime. // randomSafePrime returns a number, p, of the given size, such that p and // (p-1)/2 are both prime with high probability. func randomSafePrime(rand io.Reader, bits int) (p *big.Int, err os.Error) { if bits < 1 { err = os.EINVAL; } bytes := make([]byte, (bits+7)/8); p = new(big.Int); p2 := new(big.Int); for { _, err = io.ReadFull(rand, bytes); if err != nil { return; } // Don't let the value be too small. bytes[0] |= 0x80; // Make the value odd since an even number this large certainly isn't prime. bytes[len(bytes)-1] |= 1; p.SetBytes(bytes); if p.ProbablyPrime(20) { p2.Rsh(p, 1); // p2 = (p - 1)/2 if p2.ProbablyPrime(20) { return; } } } return; } */ // randomNumber returns a uniform random value in [0, max). func randomNumber(rand io.Reader, max *big.Int) (n *big.Int, err os.Error) { k := (max.Len() + 7)/8; // r is the number of bits in the used in the most significant byte of // max. r := uint(max.Len() % 8); if r == 0 { r = 8; } bytes := make([]byte, k); n = new(big.Int); for { _, err = io.ReadFull(rand, bytes); if err != nil { return; } // Clear bits in the first byte to increase the probability // that the candidate is < max. bytes[0] &= uint8(int(1< k - 2 * hash.Size() - 2 { err = MessageTooLongError{}; return; } hash.Write(label); lHash := hash.Sum(); hash.Reset(); em := make([]byte, k); seed := em[1 : 1 + hash.Size()]; db := em[1 + hash.Size() : len(em)]; bytes.Copy(db[0 : hash.Size()], lHash); db[len(db)-len(msg)-1] = 1; bytes.Copy(db[len(db)-len(msg) : len(db)], msg); _, err = io.ReadFull(rand, seed); if err != nil { return; } mgf1XOR(db, hash, seed); mgf1XOR(seed, hash, db); m := new(big.Int); m.SetBytes(em); c := encrypt(new(big.Int), pub, m); out = c.Bytes(); return; } // A DecryptionError represents a failure to decrypt a message. // It is deliberately vague to avoid adaptive attacks. type DecryptionError struct{} func (DecryptionError) String() string { return "RSA decryption 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) { g := new(big.Int); x := new(big.Int); y := new(big.Int); big.GcdInt(g, x, y, a, n); if big.CmpInt(x, bigOne) < 0 { // 0 is not the multiplicative inverse of any element so, if x // < 1, then x is negative. x.Add(x, n); } return x; } // decrypt performs an RSA decryption, resulting in a plaintext integer. If a // random source is given, RSA blinding is used. func decrypt(rand io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err os.Error) { // TODO(agl): can we get away with reusing blinds? if big.CmpInt(c, priv.N) > 0 { err = DecryptionError{}; return; } var ir *big.Int; if rand != nil { // Blinding enabled. Blinding involves multiplying c by r^e. // Then the decryption operation performs (m^e * r^e)^d mod n // which equals mr mod n. The factor of r can then be removed // by multipling by the multiplicative inverse of r. r, err1 := randomNumber(rand, priv.N); if err1 != nil { err = err1; return; } if big.CmpInt(r, bigZero) == 0 { r = bigOne; } ir = modInverse(r, priv.N); bigE := big.NewInt(int64(priv.E)); rpowe := new(big.Int).Exp(r, bigE, priv.N); c.Mul(c, rpowe); c.Mod(c, priv.N); } m = new(big.Int).Exp(c, priv.D, priv.N); if ir != nil { // Unblind. m.Mul(m, ir); m.Mod(m, priv.N); } return; } // DecryptOAEP decrypts ciphertext using RSA-OAEP. // If rand != nil, DecryptOAEP uses RSA blinding to avoid timing side-channel attacks. func DecryptOAEP(hash hash.Hash, rand io.Reader, priv *PrivateKey, ciphertext []byte, label []byte) (msg []byte, err os.Error) { k := (priv.N.Len() + 7)/8; if len(ciphertext) > k || k < hash.Size() * 2 + 2 { err = DecryptionError{}; return; } c := new(big.Int).SetBytes(ciphertext); m, err := decrypt(rand, priv, c); if err != nil { return; } hash.Write(label); lHash := hash.Sum(); hash.Reset(); // Converting the plaintext number to bytes will strip any // leading zeros so we may have to left pad. We do this unconditionally // to avoid leaking timing information. (Although we still probably // leak the number of leading zeros. It's not clear that we can do // anything about this.) em := leftPad(m.Bytes(), k); firstByteIsZero := subtle.ConstantTimeByteEq(em[0], 0); seed := em[1 : hash.Size() + 1]; db := em[hash.Size() + 1 : len(em)]; mgf1XOR(seed, hash, db); mgf1XOR(db, hash, seed); lHash2 := db[0 : hash.Size()]; // We have to validate the plaintext in contanst time in order to avoid // attacks like: J. Manger. A Chosen Ciphertext Attack on RSA Optimal // Asymmetric Encryption Padding (OAEP) as Standardized in PKCS #1 // v2.0. In J. Kilian, editor, Advances in Cryptology. lHash2Good := subtle.ConstantTimeCompare(lHash, lHash2); // The remainder of the plaintext must be zero or more 0x00, followed // by 0x01, followed by the message. // lookingForIndex: 1 iff we are still looking for the 0x01 // index: the offset of the first 0x01 byte // invalid: 1 iff we saw a non-zero byte before the 0x01. var lookingForIndex, index, invalid int; lookingForIndex = 1; rest := db[hash.Size() : len(db)]; for i := 0; i < len(rest); i++ { equals0 := subtle.ConstantTimeByteEq(rest[i], 0); equals1 := subtle.ConstantTimeByteEq(rest[i], 1); index = subtle.ConstantTimeSelect(lookingForIndex & equals1, i, index); lookingForIndex = subtle.ConstantTimeSelect(equals1, 0, lookingForIndex); invalid = subtle.ConstantTimeSelect(lookingForIndex & ^equals0, 1, invalid); } if firstByteIsZero & lHash2Good & ^invalid & ^lookingForIndex != 1 { err = DecryptionError{}; return; } msg = rest[index+1 : len(rest)]; return; } // leftPad returns a new slice of length size. The contents of input are right // aligned in the new slice. func leftPad(input []byte, size int) (out []byte) { n := len(input); if n > size { n = size; } out = make([]byte, size); bytes.Copy(out[len(out)-n : len(out)], input); return; }