Implement a secure ICS protocol targeting LoRa Node151 microcontroller for controlling irrigation.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

337 lines
9.2 KiB

  1. /*-
  2. * Copyright 2021 John-Mark Gurney.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. * 1. Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * 2. Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. *
  13. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  14. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  15. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  16. * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  17. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  18. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  19. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  20. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  21. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  22. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  23. * SUCH DAMAGE.
  24. *
  25. */
  26. #include <comms.h>
  27. #include <strobe_rng_init.h>
  28. static const size_t MAC_LEN = 8;
  29. static const size_t CHALLENGE_LEN = 16;
  30. static const uint8_t shared_domain[] = "com.funkthat.lora.irrigation.shared.v0.0.1";
  31. static const uint8_t ecdhe_domain[] = "com.funkthat.lora.irrigation.ecdhe.v0.0.1";
  32. static const uint8_t reqreset[] = "reqreset";
  33. static const uint8_t confirm[] = "confirm";
  34. static int comms_pktbuf_equal(struct pktbuf a, struct pktbuf b);
  35. /* returns 1 if equal, 0 if not equal */
  36. static int
  37. comms_pktbuf_equal(struct pktbuf a, struct pktbuf b)
  38. {
  39. if (a.pktlen != b.pktlen)
  40. return 0;
  41. return memcmp(a.pkt, b.pkt, a.pktlen) == 0;
  42. }
  43. size_t
  44. _comms_state_size()
  45. {
  46. return sizeof(struct comms_state);
  47. }
  48. size_t
  49. _strobe_state_size()
  50. {
  51. return sizeof(strobe_s);
  52. }
  53. int
  54. comms_init(struct comms_state *cs, process_msgfunc_t pmf,
  55. struct pktbuf *shared, struct pktbuf *resp_key, struct pktbuf *init_pubkey)
  56. {
  57. unsigned char buf[EC_PUBLIC_BYTES * 2];
  58. strobe_s *crypt;
  59. *cs = (struct comms_state){
  60. .cs_procmsg = pmf,
  61. };
  62. crypt = &cs->cs_start.cs_crypto;
  63. if (shared != NULL) {
  64. strobe_init(crypt, shared_domain, sizeof shared_domain - 1);
  65. strobe_key(crypt, SYM_KEY, shared->pkt, shared->pktlen);
  66. cs->cs_start.cs_state = COMMS_WAIT_REQUEST_SHARED;
  67. } else if (resp_key != NULL && init_pubkey != NULL) {
  68. strobe_init(crypt, ecdhe_domain, sizeof ecdhe_domain - 1);
  69. if (resp_key->pktlen != EC_PRIVATE_BYTES ||
  70. init_pubkey->pktlen != EC_PUBLIC_BYTES)
  71. return 0;
  72. /* store our private key */
  73. memcpy(cs->cs_respkey, resp_key->pkt, resp_key->pktlen);
  74. x25519_base(cs->cs_resppubkey, resp_key->pkt, 1);
  75. /* store the public key to associate with */
  76. memcpy(cs->cs_initpubkey, init_pubkey->pkt, init_pubkey->pktlen);
  77. /* public keys */
  78. memcpy(&buf[0], cs->cs_initpubkey, EC_PUBLIC_BYTES);
  79. memcpy(&buf[EC_PUBLIC_BYTES], cs->cs_resppubkey, EC_PUBLIC_BYTES);
  80. strobe_key(crypt, SYM_KEY, buf, EC_PUBLIC_BYTES * 2);
  81. cs->cs_start.cs_state = COMMS_WAIT_REQUEST_ECDHE;
  82. } else {
  83. return 0;
  84. }
  85. /* copy starting state over to initial state */
  86. cs->cs_active = cs->cs_start;
  87. cs->cs_pending = cs->cs_active;
  88. return 1;
  89. }
  90. #define CONFIRMED_STR_BASE "confirmed"
  91. #define CONFIRMED_STR ((const uint8_t *)CONFIRMED_STR_BASE)
  92. #define CONFIRMED_STR_LEN (sizeof(CONFIRMED_STR_BASE) - 1)
  93. static void
  94. _comms_process_session(struct comms_state *cs, struct comms_session *sess, struct pktbuf pbin, struct pktbuf *pbout)
  95. {
  96. strobe_s tmp;
  97. uint8_t buf[64] = {};
  98. struct pktbuf pbmsg, pbrep;
  99. ssize_t cnt, ret, msglen;
  100. /* save the state incase the message is bad */
  101. tmp = sess->cs_crypto;
  102. strobe_attach_buffer(&sess->cs_crypto, pbin.pkt, pbin.pktlen);
  103. /* if the packet is too short, ignore */
  104. if (pbin.pktlen < MAC_LEN)
  105. goto badmsg;
  106. cnt = strobe_get(&sess->cs_crypto, APP_CIPHERTEXT, buf, pbin.pktlen -
  107. MAC_LEN);
  108. msglen = cnt;
  109. cnt = strobe_get(&sess->cs_crypto, MAC, pbin.pkt +
  110. (pbin.pktlen - MAC_LEN), MAC_LEN);
  111. /* MAC check failed */
  112. if (cnt == -1) {
  113. badmsg:
  114. /* restore the previous state */
  115. sess->cs_crypto = tmp;
  116. pbout->pktlen = 0;
  117. return;
  118. }
  119. /*
  120. * if we have arrived here, MAC has been verified, and buf now
  121. * contains the data to operate upon.
  122. */
  123. /* attach the buffer for output */
  124. strobe_attach_buffer(&sess->cs_crypto, pbout->pkt, pbout->pktlen);
  125. ret = 0;
  126. switch (sess->cs_state) {
  127. case COMMS_WAIT_REQUEST_SHARED:
  128. if (msglen != 24 || memcmp(reqreset, &buf[16],
  129. sizeof reqreset - 1) != 0)
  130. goto badmsg;
  131. bare_strobe_randomize(buf, CHALLENGE_LEN);
  132. ret = strobe_put(&sess->cs_crypto, APP_CIPHERTEXT, buf,
  133. CHALLENGE_LEN);
  134. ret += strobe_put(&sess->cs_crypto, MAC, NULL, MAC_LEN);
  135. strobe_operate(&sess->cs_crypto, RATCHET, NULL, 32);
  136. sess->cs_state = COMMS_WAIT_CONFIRM;
  137. break;
  138. case COMMS_WAIT_REQUEST_ECDHE:
  139. {
  140. unsigned char ephkey[EC_PRIVATE_BYTES];
  141. unsigned char ephpubkey[EC_PUBLIC_BYTES];
  142. unsigned char keybuf[EC_PUBLIC_BYTES * 2];
  143. if (msglen != (EC_PUBLIC_BYTES + 8) || memcmp(reqreset, &buf[EC_PUBLIC_BYTES],
  144. sizeof reqreset - 1) != 0)
  145. goto badmsg;
  146. /* DH(s, re) */
  147. memcpy(&keybuf[0], cs->cs_resppubkey, EC_PUBLIC_BYTES);
  148. if (x25519(&keybuf[0], cs->cs_respkey, buf, 1) != 0)
  149. goto badmsg;
  150. /* DH(s, rs) */
  151. memcpy(&keybuf[EC_PUBLIC_BYTES], cs->cs_resppubkey, EC_PUBLIC_BYTES);
  152. if (x25519(&keybuf[EC_PUBLIC_BYTES], cs->cs_respkey, cs->cs_initpubkey, 1) != 0)
  153. goto badmsg;
  154. /* key(DH() + DH()) */
  155. strobe_key(&sess->cs_crypto, SYM_KEY, keybuf, EC_PUBLIC_BYTES * 2);
  156. /* generate ephemeral */
  157. bare_strobe_randomize(ephkey, EC_PRIVATE_BYTES);
  158. if (x25519_base(ephpubkey, ephkey, 1) != 0)
  159. goto badmsg;
  160. ret = strobe_put(&sess->cs_crypto, APP_CIPHERTEXT, ephpubkey,
  161. EC_PUBLIC_BYTES);
  162. ret += strobe_put(&sess->cs_crypto, MAC, NULL, MAC_LEN);
  163. /* DH(e, re) */
  164. memcpy(&keybuf[0], ephpubkey, EC_PUBLIC_BYTES);
  165. if (x25519(&keybuf[0], ephkey, buf, 1) != 0)
  166. goto badmsg;
  167. /* DH(e, rs) */
  168. memcpy(&keybuf[EC_PUBLIC_BYTES], ephpubkey, EC_PUBLIC_BYTES);
  169. if (x25519(&keybuf[EC_PUBLIC_BYTES], ephkey, cs->cs_initpubkey, 1) != 0)
  170. goto badmsg;
  171. /* key(DH() + DH()) */
  172. strobe_key(&sess->cs_crypto, SYM_KEY, keybuf, EC_PUBLIC_BYTES * 2);
  173. sess->cs_state = COMMS_WAIT_CONFIRM;
  174. break;
  175. }
  176. case COMMS_WAIT_CONFIRM:
  177. if (msglen != 7 || memcmp(confirm, buf,
  178. sizeof confirm - 1) != 0)
  179. goto badmsg;
  180. ret = strobe_put(&sess->cs_crypto, APP_CIPHERTEXT, CONFIRMED_STR,
  181. CONFIRMED_STR_LEN);
  182. ret += strobe_put(&sess->cs_crypto, MAC, NULL, MAC_LEN);
  183. sess->cs_state = COMMS_PROCESS_MSGS;
  184. break;
  185. case COMMS_PROCESS_MSGS: {
  186. uint8_t repbuf[pbout->pktlen - MAC_LEN];
  187. memset(repbuf, '\x00', sizeof repbuf);
  188. pbmsg.pkt = buf;
  189. pbmsg.pktlen = msglen;
  190. pbrep.pkt = repbuf;
  191. pbrep.pktlen = sizeof repbuf;
  192. cs->cs_procmsg(pbmsg, &pbrep);
  193. ret = strobe_put(&sess->cs_crypto, APP_CIPHERTEXT, repbuf,
  194. pbrep.pktlen);
  195. ret += strobe_put(&sess->cs_crypto, MAC, NULL, MAC_LEN);
  196. break;
  197. }
  198. }
  199. /* set the output buffer length */
  200. pbout->pktlen = ret;
  201. }
  202. /*
  203. * encrypted data to be processed is passed in via pbin.
  204. *
  205. * The pktbuf pointed to by pbout contains the buffer that a [encrypted]
  206. * response will be written to. The length needs to be updated, where 0
  207. * means no reply.
  208. */
  209. void
  210. comms_process(struct comms_state *cs, struct pktbuf pbin, struct pktbuf *pbout)
  211. {
  212. struct pktbuf pbouttmp;
  213. /* if the current msg matches the previous */
  214. if (comms_pktbuf_equal(pbin, cs->cs_prevmsg)) {
  215. /* send the previous response */
  216. pbout->pktlen = cs->cs_prevmsgresp.pktlen;
  217. memcpy(pbout->pkt, cs->cs_prevmsgresp.pkt, pbout->pktlen);
  218. return;
  219. }
  220. /* try to use the active session */
  221. pbouttmp = *pbout;
  222. _comms_process_session(cs, &cs->cs_active, pbin, &pbouttmp);
  223. if (pbouttmp.pktlen != 0) {
  224. retmsg:
  225. /* we accepted a new message store it */
  226. *pbout = pbouttmp;
  227. /* store the req */
  228. cs->cs_prevmsg.pkt = cs->cs_prevmsgbuf;
  229. cs->cs_prevmsg.pktlen = pbin.pktlen;
  230. memcpy(cs->cs_prevmsg.pkt, pbin.pkt, pbin.pktlen);
  231. /* store the response */
  232. cs->cs_prevmsgresp.pkt = cs->cs_prevmsgrespbuf;
  233. cs->cs_prevmsgresp.pktlen = pbout->pktlen;
  234. memcpy(cs->cs_prevmsgresp.pkt, pbout->pkt, pbout->pktlen);
  235. } else {
  236. /* active session didn't work, try cs_pending */
  237. pbouttmp = *pbout;
  238. _comms_process_session(cs, &cs->cs_pending, pbin, &pbouttmp);
  239. if (cs->cs_pending.cs_state == COMMS_PROCESS_MSGS) {
  240. /* new active state */
  241. cs->cs_active = cs->cs_pending;
  242. cs->cs_pending = cs->cs_start;
  243. goto retmsg;
  244. }
  245. /* pending session advanced (likely to _WAIT_CONFIRM) */
  246. if (pbouttmp.pktlen > 0) {
  247. *pbout = pbouttmp;
  248. return;
  249. }
  250. /* pending session didn't work, maybe new */
  251. struct comms_session tmpsess;
  252. tmpsess = cs->cs_start;
  253. pbouttmp = *pbout;
  254. _comms_process_session(cs, &tmpsess, pbin, &pbouttmp);
  255. if (tmpsess.cs_state == COMMS_WAIT_CONFIRM) {
  256. /* new request for session */
  257. cs->cs_pending = tmpsess;
  258. *pbout = pbouttmp;
  259. } else {
  260. /* no packet to reply with */
  261. pbout->pktlen = 0;
  262. }
  263. }
  264. }