D *big.Int // private exponent
Primes []*big.Int // prime factors of N, has >= 2 elements.
- // Precomputed contains precomputed values that speed up private
- // operations, if available.
+ // Precomputed contains precomputed values that speed up RSA operations,
+ // if available. It must be generated by calling PrivateKey.Precompute and
+ // must not be modified.
Precomputed PrecomputedValues
}
// and is implemented by this package without CRT optimizations to limit
// complexity.
CRTValues []CRTValue
+
+ n, p, q *modulus // moduli for CRT with Montgomery precomputed constants
}
// CRTValue contains the precomputed Chinese remainder theorem values.
Dq: Dq,
Qinv: Qinv,
CRTValues: make([]CRTValue, 0), // non-nil, to match Precompute
+ n: modulusFromNat(natFromBig(N)),
+ p: modulusFromNat(natFromBig(P)),
+ q: modulusFromNat(natFromBig(Q)),
},
}
return key, nil
N := modulusFromNat(natFromBig(pub.N))
m := natFromBytes(plaintext).expandFor(N)
-
- e := make([]byte, 8)
- binary.BigEndian.PutUint64(e, uint64(pub.E))
- for len(e) > 1 && e[0] == 0 {
- e = e[1:]
- }
+ e := intToBytes(pub.E)
out := make([]byte, modulusSize(N))
return new(nat).exp(m, e, N).fillBytes(out)
}
+// intToBytes returns i as a big-endian slice of bytes with no leading zeroes,
+// leaking only the bit size of i through timing side-channels.
+func intToBytes(i int) []byte {
+ b := make([]byte, 8)
+ binary.BigEndian.PutUint64(b, uint64(i))
+ for len(b) > 1 && b[0] == 0 {
+ b = b[1:]
+ }
+ return b
+}
+
// EncryptOAEP encrypts the given message with RSA-OAEP.
//
// OAEP is parameterised by a hash function that is used as a random oracle.
// Precompute performs some calculations that speed up private key operations
// in the future.
func (priv *PrivateKey) Precompute() {
+ if priv.Precomputed.n == nil && len(priv.Primes) == 2 {
+ priv.Precomputed.n = modulusFromNat(natFromBig(priv.N))
+ priv.Precomputed.p = modulusFromNat(natFromBig(priv.Primes[0]))
+ priv.Precomputed.q = modulusFromNat(natFromBig(priv.Primes[1]))
+ }
+
+ // Fill in the backwards-compatibility *big.Int values.
if priv.Precomputed.Dp != nil {
return
}
}
}
-// decrypt performs an RSA decryption of ciphertext into out.
-func decrypt(priv *PrivateKey, ciphertext []byte) ([]byte, error) {
+const withCheck = true
+const noCheck = false
+
+// decrypt performs an RSA decryption of ciphertext into out. If check is true,
+// m^e is calculated and compared with ciphertext, in order to defend against
+// errors in the CRT computation.
+func decrypt(priv *PrivateKey, ciphertext []byte, check bool) ([]byte, error) {
if len(priv.Primes) <= 2 {
boring.Unreachable()
}
- N := modulusFromNat(natFromBig(priv.N))
+ N := priv.Precomputed.n
+ if N == nil {
+ N = modulusFromNat(natFromBig(priv.N))
+ }
c := natFromBytes(ciphertext).expandFor(N)
if c.cmpGeq(N.nat) == 1 {
return nil, ErrDecryption
return nil, ErrDecryption
}
- // Note that because our private decryption exponents are stored as big.Int,
- // we potentially leak the exact number of bits of these exponents. This
- // isn't great, but should be fine.
- if priv.Precomputed.Dp == nil || len(priv.Primes) > 2 {
- out := make([]byte, modulusSize(N))
- return new(nat).exp(c, priv.D.Bytes(), N).fillBytes(out), nil
- }
-
- t0 := new(nat)
- P := modulusFromNat(natFromBig(priv.Primes[0]))
- Q := modulusFromNat(natFromBig(priv.Primes[1]))
- // m = c ^ Dp mod p
- m := new(nat).exp(t0.mod(c, P), priv.Precomputed.Dp.Bytes(), P)
- // m2 = c ^ Dq mod q
- m2 := new(nat).exp(t0.mod(c, Q), priv.Precomputed.Dq.Bytes(), Q)
- // m = m - m2 mod p
- m.modSub(t0.mod(m2, P), P)
- // m = m * Qinv mod p
- m.modMul(natFromBig(priv.Precomputed.Qinv).expandFor(P), P)
- // m = m * q mod N
- m.expandFor(N).modMul(t0.mod(Q.nat, N), N)
- // m = m + m2 mod N
- m.modAdd(m2.expandFor(N), N)
+ var m *nat
+ if priv.Precomputed.n == nil {
+ m = new(nat).exp(c, priv.D.Bytes(), N)
+ } else {
+ t0 := new(nat)
+ P, Q := priv.Precomputed.p, priv.Precomputed.q
+ // m = c ^ Dp mod p
+ m = new(nat).exp(t0.mod(c, P), priv.Precomputed.Dp.Bytes(), P)
+ // m2 = c ^ Dq mod q
+ m2 := new(nat).exp(t0.mod(c, Q), priv.Precomputed.Dq.Bytes(), Q)
+ // m = m - m2 mod p
+ m.modSub(t0.mod(m2, P), P)
+ // m = m * Qinv mod p
+ m.modMul(natFromBig(priv.Precomputed.Qinv).expandFor(P), P)
+ // m = m * q mod N
+ m.expandFor(N).modMul(t0.mod(Q.nat, N), N)
+ // m = m + m2 mod N
+ m.modAdd(m2.expandFor(N), N)
+ }
+
+ if check {
+ c1 := new(nat).exp(m, intToBytes(priv.E), N)
+ if c1.cmpEq(c) != 1 {
+ return nil, ErrDecryption
+ }
+ }
out := make([]byte, modulusSize(N))
return m.fillBytes(out), nil
}
-func decryptAndCheck(priv *PrivateKey, ciphertext []byte) (m []byte, err error) {
- m, err = decrypt(priv, ciphertext)
- if err != nil {
- return nil, err
- }
-
- // In order to defend against errors in the CRT computation, m^e is
- // calculated, which should match the original ciphertext.
- check := encrypt(&priv.PublicKey, m)
- if subtle.ConstantTimeCompare(ciphertext, check) != 1 {
- return nil, errors.New("rsa: internal error")
- }
- return m, nil
-}
-
// DecryptOAEP decrypts ciphertext using RSA-OAEP.
//
// OAEP is parameterised by a hash function that is used as a random oracle.
return out, nil
}
- em, err := decrypt(priv, ciphertext)
+ em, err := decrypt(priv, ciphertext, noCheck)
if err != nil {
return nil, err
}