/**
 \file 		djn.h
 \author 	Daniel Demmler
 \copyright Copyright (C) 2019 ENCRYPTO Group, TU Darmstadt
			This program is free software: you can redistribute it and/or modify
            it under the terms of the GNU Lesser General Public License as published
            by the Free Software Foundation, either version 3 of the License, or
            (at your option) any later version.
            ABY is distributed in the hope that it will be useful,
            but WITHOUT ANY WARRANTY; without even the implied warranty of
            MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
            GNU Lesser General Public License for more details.
            You should have received a copy of the GNU Lesser General Public License
            along with this program. If not, see <http://www.gnu.org/licenses/>.

 \brief
 libdjn - v0.9
 A library implementing the Damgaard Jurik Nielsen cryptosystem with s=1 (~Paillier).
 based on:
 libpaillier - A library implementing the Paillier cryptosystem.
 (http://hms.isi.jhu.edu/acsc/libpaillier/)
 */

#ifndef _DJN_H_
#define _DJN_H_
#include <gmp.h>

/*
 On memory handling:

 At no point is any special effort made to securely "shred" sensitive
 memory or prevent it from being paged out to disk. This means that
 it is important that functions dealing with private keys and
 plaintexts (e.g., djn_keygen and djn_enc) only be run on
 trusted machines. The resulting ciphertexts and public keys,
 however, may of course be handled in an untrusted manner.

 */

/******
 TYPES
 *******/

/*
 This represents a public key, which is the modulus n plus a generator h.
 */
struct djn_pubkey_t {
	int bits; /* e.g., 1024 */
	int rbits; /* e.g., 512 */
	mpz_t n; /* public modulus n = p q */
	mpz_t n_squared; /* cached to avoid recomputing */
	mpz_t h; /* generator h = -x^2 mod n */
	mpz_t h_s; /* h_s = h^n mod n^2 */
};

/*
 This represents a Paillier private key; it needs to be used with a
 djn_pubkey_t to be meaningful. It includes the Carmichael
 function (lambda) of the modulus. The other value is kept for
 efficiency and should be considered private.
 */
struct djn_prvkey_t {
	mpz_t lambda; /* lambda(n), i.e., lcm(p-1,q-1) */
	mpz_t lambda_inverse; /* inverse of lambda (mod n)*/
	mpz_t p; /* cached to avoid recomputing */
	mpz_t q; /* cached to avoid recomputing */
	mpz_t q_inverse; /* inverse of q (mod p) */
	mpz_t q_squared_inverse; /* inverse of q^2 (mod p^2) */
	mpz_t p_minusone; /* cached to avoid recomputing */
	mpz_t q_minusone; /* cached to avoid recomputing */
	mpz_t p_squared; /* cached to avoid recomputing */
	mpz_t q_squared; /* cached to avoid recomputing */
	mpz_t ordpsq; /* p^2-p */
	mpz_t ordqsq; /* q^2-q */
};

/*
 This is the type of the callback functions used to obtain the
 randomness needed by the probabilistic algorithms. The functions
 djn_get_rand_devrandom and djn_get_rand_devurandom
 (documented later) may be passed to any library function requiring a
 djn_get_rand_t, or you may implement your own. If you implement
 your own such function, it should fill in "len" random bytes in the
 array "buf".
 */
typedef void (*djn_get_rand_t)(void* buf, int len);

/*****************
 BASIC OPERATIONS
 *****************/

/*
 Generate a keypair of length modulusbits using randomness from the
 provided get_rand function. Space will be allocated for each of the
 keys, and the given pointers will be set to point to the new
 djn_pubkey_t and djn_prvkey_t structures. The functions
 djn_get_rand_devrandom and djn_get_rand_devurandom may be
 passed as the final argument.
 */
void djn_keygen(unsigned int modulusbits, djn_pubkey_t** pub, djn_prvkey_t** prv);

/*
 Encrypt the given plaintext with the given public key using
 randomness from get_rand for blinding. If res is not null, its
 contents will be overwritten with the result. Otherwise, a new
 djn_ciphertext_t will be allocated and returned.
 */
void djn_encrypt(mpz_t res, djn_pubkey_t* pub, mpz_t pt);

/*
 Encrypt the given plaintext with the given public key using
 randomness from get_rand for blinding. If res is not null, its
 contents will be overwritten with the result. Otherwise, a new
 djn_ciphertext_t will be allocated and returned.
 */
void djn_encrypt_crt(mpz_t res, djn_pubkey_t* pub, djn_prvkey_t* prv, mpz_t pt);

/**
 * fixed base encryption. Requires pre-computed fixed base table.
 */
void djn_encrypt_fb(mpz_t res, djn_pubkey_t* pub, mpz_t plaintext);

/*
 Decrypt the given ciphertext with the given key pair. If res is not
 null, its contents will be overwritten with the result. Otherwise, a
 new djn_plaintext_t will be allocated and returned.
 */
void djn_decrypt(mpz_t res, djn_pubkey_t* pub, djn_prvkey_t* prv, mpz_t ct);

/**********************
 KEY IMPORT AND EXPORT
 **********************/

/*
 Import or export public and private keys from or to hexadecimal,
 ASCII strings, which are suitable for I/O. Note that the
 corresponding public key is necessary to initialize a private key
 from a hex string. In all cases, the returned value is allocated for
 the caller and the values passed are unchanged.
 */
char* djn_pubkey_to_hex(djn_pubkey_t* pub);
char* djn_prvkey_to_hex(djn_prvkey_t* prv);
djn_pubkey_t* djn_pubkey_from_hex(char* str);
djn_prvkey_t* djn_prvkey_from_hex(char* str, djn_pubkey_t* pub);

/********
 CLEANUP
 ********/

/*
 These free the structures allocated and returned by various
 functions within library and should be used when the structures are
 no longer needed.
 */
void djn_freepubkey(djn_pubkey_t* pub);
void djn_freeprvkey(djn_prvkey_t* prv);

/***********
 MISC STUFF
 ***********/

/*
 Just a utility used internally when we need round a number of bits
 up the number of bytes necessary to hold them.
 */
#define PAILLIER_BITS_TO_BYTES(n) ((n) % 8 ? (n) / 8 + 1 : (n) / 8)

void djn_pow_mod_n_crt(mpz_t res, const mpz_t b, const mpz_t e, const djn_pubkey_t* pub, const djn_prvkey_t* prv);
void djn_pow_mod_n_squared_crt(mpz_t res, const mpz_t b, const mpz_t e, const djn_pubkey_t* pub, const djn_prvkey_t* prv);

/**
 * create full public key given only n and h (e.g., after a key exchange)
 */
void djn_complete_pubkey(unsigned int modulusbits, djn_pubkey_t** pub, mpz_t n, mpz_t h);

#endif
