Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
address_book.c
Go to the documentation of this file.
1/*****************************************************************************
2 * (c) 2026 Ledger SAS.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *****************************************************************************/
16
43/* Includes ------------------------------------------------------------------*/
44#include <string.h>
45#include "os_apdu.h"
46#include "os_helpers.h"
47#include "os_print.h"
48#include "os_utils.h"
49#include "address_book.h"
50#include "status_words.h"
51#include "identity.h"
52#ifdef HAVE_ADDRESS_BOOK_LEDGER_ACCOUNT
53#include "ledger_account.h"
54#endif
55
56#if defined(HAVE_ADDRESS_BOOK)
57
58/* Private defines------------------------------------------------------------*/
59#define P1_REGISTER_IDENTITY 0x01
60#define P1_EDIT_CONTACT_NAME 0x02
61#define P1_EDIT_IDENTIFIER 0x03
62#define P1_EDIT_SCOPE 0x04
63#define P1_PROVIDE_CONTACT 0x20
64
65#ifdef HAVE_ADDRESS_BOOK_LEDGER_ACCOUNT
66#define P1_REGISTER_LEDGER_ACCOUNT 0x11
67#define P1_EDIT_LEDGER_ACCOUNT 0x12
68#define P1_PROVIDE_LEDGER_ACCOUNT_CONTACT 0x21
69#endif
70
72#define P2_FIRST_CHUNK 0x00
74#define P2_NEXT_CHUNK 0x80
75
86#ifdef CUSTOM_IO_APDU_BUFFER_SIZE
87#define ADDRESS_BOOK_MAX_CHUNKED_PAYLOAD (CUSTOM_IO_APDU_BUFFER_SIZE - 3 - 5)
88#else
89#define ADDRESS_BOOK_MAX_CHUNKED_PAYLOAD (OS_IO_SEPH_BUFFER_SIZE - 3 - 5)
90#endif
91
92/* Private types, structures, unions -----------------------------------------*/
93typedef enum {
94 REASSEMBLY_PENDING,
95 REASSEMBLY_COMPLETE,
96 REASSEMBLY_ERROR,
97} reassembly_status_t;
98
99/* Private variables ---------------------------------------------------------*/
100static uint8_t s_chunk_buf[ADDRESS_BOOK_MAX_CHUNKED_PAYLOAD] = {0};
101static uint16_t s_chunk_total = 0;
102static uint16_t s_chunk_received = 0;
103
104/* Private functions ---------------------------------------------------------*/
105
120static reassembly_status_t reassemble_chunks(uint8_t *data,
121 size_t len,
122 uint8_t p2,
123 uint8_t **out_buf,
124 size_t *out_len)
125{
126 if (p2 == P2_FIRST_CHUNK) {
127 if (len < 2) {
128 PRINTF("[Address Book] First chunk too short (no length header)\n");
129 return REASSEMBLY_ERROR;
130 }
131 s_chunk_total = U2BE(data, 0);
132 s_chunk_received = 0;
133 data += 2;
134 len -= 2;
135 if (s_chunk_total == 0 || s_chunk_total > sizeof(s_chunk_buf)) {
136 PRINTF("[Address Book] Invalid total length: %u\n", s_chunk_total);
137 s_chunk_total = 0;
138 return REASSEMBLY_ERROR;
139 }
140 }
141 else {
142 if (s_chunk_total == 0) {
143 PRINTF("[Address Book] Unexpected continuation chunk\n");
144 return REASSEMBLY_ERROR;
145 }
146 }
147
148 if ((s_chunk_received > s_chunk_total) || (len > (s_chunk_total - s_chunk_received))) {
149 PRINTF(
150 "[Address Book] Chunk overflow: %u + %zu > %u\n", s_chunk_received, len, s_chunk_total);
151 s_chunk_total = 0;
152 return REASSEMBLY_ERROR;
153 }
154 memmove(s_chunk_buf + s_chunk_received, data, len);
155 s_chunk_received += (uint16_t) len;
156
157 if (s_chunk_received < s_chunk_total) {
158 return REASSEMBLY_PENDING;
159 }
160
161 *out_buf = s_chunk_buf;
162 *out_len = s_chunk_received;
163 s_chunk_total = 0;
164 return REASSEMBLY_COMPLETE;
165}
166
167/* Exported functions --------------------------------------------------------*/
168bolos_err_t addr_book_handle_apdu(uint8_t *buffer, size_t buffer_len, uint8_t p1, uint8_t p2)
169{
170 bolos_err_t err = SWO_CONDITIONS_NOT_SATISFIED;
171 uint8_t *payload = NULL;
172 size_t payload_len = 0;
173
174 // All sub-commands share the same chunked transport: reassemble the full
175 // payload first, then dispatch on P1. Intermediate chunks return success
176 // (an intermediate SW is sent) and wait for the next chunk.
177 switch (reassemble_chunks(buffer, buffer_len, p2, &payload, &payload_len)) {
178 case REASSEMBLY_ERROR:
179 return SWO_INCORRECT_DATA;
180 case REASSEMBLY_PENDING:
181 return SWO_SUCCESS;
182 case REASSEMBLY_COMPLETE:
183 break;
184 }
185
186 switch (p1) {
187 case P1_REGISTER_IDENTITY:
188 err = register_identity(payload, payload_len);
189 break;
190
191 case P1_EDIT_CONTACT_NAME:
192 err = edit_contact_name(payload, payload_len);
193 break;
194
195 case P1_EDIT_IDENTIFIER:
196 err = edit_identifier(payload, payload_len);
197 break;
198
199 case P1_EDIT_SCOPE:
200 err = edit_scope(payload, payload_len);
201 break;
202
203 case P1_PROVIDE_CONTACT:
204 err = provide_contact(payload, payload_len);
205 break;
206
207#ifdef HAVE_ADDRESS_BOOK_LEDGER_ACCOUNT
208 case P1_REGISTER_LEDGER_ACCOUNT:
209 err = register_ledger_account(payload, payload_len);
210 break;
211
212 case P1_EDIT_LEDGER_ACCOUNT:
213 err = edit_ledger_account(payload, payload_len);
214 break;
215
216 case P1_PROVIDE_LEDGER_ACCOUNT_CONTACT:
217 err = provide_ledger_account_contact(payload, payload_len);
218 break;
219#endif // HAVE_ADDRESS_BOOK_LEDGER_ACCOUNT
220
221 default:
222 break;
223 }
224
225 return err;
226}
227#endif // HAVE_ADDRESS_BOOK
bolos_err_t addr_book_handle_apdu(uint8_t *buffer, size_t buffer_len, uint8_t p1, uint8_t p2)
Register / Edit Contact Name / Edit Scope / Edit Identifier.
bolos_err_t provide_ledger_account_contact(uint8_t *buffer_in, size_t buffer_in_length)
bolos_err_t register_ledger_account(uint8_t *buffer_in, size_t buffer_in_length)
bolos_err_t edit_ledger_account(uint8_t *buffer_in, size_t buffer_in_length)