/** * 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; }
ssize_t pwbox_encoded_encode(uint8_t *output, size_t avail, const pwbox_encoded_t *obj) { ssize_t result = 0; size_t written = 0; uint8_t *ptr = output; const char *msg; #ifdef TRUNNEL_CHECK_ENCODED_LEN const ssize_t encoded_len = pwbox_encoded_encoded_len(obj); #endif int enforce_avail = 0; const size_t avail_orig = avail; if (NULL != (msg = pwbox_encoded_check(obj))) goto check_failed; #ifdef TRUNNEL_CHECK_ENCODED_LEN trunnel_assert(encoded_len >= 0); #endif /* Encode u32 fixedbytes0 IN [PWBOX0_CONST0] */ trunnel_assert(written <= avail); if (avail - written < 4) goto truncated; trunnel_set_uint32(ptr, trunnel_htonl(obj->fixedbytes0)); written += 4; ptr += 4; /* Encode u32 fixedbytes1 IN [PWBOX0_CONST1] */ trunnel_assert(written <= avail); if (avail - written < 4) goto truncated; trunnel_set_uint32(ptr, trunnel_htonl(obj->fixedbytes1)); written += 4; ptr += 4; /* Encode u8 header_len */ trunnel_assert(written <= avail); if (avail - written < 1) goto truncated; trunnel_set_uint8(ptr, (obj->header_len)); written += 1; ptr += 1; /* Encode u8 skey_header[header_len] */ { size_t elt_len = TRUNNEL_DYNARRAY_LEN(&obj->skey_header); trunnel_assert(obj->header_len == elt_len); trunnel_assert(written <= avail); if (avail - written < elt_len) goto truncated; memcpy(ptr, obj->skey_header.elts_, elt_len); written += elt_len; ptr += elt_len; } /* Encode u8 iv[16] */ trunnel_assert(written <= avail); if (avail - written < 16) goto truncated; memcpy(ptr, obj->iv, 16); written += 16; ptr += 16; { /* Encode u8 data[] */ { size_t elt_len = TRUNNEL_DYNARRAY_LEN(&obj->data); trunnel_assert(written <= avail); if (avail - written < elt_len) goto truncated; memcpy(ptr, obj->data.elts_, elt_len); written += elt_len; ptr += elt_len; } trunnel_assert(written <= avail); if (avail - written < 32) goto truncated; avail = written + 32; enforce_avail = 1; } /* Encode u8 hmac[32] */ trunnel_assert(written <= avail); if (avail - written < 32) { if (avail_orig - written < 32) goto truncated; else goto check_failed; } memcpy(ptr, obj->hmac, 32); written += 32; ptr += 32; trunnel_assert(ptr == output + written); if (enforce_avail && avail != written) goto check_failed; #ifdef TRUNNEL_CHECK_ENCODED_LEN { trunnel_assert(encoded_len >= 0); trunnel_assert((size_t)encoded_len == written); } #endif return written; truncated: result = -2; goto fail; check_failed: (void)msg; result = -1; goto fail; fail: trunnel_assert(result < 0); return result; }