/*
 * Copyright (c) 2003-2011
 * Distributed Systems Software.  All rights reserved.
 * See the file LICENSE for redistribution information.
 *
 * $Id: dacs_crypto.h 2547 2012-01-11 19:47:55Z brachman $
 */

#ifndef _DACS_CRYPTO_H_
#define _DACS_CRYPTO_H_

#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
#include <openssl/md5.h>

typedef enum {
  CRYPTO_SYMMETRIC_ENCRYPT = 0,
  CRYPTO_SYMMETRIC_DECRYPT = 1,
  CRYPTO_PKI_ENCRYPT       = 2,
  CRYPTO_PKI_DECRYPT       = 3
} Crypto_function;

/* Do not change these unless you understand exactly how they are used. */
enum {
  CRYPTO_DIGEST_MD5_BYTE_LENGTH   = (128 / 8),
  CRYPTO_DIGEST_SHA1_BYTE_LENGTH  = (160 / 8),
  CRYPTO_HMAC_KEY_LENGTH          = CRYPTO_DIGEST_SHA1_BYTE_LENGTH,
  CRYPTO_HMAC_BYTE_LENGTH         = CRYPTO_DIGEST_SHA1_BYTE_LENGTH,
  /*
   * This selects the method to use for generating IVs for encryption.
   * See crypto.c:make_iv()
   */
  CRYPTO_USE_RANDOMIZED_IV		    = 0
};

/* The digest algorithm to use with encryption of credentials etc. */
#ifndef CRYPTO_HMAC_DIGEST_ALG_NAME
#define CRYPTO_HMAC_DIGEST_ALG_NAME	"SHA1"
#endif

/* The digest algorithm to use to uniquely identify credentials. */
#ifndef CRYPTO_DIGEST_ALG_NAME
#define CRYPTO_DIGEST_ALG_NAME	"SHA1"
#endif

/* The cipher to use for credentials etc. */
#ifndef CRYPTO_CRYPT_ALG_NAME
#define CRYPTO_CRYPT_ALG_NAME	"AES-128-CFB"
#endif

/* The cipher to use for creating an IV - see crypto.c:make_iv() */
#ifndef CRYPTO_IV_CIPHER_NAME
#define CRYPTO_IV_CIPHER_NAME	"AES-128-CFB"
#endif

/*
 * http://www.w3.org/2000/09/xmldsig#hmac-sha1
 * http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5#hmac-sha-224
 * http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5#hmac-sha-256
 * http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5#hmac-sha-384
 * http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5#hmac-sha-512
 */
typedef enum {
  HMAC_MD5    = 0,
  HMAC_SHA1   = 1,
  HMAC_SHA224 = 224,
  HMAC_SHA256 = 256,
  HMAC_SHA384 = 384,
  HMAC_SHA512 = 512,
  HMAC_NONE   = -1
} Hmac_digest_alg;

typedef struct Hmac_handle {
  char *digest_name;
  Hmac_digest_alg alg;
  unsigned int block_size;
  unsigned int hmac_size;
  unsigned char *hmac_key_ipad;
  unsigned char *hmac_key_opad;
  EVP_MD_CTX *ctx;
} Hmac_handle;

typedef struct Cipher_handle {
  Crypto_function func;
  char *cipher_name;
  EVP_CIPHER_CTX *ctx;
} Cipher_handle;

typedef struct Hmac_digest {
  char *name;
  Hmac_digest_alg alg;
  int block_size;	/* in bytes */
  int hmac_size;	/* in bytes */
} Hmac_digest;

typedef struct PKI_session_keys {
  char *cipher_name;
  int nkeys;
  unsigned char **ek;
  int *ekl;
  EVP_PKEY **pubk;
} PKI_session_keys;

typedef struct Rng_state {
  unsigned int *V;
  unsigned int *C;
  int reseed_counter;
  int security_strength;
  int prediction_resistance_flag;
} Rng_state;

