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 <stdlib.h>
11 #include <string.h>
12 
13 #include "osdp_common.h"
14 
15 #define CMD_POLL_DATA_LEN              0
16 #define CMD_LSTAT_DATA_LEN             0
17 #define CMD_ISTAT_DATA_LEN             0
18 #define CMD_OSTAT_DATA_LEN             0
19 #define CMD_RSTAT_DATA_LEN             0
20 #define CMD_ID_DATA_LEN                1
21 #define CMD_CAP_DATA_LEN               1
22 #define CMD_OUT_DATA_LEN               4
23 #define CMD_LED_DATA_LEN               14
24 #define CMD_BUZ_DATA_LEN               5
25 #define CMD_TEXT_DATA_LEN              6   /* variable length command */
26 #define CMD_COMSET_DATA_LEN            5
27 #define CMD_KEYSET_DATA_LEN            18
28 #define CMD_CHLNG_DATA_LEN             8
29 #define CMD_SCRYPT_DATA_LEN            16
30 
31 #define REPLY_ACK_LEN                  1
32 #define REPLY_PDID_LEN                 13
33 #define REPLY_PDCAP_LEN                1   /* variable length command */
34 #define REPLY_PDCAP_ENTITY_LEN         3
35 #define REPLY_LSTATR_LEN               3
36 #define REPLY_RSTATR_LEN               2
37 #define REPLY_COM_LEN                  6
38 #define REPLY_NAK_LEN                  2
39 #define REPLY_CCRYPT_LEN               33
40 #define REPLY_RMAC_I_LEN               17
41 #define REPLY_KEYPAD_LEN               2
42 #define REPLY_RAW_LEN                  4
43 #define REPLY_FMT_LEN                  3
44 
45 enum osdp_pd_error_e {
46 	OSDP_PD_ERR_NONE = 0,
47 	OSDP_PD_ERR_NO_DATA = -1,
48 	OSDP_PD_ERR_GENERIC = -2,
49 	OSDP_PD_ERR_REPLY = -3,
50 	OSDP_PD_ERR_IGNORE = -4,
51 };
52 
53 static struct osdp_pd_id osdp_pd_id = {
54 	.version = CONFIG_OSDP_PD_ID_VERSION,
55 	.model = CONFIG_OSDP_PD_ID_MODEL,
56 	.vendor_code = CONFIG_OSDP_PD_ID_VENDOR_CODE,
57 	.serial_number = CONFIG_OSDP_PD_ID_SERIAL_NUMBER,
58 	.firmware_version = CONFIG_OSDP_PD_ID_FIRMWARE_VERSION,
59 };
60 
61 static struct osdp_pd_cap osdp_pd_cap[] = {
62 	/* Driver Implicit capabilities */
63 	{
64 		OSDP_PD_CAP_CHECK_CHARACTER_SUPPORT,
65 		1, /* The PD supports the 16-bit CRC-16 mode */
66 		0, /* N/A */
67 	},
68 	{
69 		OSDP_PD_CAP_COMMUNICATION_SECURITY,
70 #ifdef CONFIG_OSDP_SC_ENABLED
71 		1, /* (Bit-0) AES128 support */
72 		1, /* (Bit-0) default AES128 key */
73 #else
74 		0, /* SC not supported */
75 		0, /* SC not supported */
76 #endif
77 	},
78 	/* Configured from Kconfig */
79 	{
80 		OSDP_PD_CAP_CONTACT_STATUS_MONITORING,
81 		CONFIG_OSDP_PD_CAP_CONTACT_STATUS_MONITORING_COMP_LEVEL,
82 		CONFIG_OSDP_PD_CAP_CONTACT_STATUS_MONITORING_NUM_ITEMS,
83 	},
84 	{
85 		OSDP_PD_CAP_OUTPUT_CONTROL,
86 		CONFIG_OSDP_PD_CAP_OUTPUT_CONTROL_COMP_LEVEL,
87 		CONFIG_OSDP_PD_CAP_OUTPUT_CONTROL_NUM_ITEMS,
88 	},
89 	{
90 		OSDP_PD_CAP_READER_LED_CONTROL,
91 		CONFIG_OSDP_PD_CAP_READER_LED_CONTROL_COMP_LEVEL,
92 		CONFIG_OSDP_PD_CAP_READER_LED_CONTROL_NUM_ITEMS,
93 	},
94 	{
95 		OSDP_PD_CAP_READER_AUDIBLE_OUTPUT,
96 		CONFIG_OSDP_PD_CAP_READER_AUDIBLE_OUTPUT_COMP_LEVEL,
97 		CONFIG_OSDP_PD_CAP_READER_AUDIBLE_OUTPUT_NUM_ITEMS,
98 	},
99 	{
100 		OSDP_PD_CAP_READER_TEXT_OUTPUT,
101 		CONFIG_OSDP_PD_CAP_READER_TEXT_OUTPUT_COMP_LEVEL,
102 		CONFIG_OSDP_PD_CAP_READER_TEXT_OUTPUT_NUM_ITEMS,
103 	},
104 	{
105 		OSDP_PD_CAP_CARD_DATA_FORMAT,
106 		CONFIG_OSDP_PD_CAP_CARD_DATA_FORMAT_COMP_LEVEL,
107 		0, /* N/A set to 0 */
108 	},
109 	{
110 		OSDP_PD_CAP_TIME_KEEPING,
111 		CONFIG_OSDP_PD_CAP_TIME_KEEPING_COMP_LEVEL,
112 		0, /* N/A set to 0 */
113 	},
114 	{ -1, 0, 0 } /* Sentinel */
115 };
116 
pd_event_alloc(struct osdp_pd * pd)117 static struct osdp_event *pd_event_alloc(struct osdp_pd *pd)
118 {
119 	struct osdp_event *event = NULL;
120 
121 	if (k_mem_slab_alloc(&pd->event.slab, (void **)&event, K_MSEC(100))) {
122 		LOG_ERR("Memory allocation time-out");
123 		return NULL;
124 	}
125 	return event;
126 }
127 
pd_event_free(struct osdp_pd * pd,struct osdp_event * event)128 static void pd_event_free(struct osdp_pd *pd, struct osdp_event *event)
129 {
130 	k_mem_slab_free(&pd->event.slab, (void *)event);
131 }
132 
pd_event_enqueue(struct osdp_pd * pd,struct osdp_event * event)133 static void pd_event_enqueue(struct osdp_pd *pd, struct osdp_event *event)
134 {
135 	sys_slist_append(&pd->event.queue, &event->node);
136 }
137 
pd_event_dequeue(struct osdp_pd * pd,struct osdp_event ** event)138 static int pd_event_dequeue(struct osdp_pd *pd, struct osdp_event **event)
139 {
140 	sys_snode_t *node;
141 
142 	node = sys_slist_peek_head(&pd->event.queue);
143 	if (node == NULL) {
144 		return -1;
145 	}
146 	sys_slist_remove(&pd->event.queue, NULL, node);
147 	*event = CONTAINER_OF(node, struct osdp_event, node);
148 	return 0;
149 }
150 
pd_translate_event(struct osdp_pd * pd,struct osdp_event * event)151 static int pd_translate_event(struct osdp_pd *pd, struct osdp_event *event)
152 {
153 	int reply_code = 0;
154 
155 	switch (event->type) {
156 	case OSDP_EVENT_CARDREAD:
157 		if (event->cardread.format == OSDP_CARD_FMT_RAW_UNSPECIFIED ||
158 		    event->cardread.format == OSDP_CARD_FMT_RAW_WIEGAND) {
159 			reply_code = REPLY_RAW;
160 		} else if (event->cardread.format == OSDP_CARD_FMT_ASCII) {
161 			reply_code = REPLY_FMT;
162 		} else {
163 			LOG_ERR("Event: cardread; Error: unknown format");
164 			break;
165 		}
166 		break;
167 	case OSDP_EVENT_KEYPRESS:
168 		reply_code = REPLY_KEYPPAD;
169 		break;
170 	default:
171 		LOG_ERR("Unknown event type %d", event->type);
172 		break;
173 	}
174 	if (reply_code == 0) {
175 		/* POLL command cannot fail even when there are errors here */
176 		return REPLY_ACK;
177 	}
178 	memcpy(pd->ephemeral_data, event, sizeof(struct osdp_event));
179 	return reply_code;
180 }
181 
do_command_callback(struct osdp_pd * pd,struct osdp_cmd * cmd)182 static bool do_command_callback(struct osdp_pd *pd, struct osdp_cmd *cmd)
183 {
184 	int ret;
185 
186 	ret = pd->command_callback(pd->command_callback_arg, cmd);
187 	if (ret != 0) {
188 		pd->reply_id = REPLY_NAK;
189 		pd->ephemeral_data[0] = OSDP_PD_NAK_RECORD;
190 		return false;
191 	}
192 	return true;
193 }
194 
pd_cmd_cap_ok(struct osdp_pd * pd,struct osdp_cmd * cmd)195 static int pd_cmd_cap_ok(struct osdp_pd *pd, struct osdp_cmd *cmd)
196 {
197 	struct osdp_pd_cap *cap = NULL;
198 
199 	/* Validate the cmd_id against a PD capabilities where applicable */
200 	switch (pd->cmd_id) {
201 	case CMD_ISTAT:
202 		cap = &pd->cap[OSDP_PD_CAP_CONTACT_STATUS_MONITORING];
203 		if (cap->num_items == 0 || cap->compliance_level == 0) {
204 			break;
205 		}
206 		return 0; /* Remove this when REPLY_ISTATR is supported */
207 	case CMD_OSTAT:
208 		cap = &pd->cap[OSDP_PD_CAP_OUTPUT_CONTROL];
209 		if (cap->num_items == 0 || cap->compliance_level == 0) {
210 			break;
211 		}
212 		return 0; /* Remove this when REPLY_OSTATR is supported */
213 	case CMD_OUT:
214 		cap = &pd->cap[OSDP_PD_CAP_OUTPUT_CONTROL];
215 		if (!cmd || cap->compliance_level == 0 ||
216 		    cmd->output.output_no + 1 > cap->num_items) {
217 			break;
218 		}
219 		return 1;
220 	case CMD_LED:
221 		cap = &pd->cap[OSDP_PD_CAP_READER_LED_CONTROL];
222 		if (!cmd || cap->compliance_level == 0 ||
223 		    cmd->led.led_number + 1 > cap->num_items) {
224 			break;
225 		}
226 		return 1;
227 	case CMD_BUZ:
228 		cap = &pd->cap[OSDP_PD_CAP_READER_AUDIBLE_OUTPUT];
229 		if (cap->num_items == 0 || cap->compliance_level == 0) {
230 			break;
231 		}
232 		return 1;
233 	case CMD_TEXT:
234 		cap = &pd->cap[OSDP_PD_CAP_READER_TEXT_OUTPUT];
235 		if (cap->num_items == 0 || cap->compliance_level == 0) {
236 			break;
237 		}
238 		return 1;
239 	case CMD_CHLNG:
240 	case CMD_SCRYPT:
241 	case CMD_KEYSET:
242 		cap = &pd->cap[OSDP_PD_CAP_COMMUNICATION_SECURITY];
243 		if (cap->compliance_level == 0) {
244 			pd->reply_id = REPLY_NAK;
245 			pd->ephemeral_data[0] = OSDP_PD_NAK_SC_UNSUP;
246 			return 0;
247 		}
248 		return 1;
249 	}
250 
251 	pd->reply_id = REPLY_NAK;
252 	pd->ephemeral_data[0] = OSDP_PD_NAK_CMD_UNKNOWN;
253 	LOG_ERR("PD is not capable of handling CMD(%02x); "
254 		"Reply with NAK_CMD_UNKNOWN", pd->cmd_id);
255 	return 0;
256 }
257 
pd_decode_command(struct osdp_pd * pd,uint8_t * buf,int len)258 static int pd_decode_command(struct osdp_pd *pd, uint8_t *buf, int len)
259 {
260 	int ret = OSDP_PD_ERR_GENERIC;
261 	int pos = 0;
262 	struct osdp_cmd cmd;
263 	struct osdp_event *event;
264 
265 	pd->reply_id = 0;
266 	pd->cmd_id = cmd.id = buf[pos++];
267 	len--;
268 
269 	switch (pd->cmd_id) {
270 	case CMD_POLL:
271 		if (len != CMD_POLL_DATA_LEN) {
272 			break;
273 		}
274 		/* Check if we have external events in the queue */
275 		if (pd_event_dequeue(pd, &event) == 0) {
276 			ret = pd_translate_event(pd, event);
277 			pd->reply_id = ret;
278 			pd_event_free(pd, event);
279 		} else {
280 			pd->reply_id = REPLY_ACK;
281 		}
282 		ret = OSDP_PD_ERR_NONE;
283 		break;
284 	case CMD_LSTAT:
285 		if (len != CMD_LSTAT_DATA_LEN) {
286 			break;
287 		}
288 		pd->reply_id = REPLY_LSTATR;
289 		ret = OSDP_PD_ERR_NONE;
290 		break;
291 	case CMD_ISTAT:
292 		if (len != CMD_ISTAT_DATA_LEN) {
293 			break;
294 		}
295 		if (!pd_cmd_cap_ok(pd, NULL)) {
296 			ret = OSDP_PD_ERR_REPLY;
297 			break;
298 		}
299 		pd->reply_id = REPLY_ISTATR;
300 		ret = OSDP_PD_ERR_NONE;
301 		break;
302 	case CMD_OSTAT:
303 		if (len != CMD_OSTAT_DATA_LEN) {
304 			break;
305 		}
306 		if (!pd_cmd_cap_ok(pd, NULL)) {
307 			ret = OSDP_PD_ERR_REPLY;
308 			break;
309 		}
310 		pd->reply_id = REPLY_OSTATR;
311 		ret = OSDP_PD_ERR_NONE;
312 		break;
313 	case CMD_RSTAT:
314 		if (len != CMD_RSTAT_DATA_LEN) {
315 			break;
316 		}
317 		pd->reply_id = REPLY_RSTATR;
318 		ret = OSDP_PD_ERR_NONE;
319 		break;
320 	case CMD_ID:
321 		if (len != CMD_ID_DATA_LEN) {
322 			break;
323 		}
324 		pos++;		/* Skip reply type info. */
325 		pd->reply_id = REPLY_PDID;
326 		ret = OSDP_PD_ERR_NONE;
327 		break;
328 	case CMD_CAP:
329 		if (len != CMD_CAP_DATA_LEN) {
330 			break;
331 		}
332 		pos++;		/* Skip reply type info. */
333 		pd->reply_id = REPLY_PDCAP;
334 		ret = OSDP_PD_ERR_NONE;
335 		break;
336 	case CMD_OUT:
337 		if (len != CMD_OUT_DATA_LEN || !pd->command_callback) {
338 			break;
339 		}
340 		cmd.id = OSDP_CMD_OUTPUT;
341 		cmd.output.output_no = buf[pos++];
342 		cmd.output.control_code = buf[pos++];
343 		cmd.output.timer_count = buf[pos++];
344 		cmd.output.timer_count |= buf[pos++] << 8;
345 		ret = OSDP_PD_ERR_REPLY;
346 		if (!pd_cmd_cap_ok(pd, &cmd)) {
347 			break;
348 		}
349 		if (!do_command_callback(pd, &cmd)) {
350 			break;
351 		}
352 		pd->reply_id = REPLY_ACK;
353 		ret = OSDP_PD_ERR_NONE;
354 		break;
355 	case CMD_LED:
356 		if (len != CMD_LED_DATA_LEN || !pd->command_callback) {
357 			break;
358 		}
359 		cmd.id = OSDP_CMD_LED;
360 		cmd.led.reader = buf[pos++];
361 		cmd.led.led_number = buf[pos++];
362 
363 		cmd.led.temporary.control_code = buf[pos++];
364 		cmd.led.temporary.on_count = buf[pos++];
365 		cmd.led.temporary.off_count = buf[pos++];
366 		cmd.led.temporary.on_color = buf[pos++];
367 		cmd.led.temporary.off_color = buf[pos++];
368 		cmd.led.temporary.timer_count = buf[pos++];
369 		cmd.led.temporary.timer_count |= buf[pos++] << 8;
370 
371 		cmd.led.permanent.control_code = buf[pos++];
372 		cmd.led.permanent.on_count = buf[pos++];
373 		cmd.led.permanent.off_count = buf[pos++];
374 		cmd.led.permanent.on_color = buf[pos++];
375 		cmd.led.permanent.off_color = buf[pos++];
376 		ret = OSDP_PD_ERR_REPLY;
377 		if (!pd_cmd_cap_ok(pd, &cmd)) {
378 			break;
379 		}
380 		if (!do_command_callback(pd, &cmd)) {
381 			break;
382 		}
383 		pd->reply_id = REPLY_ACK;
384 		ret = OSDP_PD_ERR_NONE;
385 		break;
386 	case CMD_BUZ:
387 		if (len != CMD_BUZ_DATA_LEN || !pd->command_callback) {
388 			break;
389 		}
390 		cmd.id = OSDP_CMD_BUZZER;
391 		cmd.buzzer.reader = buf[pos++];
392 		cmd.buzzer.control_code = buf[pos++];
393 		cmd.buzzer.on_count = buf[pos++];
394 		cmd.buzzer.off_count = buf[pos++];
395 		cmd.buzzer.rep_count = buf[pos++];
396 		ret = OSDP_PD_ERR_REPLY;
397 		if (!pd_cmd_cap_ok(pd, &cmd)) {
398 			break;
399 		}
400 		if (!do_command_callback(pd, &cmd)) {
401 			break;
402 		}
403 		pd->reply_id = REPLY_ACK;
404 		ret = OSDP_PD_ERR_NONE;
405 		break;
406 	case CMD_TEXT:
407 		if (len < CMD_TEXT_DATA_LEN || !pd->command_callback) {
408 			break;
409 		}
410 		cmd.id = OSDP_CMD_TEXT;
411 		cmd.text.reader = buf[pos++];
412 		cmd.text.control_code = buf[pos++];
413 		cmd.text.temp_time = buf[pos++];
414 		cmd.text.offset_row = buf[pos++];
415 		cmd.text.offset_col = buf[pos++];
416 		cmd.text.length = buf[pos++];
417 		if (cmd.text.length > OSDP_CMD_TEXT_MAX_LEN ||
418 		    ((len - CMD_TEXT_DATA_LEN) < cmd.text.length) ||
419 		    cmd.text.length > OSDP_CMD_TEXT_MAX_LEN) {
420 			break;
421 		}
422 		memcpy(cmd.text.data, buf + pos, cmd.text.length);
423 		ret = OSDP_PD_ERR_REPLY;
424 		if (!pd_cmd_cap_ok(pd, &cmd)) {
425 			break;
426 		}
427 		if (!do_command_callback(pd, &cmd)) {
428 			break;
429 		}
430 		pd->reply_id = REPLY_ACK;
431 		ret = OSDP_PD_ERR_NONE;
432 		break;
433 	case CMD_COMSET:
434 		if (len != CMD_COMSET_DATA_LEN || !pd->command_callback) {
435 			break;
436 		}
437 		cmd.id = OSDP_CMD_COMSET;
438 		cmd.comset.address = buf[pos++];
439 		cmd.comset.baud_rate = buf[pos++];
440 		cmd.comset.baud_rate |= buf[pos++] << 8;
441 		cmd.comset.baud_rate |= buf[pos++] << 16;
442 		cmd.comset.baud_rate |= buf[pos++] << 24;
443 		if (cmd.comset.address >= 0x7F ||
444 		    (cmd.comset.baud_rate != 9600 &&
445 		     cmd.comset.baud_rate != 19200 &&
446 		     cmd.comset.baud_rate != 38400 &&
447 		     cmd.comset.baud_rate != 115200 &&
448 		     cmd.comset.baud_rate != 230400)) {
449 			LOG_ERR("COMSET Failed! command discarded");
450 			cmd.comset.address = pd->address;
451 			cmd.comset.baud_rate = pd->baud_rate;
452 			break;
453 		}
454 		if (!do_command_callback(pd, &cmd)) {
455 			ret = OSDP_PD_ERR_REPLY;
456 			break;
457 		}
458 		memcpy(pd->ephemeral_data, &cmd, sizeof(struct osdp_cmd));
459 		pd->reply_id = REPLY_COM;
460 		ret = OSDP_PD_ERR_NONE;
461 		break;
462 #ifdef CONFIG_OSDP_SC_ENABLED
463 	case CMD_KEYSET:
464 		if (len != CMD_KEYSET_DATA_LEN) {
465 			break;
466 		}
467 		/* only key_type == 1 (SCBK) and key_len == 16 is supported */
468 		if (buf[pos] != 1 || buf[pos + 1] != 16) {
469 			LOG_ERR("Keyset invalid len/type: %d/%d",
470 				buf[pos], buf[pos + 1]);
471 			break;
472 		}
473 		ret = OSDP_PD_ERR_REPLY;
474 		if (!pd_cmd_cap_ok(pd, NULL)) {
475 			break;
476 		}
477 		/**
478 		 * For CMD_KEYSET to be accepted, PD must be
479 		 * ONLINE and SC_ACTIVE.
480 		 */
481 		if (!sc_is_active(pd)) {
482 			pd->reply_id = REPLY_NAK;
483 			pd->ephemeral_data[0] = OSDP_PD_NAK_SC_COND;
484 			LOG_ERR("Keyset with SC inactive");
485 			break;
486 		}
487 		cmd.id = OSDP_CMD_KEYSET;
488 		cmd.keyset.type = buf[pos++];
489 		cmd.keyset.length = buf[pos++];
490 		memcpy(cmd.keyset.data, buf + pos, 16);
491 		if (!pd->command_callback) {
492 			LOG_ERR("Keyset without a command callback! The SC new "
493 				"SCBK will be lost when the PD reboots.");
494 		} else if (!do_command_callback(pd, &cmd)) {
495 			break;
496 		}
497 		memcpy(pd->sc.scbk, cmd.keyset.data, 16);
498 		CLEAR_FLAG(pd, PD_FLAG_SC_USE_SCBKD);
499 		CLEAR_FLAG(pd, PD_FLAG_INSTALL_MODE);
500 		sc_deactivate(pd);
501 		pd->reply_id = REPLY_ACK;
502 		ret = OSDP_PD_ERR_NONE;
503 		break;
504 	case CMD_CHLNG:
505 		if (len != CMD_CHLNG_DATA_LEN) {
506 			break;
507 		}
508 		ret = OSDP_PD_ERR_REPLY;
509 		if (!pd_cmd_cap_ok(pd, NULL)) {
510 			break;
511 		}
512 		sc_deactivate(pd);
513 		osdp_sc_setup(pd);
514 		memcpy(pd->sc.cp_random, buf + pos, 8);
515 		pd->reply_id = REPLY_CCRYPT;
516 		ret = OSDP_PD_ERR_NONE;
517 		break;
518 	case CMD_SCRYPT:
519 		if (len != CMD_SCRYPT_DATA_LEN) {
520 			break;
521 		}
522 		ret = OSDP_PD_ERR_REPLY;
523 		if (!pd_cmd_cap_ok(pd, NULL)) {
524 			break;
525 		}
526 		if (sc_is_active(pd)) {
527 			pd->reply_id = REPLY_NAK;
528 			pd->ephemeral_data[0] = OSDP_PD_NAK_SC_COND;
529 			LOG_ERR("Out of order CMD_SCRYPT; has CP gone rogue?");
530 			break;
531 		}
532 		memcpy(pd->sc.cp_cryptogram, buf + pos, CMD_SCRYPT_DATA_LEN);
533 		pd->reply_id = REPLY_RMAC_I;
534 		ret = OSDP_PD_ERR_NONE;
535 		break;
536 #endif /* CONFIG_OSDP_SC_ENABLED */
537 	default:
538 		LOG_ERR("Unknown CMD(%02x)", pd->cmd_id);
539 		pd->reply_id = REPLY_NAK;
540 		pd->ephemeral_data[0] = OSDP_PD_NAK_CMD_UNKNOWN;
541 		return OSDP_PD_ERR_REPLY;
542 	}
543 
544 	if (ret == OSDP_PD_ERR_GENERIC) {
545 		LOG_ERR("Failed to decode command: CMD(%02x) Len:%d ret:%d",
546 			pd->cmd_id, len, ret);
547 		pd->reply_id = REPLY_NAK;
548 		pd->ephemeral_data[0] = OSDP_PD_NAK_CMD_LEN;
549 		ret = OSDP_PD_ERR_REPLY;
550 	}
551 
552 	if (pd->cmd_id != CMD_POLL) {
553 		LOG_DBG("CMD: %02x REPLY: %02x", pd->cmd_id, pd->reply_id);
554 	}
555 
556 	return ret;
557 }
558 
assert_buf_len(int need,int have)559 static inline void assert_buf_len(int need, int have)
560 {
561 	__ASSERT(need < have, "OOM at build command: need:%d have:%d",
562 		 need, have);
563 }
564 
565 /**
566  * Returns:
567  * +ve: length of command
568  * -ve: error
569  */
pd_build_reply(struct osdp_pd * pd,uint8_t * buf,int max_len)570 static int pd_build_reply(struct osdp_pd *pd, uint8_t *buf, int max_len)
571 {
572 	int ret = OSDP_PD_ERR_GENERIC;
573 	int i, len = 0;
574 	struct osdp_cmd *cmd;
575 	struct osdp_event *event;
576 	int data_off = osdp_phy_packet_get_data_offset(pd, buf);
577 #ifdef CONFIG_OSDP_SC_ENABLED
578 	uint8_t *smb = osdp_phy_packet_get_smb(pd, buf);
579 #endif
580 	buf += data_off;
581 	max_len -= data_off;
582 
583 	switch (pd->reply_id) {
584 	case REPLY_ACK:
585 		assert_buf_len(REPLY_ACK_LEN, max_len);
586 		buf[len++] = pd->reply_id;
587 		ret = OSDP_PD_ERR_NONE;
588 		break;
589 	case REPLY_PDID:
590 		assert_buf_len(REPLY_PDID_LEN, max_len);
591 		buf[len++] = pd->reply_id;
592 
593 		buf[len++] = BYTE_0(pd->id.vendor_code);
594 		buf[len++] = BYTE_1(pd->id.vendor_code);
595 		buf[len++] = BYTE_2(pd->id.vendor_code);
596 
597 		buf[len++] = pd->id.model;
598 		buf[len++] = pd->id.version;
599 
600 		buf[len++] = BYTE_0(pd->id.serial_number);
601 		buf[len++] = BYTE_1(pd->id.serial_number);
602 		buf[len++] = BYTE_2(pd->id.serial_number);
603 		buf[len++] = BYTE_3(pd->id.serial_number);
604 
605 		buf[len++] = BYTE_3(pd->id.firmware_version);
606 		buf[len++] = BYTE_2(pd->id.firmware_version);
607 		buf[len++] = BYTE_1(pd->id.firmware_version);
608 		ret = OSDP_PD_ERR_NONE;
609 		break;
610 	case REPLY_PDCAP:
611 		assert_buf_len(REPLY_PDCAP_LEN, max_len);
612 		buf[len++] = pd->reply_id;
613 		for (i = 1; i < OSDP_PD_CAP_SENTINEL; i++) {
614 			if (pd->cap[i].function_code != i) {
615 				continue;
616 			}
617 			if (max_len < REPLY_PDCAP_ENTITY_LEN) {
618 				LOG_ERR("Out of buffer space!");
619 				break;
620 			}
621 			buf[len++] = i;
622 			buf[len++] = pd->cap[i].compliance_level;
623 			buf[len++] = pd->cap[i].num_items;
624 			max_len -= REPLY_PDCAP_ENTITY_LEN;
625 		}
626 		ret = OSDP_PD_ERR_NONE;
627 		break;
628 	case REPLY_LSTATR:
629 		assert_buf_len(REPLY_LSTATR_LEN, max_len);
630 		buf[len++] = pd->reply_id;
631 		buf[len++] = ISSET_FLAG(pd, PD_FLAG_TAMPER);
632 		buf[len++] = ISSET_FLAG(pd, PD_FLAG_POWER);
633 		ret = OSDP_PD_ERR_NONE;
634 		break;
635 	case REPLY_RSTATR:
636 		assert_buf_len(REPLY_RSTATR_LEN, max_len);
637 		buf[len++] = pd->reply_id;
638 		buf[len++] = ISSET_FLAG(pd, PD_FLAG_R_TAMPER);
639 		ret = OSDP_PD_ERR_NONE;
640 		break;
641 	case REPLY_KEYPPAD:
642 		event = (struct osdp_event *)pd->ephemeral_data;
643 		assert_buf_len(REPLY_KEYPAD_LEN + event->keypress.length, max_len);
644 		buf[len++] = pd->reply_id;
645 		buf[len++] = (uint8_t)event->keypress.reader_no;
646 		buf[len++] = (uint8_t)event->keypress.length;
647 		memcpy(buf + len, event->keypress.data, event->keypress.length);
648 		len += event->keypress.length;
649 		ret = OSDP_PD_ERR_NONE;
650 		break;
651 	case REPLY_RAW: {
652 		int len_bytes;
653 
654 		event = (struct osdp_event *)pd->ephemeral_data;
655 		len_bytes = (event->cardread.length + 7) / 8;
656 		assert_buf_len(REPLY_RAW_LEN + len_bytes, max_len);
657 		buf[len++] = pd->reply_id;
658 		buf[len++] = (uint8_t)event->cardread.reader_no;
659 		buf[len++] = (uint8_t)event->cardread.format;
660 		buf[len++] = BYTE_0(event->cardread.length);
661 		buf[len++] = BYTE_1(event->cardread.length);
662 		memcpy(buf + len, event->cardread.data, len_bytes);
663 		len += len_bytes;
664 		ret = OSDP_PD_ERR_NONE;
665 		break;
666 	}
667 	case REPLY_FMT:
668 		event = (struct osdp_event *)pd->ephemeral_data;
669 		assert_buf_len(REPLY_FMT_LEN + event->cardread.length, max_len);
670 		buf[len++] = pd->reply_id;
671 		buf[len++] = (uint8_t)event->cardread.reader_no;
672 		buf[len++] = (uint8_t)event->cardread.direction;
673 		buf[len++] = (uint8_t)event->cardread.length;
674 		memcpy(buf + len, event->cardread.data, event->cardread.length);
675 		len += event->cardread.length;
676 		ret = OSDP_PD_ERR_NONE;
677 		break;
678 	case REPLY_COM:
679 		assert_buf_len(REPLY_COM_LEN, max_len);
680 		/**
681 		 * If COMSET succeeds, the PD must reply with the old params and
682 		 * then switch to the new params from then then on. We have the
683 		 * new params in the commands struct that we just enqueued so
684 		 * we can peek at tail of command queue and set that to
685 		 * pd->addr/pd->baud_rate.
686 		 *
687 		 * TODO: Persist pd->address and pd->baud_rate via
688 		 * subsys/settings
689 		 */
690 		cmd = (struct osdp_cmd *)pd->ephemeral_data;
691 		buf[len++] = pd->reply_id;
692 		buf[len++] = cmd->comset.address;
693 		buf[len++] = BYTE_0(cmd->comset.baud_rate);
694 		buf[len++] = BYTE_1(cmd->comset.baud_rate);
695 		buf[len++] = BYTE_2(cmd->comset.baud_rate);
696 		buf[len++] = BYTE_3(cmd->comset.baud_rate);
697 
698 		pd->address = (int)cmd->comset.address;
699 		pd->baud_rate = (int)cmd->comset.baud_rate;
700 		LOG_INF("COMSET Succeeded! New PD-Addr: %d; Baud: %d",
701 			pd->address, pd->baud_rate);
702 		ret = OSDP_PD_ERR_NONE;
703 		break;
704 	case REPLY_NAK:
705 		assert_buf_len(REPLY_NAK_LEN, max_len);
706 		buf[len++] = pd->reply_id;
707 		buf[len++] = pd->ephemeral_data[0];
708 		ret = OSDP_PD_ERR_NONE;
709 		break;
710 #ifdef CONFIG_OSDP_SC_ENABLED
711 	case REPLY_CCRYPT:
712 		if (smb == NULL) {
713 			break;
714 		}
715 		assert_buf_len(REPLY_CCRYPT_LEN, max_len);
716 		osdp_fill_random(pd->sc.pd_random, 8);
717 		osdp_compute_session_keys(pd);
718 		osdp_compute_pd_cryptogram(pd);
719 		buf[len++] = pd->reply_id;
720 		memcpy(buf + len, pd->sc.pd_client_uid, 8);
721 		memcpy(buf + len + 8, pd->sc.pd_random, 8);
722 		memcpy(buf + len + 16, pd->sc.pd_cryptogram, 16);
723 		len += 32;
724 		smb[0] = 3;      /* length */
725 		smb[1] = SCS_12; /* type */
726 		smb[2] = ISSET_FLAG(pd, PD_FLAG_SC_USE_SCBKD) ? 0 : 1;
727 		ret = OSDP_PD_ERR_NONE;
728 		break;
729 	case REPLY_RMAC_I:
730 		if (smb == NULL) {
731 			break;
732 		}
733 		assert_buf_len(REPLY_RMAC_I_LEN, max_len);
734 		osdp_compute_rmac_i(pd);
735 		buf[len++] = pd->reply_id;
736 		memcpy(buf + len, pd->sc.r_mac, 16);
737 		len += 16;
738 		smb[0] = 3;       /* length */
739 		smb[1] = SCS_14;  /* type */
740 		if (osdp_verify_cp_cryptogram(pd) == 0) {
741 			smb[2] = 1;  /* CP auth succeeded */
742 			sc_activate(pd);
743 			pd->sc_tstamp = osdp_millis_now();
744 			if (ISSET_FLAG(pd, PD_FLAG_SC_USE_SCBKD)) {
745 				LOG_WRN("SC Active with SCBK-D");
746 			} else {
747 				LOG_INF("SC Active");
748 			}
749 		} else {
750 			smb[2] = 0;  /* CP auth failed */
751 			LOG_WRN("failed to verify CP_crypt");
752 		}
753 		ret = OSDP_PD_ERR_NONE;
754 		break;
755 #endif /* CONFIG_OSDP_SC_ENABLED */
756 	}
757 
758 #ifdef CONFIG_OSDP_SC_ENABLED
759 	if (smb && (smb[1] > SCS_14) && sc_is_active(pd)) {
760 		smb[0] = 2; /* length */
761 		smb[1] = (len > 1) ? SCS_18 : SCS_16;
762 	}
763 #endif /* CONFIG_OSDP_SC_ENABLED */
764 
765 	if (ret != 0) {
766 		/* catch all errors and report it as a RECORD error to CP */
767 		LOG_ERR("Failed to build REPLY(%02x); Sending NAK instead!",
768 			pd->reply_id);
769 		assert_buf_len(REPLY_NAK_LEN, max_len);
770 		buf[0] = REPLY_NAK;
771 		buf[1] = OSDP_PD_NAK_RECORD;
772 		len = 2;
773 	}
774 
775 	return len;
776 }
777 
pd_send_reply(struct osdp_pd * pd)778 static int pd_send_reply(struct osdp_pd *pd)
779 {
780 	int ret, len;
781 
782 	/* init packet buf with header */
783 	len = osdp_phy_packet_init(pd, pd->rx_buf, sizeof(pd->rx_buf));
784 	if (len < 0) {
785 		return OSDP_PD_ERR_GENERIC;
786 	}
787 
788 	/* fill reply data */
789 	ret = pd_build_reply(pd, pd->rx_buf, sizeof(pd->rx_buf));
790 	if (ret <= 0) {
791 		return OSDP_PD_ERR_GENERIC;
792 	}
793 	len += ret;
794 
795 	/* finalize packet */
796 	len = osdp_phy_packet_finalize(pd, pd->rx_buf, len, sizeof(pd->rx_buf));
797 	if (len < 0) {
798 		return OSDP_PD_ERR_GENERIC;
799 	}
800 
801 	/* flush rx to remove any invalid data. */
802 	if (pd->channel.flush) {
803 		pd->channel.flush(pd->channel.data);
804 	}
805 
806 	ret = pd->channel.send(pd->channel.data, pd->rx_buf, len);
807 	if (ret != len) {
808 		LOG_ERR("Channel send for %d bytes failed! ret: %d", len, ret);
809 		return OSDP_PD_ERR_GENERIC;
810 	}
811 
812 	if (IS_ENABLED(CONFIG_OSDP_PACKET_TRACE)) {
813 		if (pd->cmd_id != CMD_POLL) {
814 			osdp_dump("PD sent", pd->rx_buf, len);
815 		}
816 	}
817 
818 	return OSDP_PD_ERR_NONE;
819 }
820 
pd_decode_packet(struct osdp_pd * pd,int * one_pkt_len)821 static int pd_decode_packet(struct osdp_pd *pd, int *one_pkt_len)
822 {
823 	int err, len;
824 	uint8_t *buf;
825 
826 	err = osdp_phy_check_packet(pd, pd->rx_buf, pd->rx_buf_len, one_pkt_len);
827 
828 	/* Translate phy error codes to PD errors */
829 	switch (err) {
830 	case OSDP_ERR_PKT_NONE:
831 		break;
832 	case OSDP_ERR_PKT_NACK:
833 		return OSDP_PD_ERR_REPLY;
834 	case OSDP_ERR_PKT_WAIT:
835 		return OSDP_PD_ERR_NO_DATA;
836 	case OSDP_ERR_PKT_SKIP:
837 		return OSDP_PD_ERR_IGNORE;
838 	case OSDP_ERR_PKT_FMT:
839 		return OSDP_PD_ERR_GENERIC;
840 	default:
841 		return err; /* propagate other errors as-is */
842 	}
843 
844 	len = osdp_phy_decode_packet(pd, pd->rx_buf, *one_pkt_len, &buf);
845 	if (len <= 0) {
846 		if (len == OSDP_ERR_PKT_NACK) {
847 			return OSDP_PD_ERR_REPLY; /* Send a NAK */
848 		}
849 		return OSDP_PD_ERR_GENERIC; /* fatal errors */
850 	}
851 
852 	return pd_decode_command(pd, buf, len);
853 }
854 
pd_receive_and_process_command(struct osdp_pd * pd)855 static int pd_receive_and_process_command(struct osdp_pd *pd)
856 {
857 	uint8_t *buf;
858 	int len, err, remaining;
859 
860 	buf = pd->rx_buf + pd->rx_buf_len;
861 	remaining = sizeof(pd->rx_buf) - pd->rx_buf_len;
862 
863 	len = pd->channel.recv(pd->channel.data, buf, remaining);
864 	if (len <= 0) { /* No data received */
865 		return OSDP_PD_ERR_NO_DATA;
866 	}
867 
868 	/**
869 	 * We received some data on the bus; update pd->tstamp. A rouge CP can
870 	 * send one byte at a time to extend this command's window but that
871 	 * shouldn't cause any issues related to secure channel as it has it's
872 	 * own timestamp.
873 	 */
874 	pd->tstamp = osdp_millis_now();
875 	pd->rx_buf_len += len;
876 
877 	if (IS_ENABLED(CONFIG_OSDP_PACKET_TRACE)) {
878 		/**
879 		 * A crude way of identifying and not printing poll messages
880 		 * when CONFIG_OSDP_PACKET_TRACE is enabled. This is an early
881 		 * print to catch errors so keeping it simple.
882 		 */
883 		if (pd->rx_buf_len > 8 &&
884 		    pd->rx_buf[6] != CMD_POLL && pd->rx_buf[8] != CMD_POLL) {
885 			osdp_dump("PD received", pd->rx_buf, pd->rx_buf_len);
886 		}
887 	}
888 
889 	err = pd_decode_packet(pd, &len);
890 
891 	if (err == OSDP_PD_ERR_NO_DATA) {
892 		return err;
893 	}
894 
895 	/* We are done with the packet (error or not). Remove processed bytes */
896 	remaining = pd->rx_buf_len - len;
897 	if (remaining) {
898 		memmove(pd->rx_buf, pd->rx_buf + len, remaining);
899 	}
900 
901 	/**
902 	 * Store remaining length that needs to be processed.
903 	 * State machine will be updated accordingly.
904 	 */
905 	pd->rx_buf_len = remaining;
906 
907 	return err;
908 }
909 
pd_error_reset(struct osdp_pd * pd)910 static inline void pd_error_reset(struct osdp_pd *pd)
911 {
912 	sc_deactivate(pd);
913 	if (pd->channel.flush) {
914 		pd->channel.flush(pd->channel.data);
915 	}
916 	pd->rx_buf_len = 0;
917 }
918 
osdp_update(struct osdp * ctx)919 void osdp_update(struct osdp *ctx)
920 {
921 	int ret;
922 	struct osdp_pd *pd = osdp_to_pd(ctx, 0);
923 
924 #ifdef CONFIG_OSDP_SC_ENABLED
925 	/**
926 	 * If secure channel is established, we need to make sure that
927 	 * the session is valid before accepting a command.
928 	 */
929 	if (sc_is_active(pd) &&
930 	    osdp_millis_since(pd->sc_tstamp) > OSDP_PD_SC_TIMEOUT_MS) {
931 		LOG_INF("PD SC session timeout!");
932 		sc_deactivate(pd);
933 	}
934 #endif
935 
936 	ret = pd_receive_and_process_command(pd);
937 
938 	if (ret == OSDP_PD_ERR_IGNORE) {
939 		return;
940 	}
941 
942 	if (ret == OSDP_PD_ERR_NO_DATA) {
943 		if (pd->rx_buf_len == 0 ||
944 		    osdp_millis_since(pd->tstamp) < OSDP_RESP_TOUT_MS) {
945 			return;
946 		}
947 		LOG_DBG("rx_buf: %d", pd->rx_buf_len);
948 		osdp_dump("Buf", pd->rx_buf, pd->rx_buf_len);
949 	}
950 
951 	if (ret != OSDP_PD_ERR_NONE && ret != OSDP_PD_ERR_REPLY) {
952 		LOG_ERR("CMD receive error/timeout - err:%d", ret);
953 		pd_error_reset(pd);
954 		return;
955 	}
956 
957 #ifdef CONFIG_OSDP_SC_ENABLED
958 	if (ret == OSDP_PD_ERR_NONE && sc_is_active(pd)) {
959 		pd->sc_tstamp = osdp_millis_now();
960 	}
961 #endif
962 
963 	ret = pd_send_reply(pd);
964 	if (ret != OSDP_PD_ERR_NONE) {
965 		/**
966 		 * PD received and decoded a valid command from CP but failed to
967 		 * send the intended response?? This should not happen; but if
968 		 * it did, we cannot do anything about it, just complain about
969 		 * it and limp back home.
970 		 */
971 		LOG_WRN("REPLY send failed! CP may be waiting..");
972 		return;
973 	}
974 
975 }
976 
osdp_pd_set_attributes(struct osdp_pd * pd,struct osdp_pd_cap * cap,struct osdp_pd_id * id)977 static void osdp_pd_set_attributes(struct osdp_pd *pd, struct osdp_pd_cap *cap,
978 				   struct osdp_pd_id *id)
979 {
980 	int fc;
981 
982 	while (cap && ((fc = cap->function_code) > 0)) {
983 		if (fc >= OSDP_PD_CAP_SENTINEL) {
984 			break;
985 		}
986 		pd->cap[fc].function_code = cap->function_code;
987 		pd->cap[fc].compliance_level = cap->compliance_level;
988 		pd->cap[fc].num_items = cap->num_items;
989 		cap++;
990 	}
991 	if (id != NULL) {
992 		memcpy(&pd->id, id, sizeof(struct osdp_pd_id));
993 	}
994 }
995 
osdp_setup(struct osdp * ctx,uint8_t * key)996 int osdp_setup(struct osdp *ctx, uint8_t *key)
997 {
998 	ARG_UNUSED(key);
999 	struct osdp_pd *pd;
1000 
1001 	if (NUM_PD(ctx) != 1) {
1002 		return -1;
1003 	}
1004 	pd = osdp_to_pd(ctx, 0);
1005 	osdp_pd_set_attributes(pd, osdp_pd_cap, &osdp_pd_id);
1006 	SET_FLAG(pd, PD_FLAG_PD_MODE);
1007 #ifdef CONFIG_OSDP_SC_ENABLED
1008 	if (key == NULL) {
1009 		LOG_WRN("SCBK not provided. PD is in INSTALL_MODE");
1010 		SET_FLAG(pd, PD_FLAG_INSTALL_MODE);
1011 	} else {
1012 		memcpy(pd->sc.scbk, key, 16);
1013 	}
1014 	SET_FLAG(pd, PD_FLAG_SC_CAPABLE);
1015 #endif
1016 	return 0;
1017 }
1018 
1019 /* --- Exported Methods --- */
1020 
osdp_pd_set_command_callback(pd_command_callback_t cb,void * arg)1021 void osdp_pd_set_command_callback(pd_command_callback_t cb, void *arg)
1022 {
1023 	struct osdp_pd *pd = osdp_to_pd(osdp_get_ctx(), 0);
1024 
1025 	pd->command_callback_arg = arg;
1026 	pd->command_callback = cb;
1027 }
1028 
osdp_pd_notify_event(const struct osdp_event * event)1029 int osdp_pd_notify_event(const struct osdp_event *event)
1030 {
1031 	struct osdp_event *ev;
1032 	struct osdp_pd *pd = osdp_to_pd(osdp_get_ctx(), 0);
1033 
1034 	ev = pd_event_alloc(pd);
1035 	if (ev == NULL) {
1036 		return -1;
1037 	}
1038 
1039 	memcpy(ev, event, sizeof(struct osdp_event));
1040 	pd_event_enqueue(pd, ev);
1041 	return 0;
1042 }
1043