/* Copyright (c) 2020-2023 The Creators of Simphone

   See the file COPYING.LESSER.txt for copying permission.
*/

#ifndef _CRYPTO_H_
#define _CRYPTO_H_

#include "simtypes.h"

struct _ssl_master {      /* communicate handshaken keys from ssl.c to crypto.cpp */
  int handshake;          /* handshake type (SSL_HANDSHAKE_xxx) */
  int length;             /* premaster secret actual length (zero if not set yet) */
  void *bio;              /* openssl BIO for XTRA logging of SSL packets or NULL if none */
  simtype from;           /* ASCII address of connected contact.
                             special proxy connections have PROXY_ADDRESS_CONTROL/PROXY_ADDRESS_TRAVERSER */
  simtype to;             /* ASCII address of contact to connect to (initially nil on server) */
  simtype pub[2];         /* peer public keys: pub[0] for EC, pub[1] for RSA */
  simtype key;            /* shared RSA-encrypted symmetric key (set by crypto.cpp) */
  simbyte master[48];     /* SSL master secret [SSL_MAX_MASTER_KEY_LENGTH] */
  simbyte random[64];     /* client random [SSL3_RANDOM_SIZE] + server random [SSL3_RANDOM_SIZE] */
  simbyte premaster[128]; /* SSL premaster secret (as large as the elliptic curve size) */
};

/* random number generation */

#define FILE_RANDOM "random"

extern simrandom random_public;  /* generator for public random data */
extern simrandom random_session; /* generator for secret session keys */
extern simrandom random_private; /* generator for permanent private keys */
extern simrandom random_seeded;  /* generator for permanent private keys */

#define RANDOM_CASE_INDEX(generator) \
  ((generator) == random_public ? 0 : (generator) == random_session ? 1 : (generator) == random_private ? 2 : 3)

#define RANDOM_SIZE_BLOCK 16     /* block size of the first RANDOM_CIPHERS ciphers */
#define RANDOM_SIZE_HASH 64      /* block size of RNG hash functions */
#define RANDOM_SIZE_ENTROPY 1472 /* minimal number of bytes a for full seed */

/* generate an EC key */
simtype random_new_ec_ (simrandom generator, simbool anonymous);
/* generate an RSA key; *mseconds is estimated prime time (input/output) or zero if don't care */
simtype random_new_rsa_ (simrandom generator, simnumber *mseconds, int size);
/* validate an EC (SSL_KEY_EC) or RSA (SSL_KEY_RSA) key */
simbool random_test_key_ (simrandom generator, const simtype key, int level, int keyidx);

/* destroy the state of the specified random generator (random_xxx) */
void random_close (simrandom generator);
/* initialize generator with saved state (or nil) and entropy data (or NULL) */
void random_open (simrandom generator, const simtype saved, const simbyte *entropy, unsigned length);

/* set cipher to use for random_public */
int random_set_cipher (const char *cipher);
/* return name of set random_public cipher */
const char *random_get_cipher (void);

/* use the specified generator to produce random bytes */
void random_get (simrandom generator, simtype output);
/* use the specified generator to produce a random number (up to max) */
simunsigned random_get_number (simrandom generator, simunsigned max);

/* save state of specified random generator to memory */
simtype random_get_state (simrandom generator);
/* save state of the built-in random generators random_public and random_session to file */
int random_save (void);

/* preload random_public with "random" data */
void random_init_entropy (const simtype value);
/* initialize built-in random generators random_public (and random_session if specified). random_private to deinitialize */
int random_init (simrandom generator);

#define RANDOM_ENTROPY_FACTOR 2   /* safety factor for gathering entropy */
#define RANDOM_ENTROPY_SAMPLES 10 /* how many audio samples count for one entropy bit */

/* message digests (cryptographic hash functions) */

#define CRYPT_MD_WHIRLPOOL 1 /* Whirlpool */
#define CRYPT_MD_MD5 2       /* MD5 */
#define CRYPT_MD_SHA2 4      /* SHA2-512 */
#define CRYPT_MD_SHA 5       /* SHA2-256 */
#define CRYPT_MD_RIPEMD 6    /* RIPEMD-160 */

