diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/common/crypto_pwbox.c | 167 | ||||
-rw-r--r-- | src/common/crypto_pwbox.h | 20 | ||||
-rw-r--r-- | src/common/include.am | 2 | ||||
-rw-r--r-- | src/test/test_crypto.c | 33 |
4 files changed, 222 insertions, 0 deletions
diff --git a/src/common/crypto_pwbox.c b/src/common/crypto_pwbox.c new file mode 100644 index 0000000000..61b6ebe96f --- /dev/null +++ b/src/common/crypto_pwbox.c @@ -0,0 +1,167 @@ + +#include "crypto.h" +#include "crypto_s2k.h" +#include "crypto_pwbox.h" +#include "di_ops.h" +#include "util.h" + +/* 7 bytes "TORBOX0" + 1 byte: header len (H) + H bytes: header, denoting secret key algorithm. + Round up to multiple of 128 bytes, then encrypt: + 4 bytes: data len + data + zeros + 32 bytes: HMAC-SHA256 of all previous bytes. +*/ + +#define MAX_OVERHEAD (S2K_MAXLEN + 8 + 32) + +/** + * Make an authenticated passphrase-encrypted blob to encode the + * <b>input_len</b> bytes in <b>input</b> using the passphrase + * <b>secret</b> of <b>secret_len</b> bytes. Allocate a new chunk of memory + * to hold the encrypted data, and store a pointer to that memory in + * *<b>out</b>, and its size in <b>outlen_out</b>. Use <b>s2k_flags</b> as an + * argument to the passphrase-hashing function. + */ +int +crypto_pwbox(uint8_t **out, size_t *outlen_out, + const uint8_t *input, size_t input_len, + const char *secret, size_t secret_len, + unsigned s2k_flags) +{ + uint8_t *result, *encrypted_portion, *hmac; + size_t encrypted_len = 128 * CEIL_DIV(input_len+4, 128); + size_t result_len = encrypted_len + MAX_OVERHEAD; + int spec_len; + uint8_t keys[CIPHER_KEY_LEN + DIGEST256_LEN]; + + crypto_cipher_t *cipher; + int rv; + + /* Allocate a buffer and put things in the right place. */ + result = tor_malloc_zero(result_len); + memcpy(result, "TORBOX0", 7); + + spec_len = secret_to_key_make_specifier(result + 8, + result_len - 8, + s2k_flags); + if (spec_len < 0 || spec_len > 255) + goto err; + result[7] = (uint8_t) spec_len; + + tor_assert(8 + spec_len + encrypted_len <= result_len); + + encrypted_portion = result + 8 + spec_len; + hmac = encrypted_portion + encrypted_len; + + set_uint32(encrypted_portion, htonl(input_len)); + memcpy(encrypted_portion+4, input, input_len); + + /* Now that all the data is in position, derive some keys, encrypt, and + * digest */ + if (secret_to_key_derivekey(keys, sizeof(keys), + result+8, spec_len, + secret, secret_len) < 0) + goto err; + + cipher = crypto_cipher_new((char*)keys); + crypto_cipher_crypt_inplace(cipher, (char*)encrypted_portion, encrypted_len); + crypto_cipher_free(cipher); + + crypto_hmac_sha256((char*)hmac, + (const char*)keys + CIPHER_KEY_LEN, + sizeof(keys)-CIPHER_KEY_LEN, + (const char*)result, 8 + spec_len + encrypted_len); + + *out = result; + *outlen_out = 8 + spec_len + encrypted_len + DIGEST256_LEN; + rv = 0; + goto out; + + err: + tor_free(result); + rv = -1; + + out: + memwipe(keys, 0, sizeof(keys)); + return rv; +} + +/** + * Try to decrypt the passphrase-encrypted blob of <b>input_len</b> bytes in + * <b>input</b> using the passphrase <b>secret</b> of <b>secret_len</b> bytes. + * On success, return 0 and allocate a new chunk of memory to hold the + * decrypted data, and store a pointer to that memory in *<b>out</b>, and its + * size in <b>outlen_out</b>. On failure, return UNPWBOX_BAD_SECRET if + * the passphrase might have been wrong, and UNPWBOX_CORRUPT if the object is + * definitely corrupt. + */ +int +crypto_unpwbox(uint8_t **out, size_t *outlen_out, + const uint8_t *inp, size_t input_len, + const char *secret, size_t secret_len) +{ + uint8_t *result = NULL; + const uint8_t *encrypted; + uint8_t keys[CIPHER_KEY_LEN + DIGEST256_LEN]; + size_t spec_bytes; + uint8_t hmac[DIGEST256_LEN]; + uint32_t result_len; + crypto_cipher_t *cipher = NULL; + int rv = UNPWBOX_CORRUPTED; + + if (input_len < 32) + goto err; + + if (tor_memneq(inp, "TORBOX0", 7)) + goto err; + + spec_bytes = inp[7]; + if (input_len < 8 + spec_bytes) + goto err; + + /* Now derive the keys and check the hmac. */ + if (secret_to_key_derivekey(keys, sizeof(keys), + inp+8, spec_bytes, + secret, secret_len) < 0) + goto err; + + crypto_hmac_sha256((char *)hmac, + (const char*)keys + CIPHER_KEY_LEN, sizeof(keys)-CIPHER_KEY_LEN, + (const char*)inp, input_len - DIGEST256_LEN); + + if (tor_memneq(hmac, inp + input_len - DIGEST256_LEN, DIGEST256_LEN)) { + rv = UNPWBOX_BAD_SECRET; + goto err; + } + + encrypted = inp + 8 + spec_bytes; + + /* How long is the plaintext? */ + cipher = crypto_cipher_new((char*)keys); + crypto_cipher_decrypt(cipher, (char*)&result_len, (char*)encrypted, 4); + result_len = ntohl(result_len); + if (input_len < 8 + spec_bytes + 4 + result_len + 32) + goto err; + + /* Allocate a buffer and decrypt */ + result = tor_malloc_zero(result_len); + crypto_cipher_decrypt(cipher, (char*)result, (char*)encrypted+4, result_len); + + *out = result; + *outlen_out = result_len; + + rv = UNPWBOX_OKAY; + goto out; + + err: + tor_free(result); + + out: + crypto_cipher_free(cipher); + memwipe(keys, 0, sizeof(keys)); + return rv; +} + diff --git a/src/common/crypto_pwbox.h b/src/common/crypto_pwbox.h new file mode 100644 index 0000000000..aadd477078 --- /dev/null +++ b/src/common/crypto_pwbox.h @@ -0,0 +1,20 @@ +#ifndef CRYPTO_PWBOX_H_INCLUDED_ +#define CRYPTO_PWBOX_H_INCLUDED_ + +#include "torint.h" + +#define UNPWBOX_OKAY 0 +#define UNPWBOX_BAD_SECRET -1 +#define UNPWBOX_CORRUPTED -2 + +int crypto_pwbox(uint8_t **out, size_t *outlen_out, + const uint8_t *inp, size_t input_len, + const char *secret, size_t secret_len, + unsigned s2k_flags); + +int crypto_unpwbox(uint8_t **out, size_t *outlen_out, + const uint8_t *inp, size_t input_len, + const char *secret, size_t secret_len); + +#endif + diff --git a/src/common/include.am b/src/common/include.am index 0fb1962720..83a3706489 100644 --- a/src/common/include.am +++ b/src/common/include.am @@ -75,6 +75,7 @@ LIBOR_A_SOURCES = \ LIBOR_CRYPTO_A_SOURCES = \ src/common/aes.c \ src/common/crypto.c \ + src/common/crypto_pwbox.c \ src/common/crypto_s2k.c \ src/common/crypto_format.c \ src/common/torgzip.c \ @@ -111,6 +112,7 @@ COMMONHEADERS = \ src/common/container.h \ src/common/crypto.h \ src/common/crypto_curve25519.h \ + src/common/crypto_pwbox.h \ src/common/crypto_s2k.h \ src/common/di_ops.h \ src/common/memarea.h \ diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index d36d65745c..3da440daa0 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -14,6 +14,7 @@ #include "crypto_curve25519.h" #endif #include "crypto_s2k.h" +#include "crypto_pwbox.h" extern const char AUTHORITY_SIGNKEY_3[]; extern const char AUTHORITY_SIGNKEY_A_DIGEST[]; @@ -887,6 +888,37 @@ test_crypto_s2k_errors(void *arg) ; } +static void +test_crypto_pwbox(void *arg) +{ + uint8_t *boxed=NULL, *decoded=NULL; + size_t len, dlen; + const char msg[] = "This bunny reminds you that you still have a " + "salamander in your sylladex. She is holding the bunny Dave got you. " + "It’s sort of uncanny how similar they are, aside from the knitted " + "enhancements. Seriously, what are the odds?? So weird."; + const char pw[] = "I'm a night owl and a wise bird too"; + + (void)arg; + + tt_int_op(0, ==, crypto_pwbox(&boxed, &len, (const uint8_t*)msg, strlen(msg), + pw, strlen(pw), 0)); + tt_assert(boxed); + tt_assert(len > 128+32); + + tt_int_op(0, ==, crypto_unpwbox(&decoded, &dlen, boxed, len, + pw, strlen(pw))); + + tt_assert(decoded); + tt_uint_op(dlen, ==, strlen(msg)); + tt_mem_op(decoded, ==, msg, dlen); + + done: + tor_free(boxed); + tor_free(decoded); + +} + /** Test AES-CTR encryption and decryption with IV. */ static void test_crypto_aes_iv(void *arg) @@ -1462,6 +1494,7 @@ struct testcase_t crypto_tests[] = { { "s2k_rfc2440_legacy", test_crypto_s2k_general, 0, &pass_data, (void*)"rfc2440-legacy" }, { "s2k_errors", test_crypto_s2k_errors, 0, NULL, NULL }, + { "pwbox", test_crypto_pwbox, 0, NULL, NULL }, { "aes_iv_AES", test_crypto_aes_iv, TT_FORK, &pass_data, (void*)"aes" }, { "aes_iv_EVP", test_crypto_aes_iv, TT_FORK, &pass_data, (void*)"evp" }, CRYPTO_LEGACY(base32_decode), |