diff options
author | Ladar Levison <ladar@lavabit.com> | 2017-06-23 13:51:33 +0300 |
---|---|---|
committer | Ladar Levison <ladar@lavabit.com> | 2017-06-23 13:51:33 +0300 |
commit | c0f95ac9368a066a7c3fa97134cbed470b59cb80 (patch) | |
tree | 841eb38cc2a2f914c6ad150bee90ac7b798ff09e | |
parent | a48e11b028b14f20e4914164535465ca13eb6d10 (diff) |
First attempt at part_decrypt added to the repo. Tests still failing.
-rw-r--r-- | check/magma/prime/prime_check.c | 6 | ||||
-rw-r--r-- | src/providers/prime/messages/chunks/chunks.h | 2 | ||||
-rw-r--r-- | src/providers/prime/messages/chunks/encrypted.c | 7 | ||||
-rw-r--r-- | src/providers/prime/messages/chunks/signature.c | 4 | ||||
-rw-r--r-- | src/providers/prime/messages/messages.c | 17 | ||||
-rw-r--r-- | src/providers/prime/messages/parts/parts.c | 88 |
6 files changed, 68 insertions, 56 deletions
diff --git a/check/magma/prime/prime_check.c b/check/magma/prime/prime_check.c index 39fd93dd..ed826148 100644 --- a/check/magma/prime/prime_check.c +++ b/check/magma/prime/prime_check.c @@ -221,7 +221,7 @@ START_TEST (check_prime_chunk_encrypted_s) { st_sprint(errmsg, "Encrypted chunk creation failed."); result = false; } - else if (result && !(set = encrypted_chunk_get(signing_pub, decrypt_keks, encrypted_chunk_buffer(chunk), MANAGEDBUF(1024)))) { + else if (result && !(set = encrypted_chunk_get(signing_pub, decrypt_keks, encrypted_chunk_buffer(chunk), MANAGEDBUF(1024), NULL))) { st_sprint(errmsg, "Encrypted chunk parsing failed."); result = false; } @@ -242,7 +242,7 @@ START_TEST (check_prime_chunk_encrypted_s) { st_sprint(errmsg, "Encrypted chunk creation failed."); result = false; } - else if (result && !(set = encrypted_chunk_get(signing_pub, decrypt_keks, encrypted_chunk_buffer(chunk), MANAGEDBUF(1024)))) { + else if (result && !(set = encrypted_chunk_get(signing_pub, decrypt_keks, encrypted_chunk_buffer(chunk), MANAGEDBUF(1024), NULL))) { st_sprint(errmsg, "Encrypted chunk parsing failed."); result = false; } @@ -264,7 +264,7 @@ START_TEST (check_prime_chunk_encrypted_s) { st_sprint(errmsg, "Encrypted chunk creation failed."); result = false; } - else if (result && !(set = encrypted_chunk_get(signing_pub, decrypt_keks, encrypted_chunk_buffer(chunk), MANAGEDBUF(1024)))) { + else if (result && !(set = encrypted_chunk_get(signing_pub, decrypt_keks, encrypted_chunk_buffer(chunk), MANAGEDBUF(1024), NULL))) { st_sprint(errmsg, "Encrypted chunk parsing failed."); result = false; } diff --git a/src/providers/prime/messages/chunks/chunks.h b/src/providers/prime/messages/chunks/chunks.h index fae1dcfa..d0d0e8cb 100644 --- a/src/providers/prime/messages/chunks/chunks.h +++ b/src/providers/prime/messages/chunks/chunks.h @@ -55,8 +55,8 @@ prime_encrypted_chunk_t * encrypted_chunk_alloc(void); stringer_t * encrypted_chunk_buffer(prime_encrypted_chunk_t *chunk); void encrypted_chunk_cleanup(prime_encrypted_chunk_t *chunk); void encrypted_chunk_free(prime_encrypted_chunk_t *chunk); +stringer_t * encrypted_chunk_get(ed25519_key_t *signing, prime_chunk_keks_t *keks, stringer_t *chunk, stringer_t *output, bool_t *spanning); prime_encrypted_chunk_t * encrypted_chunk_set(prime_message_chunk_type_t type, ed25519_key_t *signing, prime_chunk_keks_t *keks, prime_message_chunk_flags_t flags, stringer_t *payload); -stringer_t * encrypted_chunk_get(ed25519_key_t *signing, prime_chunk_keks_t *keks, stringer_t *chunk, stringer_t *output); #endif diff --git a/src/providers/prime/messages/chunks/encrypted.c b/src/providers/prime/messages/chunks/encrypted.c index ab69d4f5..77a66242 100644 --- a/src/providers/prime/messages/chunks/encrypted.c +++ b/src/providers/prime/messages/chunks/encrypted.c @@ -172,7 +172,7 @@ prime_encrypted_chunk_t * encrypted_chunk_set(prime_message_chunk_type_t type, e /** * @brief Take an encrypted message chunk, in serialized form, and return the decrypted payload data. */ -stringer_t * encrypted_chunk_get(ed25519_key_t *signing, prime_chunk_keks_t *keks, stringer_t *chunk, stringer_t *output) { +stringer_t * encrypted_chunk_get(ed25519_key_t *signing, prime_chunk_keks_t *keks, stringer_t *chunk, stringer_t *output, bool_t *spanning) { int32_t payload_size = 0; uint32_t big_endian_size = 0; @@ -188,7 +188,6 @@ stringer_t * encrypted_chunk_get(ed25519_key_t *signing, prime_chunk_keks_t *kek return NULL; } - /// HIGH: Add support for payloads that span across multiple chunks. // The minimum legal chunk size would be 4 + 32 + 80 + 64 = 180, while the max would be 4 + 32 + 69 + 128 + 16,777,099 = // 16,777,332, which accounts for the chunk header, and keyslots, which might be included in the buffer, but not in the // chunk header length. @@ -252,6 +251,10 @@ stringer_t * encrypted_chunk_get(ed25519_key_t *signing, prime_chunk_keks_t *kek // Flags mm_copy((uchr_t *)&flags, (uchr_t *)st_data_get(payload) + ED25519_SIGNATURE_LEN + 3, 1); + // If the spanning variable is valid pointer, then we'll check whether the spanning flag was set and let the caller know. + if (spanning && (flags & PRIME_CHUNK_FLAG_SPANNING) == PRIME_CHUNK_FLAG_SPANNING) *spanning = true; + else if (spanning) *spanning = false; + // Padding mm_copy((uchr_t *)&padding, (uchr_t *)st_data_get(payload) + ED25519_SIGNATURE_LEN + 4, 1); diff --git a/src/providers/prime/messages/chunks/signature.c b/src/providers/prime/messages/chunks/signature.c index 14c5b213..0b4c583d 100644 --- a/src/providers/prime/messages/chunks/signature.c +++ b/src/providers/prime/messages/chunks/signature.c @@ -72,6 +72,10 @@ int_t signature_tree_add(prime_signature_tree_t *chunk, stringer_t *data) { return 0; } +/** + * @brief Calculates the tree signature value by concatenating the hashes together, and generating an Ed25519 signature + * using the result. + */ stringer_t * signature_tree_get(ed25519_key_t *signing, prime_signature_tree_t *chunk, prime_chunk_keks_t *keks) { placer_t buffer; diff --git a/src/providers/prime/messages/messages.c b/src/providers/prime/messages/messages.c index 3b026f37..bed67aaf 100644 --- a/src/providers/prime/messages/messages.c +++ b/src/providers/prime/messages/messages.c @@ -132,7 +132,7 @@ stringer_t * naked_message_get(stringer_t *message, prime_org_signet_t *org, pri remaining -= st_length_get(&chunk[1]); if (chunk_header_read(PLACER(data, remaining), &type, &size, &chunk[2]) < 0 || type != PRIME_CHUNK_HEADERS || - !(headers = encrypted_chunk_get(keys.signing, keks, &chunk[2], NULL))) { + !(headers = encrypted_chunk_get(keys.signing, keks, &chunk[2], NULL, NULL))) { ephemeral_chunk_cleanup(ephemeral); keks_free(keks); return NULL; @@ -227,6 +227,7 @@ prime_message_t * naked_message_set(stringer_t *message, prime_org_key_t *destin uint32_t big_endian_size = 0; prime_message_t *result = NULL; prime_signature_tree_t *tree = NULL; + prime_encrypted_chunk_t *chunk = NULL; stringer_t *common = NULL, *holder[10]; placer_t header = pl_null(), body = pl_null(); uint16_t type = htobe16(PRIME_MESSAGE_NAKED); @@ -266,11 +267,12 @@ prime_message_t * naked_message_set(stringer_t *message, prime_org_key_t *destin holder[8] = mail_header_fetch_cleaned(&header, PLACER("In-Reply-To", 11)); holder[9] = mail_header_fetch_cleaned(&header, PLACER("Message-ID", 10)); - /// LOW: An effective, albeit kludgey, logic to ensure common headers are formatted correctly, and the values reside on a single line. + /// LOW: Effective, albeit kludgey, logic to ensure common headers are formatted correctly, and each header field is + /// reformatted values reside on a single line. common = st_merge("nsnnsnnsnnsnnsnnsnnsnnsnnsnnsn", (holder[0] ? "Date: " : ""), holder[0], (holder[0] ? "\n" : ""), (holder[1] ? "Subject: " : ""), holder[1], (holder[1] ? "\n" : ""), - (holder[2] ? "From: " : ""), holder[2], (holder[2] ? "\encrypted_chunk_buffer(n" : ""), + (holder[2] ? "From: " : ""), holder[2], (holder[2] ? "\n" : ""), (holder[3] ? "Sender: " : ""), holder[3], (holder[3] ? "\n" : ""), (holder[4] ? "Reply-To: " : ""), holder[4], (holder[4] ? "\n" : ""), (holder[5] ? "To: " : ""), holder[5], (holder[5] ? "\n" : ""), @@ -312,7 +314,14 @@ prime_message_t * naked_message_set(stringer_t *message, prime_org_key_t *destin signature_tree_add(tree, ephemeral_chunk_buffer(result->envelope.ephemeral)); signature_tree_add(tree, encrypted_chunk_buffer(result->metadata.common)); signature_tree_add(tree, encrypted_chunk_buffer(result->metadata.headers)); - signature_tree_add(tree, encrypted_chunk_buffer(result->content.body)); + + // Because the content chunks like the message body can be spanning chunks, we need to use an iterator which goes through + // the linked list, adding each chunk to the tree signature separately. + chunk = result->content.body; + while (chunk) { + signature_tree_add(tree, encrypted_chunk_buffer(chunk)); + chunk = chunk->next; + } // Calculate the tree signature. result->signatures.tree = signature_tree_get(result->keys.signing, tree, &(result->keks)); diff --git a/src/providers/prime/messages/parts/parts.c b/src/providers/prime/messages/parts/parts.c index 65429d05..c2a9ecf2 100644 --- a/src/providers/prime/messages/parts/parts.c +++ b/src/providers/prime/messages/parts/parts.c @@ -25,8 +25,10 @@ prime_encrypted_chunk_t * part_encrypt(prime_message_chunk_type_t type, ed25519_ return NULL; } - while (remaining) { -// + // Process the payload. If the entire payload will fit, encrypt the entire buffer (or what remains), otherwise we'll set + // the spanning chunk flag, and loop around until we finish processing the entire payload. + do { + // Process the payload. If the entire payload will fit, encrypt the entire buffer (or what remains), otherwise we'll // the next 16,777,098 bytes, set the spanning chunk flag, and loop around to finish the job. if (!(holder = encrypted_chunk_set(type, signing, keks, (remaining > 16777098 ? PRIME_CHUNK_FLAG_SPANNING : PRIME_CHUNK_FLAG_NONE), @@ -47,7 +49,8 @@ prime_encrypted_chunk_t * part_encrypt(prime_message_chunk_type_t type, ed25519_ // Advance our pointer and calculate the number of remaining bytes. data += (remaining > 16777098 ? 16777098 : remaining); remaining -= (remaining > 16777098 ? 16777098 : remaining); - } + + } while (remaining); return result; } @@ -62,54 +65,47 @@ stringer_t * part_decrypt(ed25519_key_t *signing, prime_chunk_keks_t *keks, stri // uint32_t legnth = 0; // placer_t chunk = pl_null(); + uchr_t *data = NULL; + bool_t spanning = false; + size_t length = 0, remaining = 0; + stringer_t *result = NULL, *payload = NULL; + // We need a signing key, encryption key, and at least one actor. - if (!signing || ed25519_type(signing) != ED25519_PUB || + if (!signing || st_empty_out(part, &data, &remaining) ||ed25519_type(signing) != ED25519_PUB || !keks || (!keks->author && !keks->origin && !keks->destination && !keks->recipient) || !part) { log_pedantic("Invalid parameters passed to the encrypted chunk parser."); return NULL; } - return NULL; -// -// size_t part_size; -// uint8_t curr_type; -// uint32_t chunk_size = 0; -// placer_t slice = pl_null(); -// stringer_t *result = NULL, *curr_chunk = NULL; -// -// // Set the data buffer of the part stringer to the beginning of the payload. -// st_data_set(part, st_data_get(payload)); -// -// while (true) { -// -// // Read the chunk headers from the beginning of the remaining payload. -// if (chunk_header_read(payload, &curr_type, &chunk_size, &slice) != 0) break; -// part_size += st_length_get(&slice); -// -// // TODO: Instead, check if the spanning-chunk flag (128) is set in the header. -// // Check if the chunk is still a part of the part that we are decrypting. -// if (curr_type != type) break; -// -// // Pass this into encrypted_chunk_get(). -// if (!(curr_chunk = encrypted_chunk_get(signing, keks, &slice, NULL))) { -// st_cleanup(result); -// return NULL; -// } -// -// // Either append curr_chunk onto result or set result equal to chunk. -// result = st_append(result, curr_chunk); -// -// // Free curr_chunk. -// st_free(curr_chunk); -// -// // Update payload and loop if necessary. -// st_data_set(payload, st_data_get(payload) + st_length_get(&slice)); -// st_length_set(payload, st_length_get(payload) - st_length_get(&slice)); -// } -// -// // Construct the part-spanning chunk placer. -// st_length_set(part, part_size); -// -// return result; + // Decrypt the first chunk, and if the spanning chunk flag is set, keep looping until we encounter a chunk without the + // spanning chunk flag, or we exhaust the data buffer. + do { + + if (!(payload = encrypted_chunk_get(signing, keks, PLACER(data, (length = chunk_header_size(PLACER(data, remaining)))), NULL, &spanning))) { + log_pedantic("Body part decryption failed."); + return NULL; + } + else if (result) { + result = st_append(result, payload); + st_free(payload); + } + else { + result = payload; + } + + data += length; + remaining -= length; + + } while (remaining && spanning); + + // If the entire buffer was processed and the spanning flag is still set, then our result is incomplete, so rather than + // return partial data, we return NULL to indicate an error. + if (!remaining && spanning) { + log_pedantic("Body part decryption failed. The last chunk in the span is missing."); + st_free(result); + return NULL; + } + + return result; } |