Skip to content

Commit

Permalink
Moves to timing channel mitigation approach 2
Browse files Browse the repository at this point in the history
  • Loading branch information
EthanHeilman committed May 6, 2024
1 parent a4dbcc1 commit 5493737
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 26 deletions.
6 changes: 3 additions & 3 deletions src/crypto/ecdsa/ecdsa.go
Expand Up @@ -304,7 +304,7 @@ func signNISTEC[Point nistPoint[Point]](c *nistCurve[Point], priv *PrivateKey, c
if err != nil {
return nil, err
}
r, err := bigmod.NewNat().SetOverflowingBytes(Rx, c.N)
r, _, err := bigmod.NewNat().SetOverflowingBytes(Rx, c.N)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -400,7 +400,7 @@ func hashToNat[Point nistPoint[Point]](c *nistCurve[Point], e *bigmod.Nat, hash
}
}
}
_, err := e.SetOverflowingBytes(hash, c.N)
_, _, err := e.SetOverflowingBytes(hash, c.N)
if err != nil {
panic("ecdsa: internal error: truncated hash is too long")
}
Expand Down Expand Up @@ -536,7 +536,7 @@ func verifyNISTEC[Point nistPoint[Point]](c *nistCurve[Point], pub *PublicKey, h
return false
}

v, err := bigmod.NewNat().SetOverflowingBytes(Rx, c.N)
v, _, err := bigmod.NewNat().SetOverflowingBytes(Rx, c.N)
if err != nil {
return false
}
Expand Down
20 changes: 14 additions & 6 deletions src/crypto/internal/bigmod/nat.go
Expand Up @@ -5,6 +5,7 @@
package bigmod

import (
"crypto/subtle"
"encoding/binary"
"errors"
"math/big"
Expand Down Expand Up @@ -154,16 +155,22 @@ func (x *Nat) SetBytes(b []byte, m *Modulus) (*Nat, error) {
// reduces overflowing values up to 2^⌈log2(m)⌉ - 1.
//
// The output will be resized to the size of m and overwritten.
func (x *Nat) SetOverflowingBytes(b []byte, m *Modulus) (*Nat, error) {
func (x *Nat) SetOverflowingBytes(b []byte, m *Modulus) (*Nat, choice, error) {
if err := x.setBytes(b, m); err != nil {
return nil, err
return nil, 0, err
}
leading := _W - bitLen(x.limbs[len(x.limbs)-1])
if leading < m.leading {
return nil, errors.New("input overflows the modulus size")
return nil, 0, errors.New("input overflows the modulus size")
}
x.maybeSubtractModulus(no, m)
return x, nil

// If x-m has no underflow then b overflows m (m <= x). In all other cases
// no overflow occurred.
underflow := x.maybeSubtractModulus(no, m)
// We only need to check first byte of underflow because we have already
// checked bitlen(m)>=bitlen(x), thus 0 <= overflow <= 7.
overflowed := choice(subtle.ConstantTimeByteEq(uint8(underflow), uint8(0)))
return x, overflowed, nil
}

// bigEndianUint returns the contents of buf interpreted as a
Expand Down Expand Up @@ -507,13 +514,14 @@ func (out *Nat) resetFor(m *Modulus) *Nat {
// overflowed its size, meaning abstractly x > 2^_W*n > m even if x < m.
//
// x and m operands must have the same announced length.
func (x *Nat) maybeSubtractModulus(always choice, m *Modulus) {
func (x *Nat) maybeSubtractModulus(always choice, m *Modulus) uint {
t := NewNat().set(x)
underflow := t.sub(m.nat)
// We keep the result if x - m didn't underflow (meaning x >= m)
// or if always was set.
keep := not(choice(underflow)) | choice(always)
x.assign(keep, t)
return underflow
}

// Sub computes x = x - y mod m.
Expand Down
11 changes: 5 additions & 6 deletions src/crypto/rsa/pkcs1v15.go
Expand Up @@ -351,16 +351,15 @@ func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte)
return ErrVerification
}

em, encErr := encrypt(pub, sig)
// Only checking if em == nil to avoid timing attacks, encErr
// will be checked at the very end of the function.
// Please see https://golang.org/issue/67043
em, ok := timingSafeEncrypt(pub, sig)
// Not checking ok to avoid timing attacks, ok will be checked at the very
// end of the function. See https://golang.org/issue/67043
if em == nil {
return ErrVerification
}
// EM = 0x00 || 0x01 || PS || 0x00 || T

ok := subtle.ConstantTimeByteEq(em[0], 0)
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)
Expand All @@ -370,7 +369,7 @@ func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte)
ok &= subtle.ConstantTimeByteEq(em[i], 0xff)
}

