/* Copyright (c) (2012,2013,2014,2015,2016,2018,2019) Apple Inc. All rights reserved.
 *
 * corecrypto is licensed under Apple Inc.’s Internal Use License Agreement (which
 * is contained in the License.txt file distributed with corecrypto) and only to 
 * people who accept that license. IMPORTANT:  Any license rights granted to you by 
 * Apple Inc. (if any) are limited to internal use within your organization only on 
 * devices and computers you own or control, for the sole purpose of verifying the 
 * security characteristics and correct functioning of the Apple Software.  You may 
 * not, directly or indirectly, redistribute the Apple Software or any portions thereof.
 */

#include "testmore.h"
#include "testbyteBuffer.h"
#include "testccnBuffer.h"

#if (CCSRP == 0)
entryPoint(ccsrp_tests, "ccsrp test")
#else

#include <corecrypto/ccsrp.h>
#include <corecrypto/ccsrp_gp.h>
#include <corecrypto/ccsha1.h>
#include <corecrypto/ccsha2.h>

struct ccsrp_vector {
    const struct ccdigest_info *(*di)(void);
    ccdh_const_gp_t (*gp)(void);
    uint32_t opt;
    const char *U;
    const char *P;
    const char *salt;
    const char *k;
    const char *x;
    const char *v;
    const char *a;
    const char *b;
    const char *A;
    const char *B;
    const char *u;
    const char *S;
    const char *K;
    const char *M;
    const char *HAMK;
};

struct ccsrp_option {
    uint32_t v;
    const char *string;
};

const struct ccsrp_vector ccsrp_vectors[] = {
#include "../test_vectors/srp.inc"
};

/*
 Appendix B.  SRP Test Vectors

 The following test vectors demonstrate calculation of the verifier
 and premaster secret.
 */
static int srp_test_vector(const struct ccsrp_vector *test, struct ccrng_state *rng)
{
    int rc = -1;
    const struct ccdigest_info *di = test->di();
    ccsrp_const_gp_t gp = test->gp();
    const char *I = test->U;
    const char *P = test->P;
    byteBuffer salt = hexStringToBytes(test->salt);
    byteBuffer k = hexStringToBytes(test->k);
    byteBuffer x = hexStringToBytes(test->x);
    byteBuffer v = hexStringToBytes(test->v);
    byteBuffer a = hexStringToBytes(test->a);
    byteBuffer b = hexStringToBytes(test->b);
    byteBuffer A = hexStringToBytes(test->A);
    byteBuffer B = hexStringToBytes(test->B);
    byteBuffer u = hexStringToBytes(test->u);
    byteBuffer S = hexStringToBytes(test->S);
    byteBuffer K = hexStringToBytes(test->K);
    byteBuffer M = hexStringToBytes(test->M);
    byteBuffer HAMK = hexStringToBytes(test->HAMK);
    rc = ccsrp_test_calculations(di,
                                 gp,
                                 rng,
                                 I,
                                 test->opt,
                                 strlen(P),
                                 P,
                                 salt->len,
                                 salt->bytes,
                                 k->len,
                                 k->bytes,
                                 x->len,
                                 x->bytes,
                                 v->len,
                                 v->bytes,
                                 a->len,
                                 a->bytes,
                                 b->len,
                                 b->bytes,
                                 A->len,
                                 A->bytes,
                                 B->len,
                                 B->bytes,
                                 u->len,
                                 u->bytes,
                                 S->len,
                                 S->bytes,
                                 K->len,
                                 K->bytes,
                                 M->len,
                                 M->bytes,
                                 HAMK->len,
                                 HAMK->bytes);
    free(salt);
    free(k);
    free(x);
    free(u);
    free(v);
    free(a);
    free(b);
    free(A);
    free(B);
    free(S);
    free(K);
    free(M);
    free(HAMK);
    return rc;
}

static int verbose = 0;

#define NITER 100
#define TEST_HASH SRP_SHA1
#define TEST_NG SRP_NG_1024

#define SRP_TEST_MAX_SESSION_KEY_LENGTH 96

