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
check_buf_len(int need,int have)559 static inline bool check_buf_len(int need, int have)
560 {
561 if (need > have) {
562 LOG_ERR("OOM at build reply: need:%d have:%d", need, have);
563 return false;
564 }
565 return true;
566 }
567
568 /**
569 * Returns:
570 * +ve: length of command
571 * -ve: error
572 */
pd_build_reply(struct osdp_pd * pd,uint8_t * buf,int max_len)573 static int pd_build_reply(struct osdp_pd *pd, uint8_t *buf, int max_len)
574 {
575 int ret = OSDP_PD_ERR_GENERIC;
576 int i, len = 0;
577 struct osdp_cmd *cmd;
578 struct osdp_event *event;
579 int data_off = osdp_phy_packet_get_data_offset(pd, buf);
580 #ifdef CONFIG_OSDP_SC_ENABLED
581 uint8_t *smb = osdp_phy_packet_get_smb(pd, buf);
582 #endif
583 buf += data_off;
584 max_len -= data_off;
585
586 switch (pd->reply_id) {
587 case REPLY_ACK:
588 if (!check_buf_len(REPLY_ACK_LEN, max_len)) {
589 return OSDP_PD_ERR_GENERIC;
590 }
591 buf[len++] = pd->reply_id;
592 ret = OSDP_PD_ERR_NONE;
593 break;
594 case REPLY_PDID:
595 if (!check_buf_len(REPLY_PDID_LEN, max_len)) {
596 return OSDP_PD_ERR_GENERIC;
597 }
598 buf[len++] = pd->reply_id;
599
600 buf[len++] = BYTE_0(pd->id.vendor_code);
601 buf[len++] = BYTE_1(pd->id.vendor_code);
602 buf[len++] = BYTE_2(pd->id.vendor_code);
603
604 buf[len++] = pd->id.model;
605 buf[len++] = pd->id.version;
606
607 buf[len++] = BYTE_0(pd->id.serial_number);
608 buf[len++] = BYTE_1(pd->id.serial_number);
609 buf[len++] = BYTE_2(pd->id.serial_number);
610 buf[len++] = BYTE_3(pd->id.serial_number);
611
612 buf[len++] = BYTE_3(pd->id.firmware_version);
613 buf[len++] = BYTE_2(pd->id.firmware_version);
614 buf[len++] = BYTE_1(pd->id.firmware_version);
615 ret = OSDP_PD_ERR_NONE;
616 break;
617 case REPLY_PDCAP:
618 if (!check_buf_len(REPLY_PDCAP_LEN, max_len)) {
619 return OSDP_PD_ERR_GENERIC;
620 }
621 buf[len++] = pd->reply_id;
622 for (i = 1; i < OSDP_PD_CAP_SENTINEL; i++) {
623 if (pd->cap[i].function_code != i) {
624 continue;
625 }
626 if (max_len < REPLY_PDCAP_ENTITY_LEN) {
627 LOG_ERR("Out of buffer space!");
628 break;
629 }
630 buf[len++] = i;
631 buf[len++] = pd->cap[i].compliance_level;
632 buf[len++] = pd->cap[i].num_items;
633 max_len -= REPLY_PDCAP_ENTITY_LEN;
634 }
635 ret = OSDP_PD_ERR_NONE;
636 break;
637 case REPLY_LSTATR:
638 if (!check_buf_len(REPLY_LSTATR_LEN, max_len)) {
639 return OSDP_PD_ERR_GENERIC;
640 }
641 buf[len++] = pd->reply_id;
642 buf[len++] = ISSET_FLAG(pd, PD_FLAG_TAMPER);
643 buf[len++] = ISSET_FLAG(pd, PD_FLAG_POWER);
644 ret = OSDP_PD_ERR_NONE;
645 break;
646 case REPLY_RSTATR:
647 if (!check_buf_len(REPLY_RSTATR_LEN, max_len)) {
648 return OSDP_PD_ERR_GENERIC;
649 }
650 buf[len++] = pd->reply_id;
651 buf[len++] = ISSET_FLAG(pd, PD_FLAG_R_TAMPER);
652 ret = OSDP_PD_ERR_NONE;
653 break;
654 case REPLY_KEYPPAD:
655 event = (struct osdp_event *)pd->ephemeral_data;
656 if (!check_buf_len(REPLY_KEYPAD_LEN + event->keypress.length, max_len)) {
657 return OSDP_PD_ERR_GENERIC;
658 }
659 buf[len++] = pd->reply_id;
660 buf[len++] = (uint8_t)event->keypress.reader_no;
661 buf[len++] = (uint8_t)event->keypress.length;
662 memcpy(buf + len, event->keypress.data, event->keypress.length);
663 len += event->keypress.length;
664 ret = OSDP_PD_ERR_NONE;
665 break;
666 case REPLY_RAW: {
667 int len_bytes;
668
669 event = (struct osdp_event *)pd->ephemeral_data;
670 len_bytes = (event->cardread.length + 7) / 8;
671 if (!check_buf_len(REPLY_RAW_LEN + len_bytes, max_len)) {
672 return OSDP_PD_ERR_GENERIC;
673 }
674 buf[len++] = pd->reply_id;
675 buf[len++] = (uint8_t)event->cardread.reader_no;
676 buf[len++] = (uint8_t)event->cardread.format;
677 buf[len++] = BYTE_0(event->cardread.length);
678 buf[len++] = BYTE_1(event->cardread.length);
679 memcpy(buf + len, event->cardread.data, len_bytes);
680 len += len_bytes;
681 ret = OSDP_PD_ERR_NONE;
682 break;
683 }
684 case REPLY_FMT:
685 event = (struct osdp_event *)pd->ephemeral_data;
686 if (!check_buf_len(REPLY_FMT_LEN + event->cardread.length, max_len)) {
687 return OSDP_PD_ERR_GENERIC;
688 }
689 buf[len++] = pd->reply_id;
690 buf[len++] = (uint8_t)event->cardread.reader_no;
691 buf[len++] = (uint8_t)event->cardread.direction;
692 buf[len++] = (uint8_t)event->cardread.length;
693 memcpy(buf + len, event->cardread.data, event->cardread.length);
694 len += event->cardread.length;
695 ret = OSDP_PD_ERR_NONE;
696 break;
697 case REPLY_COM:
698 if (!check_buf_len(REPLY_COM_LEN, max_len)) {
699 return OSDP_PD_ERR_GENERIC;
700 }
701 /**
702 * If COMSET succeeds, the PD must reply with the old params and
703 * then switch to the new params from then then on. We have the
704 * new params in the commands struct that we just enqueued so
705 * we can peek at tail of command queue and set that to
706 * pd->addr/pd->baud_rate.
707 *
708 * TODO: Persist pd->address and pd->baud_rate via
709 * subsys/settings
710 */
711 cmd = (struct osdp_cmd *)pd->ephemeral_data;
712 buf[len++] = pd->reply_id;
713 buf[len++] = cmd->comset.address;
714 buf[len++] = BYTE_0(cmd->comset.baud_rate);
715 buf[len++] = BYTE_1(cmd->comset.baud_rate);
716 buf[len++] = BYTE_2(cmd->comset.baud_rate);
717 buf[len++] = BYTE_3(cmd->comset.baud_rate);
718
719 pd->address = (int)cmd->comset.address;
720 pd->baud_rate = (int)cmd->comset.baud_rate;
721 LOG_INF("COMSET Succeeded! New PD-Addr: %d; Baud: %d",
722 pd->address, pd->baud_rate);
723 ret = OSDP_PD_ERR_NONE;
724 break;
725 case REPLY_NAK:
726 if (!check_buf_len(REPLY_NAK_LEN, max_len)) {
727 return OSDP_PD_ERR_GENERIC;
728 }
729 buf[len++] = pd->reply_id;
730 buf[len++] = pd->ephemeral_data[0];
731 ret = OSDP_PD_ERR_NONE;
732 break;
733 #ifdef CONFIG_OSDP_SC_ENABLED
734 case REPLY_CCRYPT:
735 if (smb == NULL) {
736 break;
737 }
738 if (!check_buf_len(REPLY_CCRYPT_LEN, max_len)) {
739 return OSDP_PD_ERR_GENERIC;
740 }
741 osdp_fill_random(pd->sc.pd_random, 8);
742 osdp_compute_session_keys(pd);
743 osdp_compute_pd_cryptogram(pd);
744 buf[len++] = pd->reply_id;
745 memcpy(buf + len, pd->sc.pd_client_uid, 8);
746 memcpy(buf + len + 8, pd->sc.pd_random, 8);
747 memcpy(buf + len + 16, pd->sc.pd_cryptogram, 16);
748 len += 32;
749 smb[0] = 3; /* length */
750 smb[1] = SCS_12; /* type */
751 smb[2] = ISSET_FLAG(pd, PD_FLAG_SC_USE_SCBKD) ? 0 : 1;
752 ret = OSDP_PD_ERR_NONE;
753 break;
754 case REPLY_RMAC_I:
755 if (smb == NULL) {
756 break;
757 }
758 if (!check_buf_len(REPLY_RMAC_I_LEN, max_len)) {
759 return OSDP_PD_ERR_GENERIC;
760 }
761 osdp_compute_rmac_i(pd);
762 buf[len++] = pd->reply_id;
763 memcpy(buf + len, pd->sc.r_mac, 16);
764 len += 16;
765 smb[0] = 3; /* length */
766 smb[1] = SCS_14; /* type */
767 if (osdp_verify_cp_cryptogram(pd) == 0) {
768 smb[2] = 1; /* CP auth succeeded */
769 sc_activate(pd);
770 pd->sc_tstamp = osdp_millis_now();
771 if (ISSET_FLAG(pd, PD_FLAG_SC_USE_SCBKD)) {
772 LOG_WRN("SC Active with SCBK-D");
773 } else {
774 LOG_INF("SC Active");
775 }
776 } else {
777 smb[2] = 0; /* CP auth failed */
778 LOG_WRN("failed to verify CP_crypt");
779 }
780 ret = OSDP_PD_ERR_NONE;
781 break;
782 #endif /* CONFIG_OSDP_SC_ENABLED */
783 }
784
785 #ifdef CONFIG_OSDP_SC_ENABLED
786 if (smb && (smb[1] > SCS_14) && sc_is_active(pd)) {
787 smb[0] = 2; /* length */
788 smb[1] = (len > 1) ? SCS_18 : SCS_16;
789 }
790 #endif /* CONFIG_OSDP_SC_ENABLED */
791
792 if (ret != 0) {
793 /* catch all errors and report it as a RECORD error to CP */
794 LOG_ERR("Failed to build REPLY(%02x); Sending NAK instead!",
795 pd->reply_id);
796 if (!check_buf_len(REPLY_NAK_LEN, max_len)) {
797 return OSDP_PD_ERR_GENERIC;
798 }
799 buf[0] = REPLY_NAK;
800 buf[1] = OSDP_PD_NAK_RECORD;
801 len = 2;
802 }
803
804 return len;
805 }
806
pd_send_reply(struct osdp_pd * pd)807 static int pd_send_reply(struct osdp_pd *pd)
808 {
809 int ret, len;
810
811 /* init packet buf with header */
812 len = osdp_phy_packet_init(pd, pd->rx_buf, sizeof(pd->rx_buf));
813 if (len < 0) {
814 return OSDP_PD_ERR_GENERIC;
815 }
816
817 /* fill reply data */
818 ret = pd_build_reply(pd, pd->rx_buf, sizeof(pd->rx_buf));
819 if (ret <= 0) {
820 return OSDP_PD_ERR_GENERIC;
821 }
822 len += ret;
823
824 /* finalize packet */
825 len = osdp_phy_packet_finalize(pd, pd->rx_buf, len, sizeof(pd->rx_buf));
826 if (len < 0) {
827 return OSDP_PD_ERR_GENERIC;
828 }
829
830 /* flush rx to remove any invalid data. */
831 if (pd->channel.flush) {
832 pd->channel.flush(pd->channel.data);
833 }
834
835 ret = pd->channel.send(pd->channel.data, pd->rx_buf, len);
836 if (ret != len) {
837 LOG_ERR("Channel send for %d bytes failed! ret: %d", len, ret);
838 return OSDP_PD_ERR_GENERIC;
839 }
840
841 if (IS_ENABLED(CONFIG_OSDP_PACKET_TRACE)) {
842 if (pd->cmd_id != CMD_POLL) {
843 osdp_dump("PD sent", pd->rx_buf, len);
844 }
845 }
846
847 return OSDP_PD_ERR_NONE;
848 }
849
pd_decode_packet(struct osdp_pd * pd,int * one_pkt_len)850 static int pd_decode_packet(struct osdp_pd *pd, int *one_pkt_len)
851 {
852 int err, len;
853 uint8_t *buf;
854
855 err = osdp_phy_check_packet(pd, pd->rx_buf, pd->rx_buf_len, one_pkt_len);
856
857 /* Translate phy error codes to PD errors */
858 switch (err) {
859 case OSDP_ERR_PKT_NONE:
860 break;
861 case OSDP_ERR_PKT_NACK:
862 return OSDP_PD_ERR_REPLY;
863 case OSDP_ERR_PKT_WAIT:
864 return OSDP_PD_ERR_NO_DATA;
865 case OSDP_ERR_PKT_SKIP:
866 return OSDP_PD_ERR_IGNORE;
867 case OSDP_ERR_PKT_FMT:
868 return OSDP_PD_ERR_GENERIC;
869 default:
870 return err; /* propagate other errors as-is */
871 }
872
873 len = osdp_phy_decode_packet(pd, pd->rx_buf, *one_pkt_len, &buf);
874 if (len <= 0) {
875 if (len == OSDP_ERR_PKT_NACK) {
876 return OSDP_PD_ERR_REPLY; /* Send a NAK */
877 }
878 return OSDP_PD_ERR_GENERIC; /* fatal errors */
879 }
880
881 return pd_decode_command(pd, buf, len);
882 }
883
pd_receive_and_process_command(struct osdp_pd * pd)884 static int pd_receive_and_process_command(struct osdp_pd *pd)
885 {
886 uint8_t *buf;
887 int len, err, remaining;
888
889 buf = pd->rx_buf + pd->rx_buf_len;
890 remaining = sizeof(pd->rx_buf) - pd->rx_buf_len;
891
892 len = pd->channel.recv(pd->channel.data, buf, remaining);
893 if (len <= 0) { /* No data received */
894 return OSDP_PD_ERR_NO_DATA;
895 }
896
897 /**
898 * We received some data on the bus; update pd->tstamp. A rouge CP can
899 * send one byte at a time to extend this command's window but that
900 * shouldn't cause any issues related to secure channel as it has it's
901 * own timestamp.
902 */
903 pd->tstamp = osdp_millis_now();
904 pd->rx_buf_len += len;
905
906 if (IS_ENABLED(CONFIG_OSDP_PACKET_TRACE)) {
907 /**
908 * A crude way of identifying and not printing poll messages
909 * when CONFIG_OSDP_PACKET_TRACE is enabled. This is an early
910 * print to catch errors so keeping it simple.
911 */
912 if (pd->rx_buf_len > 8 &&
913 pd->rx_buf[6] != CMD_POLL && pd->rx_buf[8] != CMD_POLL) {
914 osdp_dump("PD received", pd->rx_buf, pd->rx_buf_len);
915 }
916 }
917
918 err = pd_decode_packet(pd, &len);
919
920 if (err == OSDP_PD_ERR_NO_DATA) {
921 return err;
922 }
923
924 /* We are done with the packet (error or not). Remove processed bytes */
925 remaining = pd->rx_buf_len - len;
926 if (remaining) {
927 memmove(pd->rx_buf, pd->rx_buf + len, remaining);
928 }
929
930 /**
931 * Store remaining length that needs to be processed.
932 * State machine will be updated accordingly.
933 */
934 pd->rx_buf_len = remaining;
935
936 return err;
937 }
938
pd_error_reset(struct osdp_pd * pd)939 static inline void pd_error_reset(struct osdp_pd *pd)
940 {
941 sc_deactivate(pd);
942 if (pd->channel.flush) {
943 pd->channel.flush(pd->channel.data);
944 }
945 pd->rx_buf_len = 0;
946 }
947
osdp_update(struct osdp * ctx)948 void osdp_update(struct osdp *ctx)
949 {
950 int ret;
951 struct osdp_pd *pd = osdp_to_pd(ctx, 0);
952
953 #ifdef CONFIG_OSDP_SC_ENABLED
954 /**
955 * If secure channel is established, we need to make sure that
956 * the session is valid before accepting a command.
957 */
958 if (sc_is_active(pd) &&
959 osdp_millis_since(pd->sc_tstamp) > OSDP_PD_SC_TIMEOUT_MS) {
960 LOG_INF("PD SC session timeout!");
961 sc_deactivate(pd);
962 }
963 #endif
964
965 ret = pd_receive_and_process_command(pd);
966
967 if (ret == OSDP_PD_ERR_IGNORE) {
968 return;
969 }
970
971 if (ret == OSDP_PD_ERR_NO_DATA) {
972 if (pd->rx_buf_len == 0 ||
973 osdp_millis_since(pd->tstamp) < OSDP_RESP_TOUT_MS) {
974 return;
975 }
976 LOG_DBG("rx_buf: %d", pd->rx_buf_len);
977 osdp_dump("Buf", pd->rx_buf, pd->rx_buf_len);
978 }
979
980 if (ret != OSDP_PD_ERR_NONE && ret != OSDP_PD_ERR_REPLY) {
981 LOG_ERR("CMD receive error/timeout - err:%d", ret);
982 pd_error_reset(pd);
983 return;
984 }
985
986 #ifdef CONFIG_OSDP_SC_ENABLED
987 if (ret == OSDP_PD_ERR_NONE && sc_is_active(pd)) {
988 pd->sc_tstamp = osdp_millis_now();
989 }
990 #endif
991
992 ret = pd_send_reply(pd);
993 if (ret != OSDP_PD_ERR_NONE) {
994 /**
995 * PD received and decoded a valid command from CP but failed to
996 * send the intended response?? This should not happen; but if
997 * it did, we cannot do anything about it, just complain about
998 * it and limp back home.
999 */
1000 LOG_WRN("REPLY send failed! CP may be waiting..");
1001 return;
1002 }
1003
1004 }
1005
osdp_pd_set_attributes(struct osdp_pd * pd,struct osdp_pd_cap * cap,struct osdp_pd_id * id)1006 static void osdp_pd_set_attributes(struct osdp_pd *pd, struct osdp_pd_cap *cap,
1007 struct osdp_pd_id *id)
1008 {
1009 int fc;
1010
1011 while (cap && ((fc = cap->function_code) > 0)) {
1012 if (fc >= OSDP_PD_CAP_SENTINEL) {
1013 break;
1014 }
1015 pd->cap[fc].function_code = cap->function_code;
1016 pd->cap[fc].compliance_level = cap->compliance_level;
1017 pd->cap[fc].num_items = cap->num_items;
1018 cap++;
1019 }
1020 if (id != NULL) {
1021 memcpy(&pd->id, id, sizeof(struct osdp_pd_id));
1022 }
1023 }
1024
osdp_setup(struct osdp * ctx,uint8_t * key)1025 int osdp_setup(struct osdp *ctx, uint8_t *key)
1026 {
1027 ARG_UNUSED(key);
1028 struct osdp_pd *pd;
1029
1030 if (NUM_PD(ctx) != 1) {
1031 return -1;
1032 }
1033 pd = osdp_to_pd(ctx, 0);
1034 osdp_pd_set_attributes(pd, osdp_pd_cap, &osdp_pd_id);
1035 SET_FLAG(pd, PD_FLAG_PD_MODE);
1036 #ifdef CONFIG_OSDP_SC_ENABLED
1037 if (key == NULL) {
1038 LOG_WRN("SCBK not provided. PD is in INSTALL_MODE");
1039 SET_FLAG(pd, PD_FLAG_INSTALL_MODE);
1040 } else {
1041 memcpy(pd->sc.scbk, key, 16);
1042 }
1043 SET_FLAG(pd, PD_FLAG_SC_CAPABLE);
1044 #endif
1045 return 0;
1046 }
1047
1048 /* --- Exported Methods --- */
1049
osdp_pd_set_command_callback(pd_command_callback_t cb,void * arg)1050 void osdp_pd_set_command_callback(pd_command_callback_t cb, void *arg)
1051 {
1052 struct osdp_pd *pd = osdp_to_pd(osdp_get_ctx(), 0);
1053
1054 pd->command_callback_arg = arg;
1055 pd->command_callback = cb;
1056 }
1057
osdp_pd_notify_event(const struct osdp_event * event)1058 int osdp_pd_notify_event(const struct osdp_event *event)
1059 {
1060 struct osdp_event *ev;
1061 struct osdp_pd *pd = osdp_to_pd(osdp_get_ctx(), 0);
1062
1063 ev = pd_event_alloc(pd);
1064 if (ev == NULL) {
1065 return -1;
1066 }
1067
1068 memcpy(ev, event, sizeof(struct osdp_event));
1069 pd_event_enqueue(pd, ev);
1070 return 0;
1071 }
1072