--- /dev/null
+/*
+ File name : morep_simulator.c
+ Projet : MERLIN
+ Date of creation : 2025/04/09
+ Version : 1.0
+ Copyright : Thales SIX
+ Author : Laurent Mazet <laurent.mazet@thalesgroup.com>
+
+ Description : MOREP simulator
+
+ History :
+ - initial version
+*/
+
+/* depend: */
+/* cflags: */
+/* linker: morep.o parse.o pdu_channel.o pdu_encrypted_data.o pdu_prng_param.o pdu_status.o pdu_clear_data.o pdu_key.o pdu_raw_data.o */
+/* winlnk: morep.o parse.o pdu_channel.o pdu_encrypted_data.o pdu_prng_param.o pdu_status.o pdu_clear_data.o pdu_key.o pdu_raw_data.o */
+
+#include <errno.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "morep.h"
+#include "parse.h"
+#include "verbose.h"
+
+#include "pdu_channel.h"
+#include "pdu_encrypted_data.h"
+#include "pdu_prng_param.h"
+#include "pdu_status.h"
+#include "pdu_clear_data.h"
+#include "pdu_key.h"
+#include "pdu_raw_data.h"
+#include "pdu_raw_data.h"
+
+char *progname = NULL;
+
+int stop = 0;
+
+DECLARE_VERBOSE_LEVEL (morep, INFO);
+
+#define BUFMAX 4096
+
+typedef enum {
+ nomod_e = 0,
+ red_e,
+ cryp_e,
+ black_e
+} module_t;
+
+typedef enum {
+ noserv_e = 0,
+ bypass_e,
+ control_e,
+ cross_crypto_e,
+ local_crypto_e,
+ prng_e,
+ provisioning_e
+} service_t;
+
+typedef enum {
+ nopdu_e = 0,
+ channel_e,
+ clear_data_e,
+ encrypted_data_e,
+ key_e,
+ prng_param_e,
+ raw_data_e,
+ status_e
+} pdu_t;
+
+typedef struct {
+ char *name;
+ service_t service;
+ uint8_t msgtype;
+ pdu_t pdu;
+} message_t;
+
+message_t message_list[] = {
+
+ /* Cross cryptographic service */
+ {"ENCRYPT_CROSS_ASYNC", cross_crypto_e, 0x00, clear_data_e},
+ {"ENCRYPTED_CROSS_ASYNC", cross_crypto_e, 0x01, encrypted_data_e},
+ {"DECRYPT_CROSS_ASYNC", cross_crypto_e, 0x02, encrypted_data_e},
+ {"DECRYPTED_CROSS_ASYNC", cross_crypto_e, 0x03, clear_data_e},
+
+ /* Local cryptographic service */
+ {"ENCRYPT_LOCAL_ASYNC", local_crypto_e, 0x00, clear_data_e},
+ {"ENCRYPTED_LOCAL_ASYNC", local_crypto_e, 0x01, encrypted_data_e},
+ {"DECRYPT_LOCAL_ASYNC", local_crypto_e, 0x02, encrypted_data_e},
+ {"DECRYPTED_LOCAL_ASYNC", local_crypto_e, 0x03, clear_data_e},
+
+ /* Provisioning service */
+ {"LOAD_KEY_REQ", provisioning_e, 0x00, key_e},
+ {"LOAD_KEY_RESP", provisioning_e, 0x01, status_e},
+ {"UNLOAD_KEY_REQ", provisioning_e, 0x02, key_e},
+ {"UNLOAD_KEY_RESP", provisioning_e, 0x03, status_e},
+ {"ERASE_KEY_REQ", provisioning_e, 0x04, key_e},
+ {"ERASE_KEY_RESP", provisioning_e, 0x05, status_e},
+ {"ASSOCIATE_CHANNEL_REQ", provisioning_e, 0x06, channel_e},
+ {"ASSOCIATE_CHANNEL_RESP", provisioning_e, 0x07, status_e},
+ {"DISSOCIATE_CHANNEL_REQ", provisioning_e, 0x08, channel_e},
+ {"DISSOCIATE_CHANNEL_RESP", provisioning_e, 0x09, status_e},
+
+ /* Bypass service */
+ {"BYPASS_CROSS_ASYNC", bypass_e, 0x00, raw_data_e},
+ {"BYPASSED_CROSS_ASYNC", bypass_e, 0x01, raw_data_e},
+
+ /* Control service */
+ {"STATUS_REQ", control_e, 0x00, nopdu_e},
+ {"STATUS_RESP", control_e, 0x01, status_e},
+ {"AUTHENTIFICATION_REQ", control_e, 0x02, raw_data_e},
+ {"AUTHENTIFICATION_RESP", control_e, 0x03, raw_data_e},
+ {"REBOOT_REQ", control_e, 0x04, nopdu_e},
+ {"REBOOT_RESP", control_e, 0x05, status_e},
+ {"ZEROIZE_REQ", control_e, 0x04, nopdu_e},
+ {"ZEROIZE_RESP", control_e, 0x05, status_e},
+ {"LOCK_REQ", control_e, 0xFE, nopdu_e},
+ {"LOCK_RESP", control_e, 0xFF, status_e}
+}
+
+void sig_handler (int sig)
+{
+ switch (sig) {
+ case SIGINT:
+ //stop = 1;
+ exit (0);
+ break;
+ case SIGTERM:
+ exit (0);
+ break;
+ }
+}
+
+char *read_stream (FILE *sd, int *plen)
+{
+ VERBOSE (morep, TRACE, PRINTF ("read_stream\n"));
+
+ /* read and store */
+ char *buffer = NULL;
+ size_t size = 0;
+ int blocklen = 0;
+ int length = 0;
+ do {
+ size += BUFMAX + (size == 0);
+ buffer = (char *) realloc (buffer, size);
+ memset (buffer + size - BUFMAX - 1, 0, BUFMAX + 1);
+ blocklen = fread (buffer + size - BUFMAX - 1, 1, BUFMAX, sd);
+ length += blocklen;
+ } while (blocklen > 0);
+
+ /* check size */
+ VERBOSE (morep, DEBUG, PRINTF ("read length: %d\n", length));
+ if (length == 0) {
+ free (buffer);
+ buffer = NULL;
+ }
+
+ if (plen) {
+ *plen = length;
+ }
+
+ return buffer;
+}
+
+void print_message (FILE *fd, char *etype, int mode, uint8_t msg, int seqnum, uint8_t *payload, int len)
+{
+ fprintf (fd ? fd : stdout, "%c%s SEG=%d MSG=%d LEN=%d", mode ? 'T' : 'R', etype, seqnum, msg, len);
+ if (len > 0) {
+ int i;
+ fprintf (fd ? fd : stdout, " PAYLOAD=");
+ for (i = 0; i < len; i++) {
+ fprintf (fd, "%02x", payload[i]);
+ }
+ }
+ fprintf (fd ? fd : stdout, "\n");
+}
+
+typedef struct {
+ int morep;
+ int mode;
+ char *etype;
+} comm_t;
+
+#define MAXCOMMS 32
+
+int main (int argc, char **argv)
+{
+ char *filename = NULL;
+ char *logname = NULL;
+ char *url = NULL;
+ int mode = -1;
+ int nbcomms = 0;
+ comm_t comm_list[MAXCOMMS] = {0};
+
+ /* get basename */
+ char *ptr = progname = argv[0];
+ while (*ptr) {
+ if ((*ptr == '/') || (*ptr == '\\')) {
+ progname = ptr + 1;
+ }
+ ptr++;
+ }
+
+ /* process argument */
+ while (argc-- > 1) {
+ char *arg = *(++argv);
+ if (arg[0] != '-') {
+ filename = arg;
+ continue;
+ }
+ char c = arg[1];
+ switch (c) {
+ case 'l':
+ arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL;
+ if (arg == NULL) {
+ VERBOSE (morep, ERROR, PRINTF ("%s: log file not specified\n", progname));
+ return 1;
+ }
+ logname = arg;
+ break;
+ case 'r':
+ arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL;
+ if (arg == NULL) {
+ VERBOSE (morep, ERROR, PRINTF ("%s: receiver url not specified\n", progname));
+ return 1;
+ }
+ url = arg;
+ mode = 0;
+ break;
+ case 't':
+ arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL;
+ if (arg == NULL) {
+ VERBOSE (morep, ERROR, PRINTF ("%s: transmitter url not specified\n", progname));
+ return 1;
+ }
+ url = arg;
+ mode = 1;
+ break;
+ case 'v':
+ arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL;
+ if (arg == NULL) {
+ VERBOSE (morep, ERROR, PRINTF ("%s: verbose level not specified\n", progname));
+ return 1;
+ }
+ CHANGE_VERBOSE_LEVEL (morep, atoi (arg));
+ break;
+ case 'h':
+ default:
+ printf ("usage: %s [-h] [-l log] [-r url] [-t url] [-v level] [file]\n", progname);
+ return (c != 'h');
+ }
+
+ /* init communication channel */
+ if (mode != -1) {
+ if (nbcomms < MAXCOMMS) {
+ int morep = MOREP_Connect (url);
+ if (morep >= 0) {
+ (comm_list + nbcomms)->morep = morep;
+ (comm_list + nbcomms)->mode = mode;
+ (comm_list + nbcomms)->etype = strrchr (url, '/') + 1;
+ nbcomms++;
+ } else {
+ VERBOSE (morep, ERROR, PRINTF ("can't connect on url '%s'\n", url));
+ return -1;
+ }
+ } else {
+ VERBOSE (morep, ERROR, PRINTF ("no more communication channel avaliable (%d)\n", nbcomms));
+ return -1;
+ }
+ mode = -1;
+ }
+ }
+
+ /* checks */
+ if (nbcomms == 0) {
+ VERBOSE (morep, ERROR, PRINTF ("no communication channel\n"));
+ return -1;
+ }
+
+ /* open script file */
+ FILE *fid = stdin;
+ if (filename != NULL) {
+ fid = fopen (filename, "r");
+ if (fid == NULL) {
+ VERBOSE (morep, ERROR, PRINTF ("can't open script file '%s' for reading\n", filename));
+ return -1;
+ }
+ }
+ char *script = read_stream (fid, NULL);
+ if (fid != stdin) {
+ fclose (fid);
+ }
+ if (script == NULL) {
+ VERBOSE (morep, ERROR, PRINTF ("no script read\n"));
+ return -1;
+ }
+
+ /* open log file */
+ FILE *log = NULL;
+ if (logname != NULL) {
+ if (strcmp (logname, "-") == 0) {
+ log = stdout;
+ } else {
+ log = fopen (logname, "w");
+ if (log == NULL) {
+ VERBOSE (morep, ERROR, PRINTF ("can't open log file '%s' for writing\n", logname));
+ return -1;
+ }
+ }
+ }
+
+ /* signals */
+ signal(SIGINT, sig_handler);
+ signal(SIGTERM, sig_handler);
+
+ /* main loop */
+ int rc = 0;
+ ptr = script;
+ while (*ptr != '\0') {
+
+ /* read line */
+ char *line = ptr;
+ TEST_CHARS (ptr, "\n\r", 1);
+ *ptr++ = '\0';
+
+ /* clean line */
+ TEST_CHARS (line, " \t", 0);
+ if ((*line == '\0') || (*line == '#')) {
+ continue;
+ }
+
+ /* analyse line */
+ mode = -1;
+ if (*line == 'R') {
+ mode = 0;
+ } else if (*line == 'T') {
+ mode = 1;
+ } else if (strncmp (line, "SLEEP", 5) == 0) {
+ int duration = atoi (line + 5);
+ VERBOSE (morep, INFO, PRINTF ("sleep %dms\n", duration));
+ usleep (duration * 1000);
+ continue;
+ }
+ if (mode == -1) {
+ VERBOSE (morep, WARNING, PRINTF ("unrecognize line '%s'\n", line));
+ continue;
+ }
+
+ /* find ethertype */
+ comm_t *comm = NULL;
+ int offset = 1;
+ int i;
+ for (i = 0; i < nbcomms; i++) {
+ comm_t *c = comm_list + i;
+ VERBOSE (morep, TRACE, PRINTF ("test %c[%s]\n", c->mode ? 'T' : 'R', c->etype));
+ if ((strncmp (line + offset, c->etype, strlen (c->etype)) == 0) && (c->mode == mode)) {
+ comm = c;
+ offset += strlen (c->etype);
+ break;
+ }
+ }
+ if (comm == NULL) {
+ VERBOSE (morep, TRACE, PRINTF ("no morep found '%s'\n", line));
+ continue;
+ }
+ VERBOSE (morep, DEBUG, PRINTF ("work with %c[%s]\n", comm->mode ? 'T' : 'R', comm->etype));
+
+ /* get values */
+ char *tmp = line + offset;
+ uint8_t msgtype;
+ TEST_CHARS (tmp, " \t", 0);
+ if (strncmp (tmp, "MSG", 3) != 0) {
+ VERBOSE (morep, WARNING, PRINTF ("can't parse line '%s' (%s)\n", line, tmp));
+ continue;
+ }
+ tmp += 3;
+ TEST_CHARS (tmp, " \t=", 0);
+ msgtype = strtol (tmp, &tmp, 0);
+ if ((*tmp != ' ') && (*tmp != '\t')) {
+ VERBOSE (morep, WARNING, PRINTF ("can't parse line '%s'\n", line));
+ continue;
+ }
+
+ RAW_DATA_t payload = {0};
+ if (parse_raw_data (tmp, &payload) != 0) {
+ VERBOSE (morep, WARNING, PRINTF ("can't parse line '%s'\n", line));
+ continue;
+ }
+ VERBOSE (morep, TRACE, PRINTF ("payload length: %d\n", payload.data_len));
+
+ /* transmit */
+ if (mode == 1) {
+ int seqnum = MOREP_Send (comm->morep, msgtype, payload.data, payload.data_len);
+ if (log) {
+ print_message (log, comm->etype, 1, msgtype, seqnum, payload.data, payload.data_len);
+ }
+ } else { /* receive */
+ uint8_t rxmsgtype = 0;
+ uint8_t rxpayload[1496 * 16 - 1] = {0};
+ int rxlen = -1;
+ int seqnum = MOREP_Receive (comm->morep, &rxmsgtype, rxpayload, &rxlen);
+
+ /* check msg type */
+ if (rxmsgtype != msgtype) {
+ VERBOSE (morep, WARNING, PRINTF ("R%sx SEQ=%d MSG=%d: expected msgtype %d\n", comm->etype, seqnum, rxmsgtype, msgtype));
+ }
+ /* check payload */
+ else if ((rxlen != payload.data_len) ||
+ ((memcmp (rxpayload, payload.data, rxlen) != 0))) {
+ VERBOSE (morep, WARNING, PRINTF ("R%s SEQ=%d MSG=%d: payloads differed %d/%d\n", comm->etype, seqnum, rxmsgtype, payload.data_len, rxlen));
+ }
+ if (log) {
+ print_message (log, comm->etype, 0, rxmsgtype, seqnum, rxpayload, rxlen);
+ }
+ }
+ }
+
+ /* cleaning */
+ free (script);
+ while (nbcomms) {
+ MOREP_Close (--nbcomms);
+ }
+ if ((log) && (log != stdout)) {
+ fclose (log);
+ }
+
+ return rc;
+}
+
+/* test: morep_valid.exe -h | grep usage */
+/* test: morep_valid.exe -r 2>&1 | grep 'url not specified' */
+/* test: morep_valid.exe -t 2>&1 | grep 'url not specified' */
+/* test: morep_valid.exe -l 2>&1 | grep 'log file not specified' */
+/* test: morep_valid.exe -l - -t lo://00:00:00:00:00:00/0808 -v 4 script-lo.eth */
+/* test: morep_valid.exe -l script.log -t lo://00:00:00:00:00:00/0808 -r lo://00:00:00:00:00:00/0808 script-lo.eth */
+
+/* vim: set ts=4 sw=4 si et: */