/*
 * 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.
 */

#include "utils/s2n_safety.h"

#include "s2n_test.h"

#define CHECK_OVF_0(fn, type, a, b)                \
    do {                                           \
        type result_val;                           \
        EXPECT_FAILURE(fn((a), (b), &result_val)); \
    } while (0)

#define CHECK_OVF(fn, type, a, b)    \
    do {                             \
        CHECK_OVF_0(fn, type, a, b); \
        CHECK_OVF_0(fn, type, b, a); \
    } while (0)

#define CHECK_NO_OVF_0(fn, type, a, b, r)          \
    do {                                           \
        type result_val;                           \
        EXPECT_SUCCESS(fn((a), (b), &result_val)); \
        EXPECT_EQUAL(result_val, (r));             \
    } while (0)

#define CHECK_NO_OVF(fn, type, a, b, r)    \
    do {                                   \
        CHECK_NO_OVF_0(fn, type, a, b, r); \
        CHECK_NO_OVF_0(fn, type, b, a, r); \
    } while (0)

static int failure_gte()
{
    POSIX_ENSURE_GTE(0, 1);

    return 0;
}

static int success_gte()
{
    POSIX_ENSURE_GTE(0, 0);
    POSIX_ENSURE_GTE(1, 0);

    return 0;
}

static int failure_gt()
{
    POSIX_ENSURE_GT(0, 0);
    POSIX_ENSURE_GT(0, 1);

    return 0;
}

static int success_gt()
{
    POSIX_ENSURE_GT(1, 0);

    return 0;
}

static int failure_lte()
{
    POSIX_ENSURE_LTE(1, 0);

    return 0;
}

static int success_lte()
{
    POSIX_ENSURE_LTE(1, 1);
    POSIX_ENSURE_LTE(0, 1);

    return 0;
}

static int failure_lt()
{
    POSIX_ENSURE_LT(1, 0);
    POSIX_ENSURE_LT(1, 1);

    return 0;
}

static int success_lt()
{
    POSIX_ENSURE_LT(0, 1);

    return 0;
}

static int success_notnull()
{
    POSIX_ENSURE_REF(&"");

    return 0;
}

static int failure_notnull()
{
    POSIX_ENSURE_REF(NULL);

    return 0;
}

static int success_memcpy()
{
    char dst[1024];
    char src[1024] = { 0 };

    POSIX_CHECKED_MEMCPY(dst, src, 1024);

    return 0;
}

static int failure_memcpy()
{
    char src[1024];
    char *ptr = NULL;

    POSIX_CHECKED_MEMCPY(ptr, src, 1024);

    return 0;
}

static int success_inclusive_range()
{
    POSIX_ENSURE_INCLUSIVE_RANGE(0, 0, 2);
    POSIX_ENSURE_INCLUSIVE_RANGE(0, 1, 2);
    POSIX_ENSURE_INCLUSIVE_RANGE(0, 2, 2);

    return 0;
}

static int failure_inclusive_range_too_high()
{
    POSIX_ENSURE_INCLUSIVE_RANGE(0, 3, 2);

    return 0;
}

static int failure_inclusive_range_too_low()
{
    POSIX_ENSURE_INCLUSIVE_RANGE(0, -1, 2);

    return 0;
}

static int success_exclusive_range()
{
    POSIX_ENSURE_EXCLUSIVE_RANGE(0, 1, 2);

    return 0;
}

static int failure_exclusive_range_too_high()
{
    POSIX_ENSURE_EXCLUSIVE_RANGE(0, 3, 2);

    return 0;
}

static int failure_exclusive_range_too_low()
{
    POSIX_ENSURE_EXCLUSIVE_RANGE(0, -1, 2);

    return 0;
}

static int failure_exclusive_range_eq_high()
{
    POSIX_ENSURE_EXCLUSIVE_RANGE(0, 2, 2);

    return 0;
}

static int failure_exclusive_range_eq_low()
{
    POSIX_ENSURE_EXCLUSIVE_RANGE(0, 0, 2);

    return 0;
}

