/*- * 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. * */ #include #include static const size_t MAC_LEN = 8; static const size_t CHALLENGE_LEN = 16; static const uint8_t domain[] = "com.funkthat.lora.irrigation.shared.v0.0.1"; static int comms_pktbuf_equal(struct pktbuf a, struct pktbuf b); /* returns 1 if equal, 0 if not equal */ static int comms_pktbuf_equal(struct pktbuf a, struct pktbuf b) { if (a.pktlen != b.pktlen) return 0; return memcmp(a.pkt, b.pkt, a.pktlen) == 0; } size_t _strobe_state_size() { return sizeof(strobe_s); } void comms_init(struct comms_state *cs, process_msgfunc_t pmf, struct pktbuf *shared) { *cs = (struct comms_state){ .cs_comm_state = COMMS_WAIT_REQUEST, .cs_procmsg = pmf, }; strobe_init(&cs->cs_start, domain, sizeof domain - 1); if (shared != NULL) strobe_key(&cs->cs_start, SYM_KEY, shared->pkt, shared->pktlen); /* copy starting state over to initial state */ cs->cs_state = cs->cs_start; } #define CONFIRMED_STR_BASE "confirmed" #define CONFIRMED_STR ((const uint8_t *)CONFIRMED_STR_BASE) #define CONFIRMED_STR_LEN (sizeof(CONFIRMED_STR_BASE) - 1) /* * encrypted data to be processed is passed in via pbin. * * The pktbuf pointed to by pbout contains the buffer that a [encrypted] * response will be written to. The length needs to be updated, where 0 * means no reply. */ void comms_process(struct comms_state *cs, struct pktbuf pbin, struct pktbuf *pbout) { uint8_t buf[64] = {}; struct pktbuf pbmsg, pbrep; ssize_t cnt, ret, msglen; /* if the current msg matches the previous */ if (comms_pktbuf_equal(pbin, cs->cs_prevmsg)) { /* send the previous response */ pbout->pktlen = cs->cs_prevmsgresp.pktlen; memcpy(pbout->pkt, cs->cs_prevmsgresp.pkt, pbout->pktlen); return; } strobe_attach_buffer(&cs->cs_state, pbin.pkt, pbin.pktlen); cnt = strobe_get(&cs->cs_state, APP_CIPHERTEXT, buf, pbin.pktlen - MAC_LEN); msglen = cnt; cnt = strobe_get(&cs->cs_state, MAC, pbin.pkt + (pbin.pktlen - MAC_LEN), MAC_LEN); /* XXX - cnt != MAC_LEN test case */ /* * if we have arrived here, MAC has been verified, and buf now * contains the data to operate upon. */ /* attach the buffer for output */ strobe_attach_buffer(&cs->cs_state, pbout->pkt, pbout->pktlen); ret = 0; switch (cs->cs_comm_state) { case COMMS_WAIT_REQUEST: /* XXX - reqreset check */ bare_strobe_randomize(buf, CHALLENGE_LEN); ret = strobe_put(&cs->cs_state, APP_CIPHERTEXT, buf, CHALLENGE_LEN); ret += strobe_put(&cs->cs_state, MAC, NULL, MAC_LEN); strobe_operate(&cs->cs_state, RATCHET, NULL, 32); cs->cs_comm_state = COMMS_WAIT_CONFIRM; break; case COMMS_WAIT_CONFIRM: /* XXX - confirm check */ ret = strobe_put(&cs->cs_state, APP_CIPHERTEXT, CONFIRMED_STR, CONFIRMED_STR_LEN); ret += strobe_put(&cs->cs_state, MAC, NULL, MAC_LEN); cs->cs_comm_state = COMMS_PROCESS_MSGS; break; case COMMS_PROCESS_MSGS: { uint8_t repbuf[pbout->pktlen - MAC_LEN]; memset(repbuf, '\x00', sizeof repbuf); pbmsg.pkt = buf; pbmsg.pktlen = msglen; pbrep.pkt = repbuf; pbrep.pktlen = sizeof repbuf; cs->cs_procmsg(pbmsg, &pbrep); ret = strobe_put(&cs->cs_state, APP_CIPHERTEXT, repbuf, pbrep.pktlen); ret += strobe_put(&cs->cs_state, MAC, NULL, MAC_LEN); break; } } /* set the output buffer length */ pbout->pktlen = ret; if (ret != 0) { /* we accepted a new message store it */ /* store the req */ cs->cs_prevmsg.pkt = cs->cs_prevmsgbuf; cs->cs_prevmsg.pktlen = pbin.pktlen; memcpy(cs->cs_prevmsg.pkt, pbin.pkt, pbin.pktlen); /* store the response */ cs->cs_prevmsgresp.pkt = cs->cs_prevmsgrespbuf; cs->cs_prevmsgresp.pktlen = pbout->pktlen; memcpy(cs->cs_prevmsgresp.pkt, pbout->pkt, pbout->pktlen); } }