/* Copyright (c) 2017-2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */

/** This is a wrapper over the little-t-tor HS ntor functions. The wrapper is
 *  used by src/test/hs_ntor_ref.py to conduct the HS ntor integration
 *  tests.
 *
 *  The logic of this wrapper is basically copied from src/test/test_ntor_cl.c
 */

#include "orconfig.h"
#include <stdio.h>
#include <stdlib.h>

#define ONION_NTOR_PRIVATE
#include "or/or.h"
#include "common/util.h"
#include "common/compat.h"
#include "lib/crypt_ops/crypto.h"
#include "lib/crypt_ops/crypto_curve25519.h"
#include "or/hs_ntor.h"
#include "or/onion_ntor.h"

#define N_ARGS(n) STMT_BEGIN {                                  \
    if (argc < (n)) {                                           \
      fprintf(stderr, "%s needs %d arguments.\n",argv[1],n);    \
      return 1;                                                 \
    }                                                           \
  } STMT_END
#define BASE16(idx, var, n) STMT_BEGIN {                                \
    const char *s = argv[(idx)];                                        \
    if (base16_decode((char*)var, n, s, strlen(s)) < (int)n ) {              \
      fprintf(stderr, "couldn't decode argument %d (%s)\n",idx,s);      \
      return 1;                                                         \
    }                                                                   \
  } STMT_END
#define INT(idx, var) STMT_BEGIN {                                      \
    var = atoi(argv[(idx)]);                                            \
    if (var <= 0) {                                                     \
      fprintf(stderr, "bad integer argument %d (%s)\n",idx,argv[(idx)]); \
    }                                                                   \
  } STMT_END

/** The first part of the HS ntor protocol. The client-side computes all
    necessary key material and sends the appropriate message to the service. */
static int
client1(int argc, char **argv)
{
  int retval;

  /* Inputs */
  curve25519_public_key_t intro_enc_pubkey;
  ed25519_public_key_t intro_auth_pubkey;
  curve25519_keypair_t client_ephemeral_enc_keypair;
  uint8_t subcredential[DIGEST256_LEN];

  /* Output */
  hs_ntor_intro_cell_keys_t hs_ntor_intro_cell_keys;

  char buf[256];

  N_ARGS(6);
  BASE16(2, intro_auth_pubkey.pubkey, ED25519_PUBKEY_LEN);
  BASE16(3, intro_enc_pubkey.public_key, CURVE25519_PUBKEY_LEN);
  BASE16(4, client_ephemeral_enc_keypair.seckey.secret_key,
         CURVE25519_SECKEY_LEN);
  BASE16(5, subcredential, DIGEST256_LEN);

  /* Generate keypair */
  curve25519_public_key_generate(&client_ephemeral_enc_keypair.pubkey,
                                 &client_ephemeral_enc_keypair.seckey);

  retval = hs_ntor_client_get_introduce1_keys(&intro_auth_pubkey,
                                              &intro_enc_pubkey,
                                              &client_ephemeral_enc_keypair,
                                              subcredential,
                                              &hs_ntor_intro_cell_keys);
  if (retval < 0) {
    goto done;
  }

  /* Send ENC_KEY */
  base16_encode(buf, sizeof(buf),
                (const char*)hs_ntor_intro_cell_keys.enc_key,
                sizeof(hs_ntor_intro_cell_keys.enc_key));
  printf("%s\n", buf);
  /* Send MAC_KEY */
  base16_encode(buf, sizeof(buf),
                (const char*)hs_ntor_intro_cell_keys.mac_key,
                sizeof(hs_ntor_intro_cell_keys.mac_key));
  printf("%s\n", buf);

 done:
  return retval;
}

/** The second part of the HS ntor protocol. The service-side computes all
    necessary key material and sends the appropriate message to the client */