#ifdef __cplusplus
extern "C" {
#endif

extern int debug_crypto;

/* Misc */
extern int crypto_init(void);
extern void crypto_log_error(void);
extern Ds *crypto_cert_thumbprint(char *stripped_cert, char **fmt);
extern Ds *pem_cert_strip_str(char *str);
extern Ds *pem_cert_strip(Ds *ds);
extern Ds *pem_cert_thumbprint(char *cert);
extern char *pem_cert_thumbprint_fmt(Ds *tp);
extern char *pem_cert_thumbprint_fmt_str(char *cert);
extern Ds *pem_cert_load_stripped(char *certfile);
extern RSA *pem_load_private_key(char *keyfile, char *passphrase);
extern RSA *pem_load_private_key_from_buf(char *buf, int buflen,
										  char *passphrase);
extern char *pem_from_evp_pub_pkey(EVP_PKEY *public_key);
extern char *pem_from_evp_priv_pkey(EVP_PKEY *private_key);
extern Ds *pem_make_cert(char *enc_cert_str);

extern Ds *sha1(unsigned char *buf, size_t buflen);
extern Ds *sha256(unsigned char *buf, size_t buflen);

/* Message Digests */
extern unsigned char *crypto_digest(char *digest_name, const void *input,
									size_t input_len,
									unsigned char *output,
									unsigned int *output_len);
extern EVP_MD_CTX *crypto_digest_open(char *digest_name);
extern void crypto_digest_hash(EVP_MD_CTX *ctx, const void *input,
							   size_t input_len);
extern unsigned char *crypto_digest_close(EVP_MD_CTX *ctx,
										  unsigned char *output,
										  unsigned int *output_len);

/* Password-style Message Digests */
#include "auth.h"
extern void crypto_passwd(char *digest_name, char *passwd, char *salt,
						  char **output);

extern const EVP_CIPHER *crypto_cipher_methodbyname(char *name);

/* PKI Signing and Verifying */
extern EVP_MD_CTX *crypto_sign_open(char *digest_name);
extern int crypto_sign_update(EVP_MD_CTX *ctx, unsigned char *buf,
							  unsigned int len);
extern unsigned char *crypto_sign_close(EVP_MD_CTX *ctx,
										unsigned char *sign_buf,
										unsigned int *sign_len,
										EVP_PKEY *private_key);
extern unsigned char *crypto_sign(char *digest_name, unsigned char *buf,
								  unsigned int len,
								  unsigned char **sign_buf,
								  unsigned int *sign_len,
								  EVP_PKEY *private_key);

extern EVP_MD_CTX *crypto_signv_open(char *digest_name);
extern int crypto_signv_update(EVP_MD_CTX *ctx, unsigned char *buf,
							   unsigned int len);
extern int crypto_signv_close(EVP_MD_CTX *ctx, unsigned char *sign_buf,
							  unsigned int sign_len, EVP_PKEY *public_key);
extern int crypto_signv(char *digest_name, unsigned char *buf,
						unsigned int len, unsigned char *sign_buf,
						unsigned int sign_len, EVP_PKEY *public_key);
extern Ds *crypto_sign_buf(Ds *data, RSA *priv_key);
extern Ds *crypto_sign_buf_len(Ds *data, RSA *priv_key,
							   unsigned int max_datalen);

/* Keyed Message Digests */
extern Hmac_handle *crypto_hmac_open(char *digest_name, unsigned char *key,
									 unsigned int keylen);
extern void crypto_hmac_hash(Hmac_handle *conf, unsigned char *str,
							 unsigned int len);
extern unsigned char *crypto_hmac_close(Hmac_handle *conf,
										unsigned char *hmac_buf,
										unsigned int *hmac_len);
extern int crypto_hmac_key_length(char *digest_name);
extern int crypto_hmac_hmac_length(char *digest_name);
extern unsigned char *crypto_hmac(char *digest_name,
								  unsigned char *key, unsigned int klen,
								  unsigned char *value, unsigned int vlen,
								  unsigned char **hmac, unsigned int *hlen);
extern Hmac_digest *hmac_lookup_digest_by_size(int size);
extern Hmac_digest *hmac_lookup_digest_by_name(char *digest_name);

/* Random material */
extern int crypto_randomize_buffer(unsigned char *buf, unsigned int len);
extern int crypto_random_uint(unsigned int lo, unsigned int hi,
							  unsigned int *uint);
extern unsigned char *crypto_make_random_buffer(unsigned int len);
extern char *crypto_make_random_string(char *prefix, int nbytes);
extern char *crypto_make_random_string_from_spec(char *tr_spec, int nbytes,
												 int cflag);
extern char *crypto_make_random_string_from_template(char *template);
extern char *crypto_make_random_a64(char *prefix, int nbytes);
extern void crypto_make_randomized_from_passphrase(unsigned char *pp,
												   unsigned int pplen,
												   unsigned int needed,
												   unsigned char **rbuf);
/* Key Derivation */
extern unsigned char *crypto_pbkdf2(unsigned char *pwd, int pwdlen,
									unsigned char *salt, int saltlen,
									unsigned int count, unsigned int dklen);
extern unsigned char *crypto_kdf(unsigned char *pwd, int pwdlen,
								 unsigned char *salt, int saltlen,
								 unsigned int count, unsigned int nbits);

/* Encryption/Decryption */
extern void crypto_set_padding(int enable);
extern int crypto_cipher_iv_length(char *cipher_name, unsigned int *len);
extern unsigned char *crypto_encipher(Crypto_function func, char *cipher,
									  unsigned char *key, unsigned char **ivp,
									  PKI_session_keys *sk,
									  unsigned char *input, int input_len,
									  unsigned char *output, int *output_len);
extern unsigned char *crypto_decipher(Crypto_function func, char *cipher,
									  unsigned char *key, unsigned char **ivp,
									  PKI_session_keys *sk,
									  unsigned char *input, int input_len,
									  unsigned char *output, int *output_len);
struct Crypt_keys;
extern unsigned int crypto_encrypt_string(struct Crypt_keys *keys,
										  unsigned char *plaintext,
										  unsigned int len,
										  unsigned char **encrypted);
extern int crypto_decrypt_string(struct Crypt_keys *keys,
								 unsigned char *encrypted,
								 unsigned int len, unsigned char **plaintext,
								 unsigned int *plaintext_length);
extern char *crypt_md5(const char *pw, const char *salt);
extern void crypt_md5_magic(char *new_magic);

/* PKI Encryption/Decryption */
extern char *crypto_pki_encrypt(char *cipher_name, EVP_PKEY *key,
								unsigned char *input, int input_len,
								char *digest_name, EVP_PKEY *sign_key);
extern unsigned char *crypto_pki_decrypt(EVP_PKEY *key, char *input,
										 int *output_len);

/* Deterministic Random Bit Generators */
extern Rng_state *rng_init(char *seed);
extern Rng_state *rng_reinit(char *new_seed, Rng_state *state);
extern int rng_generate(Rng_state *state, unsigned char *buf, size_t len);
extern char *rng_generate_string(Rng_state *state, char *prefix, size_t len);
extern void rng_end(Rng_state *state);

#ifdef __cplusplus
}
#endif

#endif
