]> git.ipfire.org Git - thirdparty/tor.git/commitdiff
Add helpful hybrid encryption functions
authorNick Mathewson <nickm@torproject.org>
Thu, 1 Apr 2004 03:08:35 +0000 (03:08 +0000)
committerNick Mathewson <nickm@torproject.org>
Thu, 1 Apr 2004 03:08:35 +0000 (03:08 +0000)
svn:r1423

src/common/crypto.c
src/common/crypto.h
src/or/test.c

index 4a63394c4dd8719131c1808615f4a16845329c3d..e0badacfcae140fd9ee4ecaa9888b1059ec78ad0 100644 (file)
@@ -114,6 +114,17 @@ crypto_cipher_evp_cipher(int type, int enc) {
     }
 }
 
+static INLINE int
+crypto_get_rsa_padding_overhead(int padding) {
+  switch(padding)
+    {
+    case RSA_NO_PADDING: return 0;
+    case RSA_PKCS1_OAEP_PADDING: return 42;
+    case RSA_PKCS1_PADDING: return 11;
+    default: assert(0); return -1;
+    }
+}
+
 static int _crypto_global_initialized = 0;
 
 int crypto_global_init()
@@ -645,6 +656,118 @@ int crypto_pk_private_sign(crypto_pk_env_t *env, unsigned char *from, int fromle
   }
 }
 
+/* Perform a hybrid (public/secret) encryption on 'fromlen' bytes of data
+ * from 'from', with padding type 'padding', storing the results on 'to'.
+ *
+ * If no padding is used, the public key must be at least as large as
+ * 'from'.
+ *
+ * Returns the number of bytes written on success, -1 on failure.
+ *
+ * The encrypted data consists of:
+ *
+ *   The source data, padded and encrypted with the public key, if the
+ *   padded source data is no longer than the public key.
+ *  OR
+ *   The beginning of the source data prefixed with a 16-symmetric key,
+ *   padded and encrypted with the public key; followed by the rest of
+ *   the source data encrypted in AES-CTR mode with the symmetric key.
+ */
+int crypto_pk_public_hybrid_encrypt(crypto_pk_env_t *env, unsigned char *from,
+                                   int fromlen, unsigned char *to,
+                                   int padding)
+{
+  int overhead, pkeylen, outlen, r, symlen;
+  crypto_cipher_env_t *cipher = NULL;
+  char buf[1024];
+
+  assert(env && from && to);
+
+  overhead = crypto_get_rsa_padding_overhead(padding);
+  pkeylen = crypto_pk_keysize(env);
+
+  if (padding == RSA_NO_PADDING && fromlen < pkeylen)
+    return -1;
+
+  if (fromlen+overhead <= pkeylen) {
+    /* It all fits in a single encrypt. */
+    return crypto_pk_public_encrypt(env,from,fromlen,to,padding);
+  }
+  cipher = crypto_new_cipher_env(CRYPTO_CIPHER_AES_CTR);
+  if (!cipher) return -1;
+  if (crypto_cipher_generate_key(cipher)<0)
+    goto err;
+  if (padding == RSA_NO_PADDING)
+    cipher->key[0] &= 0x7f;
+  if (crypto_cipher_encrypt_init_cipher(cipher)<0)
+    goto err;
+  memcpy(buf, cipher->key, 16);
+  memcpy(buf+16, from, pkeylen-overhead-16);
+
+  /* Length of symmetrically encrypted data. */
+  symlen = fromlen-(pkeylen-overhead-16);
+
+  outlen = crypto_pk_public_encrypt(env,buf,pkeylen-overhead,to,padding);
+  if (outlen!=pkeylen) {
+    goto err;
+  }
+  r = crypto_cipher_encrypt(cipher,
+                           from+pkeylen-overhead-16, symlen,
+                           to+outlen);
+
+  if (r<0) goto err;
+  memset(buf, 0, 1024);
+  crypto_free_cipher_env(cipher);
+  return outlen + symlen;
+ err:
+  memset(buf, 0, 1024);
+  if (cipher) crypto_free_cipher_env(cipher);
+  return -1;
+}
+
+/* Invert crypto_pk_public_hybrid_encrypt. */
+int crypto_pk_private_hybrid_decrypt(crypto_pk_env_t *env, unsigned char *from,
+                                   int fromlen, unsigned char *to,
+                                   int padding)
+{
+  int overhead, pkeylen, outlen, r;
+  crypto_cipher_env_t *cipher = NULL;
+  char buf[1024];
+
+  overhead = crypto_get_rsa_padding_overhead(padding);
+  pkeylen = crypto_pk_keysize(env);
+
+  if (fromlen <= pkeylen) {
+    return crypto_pk_private_decrypt(env,from,fromlen,to,padding);
+  }
+  outlen = crypto_pk_private_decrypt(env,from,pkeylen,buf,padding);
+  if (outlen<0) {
+    log_fn(LOG_WARN, "Error decrypting public-key data");
+    return -1;
+  }
+  if (outlen < 16) {
+    log_fn(LOG_WARN, "No room for a symmetric key");
+    return -1;
+  }
+  cipher = crypto_create_init_cipher(CRYPTO_CIPHER_AES_CTR, buf, "", 0);
+  if (!cipher) {
+    return -1;
+  }
+  memcpy(to,buf+16,outlen-16);
+  outlen -= 16;
+  r = crypto_cipher_decrypt(cipher, from+pkeylen, fromlen-pkeylen,
+                           to+outlen);
+  if (r<0)
+    goto err;
+  memset(buf,0,1024);
+  crypto_free_cipher_env(cipher);
+  return outlen + (fromlen-pkeylen);
+ err:
+  memset(buf, 0, 1024);
+  if (cipher) crypto_free_cipher_env(cipher);
+  return -1;
+}
+
 /* Encode the public portion of 'pk' into 'dest'.  Return -1 on error,
  * or the number of characters used on success.
  */
