int aws_cryptosdk_priv_try_encrypt_body()

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;
}