[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[or-cvs] r11489: add some crypto/util functions from karsten, as the first st (in tor/trunk/src: common or)



Author: arma
Date: 2007-09-18 13:07:56 -0400 (Tue, 18 Sep 2007)
New Revision: 11489

Modified:
   tor/trunk/src/common/crypto.c
   tor/trunk/src/common/crypto.h
   tor/trunk/src/or/test.c
Log:
add some crypto/util functions from karsten, as the first
step of integrating his new hidden service stuff


Modified: tor/trunk/src/common/crypto.c
===================================================================
--- tor/trunk/src/common/crypto.c	2007-09-18 16:38:37 UTC (rev 11488)
+++ tor/trunk/src/common/crypto.c	2007-09-18 17:07:56 UTC (rev 11489)
@@ -1144,6 +1144,170 @@
   return 0;
 }
 
+#define AES_CIPHER_BLOCK_SIZE (16)
+
+#define AES_IV_SIZE (16)
+
+/** Encrypt <b>fromlen</b> bytes (at least 1) from <b>from</b> with the
+ * symmetric key <b>key</b> of 16 bytes length to <b>to</b> of length
+ * <b>tolen</b> which needs to be <b>fromlen</b>, padded to the next 16
+ * bytes, plus exactly 16 bytes for the initialization vector. On success,
+ * return the number of bytes written, on failure, return -1.
+ */
+int
+crypto_cipher_encrypt_cbc(const char *key, char *to, size_t tolen,
+                          const char *from, size_t fromlen)
+{
+
+  EVP_CIPHER_CTX ctx_msg, ctx_iv; /* cipher contexts for message and IV */
+  unsigned char iv[AES_IV_SIZE]; /* initialization vector */
+  int outlen, tmplen; /* length of encrypted strings (w/ and wo/ final data) */
+
+  tor_assert(key);
+  tor_assert(to);
+  tor_assert(tolen >= fromlen + AES_IV_SIZE +
+                    (AES_CIPHER_BLOCK_SIZE - fromlen % AES_CIPHER_BLOCK_SIZE));
+  tor_assert(from);
+  tor_assert(fromlen > 0);
+
+  /* generate random initialization vector */
+  crypto_rand((char *)iv, AES_IV_SIZE);
+
+  /* initialize cipher context for the initialization vector */
+  EVP_CIPHER_CTX_init(&ctx_iv);
+
+  /* disable padding for encryption of initialization vector */
+  EVP_CIPHER_CTX_set_padding(&ctx_iv, 0);
+
+  /* set up cipher context for the initialization vector for encryption with
+   * cipher type AES-128 in ECB mode, default implementation, given key, and
+   * no initialization vector */
+  EVP_EncryptInit_ex(&ctx_iv, EVP_aes_128_ecb(), NULL, (unsigned char *)key,
+                     NULL);
+
+  /* encrypt initialization vector (no padding necessary) and write it to the
+   * first 16 bytes of the result */
+  if (!EVP_EncryptUpdate(&ctx_iv, (unsigned char *)to, &outlen, iv,
+                         AES_IV_SIZE)) {
+    crypto_log_errors(LOG_WARN, "encrypting initialization vector");
+    return -1;
+  }
+
+  /* clear all information from cipher context for the initialization vector
+   * and free up any allocated memory associate with it */
+  EVP_CIPHER_CTX_cleanup(&ctx_iv);
+
+  /* initialize cipher context for the message */
+  EVP_CIPHER_CTX_init(&ctx_msg);
+
+  /* set up cipher context for encryption with cipher type AES-128 in CBC mode,
+   * default implementation, given key, and initialization vector */
+  EVP_EncryptInit_ex(&ctx_msg, EVP_aes_128_cbc(), NULL, (unsigned char *)key,
+                     iv);
+
+  /* encrypt fromlen bytes from buffer from and write the encrypted version to
+   * buffer to */
+  if (!EVP_EncryptUpdate(&ctx_msg,
+                        ((unsigned char *)to) + AES_IV_SIZE, &outlen,
+                        (const unsigned char *)from, (int)fromlen)) {
+    crypto_log_errors(LOG_WARN, "encrypting");
+    return -1;
+  }
+
+  /* encrypt the final data */
+  if (!EVP_EncryptFinal_ex(&ctx_msg,
+                        ((unsigned char *)to) + AES_IV_SIZE + outlen,
+                        &tmplen)) {
+    crypto_log_errors(LOG_WARN, "encrypting the final data");
+    return -1;
+  }
+  outlen += tmplen;
+
+  /* clear all information from cipher context and free up any allocated memory
+   * associate with it */
+  EVP_CIPHER_CTX_cleanup(&ctx_msg);
+
+  /* return number of written bytes */
+  return outlen + AES_IV_SIZE;
+}
+
+/** Decrypt <b>fromlen</b> bytes (at least 1) from <b>from</b> with the
+ * symmetric key <b>key</b> of 16 bytes length to <b>to</b> of length
+ * <b>tolen</b> which may be <b>fromlen</b> minus 16 for the initialization
+ * vector (the size of padding cannot be determined in advance). On success,
+ * return the number of bytes written, on failure (NOT including providing
+ * the wrong key, which occasionally returns the correct length!), return -1.
+ */
+int
+crypto_cipher_decrypt_cbc(const char *key, char *to, size_t tolen,
+                          const char *from, size_t fromlen)
+{
+  EVP_CIPHER_CTX ctx_msg, ctx_iv; /* cipher contexts for message and IV */
+  unsigned char iv[AES_IV_SIZE]; /* initialization vector */
+  int outlen, tmplen; /* length of decrypted strings (w/ and wo/ final data) */
+
+  tor_assert(key);
+  tor_assert(to);
+  tor_assert(tolen >= fromlen - AES_IV_SIZE);
+  tor_assert(from);
+  tor_assert(fromlen > 0);
+
+  /* initialize cipher context for the initialization vector */
+  EVP_CIPHER_CTX_init(&ctx_iv);
+
+  /* disable padding for decryption of initialization vector */
+  EVP_CIPHER_CTX_set_padding(&ctx_iv, 0);
+
+  /* set up cipher context for the initialization vector for decryption with
+   * cipher type AES-128 in ECB mode, default implementation, given key, and
+   * no initialization vector */
+  EVP_DecryptInit_ex(&ctx_iv, EVP_aes_128_ecb(), NULL, (unsigned char *)key,
+                     NULL);
+
+  /* decrypt initialization vector (is not padded) */
+  if (!EVP_DecryptUpdate(&ctx_iv, iv, &outlen, (const unsigned char *)from,
+                         AES_IV_SIZE)) {
+    crypto_log_errors(LOG_WARN, "decrypting initialization vector");
+    return -1;
+  }
+
+  /* clear all information from cipher context for the initialization vector
+   * and free up any allocated memory associate with it */
+  EVP_CIPHER_CTX_cleanup(&ctx_iv);
+
+  /* initialize cipher context for the message */
+  EVP_CIPHER_CTX_init(&ctx_msg);
+
+  /* set up cipher context for decryption with cipher type AES-128 in CBC mode,
+   * default implementation, given key, and initialization vector */
+  EVP_DecryptInit_ex(&ctx_msg, EVP_aes_128_cbc(), NULL, (unsigned char *)key,
+                     iv);
+
+  /* decrypt fromlen-16 bytes from buffer from and write the decrypted version
+   * to buffer to */
+  if (!EVP_DecryptUpdate(&ctx_msg, (unsigned char *)to, &outlen,
+                        ((const unsigned char *)from) + AES_IV_SIZE,
+                        (int)fromlen - AES_IV_SIZE)) {
+    crypto_log_errors(LOG_INFO, "decrypting");
+    return -1;
+  }
+
+  /* decrypt the final data */
+  if (!EVP_DecryptFinal_ex(&ctx_msg, ((unsigned char *)to) + outlen,
+                           &tmplen)) {
+    crypto_log_errors(LOG_INFO, "decrypting the final data");
+    return -1;
+  }
+  outlen += tmplen;
+
+  /* clear all information from cipher context and free up any allocated memory
+   * associate with it */
+  EVP_CIPHER_CTX_cleanup(&ctx_msg);
+
+  /* return number of written bytes */
+  return outlen;
+}
+
 /* SHA-1 */
 
 /** Compute the SHA1 digest of <b>len</b> bytes in data stored in
@@ -1803,6 +1967,64 @@
   dest[i] = '\0';
 }
 
+/** Implements base32 decoding as in rfc3548.  Limitation: Requires
+ * that srclen*5 is a multiple of 8. Returns 0 if successful, -1 otherwise.
+ */
+int
+base32_decode(char *dest, size_t destlen, const char *src, size_t srclen)
+{
+  unsigned int nbits, i, j, bit;
+  char *tmp;
+  nbits = srclen * 5;
+
+  tor_assert((nbits%8) == 0); /* We need an even multiple of 8 bits. */
+  tor_assert((nbits/8) <= destlen); /* We need enough space. */
+  tor_assert(destlen < SIZE_T_CEILING);
+
+  /* Convert base32 encoded chars to the 5-bit values that they represent. */
+  tmp = tor_malloc_zero(srclen);
+  for (j = 0; j < srclen; ++j) {
+    if (src[j] > 0x60 && src[j] < 0x7B) tmp[j] = src[j] - 0x61;
+    else if (src[j] > 0x31 && src[j] < 0x38) tmp[j] = src[j] - 0x18;
+    else {
+      log_warn(LD_BUG, "illegal character in base32 encoded string");
+      return -1;
+    }
+  }
+
+  /* Assemble result byte-wise by applying five possible cases. */
+  for (i = 0, bit = 0; bit < nbits; ++i, bit += 8) {
+    switch (bit % 40) {
+    case 0:
+      dest[i] = (((uint8_t)tmp[(bit/5)]) << 3) +
+                (((uint8_t)tmp[(bit/5)+1]) >> 2);
+      break;
+    case 8:
+      dest[i] = (((uint8_t)tmp[(bit/5)]) << 6) +
+                (((uint8_t)tmp[(bit/5)+1]) << 1) +
+                (((uint8_t)tmp[(bit/5)+2]) >> 4);
+      break;
+    case 16:
+      dest[i] = (((uint8_t)tmp[(bit/5)]) << 4) +
+                (((uint8_t)tmp[(bit/5)+1]) >> 1);
+      break;
+    case 24:
+      dest[i] = (((uint8_t)tmp[(bit/5)]) << 7) +
+                (((uint8_t)tmp[(bit/5)+1]) << 2) +
+                (((uint8_t)tmp[(bit/5)+2]) >> 3);
+      break;
+    case 32:
+      dest[i] = (((uint8_t)tmp[(bit/5)]) << 5) +
+                ((uint8_t)tmp[(bit/5)+1]);
+      break;
+    }
+  }
+
+  tor_free(tmp);
+  tmp = NULL;
+  return 0;
+}
+
 /** Implement RFC2440-style iterated-salted S2K conversion: convert the
  * <b>secret_len</b>-byte <b>secret</b> into a <b>key_out_len</b> byte
  * <b>key_out</b>.  As in RFC2440, the first 8 bytes of s2k_specifier

Modified: tor/trunk/src/common/crypto.h
===================================================================
--- tor/trunk/src/common/crypto.h	2007-09-18 16:38:37 UTC (rev 11488)
+++ tor/trunk/src/common/crypto.h	2007-09-18 17:07:56 UTC (rev 11489)
@@ -124,6 +124,11 @@
 int crypto_cipher_decrypt(crypto_cipher_env_t *env, char *to,
                           const char *from, size_t fromlen);
 
+int crypto_cipher_encrypt_cbc(const char *key, char *to, size_t tolen,
+                              const char *from, size_t fromlen);
+int crypto_cipher_decrypt_cbc(const char *key, char *to, size_t tolen,
+                              const char *from, size_t fromlen);
+
 /* SHA-1 */
 int crypto_digest(char *digest, const char *m, size_t len);
 crypto_digest_env_t *crypto_new_digest_env(void);
