static S2N_RESULT s2n_record_write_mac()

in tls/s2n_record_write.c [256:354]


static S2N_RESULT s2n_record_write_mac(struct s2n_connection *conn, struct s2n_blob *record_header,
        struct s2n_blob *plaintext, struct s2n_stuffer *out, uint32_t *bytes_written)
{
    RESULT_ENSURE_REF(conn);
    RESULT_ENSURE_REF(conn->server);
    RESULT_ENSURE_REF(conn->client);
    RESULT_ENSURE_REF(record_header);
    RESULT_ENSURE_REF(plaintext);
    RESULT_ENSURE_REF(out);
    RESULT_ENSURE_REF(bytes_written);
    *bytes_written = 0;

    struct s2n_hmac_state *mac = &conn->server->server_record_mac;
    const struct s2n_cipher_suite *cipher_suite = conn->server->cipher_suite;
    uint8_t *sequence_number = conn->server->server_sequence_number;

    if (conn->mode == S2N_CLIENT) {
        mac = &conn->client->client_record_mac;
        cipher_suite = conn->client->cipher_suite;
        sequence_number = conn->client->client_sequence_number;
    }

    RESULT_ENSURE_REF(cipher_suite);
    RESULT_ENSURE_REF(cipher_suite->record_alg);

    if (cipher_suite->record_alg->hmac_alg == S2N_HMAC_NONE) {
        /* If the S2N_HMAC_NONE algorithm is specified, a MAC should not be explicitly written.
         * This is the case for AEAD and Composite cipher types, where the MAC is written as part
         * of encryption. This is also the case for plaintext handshake records, where the null
         * stream cipher is used.
         */
        return S2N_RESULT_OK;
    }

    /**
     *= https://www.rfc-editor.org/rfc/rfc5246#section-6.2.3.1
     *# The MAC is generated as:
     *#
     *#    MAC(MAC_write_key, seq_num +
     */
    RESULT_GUARD_POSIX(s2n_hmac_update(mac, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN));

    struct s2n_stuffer header_stuffer = { 0 };
    RESULT_GUARD_POSIX(s2n_stuffer_init_written(&header_stuffer, record_header));

    /**
     *= https://www.rfc-editor.org/rfc/rfc5246#section-6.2.3.1
     *#     TLSCompressed.type +
     */
    void *record_type_byte = s2n_stuffer_raw_read(&header_stuffer, sizeof(uint8_t));
    RESULT_ENSURE_REF(record_type_byte);
    RESULT_GUARD_POSIX(s2n_hmac_update(mac, record_type_byte, sizeof(uint8_t)));

    /**
     *= https://www.rfc-editor.org/rfc/rfc5246#section-6.2.3.1
     *#     TLSCompressed.version +
     */
    void *protocol_version_bytes = s2n_stuffer_raw_read(&header_stuffer, S2N_TLS_PROTOCOL_VERSION_LEN);
    RESULT_ENSURE_REF(protocol_version_bytes);
    if (conn->actual_protocol_version > S2N_SSLv3) {
        /* SSLv3 doesn't include the protocol version in the MAC. */
        RESULT_GUARD_POSIX(s2n_hmac_update(mac, protocol_version_bytes, S2N_TLS_PROTOCOL_VERSION_LEN));
    }

    /**
     *= https://www.rfc-editor.org/rfc/rfc5246#section-6.2.3.1
     *#     TLSCompressed.length +
     *
     * Note that the length field refers to the length of the plaintext content, not the length of
     * TLSCiphertext fragment written to the record header, which accounts for additional fields
     * such as the padding and MAC.
     */
    uint8_t content_length_bytes[sizeof(uint16_t)] = { 0 };
    struct s2n_blob content_length_blob = { 0 };
    RESULT_GUARD_POSIX(s2n_blob_init(&content_length_blob, content_length_bytes, sizeof(content_length_bytes)));
    struct s2n_stuffer content_length_stuffer = { 0 };
    RESULT_GUARD_POSIX(s2n_stuffer_init(&content_length_stuffer, &content_length_blob));
    RESULT_GUARD_POSIX(s2n_stuffer_write_uint16(&content_length_stuffer, plaintext->size));
    RESULT_GUARD_POSIX(s2n_hmac_update(mac, content_length_bytes, sizeof(content_length_bytes)));

    /**
     *= https://www.rfc-editor.org/rfc/rfc5246#section-6.2.3.1
     *#     TLSCompressed.fragment);
     *#
     *# where "+" denotes concatenation.
     */
    RESULT_GUARD_POSIX(s2n_hmac_update(mac, plaintext->data, plaintext->size));

    uint8_t mac_digest_size = 0;
    RESULT_GUARD_POSIX(s2n_hmac_digest_size(mac->alg, &mac_digest_size));
    uint8_t *digest = s2n_stuffer_raw_write(out, mac_digest_size);
    RESULT_ENSURE_REF(digest);
    RESULT_GUARD_POSIX(s2n_hmac_digest(mac, digest, mac_digest_size));
    *bytes_written = mac_digest_size;

    RESULT_GUARD_POSIX(s2n_hmac_reset(mac));

    return S2N_RESULT_OK;
}