in source/session_encrypt.c [287:398]
int aws_cryptosdk_priv_try_encrypt_body(
struct aws_cryptosdk_session *AWS_RESTRICT session,
struct aws_byte_buf *AWS_RESTRICT poutput,
struct aws_byte_cursor *AWS_RESTRICT pinput) {
/* First, figure out how much plaintext we need. */
size_t plaintext_size;
enum aws_cryptosdk_frame_type frame_type;
if (session->frame_size) {
/* This is a framed message; is it the last frame? */
if (session->precise_size_known && session->precise_size - session->data_so_far < session->frame_size) {
plaintext_size = (size_t)(session->precise_size - session->data_so_far);
frame_type = FRAME_TYPE_FINAL;
} else {
plaintext_size = (size_t)session->frame_size;
frame_type = FRAME_TYPE_FRAME;
}
} else {
/* This is a non-framed message. We need the precise size before doing anything. */
if (!session->precise_size_known) {
session->output_size_estimate = 0;
session->input_size_estimate = 0;
return AWS_OP_SUCCESS;
}
plaintext_size = (size_t)session->precise_size;
frame_type = FRAME_TYPE_SINGLE;
}
/*
* We'll use a shadow copy of the cursors; this lets us avoid modifying the
* output if the input is too small, and vice versa.
*/
struct aws_byte_buf output = *poutput;
struct aws_byte_cursor input = *pinput;
struct aws_cryptosdk_frame frame;
size_t ciphertext_size;
frame.type = frame_type;
if (session->frame_seqno > UINT32_MAX) {
return aws_raise_error(AWS_CRYPTOSDK_ERR_LIMIT_EXCEEDED);
}
frame.sequence_number = session->frame_seqno;
int rv = aws_cryptosdk_serialize_frame(&frame, &ciphertext_size, plaintext_size, &output, session->alg_props);
session->output_size_estimate = ciphertext_size;
session->input_size_estimate = plaintext_size;
if (rv) {
if (aws_last_error() == AWS_ERROR_SHORT_BUFFER) {
// The ciphertext buffer was too small. We've updated estimates;
// just return without doing any work.
return AWS_OP_SUCCESS;
} else {
// Some kind of validation failed?
return aws_raise_error(AWS_CRYPTOSDK_ERR_CRYPTO_UNKNOWN);
}
}
struct aws_byte_cursor plaintext = aws_byte_cursor_advance(&input, plaintext_size);
if (!plaintext.ptr) {
// Not enough plaintext buffer space.
return AWS_OP_SUCCESS;
}
if (aws_cryptosdk_encrypt_body(
session->alg_props,
&frame.ciphertext,
&plaintext,
&session->header.message_id,
frame.sequence_number,
frame.iv.buffer,
&session->content_key,
frame.authtag.buffer,
frame.type)) {
// Something terrible happened. Clear the ciphertext buffer and error out.
aws_byte_buf_secure_zero(poutput);
return aws_raise_error(AWS_CRYPTOSDK_ERR_CRYPTO_UNKNOWN);
}
if (session->signctx) {
// Note that the 'output' buffer contains only our ciphertext; we need to keep track of the frame
// headers as well
uint8_t *original_start = poutput->buffer + poutput->len;
uint8_t *current_end = output.buffer + output.len;
struct aws_byte_cursor to_sign = aws_byte_cursor_from_array(original_start, current_end - original_start);
if (aws_cryptosdk_sig_update(session->signctx, to_sign)) {
// Something terrible happened. Clear the ciphertext buffer and error out.
aws_secure_zero(original_start, current_end - original_start);
return aws_raise_error(AWS_CRYPTOSDK_ERR_CRYPTO_UNKNOWN);
}
}
// Success! Write back our input/output cursors now, and update our state.
*pinput = input;
*poutput = output;
session->data_so_far += plaintext_size;
session->frame_seqno++;
if (frame.type != FRAME_TYPE_FRAME) {
// We've written a final frame, move on to the trailer
aws_cryptosdk_priv_session_change_state(session, ST_WRITE_TRAILER);
}
return AWS_OP_SUCCESS;
}