summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2024-03-26 20:11:34 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2024-03-26 20:11:34 +0700
commitc984b8bfd8544dfc55dba91a02cbbbb9c580c217 (patch)
tree5ebd9ad5c5572c50a124226e7066bfba518f06b4
parentFix warning (diff)
downloadBouncyCastle.NET-ed25519-c984b8bfd8544dfc55dba91a02cbbbb9c580c217.tar.xz
Improve TLS RSA PreMasterSecret decryption
-rw-r--r--crypto/src/crypto/tls/TlsRsaKeyExchange.cs38
-rw-r--r--crypto/src/tls/crypto/impl/bc/BcDefaultTlsCredentialedDecryptor.cs5
2 files changed, 23 insertions, 20 deletions
diff --git a/crypto/src/crypto/tls/TlsRsaKeyExchange.cs b/crypto/src/crypto/tls/TlsRsaKeyExchange.cs
index c214196a9..20c2360ea 100644
--- a/crypto/src/crypto/tls/TlsRsaKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsRsaKeyExchange.cs
@@ -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));
@@ -31,24 +33,24 @@ namespace Org.BouncyCastle.Crypto.Tls
             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));
                 }
@@ -69,6 +71,11 @@ namespace Org.BouncyCastle.Crypto.Tls
             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);
@@ -116,16 +123,11 @@ namespace Org.BouncyCastle.Crypto.Tls
             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.");
         }
diff --git a/crypto/src/tls/crypto/impl/bc/BcDefaultTlsCredentialedDecryptor.cs b/crypto/src/tls/crypto/impl/bc/BcDefaultTlsCredentialedDecryptor.cs
index 5cfb2690e..31f339c92 100644
--- a/crypto/src/tls/crypto/impl/bc/BcDefaultTlsCredentialedDecryptor.cs
+++ b/crypto/src/tls/crypto/impl/bc/BcDefaultTlsCredentialedDecryptor.cs
@@ -45,7 +45,6 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
 
         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);
         }
 
@@ -53,13 +52,15 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
          * 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);
         }