static void test_static_v1_input_short(void) { ssize_t siz; const struct hash_method *hash = hash_method_lookup("sha256"); unsigned char hash_ctx[hash->context_size]; unsigned char hash_dgst[hash->digest_size]; hash->init(hash_ctx); test_begin("test_static_v1_input_short"); struct istream *is_1 = i_stream_create_file(DCRYPT_SRC_DIR"/sample-v1_short.asc", IO_BLOCK_SIZE); struct istream *is_2 = i_stream_create_base64_decoder(is_1); i_stream_unref(&is_1); struct istream *is_3 = i_stream_create_decrypt(is_2, test_v1_kp.priv); i_stream_unref(&is_2); struct istream *is_4 = i_stream_create_hash(is_3, hash, hash_ctx); i_stream_unref(&is_3); while((siz = i_stream_read(is_4))>0) { i_stream_skip(is_4, siz); } if (is_4->stream_errno != 0) i_debug("error: %s", i_stream_get_error(is_4)); test_assert(is_4->stream_errno == 0); i_stream_unref(&is_4); hash->result(hash_ctx, hash_dgst); test_assert(strcmp(test_sample_v1_short_hash, binary_to_hex(hash_dgst, sizeof(hash_dgst))) == 0); test_end(); }
static void test_static_v2_input(void) { test_begin("test_static_v2_input"); ssize_t amt; const struct hash_method *hash = hash_method_lookup("sha256"); unsigned char hash_ctx[hash->context_size]; unsigned char hash_dgst[hash->digest_size]; hash->init(hash_ctx); struct istream *is_1 = i_stream_create_file(DCRYPT_SRC_DIR"/sample-v2.asc", IO_BLOCK_SIZE); struct istream *is_2 = i_stream_create_base64_decoder(is_1); i_stream_unref(&is_1); struct istream *is_3 = i_stream_create_decrypt(is_2, test_v2_kp.priv); i_stream_unref(&is_2); struct istream *is_4 = i_stream_create_hash(is_3, hash, hash_ctx); i_stream_unref(&is_3); while((amt = i_stream_read(is_4))>0) { i_stream_skip(is_4, amt); } if (is_4->stream_errno != 0) i_debug("error: %s", i_stream_get_error(is_4)); test_assert(is_4->stream_errno == 0); i_stream_unref(&is_4); hash->result(hash_ctx, hash_dgst); test_assert(strcmp(test_sample_v2_hash, binary_to_hex(hash_dgst, sizeof(hash_dgst))) == 0); test_end(); /** this code is left here to show how the sample file is created struct istream *is = i_stream_create_file("../lib-fts/udhr_fra.txt", 8192); struct istream *is_2 = i_stream_create_hash(is, hash, hash_ctx); int fd = open("sample-v2.bin", O_CREAT|O_TRUNC|O_WRONLY, S_IRWXU); struct ostream *os = o_stream_create_fd_file(fd, 0, TRUE); struct ostream *os_2 = o_stream_create_encrypt(os, "aes-256-gcm-sha256", test_v2_kp.pub, IO_STREAM_ENC_INTEGRITY_AEAD); const unsigned char *ptr; size_t siz; while(i_stream_read_data(is_2, &ptr, &siz, 0)>0) { o_stream_nsend(os_2, ptr, siz); i_stream_skip(is_2, siz); } i_assert(o_stream_nfinish(os_2)==0); o_stream_close(os_2); i_stream_close(is_2); hash->result(hash_ctx, hash_dgst); printf("%s\n", binary_to_hex(hash_dgst, sizeof(hash_dgst))); */ }
static void pbkdf_run(const char *plaintext, const char *salt, unsigned int rounds, unsigned char key_r[PBKDF2_KEY_SIZE_SHA1]) { memset(key_r, 0, PBKDF2_KEY_SIZE_SHA1); buffer_t buf; buffer_create_from_data(&buf, key_r, PBKDF2_KEY_SIZE_SHA1); pkcs5_pbkdf(PKCS5_PBKDF2, hash_method_lookup("sha1"), (const unsigned char *)plaintext, strlen(plaintext), (const unsigned char *)salt, strlen(salt), rounds, PBKDF2_KEY_SIZE_SHA1, &buf); }
static int hash_format_parse(const char *str, unsigned int *idxp, const struct hash_method **method_r, unsigned int *bits_r, const char **error_r) { const char *name, *end, *bitsp; unsigned int bits, i = *idxp; /* we should have "hash_name}" or "hash_name:bits}" */ end = strchr(str+i, '}'); if (end == NULL) { *error_r = "Missing '}'"; return -1; } *idxp = end - str; name = t_strdup_until(str+i, end); bitsp = strchr(name, ':'); if (bitsp != NULL) name = t_strdup_until(name, bitsp++); *method_r = hash_method_lookup(name); if (*method_r == NULL) { *error_r = t_strconcat("Unknown hash method: ", name, NULL); return -1; } bits = (*method_r)->digest_size * 8; if (bitsp != NULL) { if (str_to_uint(bitsp, &bits) < 0 || bits == 0 || bits > (*method_r)->digest_size*8) { *error_r = t_strconcat("Invalid :bits number: ", bitsp, NULL); return -1; } if ((bits % 8) != 0) { *error_r = t_strconcat( "Currently :bits must be divisible by 8: ", bitsp, NULL); return -1; } } *bits_r = bits; return 0; }
static ssize_t i_stream_decrypt_key(struct decrypt_istream *stream, const char *malg, unsigned int rounds, const unsigned char *data, const unsigned char *end, buffer_t *key, size_t key_len) { const char *error; enum dcrypt_key_type ktype; int keys; bool have_key = FALSE; unsigned char dgst[32]; uint32_t val; buffer_t buf; if (data == end) return 0; keys = *data++; /* if we have a key, prefab the digest */ if (stream->key_callback == NULL) { if (stream->priv_key == NULL) { io_stream_set_error(&stream->istream.iostream, "Decryption error: no private key available"); return -1; } buffer_create_from_data(&buf, dgst, sizeof(dgst)); dcrypt_key_id_private(stream->priv_key, "sha256", &buf, NULL); } /* for each key */ for(;keys>0;keys--) { if ((size_t)(end-data) < 1 + (ssize_t)sizeof(dgst)) return 0; ktype = *data++; if (stream->key_callback != NULL) { const char *hexdgst = binary_to_hex(data, sizeof(dgst)); /* digest length */ /* hope you going to give us right key.. */ int ret = stream->key_callback(hexdgst, &(stream->priv_key), &error, stream->key_context); if (ret < 0) { io_stream_set_error(&stream->istream.iostream, "Private key not available: %s", error); return -1; } if (ret > 0) { dcrypt_key_ref_private(stream->priv_key); have_key = TRUE; break; } } else { /* see if key matches to the one we have */ if (memcmp(dgst, data, sizeof(dgst)) == 0) { have_key = TRUE; break; } } data += sizeof(dgst); /* wasn't correct key, skip over some data */ if (!get_msb32(&data, end, &val) || !get_msb32(&data, end, &val)) return 0; } /* didn't find matching key */ if (!have_key) { io_stream_set_error(&stream->istream.iostream, "Decryption error: no private key available"); return -1; } data += sizeof(dgst); const unsigned char *ephemeral_key; uint32_t ep_key_len; const unsigned char *encrypted_key; uint32_t eklen; const unsigned char *ekhash; uint32_t ekhash_len; /* read ephemeral key (can be missing for RSA) */ if (!get_msb32(&data, end, &ep_key_len) || (size_t)(end-data) < ep_key_len) return 0; ephemeral_key = data; data += ep_key_len; /* read encrypted key */ if (!get_msb32(&data, end, &eklen) || (size_t)(end-data) < eklen) return 0; encrypted_key = data; data += eklen; /* read key data hash */ if (!get_msb32(&data, end, &ekhash_len) || (size_t)(end-data) < ekhash_len) return 0; ekhash = data; data += ekhash_len; /* decrypt the seed */ if (ktype == DCRYPT_KEY_RSA) { if (!dcrypt_rsa_decrypt(stream->priv_key, encrypted_key, eklen, key, &error)) { io_stream_set_error(&stream->istream.iostream, "key decryption error: %s", error); return -1; } } else if (ktype == DCRYPT_KEY_EC) { /* perform ECDHE */ buffer_t *temp_key = buffer_create_dynamic(pool_datastack_create(), 256); buffer_t *secret = buffer_create_dynamic(pool_datastack_create(), 256); buffer_t peer_key; buffer_create_from_const_data(&peer_key, ephemeral_key, ep_key_len); if (!dcrypt_ecdh_derive_secret_local(stream->priv_key, &peer_key, secret, &error)) { io_stream_set_error(&stream->istream.iostream, "Key decryption error: corrupted header"); return -1; } /* use shared secret and peer key to generate decryption key, AES-256-CBC has 32 byte key and 16 byte IV */ if (!dcrypt_pbkdf2(secret->data, secret->used, peer_key.data, peer_key.used, malg, rounds, temp_key, 32+16, &error)) { safe_memset(buffer_get_modifiable_data(secret, 0), 0, secret->used); io_stream_set_error(&stream->istream.iostream, "Key decryption error: %s", error); return -1; } safe_memset(buffer_get_modifiable_data(secret, 0), 0, secret->used); if (temp_key->used != 32+16) { safe_memset(buffer_get_modifiable_data(temp_key, 0), 0, temp_key->used); io_stream_set_error(&stream->istream.iostream, "Cannot perform key decryption: invalid temporary key"); return -1; } struct dcrypt_context_symmetric *dctx; if (!dcrypt_ctx_sym_create("AES-256-CBC", DCRYPT_MODE_DECRYPT, &dctx, &error)) { safe_memset(buffer_get_modifiable_data(temp_key, 0), 0, temp_key->used); io_stream_set_error(&stream->istream.iostream, "Key decryption error: %s", error); return -1; } const unsigned char *ptr = temp_key->data; /* we use ephemeral_key for IV */ dcrypt_ctx_sym_set_key(dctx, ptr, 32); dcrypt_ctx_sym_set_iv(dctx, ptr+32, 16); safe_memset(buffer_get_modifiable_data(temp_key, 0), 0, temp_key->used); int ec = 0; if (!dcrypt_ctx_sym_init(dctx, &error) || !dcrypt_ctx_sym_update(dctx, encrypted_key, eklen, key, &error) || !dcrypt_ctx_sym_final(dctx, key, &error)) { io_stream_set_error(&stream->istream.iostream, "Cannot perform key decryption: %s", error); ec = -1; } if (key->used != key_len) { io_stream_set_error(&stream->istream.iostream, "Cannot perform key decryption: invalid key length"); ec = -1; } dcrypt_ctx_sym_destroy(&dctx); if (ec != 0) return ec; } else { io_stream_set_error(&stream->istream.iostream, "Decryption error: unsupported key type 0x%02x", ktype); return -1; } /* make sure we were able to decrypt the encrypted key correctly */ const struct hash_method *hash = hash_method_lookup(t_str_lcase(malg)); if (hash == NULL) { safe_memset(buffer_get_modifiable_data(key, 0), 0, key->used); io_stream_set_error(&stream->istream.iostream, "Decryption error: unsupported hash algorithm: %s", malg); return -1; } unsigned char hctx[hash->context_size]; unsigned char hres[hash->digest_size]; hash->init(hctx); hash->loop(hctx, key->data, key->used); hash->result(hctx, hres); for(int i = 1; i < 2049; i++) { uint32_t i_msb = htonl(i); hash->init(hctx); hash->loop(hctx, hres, sizeof(hres)); hash->loop(hctx, &i_msb, sizeof(i_msb)); hash->result(hctx, hres); } /* do the comparison */ if (memcmp(ekhash, hres, I_MIN(ekhash_len, sizeof(hres))) != 0) { safe_memset(buffer_get_modifiable_data(key, 0), 0, key->used); io_stream_set_error(&stream->istream.iostream, "Decryption error: corrupted header ekhash"); return -1; } return 1; }
static int o_stream_encrypt_keydata_create_v2(struct encrypt_ostream *stream, const char *malg) { const struct hash_method *hash = hash_method_lookup(malg); const char *error; size_t tagsize; const unsigned char *ptr; size_t kl; unsigned int val; buffer_t *keydata, *res; if (hash == NULL) { io_stream_set_error(&stream->ostream.iostream, "Encryption init error: Hash algorithm '%s' not supported", malg); return -1; } /* key data length for internal use */ if ((stream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) { tagsize = IOSTREAM_TAG_SIZE; } else if ((stream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == IO_STREAM_ENC_INTEGRITY_AEAD) { tagsize = IOSTREAM_TAG_SIZE; } else { /* do not include MAC */ tagsize = 0; } /* generate keydata length of random data for key/iv/mac */ kl = dcrypt_ctx_sym_get_key_length(stream->ctx_sym) + dcrypt_ctx_sym_get_iv_length(stream->ctx_sym) + tagsize; keydata = buffer_create_dynamic(pool_datastack_create(), kl); random_fill(buffer_append_space_unsafe(keydata, kl), kl); buffer_set_used_size(keydata, kl); ptr = keydata->data; res = buffer_create_dynamic(default_pool, 256); /* store number of public key(s) */ buffer_append(res, "\1", 1); /* one key for now */ /* we can do multiple keys at this point, but do it only once now */ if (o_stream_encrypt_key_for_pubkey_v2(stream, malg, ptr, kl, stream->pub, res) != 0) { buffer_free(&res); return -1; } /* create hash of the key data */ unsigned char hctx[hash->context_size]; unsigned char hres[hash->digest_size]; hash->init(hctx); hash->loop(hctx, ptr, kl); hash->result(hctx, hres); for(int i = 1; i < 2049; i++) { uint32_t i_msb = htonl(i); hash->init(hctx); hash->loop(hctx, hres, sizeof(hres)); hash->loop(hctx, &i_msb, sizeof(i_msb)); hash->result(hctx, hres); } /* store key data hash */ val = htonl(sizeof(hres)); buffer_append(res, &val, 4); buffer_append(res, hres, sizeof(hres)); /* pick up key data that goes into stream */ stream->key_data_len = res->used; stream->key_data = buffer_free_without_data(&res); /* prime contexts */ dcrypt_ctx_sym_set_key(stream->ctx_sym, ptr, dcrypt_ctx_sym_get_key_length(stream->ctx_sym)); ptr += dcrypt_ctx_sym_get_key_length(stream->ctx_sym); dcrypt_ctx_sym_set_iv(stream->ctx_sym, ptr, dcrypt_ctx_sym_get_iv_length(stream->ctx_sym)); ptr += dcrypt_ctx_sym_get_iv_length(stream->ctx_sym); if ((stream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) { dcrypt_ctx_hmac_set_key(stream->ctx_mac, ptr, tagsize); dcrypt_ctx_hmac_init(stream->ctx_mac, &error); } else if ((stream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == IO_STREAM_ENC_INTEGRITY_AEAD) { dcrypt_ctx_sym_set_aad(stream->ctx_sym, ptr, tagsize); } /* clear out private key data */ safe_memset(buffer_get_modifiable_data(keydata, 0), 0, keydata->used); if (!dcrypt_ctx_sym_init(stream->ctx_sym, &error)) { io_stream_set_error(&stream->ostream.iostream, "Encryption init error: %s", error); return -1; } return 0; }