openaliro
Aliro reader: UWB/CCC core and ESP32-S3/C5/C6 port
Loading...
Searching...
No Matches
aliro_device_apdu.h
1// Device (User-Device) side of the Aliro Access-Protocol wire codec: the inverse
2// of aliro_apdu.c. Where aliro_apdu builds reader commands and parses device
3// responses, this parses the reader's AUTH0/AUTH1/EXCHANGE commands and builds
4// the device's AUTH0/AUTH1/EXCHANGE responses. Pure byte manipulation, no crypto
5// and no platform dependency, so it is host-KAT verifiable against the reader's
6// own builders/parsers (round-trip) and the recovered layouts.
7/*
8 * Copyright (c) 2026 asxeem
9 * SPDX-License-Identifier: ISC
10 *
11 * aliro_device_apdu — the initiator half of the credential-auth wire codec.
12 * Compiled only by the device build + host tests; the reader firmware never
13 * links it, so the reader image is unchanged.
14 *
15 * Provenance: clean-room. Byte layouts from the project's reverse-engineering
16 * notes and aliro_apdu.h; the code is original.
17 */
18#pragma once
19
20#include <stddef.h>
21#include <stdint.h>
22
23#include "aliro_apdu.h" /* TLV writer/reader, tags, INS bytes, BLE envelope */
24
25#ifdef __cplusplus
26extern "C" {
27#endif
28
29/* ---- parsed reader commands (device inbound) ---- */
30
31/* Fields parsed from an AUTH0Command TLV. All are mandatory on the wire. */
32struct aliro_auth0_command {
33 uint8_t exp_phase; /* tag 0x41: ExpeditedPhaseType (0 std, 1 fast-requested) */
34 uint8_t user_policy; /* tag 0x42: UserAuthenticationPolicy */
35 uint16_t version; /* tag 0x5C: protocol version, big-endian */
36 uint8_t reader_eph_pub[65]; /* tag 0x87 */
37 uint8_t txid[16]; /* tag 0x4C */
38 uint8_t reader_id[32]; /* tag 0x4D */
39};
40
41/* Fields parsed from an AUTH1Command TLV. */
42struct aliro_auth1_command {
43 uint8_t cred_type; /* tag 0x41: AccessCredentialType */
44 uint8_t reader_sig[64]; /* tag 0x9E: reader ECDSA r|s over the reader-usage transcript */
45};
46
47/* Fields parsed from the DECRYPTED EXCHANGE command plaintext. */
48struct aliro_exchange_command {
49 int have_status; /* tag 0x97 present */
50 uint16_t reader_status;
51 int ursk_ready; /* tag 0x98 present (URSK-ready trigger) */
52};
53
54/* Strip an ISO7816 short-form command wrapper "80 <ins> 00 00 Lc <data> [Le]".
55 * Sets *ins and points data (with data_len) at the Lc-length command body. A trailing
56 * Le byte (the reader always sends 0x00) is tolerated. Returns 0 on success, -1
57 * on a malformed/short APDU. */
58int aliro_apdu_unwrap(const uint8_t *apdu, size_t len, uint8_t *ins, const uint8_t **data,
59 size_t *data_len);
60
61/* Append a 2-byte ISO7816 status word (0x9000 = OK) to a response body in place.
62 * Returns 0, or -1 if cap cannot hold the two bytes. */
63int aliro_apdu_append_sw(uint8_t *buf, size_t *len, size_t cap, uint16_t sw);
64
65/* ---- command parsers (device inbound; input is the raw command TLV) ---- */
66int aliro_dev_parse_auth0_cmd(const uint8_t *tlv, size_t len, struct aliro_auth0_command *c);
67int aliro_dev_parse_auth1_cmd(const uint8_t *tlv, size_t len, struct aliro_auth1_command *c);
68int aliro_dev_parse_exchange_cmd(const uint8_t *plain, size_t len,
69 struct aliro_exchange_command *c);
70
71/* ---- response builders (device outbound; out receives the response TLV,
72 * WITHOUT the status word — the caller seals (AUTH1/EXCHANGE) and/or appends
73 * the SW via aliro_apdu_append_sw) ---- */
74
75/* AUTH0Response: device ephemeral pub (tag 0x86, 65) [+ cryptogram tag 0x9D, 64]. */
76int aliro_dev_build_auth0_resp(const uint8_t device_eph_pub[65], const uint8_t *cryptogram64,
77 uint8_t *out, size_t cap, size_t *out_len);
78
79/* AUTH1Response plaintext: device signature (tag 0x9E, 64) [+ device pub tag 0x5A, 65]. */
80int aliro_dev_build_auth1_resp(const uint8_t device_sig[64], const uint8_t *device_pub65,
81 uint8_t *out, size_t cap, size_t *out_len);
82
83/* EXCHANGE response plaintext body: [len_be16 = 0x0002][error_be16]. error 0 = URSK
84 * armed (the reader gates on body[2]==0 && body[3]==0). */
85int aliro_dev_build_exchange_resp(uint16_t error, uint8_t *out, size_t cap, size_t *out_len);
86
87#ifdef __cplusplus
88}
89#endif