void askpass(const char *prompt, char *buf, size_t buf_size) { buffer_t str; buffer_create_from_data(&str, buf, buf_size); askpass_str(prompt, &str); buffer_append_c(&str, '\0'); }
static bool verify_b(const char *str, unsigned int i, bool starts_with_a) { unsigned int line_start = i, start, j, char_count = 0; char bufdata[1000]; buffer_t buf; buffer_create_from_data(&buf, bufdata, sizeof(bufdata)); if (strncmp(str+i, "\n\t", 2) == 0) { i += 2; line_start = i - 1; } for (;;) { if (strncmp(str+i, "=?utf-8?b?", 10) != 0) return FALSE; i += 10; start = i; for (; str[i] != '?'; i++) { if (str[i] == '\0') return FALSE; } buffer_set_used_size(&buf, 0); if (base64_decode(str+start, i-start, NULL, &buf) < 0) return FALSE; i++; if (!starts_with_a) j = 0; else { if (bufdata[0] != 'a') return FALSE; starts_with_a = FALSE; j = 1; } for (; j < buf.used; j += 2) { if (bufdata[j] != '\xc3' || bufdata[j+1] != '\xa4') return FALSE; char_count++; } if (j != buf.used) return FALSE; if (str[i++] != '=') return FALSE; if (i - line_start > 76) return FALSE; if (str[i] == '\0') break; if (strncmp(str+i, "\n\t", 2) != 0) return FALSE; i += 2; line_start = i - 1; } return char_count == 40; }
static string_t *get_digest_challenge(struct digest_auth_request *request) { const struct auth_settings *set = request->auth_request.set; buffer_t buf; string_t *str; const char *const *tmp; unsigned char nonce[16]; unsigned char nonce_base64[MAX_BASE64_ENCODED_SIZE(sizeof(nonce))+1]; int i; bool first_qop; /* realm="hostname" (multiple allowed) nonce="randomized data, at least 64bit" qop="auth,auth-int,auth-conf" maxbuf=number (with auth-int, auth-conf, defaults to 64k) charset="utf-8" (iso-8859-1 if it doesn't exist) algorithm="md5-sess" cipher="3des,des,rc4-40,rc4,rc4-56" (with auth-conf) */ /* get 128bit of random data as nonce */ random_fill(nonce, sizeof(nonce)); buffer_create_from_data(&buf, nonce_base64, sizeof(nonce_base64)); base64_encode(nonce, sizeof(nonce), &buf); buffer_append_c(&buf, '\0'); request->nonce = p_strdup(request->pool, buf.data); str = t_str_new(256); if (*set->realms_arr == NULL) { /* If no realms are given, at least Cyrus SASL client defaults to destination host name */ str_append(str, "realm=\"\","); } else { for (tmp = set->realms_arr; *tmp != NULL; tmp++) str_printfa(str, "realm=\"%s\",", *tmp); } str_printfa(str, "nonce=\"%s\",", request->nonce); str_append(str, "qop=\""); first_qop = TRUE; for (i = 0; i < QOP_COUNT; i++) { if (request->qop & (1 << i)) { if (first_qop) first_qop = FALSE; else str_append_c(str, ','); str_append(str, qop_names[i]); } } str_append(str, "\","); str_append(str, "charset=\"utf-8\"," "algorithm=\"md5-sess\""); return str; }
static int i_stream_base64_try_encode_line(struct base64_encoder_istream *bstream) { struct istream_private *stream = &bstream->istream; const unsigned char *data; size_t size, avail, buffer_avail; buffer_t buf; data = i_stream_get_data(stream->parent, &size); if (size == 0 || (size < 3 && !stream->parent->eof)) return 0; if (bstream->cur_line_len == bstream->chars_per_line) { /* @UNSAFE: end of line, add newline */ if (!i_stream_try_alloc(stream, bstream->crlf ? 2 : 1, &avail)) return -2; if (bstream->crlf) stream->w_buffer[stream->pos++] = '\r'; stream->w_buffer[stream->pos++] = '\n'; bstream->cur_line_len = 0; } i_stream_try_alloc(stream, (size+2)/3*4, &avail); buffer_avail = stream->buffer_size - stream->pos; if ((size + 2) / 3 * 4 > buffer_avail) { /* can't fit everything to destination buffer. write as much as we can. */ size = (buffer_avail / 4) * 3; if (size == 0) return -2; } else if (!stream->parent->eof && size % 3 != 0) { /* encode 3 chars at a time, so base64_encode() doesn't add '=' characters in the middle of the stream */ size -= (size % 3); } i_assert(size != 0); if (bstream->cur_line_len + (size+2)/3*4 > bstream->chars_per_line) { size = (bstream->chars_per_line - bstream->cur_line_len)/4 * 3; i_assert(size != 0); } buffer_create_from_data(&buf, stream->w_buffer + stream->pos, buffer_avail); base64_encode(data, size, &buf); i_assert(buf.used > 0); bstream->cur_line_len += buf.used; i_assert(bstream->cur_line_len <= bstream->chars_per_line); stream->pos += buf.used; i_stream_skip(stream->parent, size); return 1; }
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 bool master_input_request(struct auth_master_connection *conn, const char *args) { struct auth_client_connection *client_conn; const char *const *list, *const *params; unsigned int id, client_pid, client_id; uint8_t cookie[MASTER_AUTH_COOKIE_SIZE]; buffer_t buf; /* <id> <client-pid> <client-id> <cookie> [<parameters>] */ list = t_strsplit_tab(args); if (str_array_length(list) < 4 || str_to_uint(list[0], &id) < 0 || str_to_uint(list[1], &client_pid) < 0 || str_to_uint(list[2], &client_id) < 0) { i_error("BUG: Master sent broken REQUEST"); return FALSE; } buffer_create_from_data(&buf, cookie, sizeof(cookie)); if (hex_to_binary(list[3], &buf) < 0) { i_error("BUG: Master sent broken REQUEST cookie"); return FALSE; } params = list + 4; client_conn = auth_client_connection_lookup(client_pid); if (client_conn == NULL) { i_error("Master requested auth for nonexistent client %u", client_pid); o_stream_nsend_str(conn->output, t_strdup_printf("FAIL\t%u\n", id)); } else if (memcmp(client_conn->cookie, cookie, sizeof(cookie)) != 0) { i_error("Master requested auth for client %u with invalid cookie", client_pid); o_stream_nsend_str(conn->output, t_strdup_printf("FAIL\t%u\n", id)); } else if (!auth_request_handler_master_request( client_conn->request_handler, conn, id, client_id, params)) { i_error("Master requested auth for non-login client %u", client_pid); o_stream_nsend_str(conn->output, t_strdup_printf("FAIL\t%u\n", id)); } return TRUE; }
void mail_generate_guid_128_hash(const char *guid, guid_128_t guid_128_r) { unsigned char sha1_sum[SHA1_RESULTLEN]; buffer_t buf; if (guid_128_from_string(guid, guid_128_r) < 0) { /* not 128bit hex. use a hash of it instead. */ buffer_create_from_data(&buf, guid_128_r, GUID_128_SIZE); buffer_set_used_size(&buf, 0); sha1_get_digest(guid, strlen(guid), sha1_sum); #if SHA1_RESULTLEN < GUID_128_SIZE # error not possible #endif buffer_append(&buf, sha1_sum + SHA1_RESULTLEN - GUID_128_SIZE, GUID_128_SIZE); } }
static int i_stream_qp_try_decode_input(struct qp_decoder_istream *bstream, bool eof) { struct istream_private *stream = &bstream->istream; const unsigned char *data; size_t size, avail, buffer_avail, pos; buffer_t buf; int ret; data = i_stream_get_data(stream->parent, &size); if (size == 0) return 0; /* normally the decoded quoted-printable content can't be larger than the encoded content, but because we always use CRLFs, it may use twice as much space by only converting LFs to CRLFs. */ i_stream_try_alloc(stream, size, &avail); buffer_avail = stream->buffer_size - stream->pos; if (size > buffer_avail/2) { /* can't fit everything to destination buffer. write as much as we can. */ size = buffer_avail/2; if (size == 0) return -2; } buffer_create_from_data(&buf, stream->w_buffer + stream->pos, buffer_avail); ret = !eof ? quoted_printable_decode(data, size, &pos, &buf) : quoted_printable_decode_final(data, size, &pos, &buf); if (ret < 0) { io_stream_set_error(&stream->iostream, "Invalid quoted-printable data: 0x%s", binary_to_hex(data+pos, I_MAX(size-pos, 8))); stream->istream.stream_errno = EINVAL; return -1; } stream->pos += buf.used; i_stream_skip(stream->parent, pos); return pos > 0 ? 1 : 0; }
static int i_stream_qp_try_decode_input(struct qp_decoder_istream *bstream, bool eof) { struct istream_private *stream = &bstream->istream; const unsigned char *data; size_t size, avail, buffer_avail, pos; buffer_t buf; int ret; data = i_stream_get_data(stream->parent, &size); if (size == 0) return 0; /* the decoded quoted-printable content can never be larger than the encoded content. at worst they are equal. */ i_stream_try_alloc(stream, size, &avail); buffer_avail = stream->buffer_size - stream->pos; if (size > buffer_avail) { /* can't fit everything to destination buffer. write as much as we can. */ size = buffer_avail; if (size == 0) return -2; } buffer_create_from_data(&buf, stream->w_buffer + stream->pos, buffer_avail); ret = !eof ? quoted_printable_decode(data, size, &pos, &buf) : quoted_printable_decode_final(data, size, &pos, &buf); if (ret < 0) { stream->istream.stream_errno = EINVAL; return -1; } stream->pos += buf.used; i_stream_skip(stream->parent, pos); return pos > 0 ? 1 : 0; }
static void keywords_ext_register(struct mail_index_sync_map_ctx *ctx, uint32_t ext_map_idx, uint32_t reset_id, uint32_t hdr_size, uint32_t keywords_count) { buffer_t ext_intro_buf; struct mail_transaction_ext_intro *u; unsigned char ext_intro_data[sizeof(*u) + sizeof(MAIL_INDEX_EXT_KEYWORDS)-1]; i_assert(keywords_count > 0); buffer_create_from_data(&ext_intro_buf, ext_intro_data, sizeof(ext_intro_data)); u = buffer_append_space_unsafe(&ext_intro_buf, sizeof(*u)); u->ext_id = ext_map_idx; u->reset_id = reset_id; u->hdr_size = hdr_size; u->record_size = (keywords_count + CHAR_BIT - 1) / CHAR_BIT; if ((u->record_size % 4) != 0) { /* since we aren't properly aligned anyway, reserve one extra byte for future */ u->record_size++; } u->record_align = 1; if (ext_map_idx == (uint32_t)-1) { u->name_size = strlen(MAIL_INDEX_EXT_KEYWORDS); buffer_append(&ext_intro_buf, MAIL_INDEX_EXT_KEYWORDS, u->name_size); } ctx->internal_update = TRUE; if (mail_index_sync_ext_intro(ctx, u) < 0) i_panic("Keyword extension growing failed"); ctx->internal_update = FALSE; }
static int i_stream_base64_try_decode_block(struct base64_decoder_istream *bstream) { struct istream_private *stream = &bstream->istream; const unsigned char *data; size_t size, avail, buffer_avail, pos; buffer_t buf; data = i_stream_get_data(stream->parent, &size); if (size == 0) return 0; i_stream_try_alloc(stream, (size+3)/4*3, &avail); buffer_avail = stream->buffer_size - stream->pos; if ((size + 3) / 4 * 3 > buffer_avail) { /* can't fit everything to destination buffer. write as much as we can. */ size = (buffer_avail / 3) * 4; if (size == 0) return -2; } buffer_create_from_data(&buf, stream->w_buffer + stream->pos, buffer_avail); if (base64_decode(data, size, &pos, &buf) < 0) { io_stream_set_error(&stream->iostream, "Invalid base64 data: 0x%s", binary_to_hex(data+pos, I_MIN(size-pos, 8))); stream->istream.stream_errno = EINVAL; return -1; } stream->pos += buf.used; i_stream_skip(stream->parent, pos); return pos > 0 ? 1 : 0; }
static ssize_t i_stream_decrypt_read(struct istream_private *stream) { struct decrypt_istream *dstream = (struct decrypt_istream *)stream; const unsigned char *data; size_t size, decrypt_size; const char *error = NULL; int ret; bool check_mac = FALSE; /* not if it's broken */ if (stream->istream.stream_errno != 0) return -1; for (;;) { /* remove skipped data from buffer */ if (stream->skip > 0) { i_assert(stream->skip <= dstream->buf->used); buffer_delete(dstream->buf, 0, stream->skip); stream->pos -= stream->skip; stream->skip = 0; } stream->buffer = dstream->buf->data; i_assert(stream->pos <= dstream->buf->used); if (stream->pos >= dstream->istream.max_buffer_size) { /* stream buffer still at maximum */ return -2; } /* if something is already decrypted, return as much of it as we can */ if (dstream->initialized && dstream->buf->used > 0) { size_t new_pos, bytes; /* only return up to max_buffer_size bytes, even when buffer actually has more, as not to confuse the caller */ if (dstream->buf->used <= dstream->istream.max_buffer_size) { new_pos = dstream->buf->used; if (dstream->finalized) stream->istream.eof = TRUE; } else { new_pos = dstream->istream.max_buffer_size; } bytes = new_pos - stream->pos; stream->pos = new_pos; return (ssize_t)bytes; } if (dstream->finalized) { /* all data decrypted */ stream->istream.eof = TRUE; return -1; } /* need to read more input */ ret = i_stream_read(stream->parent); if (ret == 0 || ret == -2) return ret; data = i_stream_get_data(stream->parent, &size); if (ret == -1 && (size == 0 || stream->parent->stream_errno != 0)) { stream->istream.stream_errno = stream->parent->stream_errno; /* file was empty */ if (!dstream->initialized && size == 0 && stream->parent->eof) { stream->istream.eof = TRUE; return -1; } if (stream->istream.stream_errno != 0) return -1; if (!dstream->initialized) { io_stream_set_error(&stream->iostream, "Decryption error: %s", "Input truncated in decryption header"); stream->istream.stream_errno = EINVAL; return -1; } /* final block */ if (dcrypt_ctx_sym_final(dstream->ctx_sym, dstream->buf, &error)) { dstream->finalized = TRUE; continue; } io_stream_set_error(&stream->iostream, "MAC error: %s", error); stream->istream.stream_errno = EINVAL; return -1; } if (!dstream->initialized) { ssize_t hret; if ((hret=i_stream_decrypt_read_header(dstream, data, size)) <= 0) { if (hret < 0) { if (stream->istream.stream_errno == 0) /* assume temporary failure */ stream->istream.stream_errno = EIO; return -1; } if (hret == 0 && stream->parent->eof) { /* not encrypted by us */ stream->istream.stream_errno = EINVAL; io_stream_set_error(&stream->iostream, "Truncated header"); return -1; } } if (hret == 0) { /* see if we can get more data */ continue; } else { /* clean up buffer */ safe_memset(buffer_get_modifiable_data(dstream->buf, 0), 0, dstream->buf->used); buffer_set_used_size(dstream->buf, 0); i_stream_skip(stream->parent, hret); } data = i_stream_get_data(stream->parent, &size); } decrypt_size = size; if (dstream->use_mac) { if (stream->parent->eof) { if (decrypt_size < dstream->ftr) { io_stream_set_error(&stream->iostream, "Decryption error: footer is longer than data"); stream->istream.stream_errno = EINVAL; return -1; } check_mac = TRUE; } else { /* ignore footer's length of data until we reach EOF */ size -= dstream->ftr; } decrypt_size -= dstream->ftr; if ((dstream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) { if (!dcrypt_ctx_hmac_update(dstream->ctx_mac, data, decrypt_size, &error)) { io_stream_set_error(&stream->iostream, "MAC error: %s", error); stream->istream.stream_errno = EINVAL; return -1; } } } if (check_mac) { if ((dstream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) { unsigned char dgst[dcrypt_ctx_hmac_get_digest_length(dstream->ctx_mac)]; buffer_t db; buffer_create_from_data(&db, dgst, sizeof(dgst)); if (!dcrypt_ctx_hmac_final(dstream->ctx_mac, &db, &error)) { io_stream_set_error(&stream->iostream, "Cannot verify MAC: %s", error); stream->istream.stream_errno = EINVAL; return -1; } if (memcmp(dgst, data + decrypt_size, dcrypt_ctx_hmac_get_digest_length(dstream->ctx_mac)) != 0) { io_stream_set_error(&stream->iostream, "Cannot verify MAC: mismatch"); stream->istream.stream_errno = EINVAL; return -1; } } else if ((dstream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == IO_STREAM_ENC_INTEGRITY_AEAD) { dcrypt_ctx_sym_set_tag(dstream->ctx_sym, data + decrypt_size, dstream->ftr); } } if (!dcrypt_ctx_sym_update(dstream->ctx_sym, data, decrypt_size, dstream->buf, &error)) { io_stream_set_error(&stream->iostream, "Decryption error: %s", error); stream->istream.stream_errno = EINVAL; return -1; } i_stream_skip(stream->parent, size); } }
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_v1(struct encrypt_ostream *stream) { buffer_t *encrypted_key, *ephemeral_key, *secret, *res, buf; const char *error = NULL; const struct hash_method *hash = &hash_method_sha256; /* various temporary buffers */ unsigned char seed[IO_STREAM_ENCRYPT_SEED_SIZE]; unsigned char pkhash[hash->digest_size]; unsigned char ekhash[hash->digest_size]; unsigned char hres[hash->digest_size]; unsigned char hctx[hash->context_size]; /* hash the public key first */ buffer_create_from_data(&buf, pkhash, sizeof(pkhash)); if (!dcrypt_key_id_public_old(stream->pub, &buf, &error)) { io_stream_set_error(&stream->ostream.iostream, "Key hash failed: %s", error); return -1; } /* hash the key base */ hash->init(hctx); hash->loop(hctx, seed, sizeof(seed)); hash->result(hctx, ekhash); ephemeral_key = buffer_create_dynamic(pool_datastack_create(), 256); encrypted_key = buffer_create_dynamic(pool_datastack_create(), 256); secret = buffer_create_dynamic(pool_datastack_create(), 256); if (!dcrypt_ecdh_derive_secret_peer(stream->pub, ephemeral_key, secret, &error)) { io_stream_set_error(&stream->ostream.iostream, "Cannot perform ECDH: %s", error); return -1; } /* hash the secret data */ hash->init(hctx); hash->loop(hctx, secret->data, secret->used); hash->result(hctx, hres); safe_memset(buffer_get_modifiable_data(secret, 0), 0, secret->used); /* use it to encrypt the actual encryption key */ struct dcrypt_context_symmetric *dctx; if (!dcrypt_ctx_sym_create("aes-256-ctr", DCRYPT_MODE_ENCRYPT, &dctx, &error)) { io_stream_set_error(&stream->ostream.iostream, "Key encryption error: %s", error); return -1; } random_fill(seed, sizeof(seed)); hash->init(hctx); hash->loop(hctx, seed, sizeof(seed)); hash->result(hctx, ekhash); int ec = 0; /* NB! The old code was broken and used this kind of IV - it is not correct, but we need to stay compatible with old data */ dcrypt_ctx_sym_set_iv(dctx, (const unsigned char*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16); dcrypt_ctx_sym_set_key(dctx, hres, sizeof(hres)); if (!dcrypt_ctx_sym_init(dctx, &error) || !dcrypt_ctx_sym_update(dctx, seed, sizeof(seed), encrypted_key, &error) || !dcrypt_ctx_sym_final(dctx, encrypted_key, &error)) { ec = -1; } dcrypt_ctx_sym_destroy(&dctx); if (ec != 0) { safe_memset(seed, 0, sizeof(seed)); io_stream_set_error(&stream->ostream.iostream, "Key encryption error: %s", error); return -1; } /* same as above */ dcrypt_ctx_sym_set_iv(stream->ctx_sym, (const unsigned char*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16); dcrypt_ctx_sym_set_key(stream->ctx_sym, seed, sizeof(seed)); safe_memset(seed, 0, sizeof(seed)); if (!dcrypt_ctx_sym_init(stream->ctx_sym, &error)) { io_stream_set_error(&stream->ostream.iostream, "Encryption init error: %s", error); return -1; } res = buffer_create_dynamic(default_pool, 256); /* ephemeral key */ unsigned short s; s = htons(ephemeral_key->used); buffer_append(res, &s, 2); buffer_append(res, ephemeral_key->data, ephemeral_key->used); /* public key hash */ s = htons(sizeof(pkhash)); buffer_append(res, &s, 2); buffer_append(res, pkhash, sizeof(pkhash)); /* encrypted key hash */ s = htons(sizeof(ekhash)); buffer_append(res, &s, 2); buffer_append(res, ekhash, sizeof(ekhash)); /* encrypted key */ s = htons(encrypted_key->used); buffer_append(res, &s, 2); buffer_append(res, encrypted_key->data, encrypted_key->used); stream->key_data_len = res->used; stream->key_data = buffer_free_without_data(&res); return 0; }
static void mail_transaction_log_append_ext_intros(struct mail_index_export_context *ctx) { struct mail_index_transaction *t = ctx->trans; const struct mail_transaction_ext_intro *resize; const struct mail_index_transaction_ext_hdr_update *hdrs; struct mail_transaction_ext_reset ext_reset; unsigned int resize_count, ext_count = 0; unsigned int hdrs_count, reset_id_count, reset_count; uint32_t ext_id, reset_id; const struct mail_transaction_ext_reset *reset; const uint32_t *reset_ids; buffer_t reset_buf; if (!array_is_created(&t->ext_resizes)) { resize = NULL; resize_count = 0; } else { resize = array_get(&t->ext_resizes, &resize_count); if (ext_count < resize_count) ext_count = resize_count; } if (!array_is_created(&t->ext_reset_ids)) { reset_ids = NULL; reset_id_count = 0; } else { reset_ids = array_get(&t->ext_reset_ids, &reset_id_count); } if (!array_is_created(&t->ext_resets)) { reset = NULL; reset_count = 0; } else { reset = array_get(&t->ext_resets, &reset_count); if (ext_count < reset_count) ext_count = reset_count; } if (!array_is_created(&t->ext_hdr_updates)) { hdrs = NULL; hdrs_count = 0; } else { hdrs = array_get(&t->ext_hdr_updates, &hdrs_count); if (ext_count < hdrs_count) ext_count = hdrs_count; } memset(&ext_reset, 0, sizeof(ext_reset)); buffer_create_from_data(&reset_buf, &ext_reset, sizeof(ext_reset)); buffer_set_used_size(&reset_buf, sizeof(ext_reset)); for (ext_id = 0; ext_id < ext_count; ext_id++) { if (ext_id < reset_count) ext_reset = reset[ext_id]; else ext_reset.new_reset_id = 0; if ((ext_id < resize_count && resize[ext_id].name_size) || ext_reset.new_reset_id != 0 || (ext_id < hdrs_count && hdrs[ext_id].alloc_size > 0)) { if (ext_reset.new_reset_id != 0) { /* we're going to reset this extension immediately after the intro */ reset_id = 0; } else { reset_id = ext_id < reset_id_count ? reset_ids[ext_id] : 0; } log_append_ext_intro(ctx, ext_id, reset_id); } if (ext_reset.new_reset_id != 0) { i_assert(ext_id < reset_id_count && ext_reset.new_reset_id == reset_ids[ext_id]); log_append_buffer(ctx, &reset_buf, MAIL_TRANSACTION_EXT_RESET); } if (ext_id < hdrs_count && hdrs[ext_id].alloc_size > 0) { T_BEGIN { log_append_ext_hdr_update(ctx, &hdrs[ext_id]); } T_END; } }