index 761a8d9fc2490a96ecef2bba0251f085bbf279f0..d256b1cb0b04c29db31a5454be08c138b3fdfd28 100644 (file)
@@ -59,6 +59,13 @@ int crypto_pk_public_encrypt(crypto_pk_env_t *env, unsigned char *from, int from
 int crypto_pk_private_decrypt(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to, int padding);
 int crypto_pk_private_sign(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to);
 int crypto_pk_public_checksig(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to);
+int crypto_pk_public_hybrid_encrypt(crypto_pk_env_t *env, unsigned char *from,
+                                   int fromlen, unsigned char *to,
+                                   int padding);
+int crypto_pk_private_hybrid_decrypt(crypto_pk_env_t *env, unsigned char *from,
+                                   int fromlen, unsigned char *to,
+                                   int padding);
+
 #define FINGERPRINT_LEN 49
 int crypto_pk_asn1_encode(crypto_pk_env_t *pk, char *dest, int dest_len);
 crypto_pk_env_t *crypto_pk_asn1_decode(const char *str, int len);
index f845b157bacd093b20238d6ed5e6bb94f3cd394b..18b7541f280da1ee9c23c4a2b79d8e01c75284dc 100644 (file)
@@ -242,7 +242,7 @@ test_crypto()
   crypto_pk_env_t *pk1, *pk2;
   char *data1, *data2, *data3, *cp;
   FILE *f;
-  int i, j;
+  int i, j, p, len;
   int str_ciphers[] = { CRYPTO_CIPHER_IDENTITY,
                         CRYPTO_CIPHER_DES,
                         CRYPTO_CIPHER_RC4,
@@ -416,6 +416,23 @@ test_crypto()
   pk2 = crypto_pk_asn1_decode(data1, i);
   test_assert(crypto_pk_cmp_keys(pk1,pk2) == 0);
 
+  /* Try with hybrid encryption wrappers. */
+  crypto_rand(1024, data1);
+  for (i = 0; i < 3; ++i) {
+    for (j = 85; j < 140; ++j) {
+      memset(data2,0,1024);
+      memset(data3,0,1024);
+      if (i == 0 && j < 129)
+        continue;
+      p = (i==0)?RSA_NO_PADDING:
+        (i==1)?RSA_PKCS1_PADDING:RSA_PKCS1_OAEP_PADDING;
+      len = crypto_pk_public_hybrid_encrypt(pk1,data1,j,data2,p);
+      test_assert(len>=0);
+      len = crypto_pk_private_hybrid_decrypt(pk1,data2,len,data3,p);
+      test_eq(len,j);
+      test_memeq(data1,data3,j);
+    }
+  }
   crypto_free_pk_env(pk1);
   crypto_free_pk_env(pk2);