/*
 * PgBouncer - Lightweight connection pooler for PostgreSQL.
 *
 * Copyright (c) 2007-2009  Marko Kreen, Skype Technologies OÜ
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

 /*
  * Ought to match NAMEDATALEN.  Some cloud services use longer user
  * names, so give it some extra room.
  */
#define MAX_USERNAME    128

  /*
   * Some cloud services use very long generated passwords, so give it
   * plenty of room.
   */
#define MAX_PASSWORD    2048

struct ScramState {
	char* client_nonce;
	char* client_first_message_bare;
	char* client_final_message_without_proof;
	char* server_nonce;
	char* server_first_message;
	uint8_t* SaltedPassword;
	char cbind_flag;
	bool adhoc;	/* SCRAM data made up from plain-text password */
	int iterations;
	char* salt;	/* base64-encoded */
	uint8_t ClientKey[32];	/* SHA256_DIGEST_LENGTH */
	uint8_t StoredKey[32];
	uint8_t ServerKey[32];
};

struct PgCredentials {
	char name[MAX_USERNAME];
	char passwd[MAX_PASSWORD];
	uint8_t scram_ClientKey[32];
	uint8_t scram_ServerKey[32];
	bool has_scram_keys;		/* true if the above two are valid */
	bool mock_auth;
};

typedef struct ScramState ScramState;
typedef struct PgCredentials PgCredentials;

typedef enum PasswordType {
	PASSWORD_TYPE_PLAINTEXT = 0,
	PASSWORD_TYPE_MD5,
	PASSWORD_TYPE_SCRAM_SHA_256
} PasswordType;

#ifdef __cplusplus
extern "C" {
#endif

	PasswordType get_password_type(const char* shadow_pass);

	// Returns the last error message
	const char* scram_error();

	// Resets the error message
	void scram_reset_error();

	// Initializes a new ScramState object
	ScramState* scram_state_init();

	// Frees the memory allocated for a ScramState object
	void free_scram_state(ScramState* scram_state);

	/*
	 * Functions for communicating as a client with the server
	 */
	char *build_client_first_message(ScramState *scram_state);

	char *build_client_final_message(ScramState *scram_state,
					 const PgCredentials *credentials,
					 const char *server_nonce,
					 const char *salt,
					 int saltlen,
					 int iterations);

	bool read_server_first_message(ScramState* scram_state, char *input,
					   char **server_nonce_p, char **salt_p, int *saltlen_p, int *iterations_p);

	bool read_server_final_message(char *input, char *ServerSignature);

	bool verify_server_signature(ScramState *scram_state, const PgCredentials *credentials, const char *ServerSignature);


	/*
	 * Functions for communicating as a server to the client
	 */

	bool read_client_first_message(char *input,
					   char *cbind_flag_p,
					   char **client_first_message_bare_p,
					   char **client_nonce_p);

	bool read_client_final_message(ScramState* scram_state, const uint8_t *raw_input, char *input,
					   const char **client_final_nonce_p,
					   char **proof_p);

	char *build_server_first_message(ScramState *scram_state,
					 const char *username, const char *stored_secret);

	char *build_server_final_message(ScramState *scram_state);

	bool verify_final_nonce(const ScramState *scram_state, const char *client_final_nonce);

	bool verify_client_proof(ScramState *state, const char *ClientProof);

	bool scram_verify_plain_password(const char *username, const char *password, const char *secret);

#ifdef __cplusplus
}
#endif