static int test_srp(const struct ccdigest_info *di,
                    ccsrp_const_gp_t gp,
                    struct ccrng_state *rng,
                    uint32_t option)
{
    ccsrp_ctx_decl(di, gp, client_srp);
    ccsrp_ctx_decl(di, gp, server_srp);
    ccsrp_ctx_init_option(client_srp, di, gp, option, rng);
    ccsrp_ctx_init_option(server_srp, di, gp, option, rng);
    size_t pki_size = ccsrp_ctx_sizeof_n(client_srp);
    const char *username = "testuser";
    const char *password = "password";
    uint8_t salt[64];
    uint8_t verifier[pki_size];
    uint8_t A[pki_size];
    uint8_t B[pki_size];
    uint8_t M[ccsrp_ctx_M_HAMK_size(client_srp)];
    uint8_t bytes_HAMK[ccsrp_ctx_M_HAMK_size(client_srp)];
    size_t client_session_key_length = SRP_TEST_MAX_SESSION_KEY_LENGTH;
    size_t server_session_key_length = SRP_TEST_MAX_SESSION_KEY_LENGTH;
    size_t salt_len, password_len;

    salt_len = 64;

    password_len = strlen(password);

    if (verbose)
        diag("test_srp.0\n");

    ok_or_fail(ccsrp_generate_salt_and_verification(
                   client_srp, rng, username, password_len, password, salt_len, salt, verifier) ==
                   0,
               "Generate Salt and Verifier");

    // Generate a and A
    if (verbose)
        diag("test_srp.2\n");
    ok_or_fail(ccsrp_client_start_authentication(client_srp, rng, A) == 0,
               "Start client authentication");

    // Client sends A to server

    // Generate b and B using A
    if (verbose)
        diag("test_srp.3\n");
    ok_or_fail(ccsrp_server_start_authentication(
                   server_srp, rng, username, salt_len, salt, verifier, A, B) == 0,
               "Verifier SRP-6a safety check");

    // Client uses s and B to generate M to answer challenge
    if (verbose)
        diag("test_srp.4\n");
    ok_or_fail(ccsrp_client_process_challenge(
                   client_srp, username, password_len, password, salt_len, salt, B, M) == 0,
               "User SRP-6a safety check");

    // Verify session key.
    const void *ck = ccsrp_get_session_key(client_srp, &client_session_key_length);
    const void *sk = ccsrp_get_session_key(server_srp, &server_session_key_length);
    ok(ck && sk &&
           ccsrp_get_session_key_length(client_srp) == ccsrp_get_session_key_length(server_srp) &&
           ccsrp_get_session_key_length(client_srp) == client_session_key_length &&
           ccsrp_get_session_key_length(server_srp) == server_session_key_length &&
           memcmp(ck, sk, ccsrp_get_session_key_length(client_srp)) == 0,
       "Session Keys don't match");

    // Verify M was generated correctly - generate HAMK
    if (verbose)
        diag("test_srp.5\n");
    ok_or_fail(ccsrp_server_verify_session(server_srp, M, bytes_HAMK), "User authentication");

    // Client verifies correct HAMK
    if (verbose)
        diag("test_srp.6\n");
    ok_or_fail(ccsrp_client_verify_session(client_srp, bytes_HAMK), "Server Authentication");

    if (verbose)
        diag("test_srp.7\n");
    ok(ccsrp_is_authenticated(client_srp), "Server Authentication");
    ccsrp_ctx_clear(di, gp, client_srp);
    ccsrp_ctx_clear(di, gp, server_srp);
    return 1;
}

#define SRP_OPTION_TEST(t) \
    {                      \
        t, #t              \
    }

