/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

/**
 * This module implements key logging as defined by the NSS Key Log Format
 *
 * See https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format
 *
 * This key log file is a series of lines. Comment lines begin with a sharp
 * character ('#') and are ignored. Secrets follow the format
 * <Label> <space> <ClientRandom> <space> <Secret> where:
 *
 *   <Label> describes the following secret.
 *   <ClientRandom> is 32 bytes Random value from the Client Hello message, encoded as 64 hexadecimal characters.
 *   <Secret> depends on the Label (see below).
 *
 * The following labels are defined, followed by a description of the secret:
 *
 *   RSA: 48 bytes for the premaster secret, encoded as 96 hexadecimal characters (removed in NSS 3.34)
 *   CLIENT_RANDOM: 48 bytes for the master secret, encoded as 96 hexadecimal characters (for SSL 3.0, TLS 1.0, 1.1 and 1.2)
 *   CLIENT_EARLY_TRAFFIC_SECRET: the hex-encoded early traffic secret for the client side (for TLS 1.3)
 *   CLIENT_HANDSHAKE_TRAFFIC_SECRET: the hex-encoded handshake traffic secret for the client side (for TLS 1.3)
 *   SERVER_HANDSHAKE_TRAFFIC_SECRET: the hex-encoded handshake traffic secret for the server side (for TLS 1.3)
 *   CLIENT_TRAFFIC_SECRET_0: the first hex-encoded application traffic secret for the client side (for TLS 1.3)
 *   SERVER_TRAFFIC_SECRET_0: the first hex-encoded application traffic secret for the server side (for TLS 1.3)
 *   EARLY_EXPORTER_SECRET: the hex-encoded early exporter secret (for TLS 1.3).
 *   EXPORTER_SECRET: the hex-encoded exporter secret (for TLS 1.3)
 */

#include "api/s2n.h"
#include "tls/s2n_config.h"
#include "tls/s2n_connection.h"
#include "tls/s2n_crypto_constants.h"
#include "tls/s2n_quic_support.h" /* this currently holds the s2n_secret_type_t enum */
#include "utils/s2n_blob.h"
#include "utils/s2n_safety.h"

/* hex requires 2 chars per byte */
#define HEX_ENCODING_SIZE 2

S2N_RESULT s2n_key_log_hex_encode(struct s2n_stuffer *output, uint8_t *bytes, size_t len)
{
    RESULT_ENSURE_MUT(output);
    RESULT_ENSURE_REF(bytes);

    const uint8_t chars[] = "0123456789abcdef";

    for (size_t i = 0; i < len; i++) {
        uint8_t upper = bytes[i] >> 4;
        uint8_t lower = bytes[i] & 0x0f;

        RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(output, chars[upper]));
        RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(output, chars[lower]));
    }

    return S2N_RESULT_OK;
}

S2N_RESULT s2n_key_log_tls13_secret(struct s2n_connection *conn, const struct s2n_blob *secret, s2n_secret_type_t secret_type)
{
    RESULT_ENSURE_REF(conn);
    RESULT_ENSURE_REF(conn->config);
    RESULT_ENSURE_REF(secret);

    /* only emit keys if the callback has been set */
    if (!conn->config->key_log_cb) {
        return S2N_RESULT_OK;
    }

    const uint8_t client_early_traffic_label[] = "CLIENT_EARLY_TRAFFIC_SECRET ";
    const uint8_t client_handshake_label[] = "CLIENT_HANDSHAKE_TRAFFIC_SECRET ";
    const uint8_t server_handshake_label[] = "SERVER_HANDSHAKE_TRAFFIC_SECRET ";
    const uint8_t client_traffic_label[] = "CLIENT_TRAFFIC_SECRET_0 ";
    const uint8_t server_traffic_label[] = "SERVER_TRAFFIC_SECRET_0 ";

    const uint8_t *label = NULL;
    uint8_t label_size = 0;

    switch (secret_type) {
        case S2N_CLIENT_EARLY_TRAFFIC_SECRET:
            label = client_early_traffic_label;
            label_size = sizeof(client_early_traffic_label) - 1;
            break;
        case S2N_CLIENT_HANDSHAKE_TRAFFIC_SECRET:
            label = client_handshake_label;
            label_size = sizeof(client_handshake_label) - 1;
            break;
        case S2N_SERVER_HANDSHAKE_TRAFFIC_SECRET:
            label = server_handshake_label;
            label_size = sizeof(server_handshake_label) - 1;
            break;
        case S2N_CLIENT_APPLICATION_TRAFFIC_SECRET:
            label = client_traffic_label;
            label_size = sizeof(client_traffic_label) - 1;
            break;
        case S2N_SERVER_APPLICATION_TRAFFIC_SECRET:
            label = server_traffic_label;
            label_size = sizeof(server_traffic_label) - 1;
            break;
        default:
            /* Ignore the secret types we don't understand */
            return S2N_RESULT_OK;
    }

    const uint8_t len = label_size
            + S2N_TLS_RANDOM_DATA_LEN * HEX_ENCODING_SIZE
            + 1 /* SPACE */
            + secret->size * HEX_ENCODING_SIZE;

    DEFER_CLEANUP(struct s2n_stuffer output, s2n_stuffer_free);
    RESULT_GUARD_POSIX(s2n_stuffer_alloc(&output, len));

    RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&output, label, label_size));
    RESULT_GUARD(s2n_key_log_hex_encode(&output, conn->handshake_params.client_random, S2N_TLS_RANDOM_DATA_LEN));
    RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&output, ' '));
    RESULT_GUARD(s2n_key_log_hex_encode(&output, secret->data, secret->size));

    uint8_t *data = s2n_stuffer_raw_read(&output, len);
    RESULT_ENSURE_REF(data);

    conn->config->key_log_cb(conn->config->key_log_ctx, conn, data, len);

    return S2N_RESULT_OK;
}

S2N_RESULT s2n_key_log_tls12_secret(struct s2n_connection *conn)
{
    RESULT_ENSURE_REF(conn);
    RESULT_ENSURE_REF(conn->config);

    /* only emit keys if the callback has been set */
    if (!conn->config->key_log_cb) {
        return S2N_RESULT_OK;
    }

    /* CLIENT_RANDOM: 48 bytes for the master secret, encoded as 96 hexadecimal characters (for SSL 3.0, TLS 1.0, 1.1 and 1.2) */
    const uint8_t label[] = "CLIENT_RANDOM ";
    const uint8_t label_size = sizeof(label) - 1;

    const uint8_t len = label_size
            + S2N_TLS_RANDOM_DATA_LEN * HEX_ENCODING_SIZE
            + 1 /* SPACE */
            + S2N_TLS_SECRET_LEN * HEX_ENCODING_SIZE;

    DEFER_CLEANUP(struct s2n_stuffer output, s2n_stuffer_free);
    RESULT_GUARD_POSIX(s2n_stuffer_alloc(&output, len));

    RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&output, label, label_size));
    RESULT_GUARD(s2n_key_log_hex_encode(&output, conn->handshake_params.client_random, S2N_TLS_RANDOM_DATA_LEN));
    RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&output, ' '));
    RESULT_GUARD(s2n_key_log_hex_encode(&output, conn->secrets.version.tls12.master_secret, S2N_TLS_SECRET_LEN));

    uint8_t *data = s2n_stuffer_raw_read(&output, len);
    RESULT_ENSURE_REF(data);

    conn->config->key_log_cb(conn->config->key_log_ctx, conn, data, len);

    return S2N_RESULT_OK;
}
