1 /*
2  * Copyright (c) 2020 Siddharth Chandrasekaran <siddharth@embedjournal.com>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_DECLARE(osdp, CONFIG_OSDP_LOG_LEVEL);
9 
10 #include <string.h>
11 #include "osdp_common.h"
12 
13 #define OSDP_PKT_MARK                  0xFF
14 #define OSDP_PKT_SOM                   0x53
15 #define PKT_CONTROL_SQN                0x03
16 #define PKT_CONTROL_CRC                0x04
17 #define PKT_CONTROL_SCB                0x08
18 
19 struct osdp_packet_header {
20 	uint8_t som;
21 	uint8_t pd_address;
22 	uint8_t len_lsb;
23 	uint8_t len_msb;
24 	uint8_t control;
25 	uint8_t data[];
26 } __packed;
27 
packet_has_mark(struct osdp_pd * pd)28 static inline bool packet_has_mark(struct osdp_pd *pd)
29 {
30 	return ISSET_FLAG(pd, PD_FLAG_PKT_HAS_MARK);
31 }
32 
packet_set_mark(struct osdp_pd * pd,bool mark)33 static inline void packet_set_mark(struct osdp_pd *pd, bool mark)
34 {
35 	if (mark) {
36 		SET_FLAG(pd, PD_FLAG_PKT_HAS_MARK);
37 	} else {
38 		CLEAR_FLAG(pd, PD_FLAG_PKT_HAS_MARK);
39 	}
40 }
41 
osdp_compute_checksum(uint8_t * msg,int length)42 uint8_t osdp_compute_checksum(uint8_t *msg, int length)
43 {
44 	uint8_t checksum = 0;
45 	int i, whole_checksum;
46 
47 	whole_checksum = 0;
48 	for (i = 0; i < length; i++) {
49 		whole_checksum += msg[i];
50 		checksum = ~(0xff & whole_checksum) + 1;
51 	}
52 	return checksum;
53 }
54 
osdp_phy_get_seq_number(struct osdp_pd * pd,int do_inc)55 static int osdp_phy_get_seq_number(struct osdp_pd *pd, int do_inc)
56 {
57 	/* pd->seq_num is set to -1 to reset phy cmd state */
58 	if (do_inc) {
59 		pd->seq_number += 1;
60 		if (pd->seq_number > 3) {
61 			pd->seq_number = 1;
62 		}
63 	}
64 	return pd->seq_number & PKT_CONTROL_SQN;
65 }
66 
osdp_phy_packet_get_data_offset(struct osdp_pd * pd,const uint8_t * buf)67 int osdp_phy_packet_get_data_offset(struct osdp_pd *pd, const uint8_t *buf)
68 {
69 	int sb_len = 0, mark_byte_len = 0;
70 	struct osdp_packet_header *pkt;
71 
72 	ARG_UNUSED(pd);
73 	if (packet_has_mark(pd)) {
74 		mark_byte_len = 1;
75 		buf += 1;
76 	}
77 	pkt = (struct osdp_packet_header *)buf;
78 	if (pkt->control & PKT_CONTROL_SCB) {
79 		sb_len = pkt->data[0];
80 	}
81 	return mark_byte_len + sizeof(struct osdp_packet_header) + sb_len;
82 }
83 
osdp_phy_packet_get_smb(struct osdp_pd * pd,const uint8_t * buf)84 uint8_t *osdp_phy_packet_get_smb(struct osdp_pd *pd, const uint8_t *buf)
85 {
86 	struct osdp_packet_header *pkt;
87 
88 	ARG_UNUSED(pd);
89 	if (packet_has_mark(pd)) {
90 		buf += 1;
91 	}
92 	pkt = (struct osdp_packet_header *)buf;
93 	if (pkt->control & PKT_CONTROL_SCB) {
94 		return pkt->data;
95 	}
96 	return NULL;
97 }
98 
osdp_phy_in_sc_handshake(int is_reply,int id)99 int osdp_phy_in_sc_handshake(int is_reply, int id)
100 {
101 	if (is_reply) {
102 		return (id == REPLY_CCRYPT || id == REPLY_RMAC_I);
103 	} else {
104 		return (id == CMD_CHLNG || id == CMD_SCRYPT);
105 	}
106 }
107 
osdp_phy_packet_init(struct osdp_pd * pd,uint8_t * buf,int max_len)108 int osdp_phy_packet_init(struct osdp_pd *pd, uint8_t *buf, int max_len)
109 {
110 	int exp_len, id, scb_len = 0, mark_byte_len = 0;
111 	struct osdp_packet_header *pkt;
112 
113 	exp_len = sizeof(struct osdp_packet_header) + 64; /* 64 is estimated */
114 	if (max_len < exp_len) {
115 		LOG_ERR("packet_init: out of space! CMD: %02x", pd->cmd_id);
116 		return OSDP_ERR_PKT_FMT;
117 	}
118 
119 	/**
120 	 * In PD mode just follow what we received from CP. In CP mode, as we
121 	 * initiate the transaction, choose based on CONFIG_OSDP_SKIP_MARK_BYTE.
122 	 */
123 	if ((is_pd_mode(pd) && packet_has_mark(pd)) ||
124 	    (is_cp_mode(pd) && !ISSET_FLAG(pd, PD_FLAG_PKT_SKIP_MARK))) {
125 		buf[0] = OSDP_PKT_MARK;
126 		buf++;
127 		mark_byte_len = 1;
128 		packet_set_mark(pd, true);
129 	}
130 
131 	/* Fill packet header */
132 	pkt = (struct osdp_packet_header *)buf;
133 	pkt->som = OSDP_PKT_SOM;
134 	pkt->pd_address = pd->address & 0x7F;	/* Use only the lower 7 bits */
135 	if (is_pd_mode(pd)) {
136 		/* PD must reply with MSB of it's address set */
137 		pkt->pd_address |= 0x80;
138 		id = pd->reply_id;
139 	} else {
140 		id = pd->cmd_id;
141 	}
142 	pkt->control = osdp_phy_get_seq_number(pd, is_cp_mode(pd));
143 	pkt->control |= PKT_CONTROL_CRC;
144 
145 	if (sc_is_active(pd)) {
146 		pkt->control |= PKT_CONTROL_SCB;
147 		pkt->data[0] = scb_len = 2;
148 		pkt->data[1] = SCS_15;
149 	} else if (osdp_phy_in_sc_handshake(is_pd_mode(pd), id)) {
150 		pkt->control |= PKT_CONTROL_SCB;
151 		pkt->data[0] = scb_len = 3;
152 		pkt->data[1] = SCS_11;
153 	}
154 
155 	return mark_byte_len + sizeof(struct osdp_packet_header) + scb_len;
156 }
157 
osdp_phy_packet_finalize(struct osdp_pd * pd,uint8_t * buf,int len,int max_len)158 int osdp_phy_packet_finalize(struct osdp_pd *pd, uint8_t *buf,
159 			     int len, int max_len)
160 {
161 	uint16_t crc16;
162 	struct osdp_packet_header *pkt;
163 
164 	/* Do a sanity check only; we expect header to be pre-filled */
165 	if ((unsigned long)len <= sizeof(struct osdp_packet_header)) {
166 		LOG_ERR("PKT_F: Invalid header");
167 		return OSDP_ERR_PKT_FMT;
168 	}
169 
170 	if (packet_has_mark(pd)) {
171 		if (buf[0] != OSDP_PKT_MARK) {
172 			LOG_ERR("PKT_F: MARK validation failed! ID: 0x%02x",
173 				is_cp_mode(pd) ? pd->cmd_id : pd->reply_id);
174 			return OSDP_ERR_PKT_FMT;
175 		}
176 		/* temporarily get rid of mark byte */
177 		buf += 1;
178 		len -= 1;
179 		max_len -= 1;
180 	}
181 	pkt = (struct osdp_packet_header *)buf;
182 	if (pkt->som != OSDP_PKT_SOM) {
183 		LOG_ERR("PKT_F: header SOM validation failed! ID: 0x%02x",
184 			is_cp_mode(pd) ? pd->cmd_id : pd->reply_id);
185 		return OSDP_ERR_PKT_FMT;
186 	}
187 
188 	/* len: with 2 byte CRC */
189 	pkt->len_lsb = BYTE_0(len + 2);
190 	pkt->len_msb = BYTE_1(len + 2);
191 
192 #ifdef CONFIG_OSDP_SC_ENABLED
193 	uint8_t *data;
194 	int data_len;
195 
196 	if (sc_is_active(pd) && (pkt->control & PKT_CONTROL_SCB) && pkt->data[1] >= SCS_15) {
197 		if (pkt->data[1] == SCS_17 || pkt->data[1] == SCS_18) {
198 			/**
199 			 * Only the data portion of message (after id byte)
200 			 * is encrypted. While (en/de)crypting, we must skip
201 			 * header, security block, and cmd/reply ID byte.
202 			 *
203 			 * Note: if cmd/reply has no data, we must set type to
204 			 * SCS_15/SCS_16 and send them.
205 			 */
206 			data = pkt->data + pkt->data[0] + 1;
207 			data_len = len - (sizeof(struct osdp_packet_header) +
208 					  pkt->data[0] + 1);
209 			len -= data_len;
210 			/**
211 			 * check if the passed buffer can hold the encrypted
212 			 * data where length may be rounded up to the nearest
213 			 * 16 byte block boundary.
214 			 */
215 			if (AES_PAD_LEN(data_len + 1) > max_len) {
216 				/* data_len + 1 for OSDP_SC_EOM_MARKER */
217 				goto out_of_space_error;
218 			}
219 			len += osdp_encrypt_data(pd, is_cp_mode(pd), data, data_len);
220 		}
221 		/* len: with 4bytes MAC; with 2 byte CRC; without 1 byte mark */
222 		if (len + 4 > max_len) {
223 			goto out_of_space_error;
224 		}
225 
226 		/* len: with 2 byte CRC; with 4 byte MAC */
227 		pkt->len_lsb = BYTE_0(len + 2 + 4);
228 		pkt->len_msb = BYTE_1(len + 2 + 4);
229 
230 		/* compute and extend the buf with 4 MAC bytes */
231 		osdp_compute_mac(pd, is_cp_mode(pd), buf, len);
232 		data = is_cp_mode(pd) ? pd->sc.c_mac : pd->sc.r_mac;
233 		memcpy(buf + len, data, 4);
234 		len += 4;
235 	}
236 #endif /* CONFIG_OSDP_SC_ENABLED */
237 
238 	/* fill crc16 */
239 	if (len + 2 > max_len) {
240 		goto out_of_space_error;
241 	}
242 	crc16 = osdp_compute_crc16(buf, len);
243 	buf[len + 0] = BYTE_0(crc16);
244 	buf[len + 1] = BYTE_1(crc16);
245 	len += 2;
246 
247 	if (packet_has_mark(pd)) {
248 		len += 1; /* put back mark byte */
249 	}
250 
251 	return len;
252 
253 out_of_space_error:
254 	LOG_ERR("PKT_F: Out of buffer space! CMD(%02x)", pd->cmd_id);
255 	return OSDP_ERR_PKT_FMT;
256 }
257 
osdp_phy_check_packet(struct osdp_pd * pd,uint8_t * buf,int len,int * one_pkt_len)258 int osdp_phy_check_packet(struct osdp_pd *pd, uint8_t *buf, int len,
259 			  int *one_pkt_len)
260 {
261 	uint16_t comp, cur;
262 	int pd_addr, pkt_len;
263 	struct osdp_packet_header *pkt;
264 
265 	/* wait till we have the header */
266 	if ((unsigned long)len < sizeof(struct osdp_packet_header)) {
267 		/* incomplete data */
268 		return OSDP_ERR_PKT_WAIT;
269 	}
270 
271 	packet_set_mark(pd, false);
272 	if (buf[0] == OSDP_PKT_MARK) {
273 		buf += 1;
274 		len -= 1;
275 		packet_set_mark(pd, true);
276 	}
277 
278 	pkt = (struct osdp_packet_header *)buf;
279 
280 	/* validate packet header */
281 	if (pkt->som != OSDP_PKT_SOM) {
282 		LOG_ERR("Invalid SOM 0x%02x", pkt->som);
283 		return OSDP_ERR_PKT_FMT;
284 	}
285 
286 	if (is_cp_mode(pd) && !(pkt->pd_address & 0x80)) {
287 		LOG_ERR("Reply without address MSB set!");
288 		return OSDP_ERR_PKT_FMT;
289 	}
290 
291 	/* validate packet length */
292 	pkt_len = (pkt->len_msb << 8) | pkt->len_lsb;
293 	if (len < pkt_len) {
294 		/* wait for more data? */
295 		return OSDP_ERR_PKT_WAIT;
296 	}
297 
298 	if (pkt_len > OSDP_PACKET_BUF_SIZE ||
299 	    (unsigned long)pkt_len < sizeof(struct osdp_packet_header)) {
300 		pd->reply_id = REPLY_NAK;
301 		pd->ephemeral_data[0] = OSDP_PD_NAK_CMD_LEN;
302 		return OSDP_ERR_PKT_NACK;
303 	}
304 
305 	*one_pkt_len = pkt_len + (packet_has_mark(pd) ? 1 : 0);
306 
307 	/* validate CRC/checksum */
308 	if (pkt->control & PKT_CONTROL_CRC) {
309 		pkt_len -= 2; /* consume CRC */
310 		cur = (buf[pkt_len + 1] << 8) | buf[pkt_len];
311 		comp = osdp_compute_crc16(buf, pkt_len);
312 		if (comp != cur) {
313 			LOG_ERR("Invalid crc 0x%04x/0x%04x", comp, cur);
314 			pd->reply_id = REPLY_NAK;
315 			pd->ephemeral_data[0] = OSDP_PD_NAK_MSG_CHK;
316 			return OSDP_ERR_PKT_NACK;
317 		}
318 	} else {
319 		pkt_len -= 1; /* consume checksum */
320 		cur = buf[pkt_len];
321 		comp = osdp_compute_checksum(buf, pkt_len);
322 		if (comp != cur) {
323 			LOG_ERR("Invalid checksum %02x/%02x", comp, cur);
324 			pd->reply_id = REPLY_NAK;
325 			pd->ephemeral_data[0] = OSDP_PD_NAK_MSG_CHK;
326 			return OSDP_ERR_PKT_NACK;
327 		}
328 	}
329 
330 	/* validate PD address */
331 	pd_addr = pkt->pd_address & 0x7F;
332 	if (pd_addr != pd->address && pd_addr != 0x7F) {
333 		/* not addressed to us and was not broadcasted */
334 		if (is_cp_mode(pd)) {
335 			LOG_ERR("Invalid pd address %d", pd_addr);
336 			return OSDP_ERR_PKT_CHECK;
337 		}
338 		return OSDP_ERR_PKT_SKIP;
339 	}
340 
341 	/* validate sequence number */
342 	comp = pkt->control & PKT_CONTROL_SQN;
343 	if (is_pd_mode(pd)) {
344 		if (comp == 0) {
345 			/**
346 			 * CP is trying to restart communication by sending a 0.
347 			 * The current PD implementation does not hold any state
348 			 * between commands so we can just set seq_number to -1
349 			 * (so it gets incremented to 0 with a call to
350 			 * phy_get_seq_number()) and invalidate any established
351 			 * secure channels.
352 			 */
353 			pd->seq_number = -1;
354 			sc_deactivate(pd);
355 		}
356 		if (comp == pd->seq_number) {
357 			/**
358 			 * TODO: PD must resend the last response if CP send the
359 			 * same sequence number again.
360 			 */
361 			LOG_ERR("seq-repeat/reply-resend not supported!");
362 			pd->reply_id = REPLY_NAK;
363 			pd->ephemeral_data[0] = OSDP_PD_NAK_SEQ_NUM;
364 			return OSDP_ERR_PKT_NACK;
365 		}
366 	} else {
367 		if (comp == 0) {
368 			/**
369 			 * Check for receiving a busy reply from the PD which would
370 			 * have a sequence number of 0, come in an unsecured packet
371 			 * of minimum length, and have the reply ID REPLY_BUSY.
372 			 */
373 			if ((pkt_len == 6) && (pkt->data[0] == REPLY_BUSY)) {
374 				pd->seq_number -= 1;
375 				return OSDP_ERR_PKT_BUSY;
376 			}
377 		}
378 	}
379 	cur = osdp_phy_get_seq_number(pd, is_pd_mode(pd));
380 	if (cur != comp && !ISSET_FLAG(pd, PD_FLAG_SKIP_SEQ_CHECK)) {
381 		LOG_ERR("packet seq mismatch %d/%d", cur, comp);
382 		pd->reply_id = REPLY_NAK;
383 		pd->ephemeral_data[0] = OSDP_PD_NAK_SEQ_NUM;
384 		return OSDP_ERR_PKT_NACK;
385 	}
386 
387 	return OSDP_ERR_PKT_NONE;
388 }
389 
osdp_phy_decode_packet(struct osdp_pd * pd,uint8_t * buf,int len,uint8_t ** pkt_start)390 int osdp_phy_decode_packet(struct osdp_pd *pd, uint8_t *buf, int len,
391 			   uint8_t **pkt_start)
392 {
393 	uint8_t *data;
394 	int mac_offset;
395 	struct osdp_packet_header *pkt;
396 
397 	if (packet_has_mark(pd)) {
398 		/* Consume mark byte */
399 		buf += 1;
400 		len -= 1;
401 	}
402 
403 	pkt = (struct osdp_packet_header *)buf;
404 	len -= pkt->control & PKT_CONTROL_CRC ? 2 : 1;
405 	mac_offset = len - 4;
406 	data = pkt->data;
407 	len -= sizeof(struct osdp_packet_header);
408 
409 #ifdef CONFIG_OSDP_SC_ENABLED
410 	uint8_t *mac;
411 	int is_cmd;
412 
413 	if (pkt->control & PKT_CONTROL_SCB) {
414 		if (is_pd_mode(pd) && !sc_is_capable(pd)) {
415 			LOG_ERR("PD is not SC capable");
416 			pd->reply_id = REPLY_NAK;
417 			pd->ephemeral_data[0] = OSDP_PD_NAK_SC_UNSUP;
418 			return OSDP_ERR_PKT_NACK;
419 		}
420 		if (pkt->data[1] < SCS_11 || pkt->data[1] > SCS_18) {
421 			LOG_ERR("Invalid SB Type");
422 			pd->reply_id = REPLY_NAK;
423 			pd->ephemeral_data[0] = OSDP_PD_NAK_SC_COND;
424 			return OSDP_ERR_PKT_NACK;
425 		}
426 		if (!sc_is_active(pd) && pkt->data[1] > SCS_14) {
427 			LOG_ERR("Received invalid secure message!");
428 			pd->reply_id = REPLY_NAK;
429 			pd->ephemeral_data[0] = OSDP_PD_NAK_SC_COND;
430 			return OSDP_ERR_PKT_NACK;
431 		}
432 		if (pkt->data[1] == SCS_11 || pkt->data[1] == SCS_13) {
433 			/**
434 			 * CP signals PD to use SCBKD by setting SB data byte
435 			 * to 0. In CP, PD_FLAG_SC_USE_SCBKD comes from FSM; on
436 			 * PD we extract it from the command itself. But this
437 			 * usage of SCBKD is allowed only when the PD is in
438 			 * install mode (indicated by PD_FLAG_INSTALL_MODE).
439 			 */
440 			if (ISSET_FLAG(pd, PD_FLAG_INSTALL_MODE) &&
441 			    pkt->data[2] == 0) {
442 				SET_FLAG(pd, PD_FLAG_SC_USE_SCBKD);
443 			}
444 		}
445 		data = pkt->data + pkt->data[0];
446 		len -= pkt->data[0]; /* consume security block */
447 	} else {
448 		/**
449 		 * If the current packet is an ACK for a KEYSET, the PD might
450 		 * have discarded the secure channel session keys in favour of
451 		 * the new key we sent and hence this packet may reach us in
452 		 * plain text. To work with such PDs, we must also discard our
453 		 * secure session.
454 		 *
455 		 * The way we do this is by calling osdp_keyset_complete() which
456 		 * copies the key in ephemeral_data to the current SCBK.
457 		 */
458 		if (is_cp_mode(pd) && pd->cmd_id == CMD_KEYSET &&
459 		    pkt->data[0] == REPLY_ACK) {
460 			osdp_keyset_complete(pd);
461 		}
462 
463 		if (sc_is_active(pd)) {
464 			LOG_ERR("Received plain-text message in SC");
465 			pd->reply_id = REPLY_NAK;
466 			pd->ephemeral_data[0] = OSDP_PD_NAK_SC_COND;
467 			return OSDP_ERR_PKT_NACK;
468 		}
469 	}
470 
471 	if (sc_is_active(pd) &&
472 	    pkt->control & PKT_CONTROL_SCB && pkt->data[1] >= SCS_15) {
473 		/* validate MAC */
474 		is_cmd = is_pd_mode(pd);
475 		osdp_compute_mac(pd, is_cmd, buf, mac_offset);
476 		mac = is_cmd ? pd->sc.c_mac : pd->sc.r_mac;
477 		if (memcmp(buf + mac_offset, mac, 4) != 0) {
478 			LOG_ERR("Invalid MAC; discarding SC");
479 			sc_deactivate(pd);
480 			pd->reply_id = REPLY_NAK;
481 			pd->ephemeral_data[0] = OSDP_PD_NAK_SC_COND;
482 			return OSDP_ERR_PKT_NACK;
483 		}
484 		len -= 4; /* consume MAC */
485 
486 		/* decrypt data block */
487 		if (pkt->data[1] == SCS_17 || pkt->data[1] == SCS_18) {
488 			/**
489 			 * Only the data portion of message (after id byte)
490 			 * is encrypted. While (en/de)crypting, we must skip
491 			 * header (6), security block (2) and cmd/reply id (1)
492 			 * bytes if cmd/reply has no data, use SCS_15/SCS_16.
493 			 *
494 			 * At this point, the header and security block is
495 			 * already consumed. So we can just skip the cmd/reply
496 			 * ID (data[0])  when calling osdp_decrypt_data().
497 			 */
498 			len = osdp_decrypt_data(pd, is_cmd, data + 1, len - 1);
499 			if (len < 0) {
500 				LOG_ERR("Failed at decrypt; discarding SC");
501 				sc_deactivate(pd);
502 				pd->reply_id = REPLY_NAK;
503 				pd->ephemeral_data[0] = OSDP_PD_NAK_SC_COND;
504 				return OSDP_ERR_PKT_NACK;
505 			}
506 			if (len == 0) {
507 				/**
508 				 * If cmd/reply has no data, PD "should" have
509 				 * used SCS_15/SCS_16 but we will be tolerant
510 				 * towards those faulty implementations.
511 				 */
512 				LOG_INF("Received encrypted data block with 0 "
513 					"length; tolerating non-conformance!");
514 			}
515 			len += 1; /* put back cmd/reply ID */
516 		}
517 	}
518 #endif /* CONFIG_OSDP_SC_ENABLED */
519 
520 	*pkt_start = data;
521 	return len;
522 }
523 
osdp_phy_state_reset(struct osdp_pd * pd)524 void osdp_phy_state_reset(struct osdp_pd *pd)
525 {
526 #ifdef CONFIG_OSDP_MODE_CP
527 	pd->phy_state = 0;
528 #endif
529 	pd->seq_number = -1;
530 	pd->rx_buf_len = 0;
531 }
532