if ok != 1 || encErr != nil {
if ok != 1 {
return ErrVerification
}

Expand Down
11 changes: 6 additions & 5 deletions src/crypto/rsa/pss.go
Expand Up @@ -338,6 +338,10 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte,
// result of hashing the input message using the given hash function. The opts
// argument may be nil, in which case sensible defaults are used. opts.Hash is
// ignored.
//
// Note: As neither the signature nor the public key is viewed as secret;
// VerifyPSS does not provide confidentiality guarantees on the public key or
// signature.
func VerifyPSS(pub *PublicKey, hash crypto.Hash, digest []byte, sig []byte, opts *PSSOptions) error {
if boring.Enabled {
bkey, err := boringPublicKey(pub)
Expand All @@ -361,10 +365,7 @@ func VerifyPSS(pub *PublicKey, hash crypto.Hash, digest []byte, sig []byte, opts

emBits := pub.N.BitLen() - 1
emLen := (emBits + 7) / 8
em, encErr := encrypt(pub, sig)
// Only checking if em == nil to avoid timing attacks, encErr
// will be checked at the very end of the function.
// Please see https://golang.org/issue/67043
em, ok := timingSafeEncrypt(pub, sig)
if em == nil {
return ErrVerification
}
Expand All @@ -382,7 +383,7 @@ func VerifyPSS(pub *PublicKey, hash crypto.Hash, digest []byte, sig []byte, opts
}

err := emsaPSSVerify(digest, em, emBits, opts.saltLength(), hash.New())
if err != nil || encErr != nil {
if err != nil || ok != 1 {
return ErrVerification
}
return nil
Expand Down
38 changes: 32 additions & 6 deletions src/crypto/rsa/rsa.go
Expand Up @@ -20,9 +20,9 @@
// Decrypter and Signer interfaces from the crypto package.
//
// Operations in this package are implemented using constant-time algorithms,
// except for [GenerateKey], [PrivateKey.Precompute], and [PrivateKey.Validate].
// Every other operation only leaks the bit size of the involved values, which
// all depend on the selected key size.
// except for [encrypt], [GenerateKey], [PrivateKey.Precompute],
// and [PrivateKey.Validate]. Every other operation only leaks the bit size of
// the involved values, which all depend on the selected key size.
package rsa

import (
Expand Down Expand Up @@ -479,18 +479,44 @@ func mgf1XOR(out []byte, hash hash.Hash, seed []byte) {
// be returned if the size of the salt is too large.
var ErrMessageTooLong = errors.New("crypto/rsa: message too long for RSA key size")

// WARNING: encrypt contains a timing channel that reveals if the signature
// is numerically larger than the modulus N, potentially leaking a bit of
// the public key or the plaintext. See https://golang.org/issue/67043
//
// To avoid this timing channel use timeSafeEncrypt instead
func encrypt(pub *PublicKey, plaintext []byte) ([]byte, error) {
boring.Unreachable()

N, err := bigmod.NewModulusFromBig(pub.N)
if err != nil {
return nil, err
}
m := bigmod.NewNat()
_, encErr := m.SetOverflowingBytes(plaintext, N)
m, err := bigmod.NewNat().SetBytes(plaintext, N)
if err != nil {
return nil, err
}
e := uint(pub.E)

return bigmod.NewNat().ExpShortVarTime(m, e, N).Bytes(N), nil
}

// timingSafeEncrypt performs the same operation as encrypt but doesn't
// leak information about the signature or public key via a timing channel.
func timingSafeEncrypt(pub *PublicKey, plaintext []byte) ([]byte, int) {
boring.Unreachable()

N, err := bigmod.NewModulusFromBig(pub.N)
if err != nil {
return nil, 0
}
m, overflowed, err := bigmod.NewNat().SetOverflowingBytes(plaintext, N)
if err != nil {
return nil, 0
}
e := uint(pub.E)

return bigmod.NewNat().ExpShortVarTime(m, e, N).Bytes(N), encErr
ok := int(overflowed ^ 1)
return bigmod.NewNat().ExpShortVarTime(m, e, N).Bytes(N), ok
}

// EncryptOAEP encrypts the given message with RSA-OAEP.
Expand Down

0 comments on commit 5493737

Please sign in to comment.