Skip to content

Commit

Permalink
Improve TLS RSA PreMasterSecret decryption
Browse files Browse the repository at this point in the history
  • Loading branch information
peterdettman committed Mar 26, 2024
1 parent e1bbb04 commit c984b8b
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 20 deletions.
38 changes: 20 additions & 18 deletions crypto/src/crypto/tls/TlsRsaKeyExchange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ namespace Org.BouncyCastle.Crypto.Tls
{
public static class TlsRsaKeyExchange
{
public static byte[] DecryptPreMasterSecret(byte[] encryptedPreMasterSecret, RsaKeyParameters privateKey,
public const int PreMasterSecretLength = 48;

public static byte[] DecryptPreMasterSecret(byte[] buf, int off, int len, RsaKeyParameters privateKey,
int protocolVersion, SecureRandom secureRandom)
{
if (Arrays.IsNullOrEmpty(encryptedPreMasterSecret))
throw new ArgumentException("cannot be null or empty", nameof(encryptedPreMasterSecret));
if (buf == null || len < 1 || len > GetInputLimit(privateKey) || off < 0 || off > buf.Length - len)
throw new ArgumentException("input not a valid EncryptedPreMasterSecret");

if (!privateKey.IsPrivate)
throw new ArgumentException("must be an RSA private key", nameof(privateKey));
Expand All @@ -31,24 +33,24 @@ public static class TlsRsaKeyExchange
secureRandom = CryptoServicesRegistrar.GetSecureRandom(secureRandom);

/*
* Generate 48 random bytes we can use as a Pre-Master-Secret if the decrypted value is invalid.
* Generate random bytes we can use as a Pre-Master-Secret if the decrypted value is invalid.
*/
byte[] result = new byte[48];
byte[] result = new byte[PreMasterSecretLength];
secureRandom.NextBytes(result);

try
{
BigInteger input = ConvertInput(modulus, encryptedPreMasterSecret);
BigInteger input = ConvertInput(modulus, buf, off, len);
byte[] encoding = RsaBlinded(privateKey, input, secureRandom);

int pkcs1Length = (bitLength - 1) / 8;
int plainTextOffset = encoding.Length - 48;
int plainTextOffset = encoding.Length - PreMasterSecretLength;

int badEncodingMask = CheckPkcs1Encoding2(encoding, pkcs1Length, 48);
int badEncodingMask = CheckPkcs1Encoding2(encoding, pkcs1Length, PreMasterSecretLength);
int badVersionMask = -(Pack.BE_To_UInt16(encoding, plainTextOffset) ^ protocolVersion) >> 31;
int fallbackMask = badEncodingMask | badVersionMask;

for (int i = 0; i < 48; ++i)
for (int i = 0; i < PreMasterSecretLength; ++i)
{
result[i] = (byte)((result[i] & fallbackMask) | (encoding[plainTextOffset + i] & ~fallbackMask));
}
Expand All @@ -69,6 +71,11 @@ public static class TlsRsaKeyExchange
return result;
}

public static int GetInputLimit(RsaKeyParameters privateKey)
{
return (privateKey.Modulus.BitLength + 7) / 8;
}

private static int CAddTo(int len, int cond, byte[] x, byte[] z)
{
Debug.Assert(cond == 0 || cond == -1);
Expand Down Expand Up @@ -116,16 +123,11 @@ private static int CheckPkcs1Encoding2(byte[] buf, int pkcs1Length, int plaintex
return errorSign >> 31;
}

private static BigInteger ConvertInput(BigInteger modulus, byte[] input)
private static BigInteger ConvertInput(BigInteger modulus, byte[] buf, int off, int len)
{
int inputLimit = (modulus.BitLength + 7) / 8;

if (input.Length <= inputLimit)
{
BigInteger result = new BigInteger(1, input);
if (result.CompareTo(modulus) < 0)
return result;
}
BigInteger result = BigIntegers.FromUnsignedByteArray(buf, off, len);
if (result.CompareTo(modulus) < 0)
return result;

throw new DataLengthException("input too large for RSA cipher.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,22 @@ public class BcDefaultTlsCredentialedDecryptor

public virtual TlsSecret Decrypt(TlsCryptoParameters cryptoParams, byte[] ciphertext)
{
// TODO Keep only the decryption itself here - move error handling outside
return SafeDecryptPreMasterSecret(cryptoParams, (RsaKeyParameters)m_privateKey, ciphertext);
}

/*
* TODO[tls-ops] Probably need to make RSA encryption/decryption into TlsCrypto functions so that users can
* implement "generic" encryption credentials externally
*/
// TODO[api] Just inline this into Decrypt
protected virtual TlsSecret SafeDecryptPreMasterSecret(TlsCryptoParameters cryptoParams,
RsaKeyParameters rsaServerPrivateKey, byte[] encryptedPreMasterSecret)
{
ProtocolVersion expectedVersion = cryptoParams.RsaPreMasterSecretVersion;

byte[] preMasterSecret = Org.BouncyCastle.Crypto.Tls.TlsRsaKeyExchange.DecryptPreMasterSecret(
encryptedPreMasterSecret, rsaServerPrivateKey, expectedVersion.FullVersion, m_crypto.SecureRandom);
encryptedPreMasterSecret, 0, encryptedPreMasterSecret.Length, rsaServerPrivateKey,
expectedVersion.FullVersion, m_crypto.SecureRandom);

return m_crypto.CreateSecret(preMasterSecret);
}
Expand Down

0 comments on commit c984b8b

Please sign in to comment.