static int success_ct_pkcs1()
{
    uint8_t pkcs1_data[] = { 0x00, 0x02, 0x80, 0x08, 0x0c, 0x00, 0xab, 0xcd, 0xef, 0x00 };
    uint8_t outbuf[] = { 0x11, 0x22, 0x33, 0x44 };
    uint8_t expected[] = { 0xab, 0xcd, 0xef, 0x00 };

    s2n_constant_time_pkcs1_unpad_or_dont(outbuf, pkcs1_data, sizeof(pkcs1_data), sizeof(outbuf));

    return memcmp(outbuf, expected, sizeof(expected)) ? -1 : 0;
}

static int success_ct_pkcs1_negative()
{
    uint8_t pkcs1_data_too_long[] = { 0x00, 0x02, 0x80, 0x0f, 0x00, 0x10, 0xab, 0xcd, 0xef, 0x00 };
    uint8_t outbuf[] = { 0x11, 0x22, 0x33, 0x44 };
    uint8_t expected[] = { 0x11, 0x22, 0x33, 0x44 };

    s2n_constant_time_pkcs1_unpad_or_dont(outbuf, pkcs1_data_too_long, sizeof(pkcs1_data_too_long), sizeof(outbuf));
    if (memcmp(outbuf, expected, sizeof(expected))) {
        return -1;
    }

    uint8_t pkcs1_data_too_short[] = { 0x00, 0x02, 0x80, 0x01, 0x02, 0x07, 0x00, 0xcd, 0xef, 0x00 };

    s2n_constant_time_pkcs1_unpad_or_dont(outbuf, pkcs1_data_too_short, sizeof(pkcs1_data_too_short), sizeof(outbuf));
    if (memcmp(outbuf, expected, sizeof(expected))) {
        return -1;
    }

    uint8_t pkcs1_data_zeroes_in_pad[] = { 0x00, 0x02, 0x80, 0x00, 0x0c, 0x00, 0xab, 0xcd, 0xef, 0x00 };
    s2n_constant_time_pkcs1_unpad_or_dont(outbuf, pkcs1_data_zeroes_in_pad, sizeof(pkcs1_data_zeroes_in_pad), sizeof(outbuf));
    if (memcmp(outbuf, expected, sizeof(expected))) {
        return -1;
    }

    uint8_t pkcs1_data_zeroes_in_pad2[] = { 0x00, 0x02, 0x80, 0x11, 0x00, 0x00, 0xab, 0xcd, 0xef, 0x00 };
    s2n_constant_time_pkcs1_unpad_or_dont(outbuf, pkcs1_data_zeroes_in_pad2, sizeof(pkcs1_data_zeroes_in_pad2), sizeof(outbuf));
    if (memcmp(outbuf, expected, sizeof(expected))) {
        return -1;
    }

    uint8_t pkcs1_data_bad_prefix1[] = { 0x01, 0x02, 0x80, 0x08, 0x0c, 0x00, 0xab, 0xcd, 0xef, 0x00 };
    s2n_constant_time_pkcs1_unpad_or_dont(outbuf, pkcs1_data_bad_prefix1, sizeof(pkcs1_data_bad_prefix1), sizeof(outbuf));
    if (memcmp(outbuf, expected, sizeof(expected))) {
        return -1;
    }

    uint8_t pkcs1_data_bad_prefix2[] = { 0x00, 0x12, 0x80, 0x08, 0x0c, 0x00, 0xab, 0xcd, 0xef, 0x00 };
    s2n_constant_time_pkcs1_unpad_or_dont(outbuf, pkcs1_data_bad_prefix2, sizeof(pkcs1_data_bad_prefix2), sizeof(outbuf));
    if (memcmp(outbuf, expected, sizeof(expected))) {
        return -1;
    }

    return 0;
}

