[tor-commits] [tor/master] When parsing, reject >1024-bit RSA private keys sooner.

nickm at torproject.org nickm at torproject.org
Wed Mar 18 13:34:39 UTC 2020


commit 9e1085c924133d7b73c7b0a42282fb13b34f28b8
Author: Nick Mathewson <nickm at torproject.org>
Date:   Wed Feb 5 11:11:35 2020 -0500

    When parsing, reject >1024-bit RSA private keys sooner.
    
    Private-key validation is fairly expensive for long keys in openssl,
    so we need to avoid it sooner.
---
 src/feature/dirparse/parsecommon.c     |  3 ++-
 src/lib/crypt_ops/crypto_rsa.c         | 27 +++++++++++++++++++++------
 src/lib/crypt_ops/crypto_rsa.h         |  5 ++++-
 src/lib/crypt_ops/crypto_rsa_nss.c     | 14 +++++++++++++-
 src/lib/crypt_ops/crypto_rsa_openssl.c | 11 +++++++++--
 5 files changed, 49 insertions(+), 11 deletions(-)

diff --git a/src/feature/dirparse/parsecommon.c b/src/feature/dirparse/parsecommon.c
index 632f5adff..5b753f0f2 100644
--- a/src/feature/dirparse/parsecommon.c
+++ b/src/feature/dirparse/parsecommon.c
@@ -389,7 +389,8 @@ get_next_token(memarea_t *area,
       RET_ERR("Couldn't parse public key.");
   } else if (!strcmp(tok->object_type, "RSA PRIVATE KEY")) { /* private key */
     tok->key = crypto_pk_new();
-    if (crypto_pk_read_private_key_from_string(tok->key, obstart, eol-obstart))
+    if (crypto_pk_read_private_key1024_from_string(tok->key,
+                                                   obstart, eol-obstart))
       RET_ERR("Couldn't parse private key.");
   } else { /* If it's something else, try to base64-decode it */
     int r;
diff --git a/src/lib/crypt_ops/crypto_rsa.c b/src/lib/crypt_ops/crypto_rsa.c
index c9189b0df..8fd8a8aa7 100644
--- a/src/lib/crypt_ops/crypto_rsa.c
+++ b/src/lib/crypt_ops/crypto_rsa.c
@@ -490,7 +490,7 @@ crypto_pk_write_private_key_to_string(crypto_pk_t *env,
 static int
 crypto_pk_read_from_string_generic(crypto_pk_t *env, const char *src,
                                    size_t len, int severity,
-                                   bool private_key)
+                                   bool private_key, int max_bits)
 {
   if (len == (size_t)-1) // "-1" indicates "use the length of the string."
     len = strlen(src);
@@ -510,7 +510,7 @@ crypto_pk_read_from_string_generic(crypto_pk_t *env, const char *src,
   }
 
   crypto_pk_t *pk = private_key
-    ? crypto_pk_asn1_decode_private((const char*)buf, n)
+    ? crypto_pk_asn1_decode_private((const char*)buf, n, max_bits)
     : crypto_pk_asn1_decode((const char*)buf, n);
   if (! pk) {
     log_fn(severity, LD_CRYPTO,
@@ -539,7 +539,8 @@ int
 crypto_pk_read_public_key_from_string(crypto_pk_t *env,
                                       const char *src, size_t len)
 {
-  return crypto_pk_read_from_string_generic(env, src, len, LOG_INFO, false);
+  return crypto_pk_read_from_string_generic(env, src, len, LOG_INFO, false,
+                                            -1);
 }
 
 /** Read a PEM-encoded private key from the <b>len</b>-byte string <b>src</b>
@@ -550,7 +551,21 @@ int
 crypto_pk_read_private_key_from_string(crypto_pk_t *env,
                                        const char *src, ssize_t len)
 {
-  return crypto_pk_read_from_string_generic(env, src, len, LOG_INFO, true);
+  return crypto_pk_read_from_string_generic(env, src, len, LOG_INFO, true,
+                                            -1);
+}
+
+/**
+ * As crypto_pk_read_private_key_from_string(), but reject any key
+ * with a modulus longer than 1024 bits before doing any expensive
+ * validation on it.
+ */
+int
+crypto_pk_read_private_key1024_from_string(crypto_pk_t *env,
+                                           const char *src, ssize_t len)
+{
+  return crypto_pk_read_from_string_generic(env, src, len, LOG_INFO, true,
+                                            1024);
 }
 
 /** If a file is longer than this, we won't try to decode its private key */
@@ -578,7 +593,7 @@ crypto_pk_read_private_key_from_filename(crypto_pk_t *env,
   }
 
   int rv = crypto_pk_read_from_string_generic(env, buf, (ssize_t)st.st_size,
-                                              LOG_WARN, true);
+                                              LOG_WARN, true, -1);
   if (rv < 0) {
     log_warn(LD_CRYPTO, "Unable to decode private key from file %s",
              escaped(keyfile));
@@ -662,7 +677,7 @@ crypto_pk_base64_decode_private(const char *str, size_t len)
     goto out;
   }
 
-  pk = crypto_pk_asn1_decode_private(der, der_len);
+  pk = crypto_pk_asn1_decode_private(der, der_len, -1);
 
  out:
   memwipe(der, 0, len+1);
diff --git a/src/lib/crypt_ops/crypto_rsa.h b/src/lib/crypt_ops/crypto_rsa.h
index c1ea767f8..6d9cc8d30 100644
--- a/src/lib/crypt_ops/crypto_rsa.h
+++ b/src/lib/crypt_ops/crypto_rsa.h
@@ -61,6 +61,8 @@ int crypto_pk_read_public_key_from_string(crypto_pk_t *env,
                                           const char *src, size_t len);
 int crypto_pk_read_private_key_from_string(crypto_pk_t *env,
                                            const char *s, ssize_t len);
+int crypto_pk_read_private_key1024_from_string(crypto_pk_t *env,
+                                               const char *src, ssize_t len);
 int crypto_pk_write_private_key_to_filename(crypto_pk_t *env,
                                             const char *fname);
 
@@ -95,7 +97,8 @@ int crypto_pk_asn1_encode(const crypto_pk_t *pk, char *dest, size_t dest_len);
 crypto_pk_t *crypto_pk_asn1_decode(const char *str, size_t len);
 int crypto_pk_asn1_encode_private(const crypto_pk_t *pk,
                                   char *dest, size_t dest_len);
-crypto_pk_t *crypto_pk_asn1_decode_private(const char *str, size_t len);
+crypto_pk_t *crypto_pk_asn1_decode_private(const char *str, size_t len,
+                                           int max_bits);
 int crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out,int add_space);
 int crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out);
 void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in);