int ccsrp_tests(TM_UNUSED int argc, TM_UNUSED char *const *argv)
{
    struct ccrng_state *rng = global_test_rng;
    struct ccsrp_option test_options[] = { SRP_OPTION_TEST(CCSRP_OPTION_SRP6a_HASH),
                                           SRP_OPTION_TEST(CCSRP_OPTION_SRP6a_MGF1),
                                           SRP_OPTION_TEST(CCSRP_OPTION_RFC2945_INTERLEAVED) };

    plan_tests(15 + 3 * 5 * 5 * (8 + 1));
    for (size_t i = 0; i < sizeof(ccsrp_vectors) / sizeof(ccsrp_vectors[0]); i++) {
        diag("SRP KAT Test %d", i);
        ok(srp_test_vector(&ccsrp_vectors[i], rng) == 0, "SRP KAT Test");
    }
    for (size_t i = 0; i < sizeof(test_options) / sizeof(test_options[0]); i++) {
        diag("SRP 1024 tests (%s)", test_options[i].string);
        ok(test_srp(ccsha1_di(), ccsrp_gp_rfc5054_1024(), rng, test_options[i].v), "SHA1/GP1024");
        ok(test_srp(ccsha224_di(), ccsrp_gp_rfc5054_1024(), rng, test_options[i].v),
           "SHA224/GP1024");
        ok(test_srp(ccsha256_di(), ccsrp_gp_rfc5054_1024(), rng, test_options[i].v),
           "SHA256/GP1024");
        ok(test_srp(ccsha384_di(), ccsrp_gp_rfc5054_1024(), rng, test_options[i].v),
           "SHA384/GP1024");
        ok(test_srp(ccsha512_di(), ccsrp_gp_rfc5054_1024(), rng, test_options[i].v),
           "SHA512/GP1024");

        diag("SRP 2048 tests (%s)", test_options[i].string);
        ok(test_srp(ccsha1_di(), ccsrp_gp_rfc5054_2048(), rng, test_options[i].v), "SHA1/GP2048");
        ok(test_srp(ccsha224_di(), ccsrp_gp_rfc5054_2048(), rng, test_options[i].v),
           "SHA224/GP2048");
        ok(test_srp(ccsha256_di(), ccsrp_gp_rfc5054_2048(), rng, test_options[i].v),
           "SHA256/GP2048");
        ok(test_srp(ccsha384_di(), ccsrp_gp_rfc5054_2048(), rng, test_options[i].v),
           "SHA384/GP2048");
        ok(test_srp(ccsha512_di(), ccsrp_gp_rfc5054_2048(), rng, test_options[i].v),
           "SHA512/GP2048");
#if CORECRYPTO_HACK_FOR_WINDOWS_DEVELOPMENT
        diag("*skipping tests on Windows");
#else
        diag("SRP 3072 tests (%s)", test_options[i].string);
        ok(test_srp(ccsha1_di(), ccsrp_gp_rfc5054_3072(), rng, test_options[i].v), "SHA1/GP3072");
        ok(test_srp(ccsha224_di(), ccsrp_gp_rfc5054_3072(), rng, test_options[i].v),
           "SHA224/GP3072");
        ok(test_srp(ccsha256_di(), ccsrp_gp_rfc5054_3072(), rng, test_options[i].v),
           "SHA256/GP3072");
        ok(test_srp(ccsha384_di(), ccsrp_gp_rfc5054_3072(), rng, test_options[i].v),
           "SHA384/GP3072");
        ok(test_srp(ccsha512_di(), ccsrp_gp_rfc5054_3072(), rng, test_options[i].v),
           "SHA512/GP3072");

        diag("SRP 4096 tests (%s)", test_options[i].string);
        ok(test_srp(ccsha1_di(), ccsrp_gp_rfc5054_4096(), rng, test_options[i].v), "SHA1/GP4096");
        ok(test_srp(ccsha224_di(), ccsrp_gp_rfc5054_4096(), rng, test_options[i].v),
           "SHA224/GP4096");
        ok(test_srp(ccsha256_di(), ccsrp_gp_rfc5054_4096(), rng, test_options[i].v),
           "SHA256/GP4096");
        ok(test_srp(ccsha384_di(), ccsrp_gp_rfc5054_4096(), rng, test_options[i].v),
           "SHA384/GP4096");
        ok(test_srp(ccsha512_di(), ccsrp_gp_rfc5054_4096(), rng, test_options[i].v),
           "SHA512/GP4096");

        diag("SRP 8192 tests (%s)", test_options[i].string);
        ok(test_srp(ccsha1_di(), ccsrp_gp_rfc5054_8192(), rng, test_options[i].v), "SHA1/GP8192");
        ok(test_srp(ccsha224_di(), ccsrp_gp_rfc5054_8192(), rng, test_options[i].v),
           "SHA224/GP8192");
        ok(test_srp(ccsha256_di(), ccsrp_gp_rfc5054_8192(), rng, test_options[i].v),
           "SHA256/GP8192");
        ok(test_srp(ccsha384_di(), ccsrp_gp_rfc5054_8192(), rng, test_options[i].v),
           "SHA384/GP8192");
        ok(test_srp(ccsha512_di(), ccsrp_gp_rfc5054_8192(), rng, test_options[i].v),
           "SHA512/GP8192");
#endif
    }
    return 0;
}

#endif
