/*-
 * Copyright 2021 John-Mark Gurney.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#ifndef __COMMS_H__
#define __COMMS_H__ 1

#include <sys/types.h>
#include <stdint.h>

#include <strobe.h>
#include <x25519.h>

#define COMMS_MAXMSG	64

struct pktbuf {
	uint8_t	*pkt;
	uint16_t pktlen;
};

/* first arg is input buffer, second arg is what will be sent as reply */
typedef void (*process_msgfunc_t)(struct pktbuf, struct pktbuf *);

enum comm_state {
	COMMS_WAIT_REQUEST_SHARED = 1,
	COMMS_WAIT_REQUEST_ECDHE,
	COMMS_WAIT_CONFIRM,
	COMMS_PROCESS_MSGS,
};

struct comms_session {
	strobe_s		cs_crypto;
	enum comm_state		cs_state;
};

/*
 * Each message will be passed to each state.
 *
 * cs_active can be in any state.
 * cs_pending can only be in a _WAIT_* state.
 *
 * When cs_pending advances to _PROCESS_MSGS, it will
 * replace cs_active, and cs_pending w/ be copied from cache
 * and set to _WAIT_REQUEST.
 *
 * If any message was not processed by the first to, a new session
 * will be attempted w/ the _start crypto state, and if it progresses
 * to _WAIT_CONFIG, it will replace cs_pending.
 *
 * We don't have to save the reply from a new session, because if the
 * reply gets lost, the initiator will send the request again and we'll
 * restart the session.
 */
struct comms_state {
	struct comms_session	cs_active;	/* current active session */
	struct comms_session	cs_pending;	/* current pending session */

	unsigned char cs_respkey[EC_PRIVATE_BYTES]; /* private key for device */
	unsigned char cs_resppubkey[EC_PUBLIC_BYTES]; /* public key for device */
	unsigned char cs_initpubkey[EC_PUBLIC_BYTES]; /* public key for initiator */

	struct comms_session	cs_start;	/* special starting state cache */

	process_msgfunc_t	cs_procmsg;

	struct pktbuf		cs_prevmsg;
	struct pktbuf		cs_prevmsgresp;

	uint8_t			cs_prevmsgbuf[COMMS_MAXMSG];
	uint8_t			cs_prevmsgrespbuf[COMMS_MAXMSG];
};

size_t _strobe_state_size();
size_t _comms_state_size();

int comms_init(struct comms_state *, process_msgfunc_t, struct pktbuf *, struct pktbuf *, struct pktbuf *);
void comms_process(struct comms_state *, struct pktbuf, struct pktbuf *);

#endif /* __COMMS_H__ */