static void run_s2k_tests(const unsigned flags, const unsigned type, int speclen, const int keylen, int legacy) { uint8_t buf[S2K_MAXLEN], buf2[S2K_MAXLEN], buf3[S2K_MAXLEN]; int r; size_t sz; const char pw1[] = "You can't come in here unless you say swordfish!"; const char pw2[] = "Now, I give you one more guess."; r = secret_to_key_new(buf, sizeof(buf), &sz, pw1, strlen(pw1), flags); tt_int_op(r, OP_EQ, S2K_OKAY); tt_int_op(buf[0], OP_EQ, type); tt_int_op(sz, OP_EQ, keylen + speclen); if (legacy) { memmove(buf, buf+1, sz-1); --sz; --speclen; } tt_int_op(S2K_OKAY, OP_EQ, secret_to_key_check(buf, sz, pw1, strlen(pw1))); tt_int_op(S2K_BAD_SECRET, OP_EQ, secret_to_key_check(buf, sz, pw2, strlen(pw2))); /* Move key to buf2, and clear it. */ memset(buf3, 0, sizeof(buf3)); memcpy(buf2, buf+speclen, keylen); memset(buf+speclen, 0, sz - speclen); /* Derivekey should produce the same results. */ tt_int_op(S2K_OKAY, OP_EQ, secret_to_key_derivekey(buf3, keylen, buf, speclen, pw1, strlen(pw1))); tt_mem_op(buf2, OP_EQ, buf3, keylen); /* Derivekey with a longer output should fill the output. */ memset(buf2, 0, sizeof(buf2)); tt_int_op(S2K_OKAY, OP_EQ, secret_to_key_derivekey(buf2, sizeof(buf2), buf, speclen, pw1, strlen(pw1))); tt_mem_op(buf2, OP_NE, buf3, sizeof(buf2)); memset(buf3, 0, sizeof(buf3)); tt_int_op(S2K_OKAY, OP_EQ, secret_to_key_derivekey(buf3, sizeof(buf3), buf, speclen, pw1, strlen(pw1))); tt_mem_op(buf2, OP_EQ, buf3, sizeof(buf3)); tt_assert(!tor_mem_is_zero((char*)buf2+keylen, sizeof(buf2)-keylen)); done: ; }
/** * 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 = NULL, *encrypted_portion; size_t encrypted_len = 128 * CEIL_DIV(input_len+4, 128); ssize_t result_len; int spec_len; uint8_t keys[CIPHER_KEY_LEN + DIGEST256_LEN]; pwbox_encoded_t *enc = NULL; ssize_t enc_len; crypto_cipher_t *cipher; int rv; enc = pwbox_encoded_new(); pwbox_encoded_setlen_skey_header(enc, S2K_MAXLEN); spec_len = secret_to_key_make_specifier( pwbox_encoded_getarray_skey_header(enc), S2K_MAXLEN, s2k_flags); if (BUG(spec_len < 0 || spec_len > S2K_MAXLEN)) goto err; pwbox_encoded_setlen_skey_header(enc, spec_len); enc->header_len = spec_len; crypto_rand((char*)enc->iv, sizeof(enc->iv)); pwbox_encoded_setlen_data(enc, encrypted_len); encrypted_portion = pwbox_encoded_getarray_data(enc); set_uint32(encrypted_portion, htonl((uint32_t)input_len)); memcpy(encrypted_portion+4, input, input_len); /* Now that all the data is in position, derive some keys, encrypt, and * digest */ const int s2k_rv = secret_to_key_derivekey(keys, sizeof(keys), pwbox_encoded_getarray_skey_header(enc), spec_len, secret, secret_len); if (BUG(s2k_rv < 0)) goto err; cipher = crypto_cipher_new_with_iv((char*)keys, (char*)enc->iv); crypto_cipher_crypt_inplace(cipher, (char*)encrypted_portion, encrypted_len); crypto_cipher_free(cipher); result_len = pwbox_encoded_encoded_len(enc); if (BUG(result_len < 0)) goto err; result = tor_malloc(result_len); enc_len = pwbox_encoded_encode(result, result_len, enc); if (BUG(enc_len < 0)) goto err; tor_assert(enc_len == result_len); crypto_hmac_sha256((char*) result + result_len - 32, (const char*)keys + CIPHER_KEY_LEN, DIGEST256_LEN, (const char*)result, result_len - 32); *out = result; *outlen_out = result_len; rv = 0; goto out; err: /* LCOV_EXCL_START This error case is often unreachable if we're correctly coded, unless somebody adds a new error case somewhere, or unless you're building without scrypto support. - make_specifier can't fail, unless S2K_MAX_LEN is too short. - secret_to_key_derivekey can't really fail unless we're missing scrypt, or the underlying function fails, or we pass it a bogus algorithm or parameters. - pwbox_encoded_encoded_len can't fail unless we're using trunnel incorrectly. - pwbox_encoded_encode can't fail unless we're using trunnel wrong, or it's buggy. */ tor_free(result); rv = -1; /* LCOV_EXCL_STOP */ out: pwbox_encoded_free(enc); memwipe(keys, 0, sizeof(keys)); return rv; }
static void test_crypto_s2k_errors(void *arg) { uint8_t buf[S2K_MAXLEN], buf2[S2K_MAXLEN]; size_t sz; (void)arg; /* Bogus specifiers: simple */ tt_int_op(S2K_BAD_LEN, OP_EQ, secret_to_key_derivekey(buf, sizeof(buf), (const uint8_t*)"", 0, "ABC", 3)); tt_int_op(S2K_BAD_ALGORITHM, OP_EQ, secret_to_key_derivekey(buf, sizeof(buf), (const uint8_t*)"\x10", 1, "ABC", 3)); tt_int_op(S2K_BAD_LEN, OP_EQ, secret_to_key_derivekey(buf, sizeof(buf), (const uint8_t*)"\x01\x02", 2, "ABC", 3)); tt_int_op(S2K_BAD_LEN, OP_EQ, secret_to_key_check((const uint8_t*)"", 0, "ABC", 3)); tt_int_op(S2K_BAD_ALGORITHM, OP_EQ, secret_to_key_check((const uint8_t*)"\x10", 1, "ABC", 3)); tt_int_op(S2K_BAD_LEN, OP_EQ, secret_to_key_check((const uint8_t*)"\x01\x02", 2, "ABC", 3)); /* too long gets "BAD_LEN" too */ memset(buf, 0, sizeof(buf)); buf[0] = 2; tt_int_op(S2K_BAD_LEN, OP_EQ, secret_to_key_derivekey(buf2, sizeof(buf2), buf, sizeof(buf), "ABC", 3)); /* Truncated output */ #ifdef HAVE_LIBSCRYPT_H tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 50, &sz, "ABC", 3, 0)); tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 50, &sz, "ABC", 3, S2K_FLAG_LOW_MEM)); #endif tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 37, &sz, "ABC", 3, S2K_FLAG_USE_PBKDF2)); tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 29, &sz, "ABC", 3, S2K_FLAG_NO_SCRYPT)); #ifdef HAVE_LIBSCRYPT_H tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 18, 0)); tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 18, S2K_FLAG_LOW_MEM)); #endif tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 17, S2K_FLAG_USE_PBKDF2)); tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 9, S2K_FLAG_NO_SCRYPT)); /* Now try using type-specific bogus specifiers. */ /* It's a bad pbkdf2 buffer if it has an iteration count that would overflow * int32_t. */ memset(buf, 0, sizeof(buf)); buf[0] = 1; /* pbkdf2 */ buf[17] = 100; /* 1<<100 is much bigger than INT32_MAX */ tt_int_op(S2K_BAD_PARAMS, OP_EQ, secret_to_key_derivekey(buf2, sizeof(buf2), buf, 18, "ABC", 3)); #ifdef HAVE_LIBSCRYPT_H /* It's a bad scrypt buffer if N would overflow uint64 */ memset(buf, 0, sizeof(buf)); buf[0] = 2; /* scrypt */ buf[17] = 100; /* 1<<100 is much bigger than UINT64_MAX */ tt_int_op(S2K_BAD_PARAMS, OP_EQ, secret_to_key_derivekey(buf2, sizeof(buf2), buf, 19, "ABC", 3)); #endif done: ; }
/** * 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]; uint8_t hmac[DIGEST256_LEN]; uint32_t result_len; size_t encrypted_len; crypto_cipher_t *cipher = NULL; int rv = UNPWBOX_CORRUPTED; ssize_t got_len; pwbox_encoded_t *enc = NULL; got_len = pwbox_encoded_parse(&enc, inp, input_len); if (got_len < 0 || (size_t)got_len != input_len) goto err; /* Now derive the keys and check the hmac. */ if (secret_to_key_derivekey(keys, sizeof(keys), pwbox_encoded_getarray_skey_header(enc), pwbox_encoded_getlen_skey_header(enc), secret, secret_len) < 0) goto err; crypto_hmac_sha256((char *)hmac, (const char*)keys + CIPHER_KEY_LEN, DIGEST256_LEN, (const char*)inp, input_len - DIGEST256_LEN); if (tor_memneq(hmac, enc->hmac, DIGEST256_LEN)) { rv = UNPWBOX_BAD_SECRET; goto err; } /* How long is the plaintext? */ encrypted = pwbox_encoded_getarray_data(enc); encrypted_len = pwbox_encoded_getlen_data(enc); if (encrypted_len < 4) goto err; cipher = crypto_cipher_new_with_iv((char*)keys, (char*)enc->iv); crypto_cipher_decrypt(cipher, (char*)&result_len, (char*)encrypted, 4); result_len = ntohl(result_len); if (encrypted_len < result_len + 4) 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); pwbox_encoded_free(enc); memwipe(keys, 0, sizeof(keys)); return rv; }