@@ -164,6 +169,7 @@
 /** Characters that can appear (case-insensitively) in a base-32 encoding. */
 #define BASE32_CHARS "abcdefghijklmnopqrstuvwxyz234567"
 void base32_encode(char *dest, size_t destlen, const char *src, size_t srclen);
+int base32_decode(char *dest, size_t destlen, const char *src, size_t srclen);
 
 int digest_to_base64(char *d64, const char *digest);
 int digest_from_base64(char *digest, const char *d64);

Modified: tor/trunk/src/or/test.c
===================================================================
--- tor/trunk/src/or/test.c	2007-09-18 16:38:37 UTC (rev 11488)
+++ tor/trunk/src/or/test.c	2007-09-18 17:07:56 UTC (rev 11489)
@@ -3084,6 +3084,115 @@
   smartlist_free(allocated);
 }
 
+/* Test AES-CBC encryption and decryption. */
+static void
+test_crypto_aes_cbc(void)
+{
+  char *plain, *encrypted1, *encrypted2, *decrypted1, *decrypted2;
+  char plain_1[1], plain_15[15], plain_16[16], plain_17[17];
+  char key1[16], key2[16];
+  size_t encrypted_size, decrypted_size;
+  plain = tor_malloc(4095);
+  encrypted1 = tor_malloc(4095 + 1 + 16);
+  encrypted2 = tor_malloc(4095 + 1 + 16);
+  decrypted1 = tor_malloc(4095 + 1);
+  decrypted2 = tor_malloc(4095 + 1);
+  crypto_rand(plain, 4095);
+  crypto_rand(key1, 16);
+  crypto_rand(key2, 16);
+  crypto_rand(plain_1, 1);
+  crypto_rand(plain_15, 15);
+  crypto_rand(plain_16, 16);
+  crypto_rand(plain_17, 17);
+  key1[0] = key2[0] + 128; /* Make sure that contents are different. */
+  /* Encrypt and decrypt with the same key. */
+  encrypted_size = crypto_cipher_encrypt_cbc(key1, encrypted1, 4095 + 1 + 16,
+                                             plain, 4095);
+  test_eq(encrypted_size, 4095 + 1 + 16);
+  decrypted_size = crypto_cipher_decrypt_cbc(key1, decrypted1, 4095 + 1,
+                                             encrypted1, encrypted_size);
+  test_eq(decrypted_size, 4095);
+  test_memeq(plain, decrypted1, 4095);
+  /* Encrypt a second time (with a new random initialization vector). */
+  encrypted_size = crypto_cipher_encrypt_cbc(key1, encrypted2, 4095 + 1 + 16,
+                                             plain, 4095);
+  test_eq(encrypted_size, 4095 + 1 + 16);
+  decrypted_size = crypto_cipher_decrypt_cbc(key1, decrypted2, 4095 + 1,
+                                             encrypted2, encrypted_size);
+  test_eq(decrypted_size, 4095);
+  test_memeq(plain, decrypted2, 4095);
+  test_memneq(encrypted1, encrypted2, encrypted_size);
+  /* Decrypt with the wrong key. */
+  decrypted_size = crypto_cipher_decrypt_cbc(key2, decrypted2, 4095 + 1,
+                                             encrypted1, encrypted_size);
+  test_memneq(plain, decrypted2, encrypted_size);
+  /* Alter the initialization vector. */
+  encrypted1[0] += 42;
+  decrypted_size = crypto_cipher_decrypt_cbc(key1, decrypted1, 4095 + 1,
+                                             encrypted1, encrypted_size);
+  test_memneq(plain, decrypted2, 4095);
+  /* Special length case: 1. */
+  encrypted_size = crypto_cipher_encrypt_cbc(key1, encrypted1, 32,
+                                             plain_1, 1);
+  test_eq(encrypted_size, 32);
+  decrypted_size = crypto_cipher_decrypt_cbc(key1, decrypted1, 16,
+                                             encrypted1, 32);
+  test_eq(decrypted_size, 1);
+  test_memeq(plain_1, decrypted1, 1);
+  /* Special length case: 15. */
+  encrypted_size = crypto_cipher_encrypt_cbc(key1, encrypted1, 32,
+                                             plain_15, 15);
+  test_eq(encrypted_size, 32);
+  decrypted_size = crypto_cipher_decrypt_cbc(key1, decrypted1, 16,
+                                             encrypted1, 32);
+  test_eq(decrypted_size, 15);
+  test_memeq(plain_15, decrypted1, 15);
+  /* Special length case: 16. */
+  encrypted_size = crypto_cipher_encrypt_cbc(key1, encrypted1, 48,
+                                             plain_16, 16);
+  test_eq(encrypted_size, 48);
+  decrypted_size = crypto_cipher_decrypt_cbc(key1, decrypted1, 32,
+                                             encrypted1, 48);
+  test_eq(decrypted_size, 16);
+  test_memeq(plain_16, decrypted1, 16);
+  /* Special length case: 17. */
+  encrypted_size = crypto_cipher_encrypt_cbc(key1, encrypted1, 48,
+                                             plain_17, 17);
+  test_eq(encrypted_size, 48);
+  decrypted_size = crypto_cipher_decrypt_cbc(key1, decrypted1, 32,
+                                             encrypted1, 48);
+  test_eq(decrypted_size, 17);
+  test_memeq(plain_17, decrypted1, 17);
+  /* Free memory. */
+  tor_free(plain);
+  tor_free(encrypted1);
+  tor_free(encrypted2);
+  tor_free(decrypted1);
+  tor_free(decrypted2);
+}
+
+/* Test base32 decoding. */
+static void
+test_crypto_base32_decode(void)
+{
+  char plain[60], encoded[96 + 1], decoded[60];
+  int res;
+  crypto_rand(plain, 60);
+  /* Encode and decode a random string. */
+  base32_encode(encoded, 96 + 1, plain, 60);
+  res = base32_decode(decoded, 60, encoded, 96);
+  test_eq(res, 0);
+  test_memeq(plain, decoded, 60);
+  /* Change encoded string and decode. */
+  if (encoded[0] == 'a')
+    encoded[0] = 'b';
+  else
+    encoded[0] = 'a';
+  res = base32_decode(decoded, 60, encoded, 96);
+  test_eq(res, 0);
+  test_memneq(plain, decoded, 60);
+}
+
 #define ENT(x) { #x, test_ ## x, 0, 0 }
 #define SUBENT(x,y) { #x "/" #y, test_ ## x ## _ ## y, 1, 0 }
 
@@ -3097,6 +3206,8 @@
   ENT(crypto),
   SUBENT(crypto, dh),
   SUBENT(crypto, s2k),
+  SUBENT(crypto, aes_cbc),
+  SUBENT(crypto, base32_decode),
   ENT(util),
   SUBENT(util, ip6_helpers),
   SUBENT(util, gzip),