#include <string.h>
#include "lib/arch/bytes.h"
#include "lib/crypt_ops/crypto_cipher.h"
#include "lib/crypt_ops/crypto_digest.h"
#include "lib/crypt_ops/crypto_pwbox.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_s2k.h"
#include "lib/crypt_ops/crypto_util.h"
#include "lib/ctime/di_ops.h"
#include "lib/intmath/muldiv.h"
#include "trunnel/pwbox.h"
#include "lib/log/util_bug.h"
#define MAX_OVERHEAD (S2K_MAXLEN + 8 + 1 + 32 + CIPHER_IV_LEN)
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();
tor_assert(enc);
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, tor_htonl((uint32_t)input_len));
memcpy(encrypted_portion+4, input, input_len);
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:
tor_free(result);
rv = -1;
out:
pwbox_encoded_free(enc);
memwipe(keys, 0, sizeof(keys));
return rv;
}
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;
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;
}
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 = tor_ntohl(result_len);
if (encrypted_len < result_len + 4)
goto err;
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;
}