diff options
author | Roger Dingledine <arma@torproject.org> | 2007-09-18 17:07:56 +0000 |
---|---|---|
committer | Roger Dingledine <arma@torproject.org> | 2007-09-18 17:07:56 +0000 |
commit | f15a4c8bd762d1eb6cada0334f81a71f424626ec (patch) | |
tree | 27afc3e81b555e3a875c76cc53654a205adbb67d | |
parent | 5985135ed69d10ca35bcfdb4cbfe6d60ad3de61d (diff) | |
download | tor-f15a4c8bd762d1eb6cada0334f81a71f424626ec.tar tor-f15a4c8bd762d1eb6cada0334f81a71f424626ec.tar.gz |
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.c | 222 | ||||
-rw-r--r-- | src/common/crypto.h | 6 | ||||
-rw-r--r-- | src/or/test.c | 111 |
3 files changed, 339 insertions, 0 deletions
diff --git a/src/common/crypto.c b/src/common/crypto.c index 588d63ac0..9b8d9b98b 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 5dc52f329..1d1f758fc 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 b420222d3..8cde128d0 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), |