/* create and return hash context */
void *sim_crypt_md_new (int md);
/* hash data */
void sim_crypt_md_update (void *md, const simbyte *input, unsigned length);
/* free hash context and return digest as dynamically allocated string simtype */
simtype sim_crypt_md_free (void *md);
/* return a copy of hash context */
void *sim_crypt_md_copy (void *md);
/* update and free in one step */
simtype sim_crypt_md_hash (void *md, const simtype ec, const simtype rsa, const simtype salt);
/* double hash (frees both md1 and md2) */
simtype sim_crypt_md_hash_address (void *md1, void *md2, const simtype ec, const simtype rsa);

/* symmetric key encryption */

/* create and return encrypt or decrypt context */
void *sim_crypt_new (int cipher, const simtype key, simbool encrypt);
/* encrypt data */
void sim_crypt_encrypt (void *crypt, const simtype input, simbyte *output);
/* decrypt data */
void sim_crypt_decrypt (void *crypt, const simtype input, simbyte *output);
/* free encrypt or decrypt context */
void sim_crypt_free (void *crypt, simbool encrypt);
/* start, encrypt and stop in one step */
void sim_crypt_auth_encrypt (void *crypt, simtype buffer, const simtype iv, simnumber sequence, simbyte *digest);

/* authenticated encryption (needed also with symmetric key encryption) */

/* return cipher block size (equal to digest size) */
unsigned sim_crypt_auth_size (const void *crypt);
/* start hashing with IV and SSL sequence */
void sim_crypt_auth_start (void *crypt, unsigned length, const simbyte *iv, unsigned ivlen, simnumber sequence);
/* hash data */
void sim_crypt_auth_update (void *crypt, const simbyte *input, unsigned length);
/* finish hashing and return digest */
void sim_crypt_auth_stop (void *crypt, simbyte *digest);
/* finish hashing and verify digest. return true if OK */
simbool sim_crypt_auth_verify (void *crypt, const simbyte *digest);
/* start, update and verify in one step */
simbool sim_crypt_auth_check (void *crypt, const simtype input, const simtype iv,
                              simnumber sequence, const simbyte *digest);

/* manage cipher list */

int sim_crypt_get_version (void); /* return crypto++ version number */
/* set list of preferred cipher names. force = true to ignore unknown ciphers */
int crypt_set_ciphers (const simtype list, simbool force);
/* cipher index is 1-based, return NULL if no more */
const char *sim_crypt_cipher_get (int cipher, unsigned *keysize, simnumber *identifier);
/* return cipher index and whether preferred or -1 if not found. idx must be zero */
int crypt_cipher_find (const char *cipher, int idx, simbool *prefer);
/* return maximal supported cipher tag size. param must be NULL */
int crypt_cipher_size_max (const char *param);

/* cipher setup */

/* encrypt random bytes with cipher */
simtype crypt_cipher_encrypt (int cipher, const struct _ssl_master *master, int counter);
/* decrypt encrypted random bytes to cipher */
int sim_crypt_cipher_decrypt (const simtype input, const struct _ssl_master *master, int counter);

/* key derivation */

/* derive key from password and salt using PBKDF2 */
simtype sim_crypt_convert_password (const char *password, const simtype salt);
/* derive key from SSL master key and call sim_crypt_new */
void *sim_crypt_open (int cipher, const struct _ssl_master *master, int counter, simbool encrypt);

/* RSA key exchange */

void crypt_open_socket (simsocket sock, int decrypt, int encrypt, simnumber decryptsize, int encryptsize,
                        simbool server, simbool proxy);
void crypt_close_socket (simsocket sock, simbool proxy); /* remove encryption from socket */

int crypt_rsa_handshake_server_ (simsocket sock, simcontact contact, const simtype handshake);
int crypt_rsa_handshake_client_ (simsocket sock, simcontact contact, const simtype proto, const simtype shakehand);

/* cipher key exchange */

int crypt_handshake_server_ (simsocket sock, simtype shakehand, const simtype handshake);
int crypt_handshake_client_ (simsocket sock, simtype *handshake, const simtype proto, const char *type);
int sim_crypt_handshake (const struct _ssl_master *master); /* internal use only */

#endif
