summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoger Dingledine <arma@torproject.org>2007-09-18 17:07:56 +0000
committerRoger Dingledine <arma@torproject.org>2007-09-18 17:07:56 +0000
commitf15a4c8bd762d1eb6cada0334f81a71f424626ec (patch)
tree27afc3e81b555e3a875c76cc53654a205adbb67d
parent5985135ed69d10ca35bcfdb4cbfe6d60ad3de61d (diff)
downloadtor-f15a4c8bd762d1eb6cada0334f81a71f424626ec.tar.gz
tor-f15a4c8bd762d1eb6cada0334f81a71f424626ec.zip
add some crypto/util functions from karsten, as the first
step of integrating his new hidden service stuff svn:r11489
-rw-r--r--src/common/crypto.c222
-rw-r--r--src/common/crypto.h6
-rw-r--r--src/or/test.c111
3 files changed, 339 insertions, 0 deletions
diff --git a/src/common/crypto.c b/src/common/crypto.c
index 588d63ac0e..9b8d9b98ba 100644
--- a/src/common/crypto.c
+++ b/src/common/crypto.c
@@ -1144,6 +1144,170 @@ crypto_cipher_decrypt(crypto_cipher_env_t *env, char *to,
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 @@ base32_encode(char *dest, size_t destlen, const char *src, size_t srclen)
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
diff --git a/src/common/crypto.h b/src/common/crypto.h
index 5dc52f3296..1d1f758fcb 100644
--- a/src/common/crypto.h
+++ b/src/common/crypto.h
@@ -124,6 +124,11 @@ int crypto_cipher_encrypt(crypto_cipher_env_t *env, char *to,
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 @@ int base64_decode(char *dest, size_t destlen, const char *src, size_t srclen);
/** 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);
diff --git a/src/or/test.c b/src/or/test.c
index b420222d32..8cde128d0c 100644
--- a/src/or/test.c
+++ b/src/or/test.c
@@ -3084,6 +3084,115 @@ test_util_mempool(void)
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 @@ static struct {
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),