[or-cvs] Add helpful hybrid encryption functions

Nick Mathewson nickm at seul.org
Thu Apr 1 03:08:37 UTC 2004


Update of /home/or/cvsroot/src/common
In directory moria.mit.edu:/tmp/cvs-serv11980/src/common

Modified Files:
	crypto.c crypto.h 
Log Message:
Add helpful hybrid encryption functions

Index: crypto.c
===================================================================
RCS file: /home/or/cvsroot/src/common/crypto.c,v
retrieving revision 1.62
retrieving revision 1.63
diff -u -d -r1.62 -r1.63
--- crypto.c	31 Mar 2004 22:41:25 -0000	1.62
+++ crypto.c	1 Apr 2004 03:08:34 -0000	1.63
@@ -114,6 +114,17 @@
     }
 }
 
+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 @@
   }
 }
 
+/* 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: crypto.h
===================================================================
RCS file: /home/or/cvsroot/src/common/crypto.h,v
retrieving revision 1.30
retrieving revision 1.31
diff -u -d -r1.30 -r1.31
--- crypto.h	30 Mar 2004 19:47:32 -0000	1.30
+++ crypto.h	1 Apr 2004 03:08:35 -0000	1.31
@@ -59,6 +59,13 @@
 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);



More information about the tor-commits mailing list