int main(int argc, char **argv)
{
    BEGIN_TEST();
    EXPECT_SUCCESS(s2n_disable_tls13_in_test());

    EXPECT_FAILURE(failure_gte());
    EXPECT_FAILURE(failure_lte());
    EXPECT_FAILURE(failure_gt());
    EXPECT_FAILURE(failure_lt());
    EXPECT_FAILURE(failure_notnull());
    EXPECT_FAILURE(failure_memcpy());
    EXPECT_FAILURE(failure_inclusive_range_too_high());
    EXPECT_FAILURE(failure_inclusive_range_too_low());
    EXPECT_FAILURE(failure_exclusive_range_too_high());
    EXPECT_FAILURE(failure_exclusive_range_too_low());
    EXPECT_FAILURE(failure_exclusive_range_eq_high());
    EXPECT_FAILURE(failure_exclusive_range_eq_low());

    EXPECT_SUCCESS(success_gte());
    EXPECT_SUCCESS(success_lte());
    EXPECT_SUCCESS(success_gt());
    EXPECT_SUCCESS(success_lt());
    EXPECT_SUCCESS(success_notnull());
    EXPECT_SUCCESS(success_memcpy());
    EXPECT_SUCCESS(success_inclusive_range());
    EXPECT_SUCCESS(success_exclusive_range());
    EXPECT_SUCCESS(success_ct_pkcs1());
    EXPECT_SUCCESS(success_ct_pkcs1_negative());

    /* Check the special case where one parameter refers to an array of N bytes */
    /* where all elements are 0x00, and the other parameter is NULL             */
    uint8_t all_zero[4] = { 0, 0, 0, 0 };

    EXPECT_FALSE(s2n_constant_time_equals(all_zero, NULL, sizeof(all_zero)));
    EXPECT_FALSE(s2n_constant_time_equals(NULL, all_zero, sizeof(all_zero)));

    uint8_t a[4] = { 1, 2, 3, 4 };
    uint8_t b[4] = { 1, 2, 3, 4 };
    uint8_t c[4] = { 5, 6, 7, 8 };
    uint8_t d[4] = { 5, 6, 7, 8 };
    uint8_t e[4] = { 1, 2, 3, 4 };
    uint8_t f[4] = { 1, 2, 3, 5 };

    EXPECT_TRUE(s2n_constant_time_equals(a, b, sizeof(a)));
    EXPECT_FALSE(s2n_constant_time_equals(a, c, sizeof(a)));
    EXPECT_FALSE(s2n_constant_time_equals(a, NULL, sizeof(a)));
    EXPECT_FALSE(s2n_constant_time_equals(NULL, b, sizeof(b)));
    EXPECT_TRUE(s2n_constant_time_equals(NULL, NULL, 0));
    EXPECT_FALSE(s2n_constant_time_equals(NULL, NULL, sizeof(a)));
    EXPECT_TRUE(s2n_constant_time_equals(a, c, 0));
    /* ensure the function checks all of the bytes */
    EXPECT_TRUE(s2n_constant_time_equals(a, f, 3));
    EXPECT_FALSE(s2n_constant_time_equals(a, f, 4));

    EXPECT_SUCCESS(s2n_constant_time_copy_or_dont(a, c, sizeof(a), 0));
    EXPECT_EQUAL(s2n_constant_time_equals(a, c, sizeof(a)), 1);

    for (int i = 1; i < 256; i++) {
        EXPECT_SUCCESS(s2n_constant_time_copy_or_dont(b, d, sizeof(a), i));
        EXPECT_EQUAL(s2n_constant_time_equals(b, d, sizeof(a)), 0);
        EXPECT_EQUAL(s2n_constant_time_equals(b, e, sizeof(a)), 1);
    }

    uint8_t x[1];
    uint8_t y[1];

    for (int i = 0; i < 256; i++) {
        for (int j = 0; j < 256; j++) {
            x[0] = i;
            y[0] = j;

            int expected = 0;

            if (i == j) {
                expected = 1;
            }

            EXPECT_EQUAL(s2n_constant_time_equals(x, y, sizeof(x)), expected);
        }
    }

    CHECK_NO_OVF(s2n_mul_overflow, uint32_t, 0, 0, 0);
    CHECK_NO_OVF(s2n_mul_overflow, uint32_t, 0, 1, 0);
    CHECK_NO_OVF(s2n_mul_overflow, uint32_t, 0, ~0u, 0);
    CHECK_NO_OVF(s2n_mul_overflow, uint32_t, 4, 5, 20);
    CHECK_NO_OVF(s2n_mul_overflow, uint32_t, 1234, 4321, 5332114);

    CHECK_NO_OVF(s2n_mul_overflow, uint32_t, 0xFFFFFFFF, 1, 0xFFFFFFFF);
    CHECK_NO_OVF(s2n_mul_overflow, uint32_t, 0xFFFF, 1, 0xFFFF);
    CHECK_NO_OVF(s2n_mul_overflow, uint32_t, 0xFFFF, 0xFFFF, 0xfffe0001u);
    CHECK_NO_OVF(s2n_mul_overflow, uint32_t, 0x10000, 0xFFFF, 0xFFFF0000u);
    CHECK_NO_OVF(s2n_mul_overflow, uint32_t, 0x10001, 0xFFFF, 0xFFFFFFFFu);
    CHECK_NO_OVF(s2n_mul_overflow, uint32_t, 0x10001, 0xFFFE, 0xFFFEFFFEu);
    CHECK_NO_OVF(s2n_mul_overflow, uint32_t, 0x10002, 0xFFFE, 0xFFFFFFFCu);
    CHECK_OVF(s2n_mul_overflow, uint32_t, 0x10003, 0xFFFE);
    CHECK_NO_OVF(s2n_mul_overflow, uint32_t, 0xFFFE, 0xFFFE, 0xFFFC0004u);
    CHECK_OVF(s2n_mul_overflow, uint32_t, 0x1FFFF, 0x1FFFF);
    CHECK_OVF(s2n_mul_overflow, uint32_t, ~0u, ~0u);

    uint32_t result = 1;
    EXPECT_SUCCESS(s2n_align_to(0, 10, &result));
    EXPECT_EQUAL(result, 0);

    EXPECT_FAILURE(s2n_align_to(1, 0, &result));

    EXPECT_SUCCESS(s2n_align_to(10, 16, &result));
    EXPECT_EQUAL(result, 16);

    EXPECT_SUCCESS(s2n_align_to(20, 16, &result));
    EXPECT_EQUAL(result, 32);

    EXPECT_FAILURE(s2n_align_to(UINT32_MAX, 4, &result));

    EXPECT_SUCCESS(s2n_align_to(10, 4096, &result));
    EXPECT_EQUAL(result, 4096);

    EXPECT_SUCCESS(s2n_align_to(4097, 4096, &result));
    EXPECT_EQUAL(result, 8192);

    EXPECT_SUCCESS(s2n_align_to(4096, 4096, &result));
    EXPECT_EQUAL(result, 4096);

    EXPECT_FAILURE(s2n_align_to(UINT32_MAX - 4000, 4096, &result));
    EXPECT_FAILURE(s2n_align_to(UINT32_MAX, 4096, &result));
    const uint32_t HALF_MAX = UINT32_MAX / 2;
    const uint32_t ACTUAL_MAX = UINT32_MAX;

    CHECK_NO_OVF(s2n_add_overflow, uint32_t, 0, 0, 0);
    CHECK_NO_OVF(s2n_add_overflow, uint32_t, 0, 1, 1);
    CHECK_NO_OVF(s2n_add_overflow, uint32_t, 4, 5, 9);
    CHECK_NO_OVF(s2n_add_overflow, uint32_t, 1234, 4321, 5555);
    CHECK_NO_OVF(s2n_add_overflow, uint32_t, 0, ACTUAL_MAX, ACTUAL_MAX);
    CHECK_NO_OVF(s2n_add_overflow, uint32_t, HALF_MAX, HALF_MAX, ACTUAL_MAX - 1);
    CHECK_NO_OVF(s2n_add_overflow, uint32_t, HALF_MAX + 1, HALF_MAX, ACTUAL_MAX);
    CHECK_NO_OVF(s2n_add_overflow, uint32_t, 100, ACTUAL_MAX - 102, ACTUAL_MAX - 2);
    CHECK_NO_OVF(s2n_add_overflow, uint32_t, 100, ACTUAL_MAX - 100, ACTUAL_MAX);
    CHECK_OVF(s2n_add_overflow, uint32_t, 1, ACTUAL_MAX);
    CHECK_OVF(s2n_add_overflow, uint32_t, 100, ACTUAL_MAX);
    CHECK_OVF(s2n_add_overflow, uint32_t, HALF_MAX, ACTUAL_MAX);
    CHECK_OVF(s2n_add_overflow, uint32_t, ACTUAL_MAX, ACTUAL_MAX);
    CHECK_OVF(s2n_add_overflow, uint32_t, HALF_MAX + 1, HALF_MAX + 1);
    CHECK_OVF(s2n_add_overflow, uint32_t, 100, ACTUAL_MAX - 99);
    CHECK_OVF(s2n_add_overflow, uint32_t, 100, ACTUAL_MAX - 1);

    /* Test: S2N_ADD_IS_OVERFLOW_SAFE */
    {
        const size_t num = 100;

        uint64_t success_test_values[][3] = {
            { 0, 0, 0 },
            { 1, 0, 1 },
            { 0, 0, UINT8_MAX },
            { 1, 1, UINT8_MAX },
            { UINT8_MAX, 0, UINT8_MAX },
            { UINT8_MAX - num, num, UINT8_MAX },
            { UINT8_MAX / 2, UINT8_MAX / 2, UINT8_MAX },
            { 1, 1, UINT64_MAX },
            { UINT64_MAX, 0, UINT64_MAX },
            { UINT64_MAX - num, num, UINT64_MAX },
            { UINT64_MAX / 2, UINT64_MAX / 2, UINT64_MAX },
        };
        for (size_t i = 0; i < s2n_array_len(success_test_values); i++) {
            uint64_t v1 = success_test_values[i][0];
            uint64_t v2 = success_test_values[i][1];
            uint64_t max = success_test_values[i][2];
            EXPECT_TRUE(S2N_ADD_IS_OVERFLOW_SAFE(v1, v2, max));
            EXPECT_TRUE(S2N_ADD_IS_OVERFLOW_SAFE(v2, v1, max));
        }

        uint64_t failure_test_values[][3] = {
            { 1, 0, 0 },
            { UINT8_MAX, 0, 0 },
            { UINT64_MAX, 0, UINT8_MAX },
            { UINT64_MAX, UINT64_MAX, UINT8_MAX },
            { UINT8_MAX, 1, UINT8_MAX },
            { UINT8_MAX - 1, UINT8_MAX - 1, UINT8_MAX },
            { UINT16_MAX, 1, UINT16_MAX },
            { UINT64_MAX, 1, UINT64_MAX },
            { UINT8_MAX, num, UINT8_MAX },
            { UINT16_MAX, num, UINT16_MAX },
            { UINT64_MAX, num, UINT64_MAX },
            { UINT8_MAX, UINT8_MAX, UINT8_MAX },
            { UINT16_MAX, UINT16_MAX, UINT16_MAX },
            { UINT64_MAX, UINT64_MAX, UINT64_MAX },
            { UINT64_MAX - num, UINT64_MAX - num, UINT64_MAX },
        };
        for (size_t i = 0; i < s2n_array_len(failure_test_values); i++) {
            uint64_t v1 = failure_test_values[i][0];
            uint64_t v2 = failure_test_values[i][1];
            uint64_t max = failure_test_values[i][2];
            EXPECT_FALSE(S2N_ADD_IS_OVERFLOW_SAFE(v1, v2, max));
            EXPECT_FALSE(S2N_ADD_IS_OVERFLOW_SAFE(v2, v1, max));
        }
    }

    /* Test: s2n_array_len */
    {
        /* Must return correct length */
        uint16_t test_data[10] = { 0 };
        EXPECT_EQUAL(s2n_array_len(test_data), 10);
        EXPECT_NOT_EQUAL(s2n_array_len(test_data), sizeof(test_data));

        /* Must be usable as an array size / constant expression */
        uint16_t test_data_dup[s2n_array_len(test_data)] = { 0 };
        EXPECT_EQUAL(sizeof(test_data), sizeof(test_data_dup));
    }

    END_TEST();
    return 0;
}