diff --git a/src/lib/crypt_ops/crypto_rsa_nss.c b/src/lib/crypt_ops/crypto_rsa_nss.c
index ad2ad38b6..4cf699067 100644
--- a/src/lib/crypt_ops/crypto_rsa_nss.c
+++ b/src/lib/crypt_ops/crypto_rsa_nss.c
@@ -679,9 +679,12 @@ crypto_pk_asn1_encode_private(const crypto_pk_t *pk,
 /** Given a buffer containing the DER representation of the
  * private key <b>str</b>, decode and return the result on success, or NULL
  * on failure.
+ *
+ * If <b>max_bits</b> is nonnegative, reject any key longer than max_bits
+ * without performing any expensive validation on it.
  */
 crypto_pk_t *
-crypto_pk_asn1_decode_private(const char *str, size_t len)
+crypto_pk_asn1_decode_private(const char *str, size_t len, int max_bits)
 {
   tor_assert(str);
   tor_assert(len < INT_MAX);
@@ -731,6 +734,15 @@ crypto_pk_asn1_decode_private(const char *str, size_t len)
     output = NULL;
   }
 
+  if (output) {
+    const int bits = SECKEY_PublicKeyStrengthInBits(output->pubkey);
+    if (max_bits > 0 && bits > max_bits) {
+      log_info(LD_CRYPTO, "Private key longer than expected.");
+      crypto_pk_free(output);
+      output = NULL;
+    }
+  }
+
   if (slot)
     PK11_FreeSlot(slot);
 
diff --git a/src/lib/crypt_ops/crypto_rsa_openssl.c b/src/lib/crypt_ops/crypto_rsa_openssl.c
index fbdc76ccd..022a0dc09 100644
--- a/src/lib/crypt_ops/crypto_rsa_openssl.c
+++ b/src/lib/crypt_ops/crypto_rsa_openssl.c
@@ -566,9 +566,12 @@ crypto_pk_asn1_encode_private(const crypto_pk_t *pk, char *dest,
 
 /** Decode an ASN.1-encoded private key from <b>str</b>; return the result on
  * success and NULL on failure.
+ *
+ * If <b>max_bits</b> is nonnegative, reject any key longer than max_bits
+ * without performing any expensive validation on it.
  */
 crypto_pk_t *
-crypto_pk_asn1_decode_private(const char *str, size_t len)
+crypto_pk_asn1_decode_private(const char *str, size_t len, int max_bits)
 {
   RSA *rsa;
   unsigned char *buf;
@@ -578,7 +581,11 @@ crypto_pk_asn1_decode_private(const char *str, size_t len)
   rsa = d2i_RSAPrivateKey(NULL, &cp, len);
   tor_free(buf);
   if (!rsa) {
-    crypto_openssl_log_errors(LOG_WARN,"decoding public key");
+    crypto_openssl_log_errors(LOG_WARN,"decoding private key");
+    return NULL;
+  }
+  if (max_bits >= 0 && RSA_bits(rsa) > max_bits) {
+    log_info(LD_CRYPTO, "Private key longer than expected.");
     return NULL;
   }
   crypto_pk_t *result = crypto_new_pk_from_openssl_rsa_(rsa);





More information about the tor-commits mailing list