static int
server1(int argc, char **argv)
{
  int retval;

  /* Inputs */
  curve25519_keypair_t intro_enc_keypair;
  ed25519_public_key_t intro_auth_pubkey;
  curve25519_public_key_t client_ephemeral_enc_pubkey;
  uint8_t subcredential[DIGEST256_LEN];

  /* Output */
  hs_ntor_intro_cell_keys_t hs_ntor_intro_cell_keys;
  hs_ntor_rend_cell_keys_t hs_ntor_rend_cell_keys;
  curve25519_keypair_t service_ephemeral_rend_keypair;

  char buf[256];

  N_ARGS(6);
  BASE16(2, intro_auth_pubkey.pubkey, ED25519_PUBKEY_LEN);
  BASE16(3, intro_enc_keypair.seckey.secret_key, CURVE25519_SECKEY_LEN);
  BASE16(4, client_ephemeral_enc_pubkey.public_key, CURVE25519_PUBKEY_LEN);
  BASE16(5, subcredential, DIGEST256_LEN);

  /* Generate keypair */
  curve25519_public_key_generate(&intro_enc_keypair.pubkey,
                                 &intro_enc_keypair.seckey);
  curve25519_keypair_generate(&service_ephemeral_rend_keypair, 0);

  /* Get INTRODUCE1 keys */
  retval = hs_ntor_service_get_introduce1_keys(&intro_auth_pubkey,
                                               &intro_enc_keypair,
                                               &client_ephemeral_enc_pubkey,
                                               subcredential,
                                               &hs_ntor_intro_cell_keys);
  if (retval < 0) {
    goto done;
  }

  /* Get RENDEZVOUS1 keys */
  retval = hs_ntor_service_get_rendezvous1_keys(&intro_auth_pubkey,
                                               &intro_enc_keypair,
                                               &service_ephemeral_rend_keypair,
                                               &client_ephemeral_enc_pubkey,
                                               &hs_ntor_rend_cell_keys);
  if (retval < 0) {
    goto done;
  }

  /* Send ENC_KEY */
  base16_encode(buf, sizeof(buf),
                (const char*)hs_ntor_intro_cell_keys.enc_key,
                sizeof(hs_ntor_intro_cell_keys.enc_key));
  printf("%s\n", buf);
  /* Send MAC_KEY */
  base16_encode(buf, sizeof(buf),
                (const char*)hs_ntor_intro_cell_keys.mac_key,
                sizeof(hs_ntor_intro_cell_keys.mac_key));
  printf("%s\n", buf);
  /* Send AUTH_MAC */
  base16_encode(buf, sizeof(buf),
                (const char*)hs_ntor_rend_cell_keys.rend_cell_auth_mac,
                sizeof(hs_ntor_rend_cell_keys.rend_cell_auth_mac));
  printf("%s\n", buf);
  /* Send NTOR_KEY_SEED */
  base16_encode(buf, sizeof(buf),
                (const char*)hs_ntor_rend_cell_keys.ntor_key_seed,
                sizeof(hs_ntor_rend_cell_keys.ntor_key_seed));
  printf("%s\n", buf);
  /* Send service ephemeral pubkey (Y) */
  base16_encode(buf, sizeof(buf),
                (const char*)service_ephemeral_rend_keypair.pubkey.public_key,
                sizeof(service_ephemeral_rend_keypair.pubkey.public_key));
  printf("%s\n", buf);

 done:
  return retval;
}

/** The final step of the ntor protocol, the client computes and returns the
 *  rendezvous key material. */
static int
client2(int argc, char **argv)
{
  int retval;

  /* Inputs */
  curve25519_public_key_t intro_enc_pubkey;
  ed25519_public_key_t intro_auth_pubkey;
  curve25519_keypair_t client_ephemeral_enc_keypair;
  curve25519_public_key_t service_ephemeral_rend_pubkey;
  uint8_t subcredential[DIGEST256_LEN];

  /* Output */
  hs_ntor_rend_cell_keys_t hs_ntor_rend_cell_keys;

  char buf[256];

  N_ARGS(7);
  BASE16(2, intro_auth_pubkey.pubkey, ED25519_PUBKEY_LEN);
  BASE16(3, client_ephemeral_enc_keypair.seckey.secret_key,
         CURVE25519_SECKEY_LEN);
  BASE16(4, intro_enc_pubkey.public_key, CURVE25519_PUBKEY_LEN);
  BASE16(5, service_ephemeral_rend_pubkey.public_key, CURVE25519_PUBKEY_LEN);
  BASE16(6, subcredential, DIGEST256_LEN);

  /* Generate keypair */
  curve25519_public_key_generate(&client_ephemeral_enc_keypair.pubkey,
                                 &client_ephemeral_enc_keypair.seckey);

  /* Get RENDEZVOUS1 keys */
  retval = hs_ntor_client_get_rendezvous1_keys(&intro_auth_pubkey,
                                               &client_ephemeral_enc_keypair,
                                               &intro_enc_pubkey,
                                               &service_ephemeral_rend_pubkey,
                                               &hs_ntor_rend_cell_keys);
  if (retval < 0) {
    goto done;
  }

  /* Send AUTH_MAC */
  base16_encode(buf, sizeof(buf),
                (const char*)hs_ntor_rend_cell_keys.rend_cell_auth_mac,
                sizeof(hs_ntor_rend_cell_keys.rend_cell_auth_mac));
  printf("%s\n", buf);
  /* Send NTOR_KEY_SEED */
  base16_encode(buf, sizeof(buf),
                (const char*)hs_ntor_rend_cell_keys.ntor_key_seed,
                sizeof(hs_ntor_rend_cell_keys.ntor_key_seed));
  printf("%s\n", buf);

 done:
  return 1;
}

/** Perform a different part of the protocol depdning on the argv used. */
int
main(int argc, char **argv)
{
  if (argc < 2) {
    fprintf(stderr, "I need arguments. Read source for more info.\n");
    return 1;
  }

  curve25519_init();
  if (!strcmp(argv[1], "client1")) {
    return client1(argc, argv);
  } else if (!strcmp(argv[1], "server1")) {
    return server1(argc, argv);
  } else if (!strcmp(argv[1], "client2")) {
    return client2(argc, argv);
  } else {
    fprintf(stderr, "What's a %s?\n", argv[1]);
    return 1;
  }
}

