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