Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
identity_provide_contact.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
40/* Includes ------------------------------------------------------------------*/
41#include <string.h>
42#include "os_utils.h"
43#include "status_words.h"
44#include "tlv_library.h"
45#include "buffer.h"
46#include "bip32.h"
47#include "address_book.h"
49#include "address_book_crypto.h"
50#include "address_book_common.h"
51#include "identity.h"
52
53#if defined(HAVE_ADDRESS_BOOK)
54
55/* Private defines------------------------------------------------------------*/
56#define STRUCT_VERSION 0x01
57
58/* Private types, structures, unions -----------------------------------------*/
59typedef struct {
60 identity_t *identity;
61 TLV_reception_t received_tags;
62 uint8_t hmac_proof[CX_SHA256_SIZE];
63 uint8_t hmac_rest[CX_SHA256_SIZE];
64 uint8_t group_handle[GROUP_HANDLE_SIZE];
65} s_provide_contact_ctx;
66
67/* Private macros-------------------------------------------------------------*/
68#define PROVIDE_CONTACT_TAGS(X) \
69 X(0x01, TAG_STRUCTURE_TYPE, handle_struct_type, ENFORCE_UNIQUE_TAG) \
70 X(0x02, TAG_STRUCTURE_VERSION, handle_struct_version, ENFORCE_UNIQUE_TAG) \
71 X(0xf0, TAG_CONTACT_NAME, handle_contact_name, ENFORCE_UNIQUE_TAG) \
72 X(0xf1, TAG_SCOPE, handle_scope, ENFORCE_UNIQUE_TAG) \
73 X(0xf2, TAG_ACCOUNT_IDENTIFIER, handle_identifier, ENFORCE_UNIQUE_TAG) \
74 X(0xf6, TAG_GROUP_HANDLE, handle_group_handle, ENFORCE_UNIQUE_TAG) \
75 X(0x23, TAG_CHAIN_ID, handle_chain_id, ENFORCE_UNIQUE_TAG) \
76 X(0x51, TAG_BLOCKCHAIN_FAMILY, handle_blockchain_family, ENFORCE_UNIQUE_TAG) \
77 X(0x29, TAG_HMAC_PROOF, handle_hmac_proof, ENFORCE_UNIQUE_TAG) \
78 X(0xf7, TAG_HMAC_REST, handle_hmac_rest, ENFORCE_UNIQUE_TAG)
79
80/* Private variables ---------------------------------------------------------*/
81
82/* Private functions ---------------------------------------------------------*/
83
91static bool handle_struct_type(const tlv_data_t *data, s_provide_contact_ctx *context)
92{
93 UNUSED(context);
94 if (!tlv_enforce_u8_value(data, TYPE_PROVIDE_CONTACT)) {
95 PRINTF("[Provide Contact] Invalid STRUCTURE_TYPE value\n");
96 return false;
97 }
98 return true;
99}
100
108static bool handle_struct_version(const tlv_data_t *data, s_provide_contact_ctx *context)
109{
110 UNUSED(context);
111 if (!tlv_enforce_u8_value(data, STRUCT_VERSION)) {
112 PRINTF("[Provide Contact] Invalid STRUCTURE_VERSION value\n");
113 return false;
114 }
115 return true;
116}
117
125static bool handle_contact_name(const tlv_data_t *data, s_provide_contact_ctx *context)
126{
127 if (!address_book_handle_printable_string(
128 data, context->identity->contact_name, sizeof(context->identity->contact_name))) {
129 PRINTF("[Provide Contact] CONTACT_NAME: failed to parse\n");
130 return false;
131 }
132 return true;
133}
134
142static bool handle_scope(const tlv_data_t *data, s_provide_contact_ctx *context)
143{
144 if (!address_book_handle_printable_string(
145 data, context->identity->scope, sizeof(context->identity->scope))) {
146 PRINTF("[Provide Contact] SCOPE: failed to parse\n");
147 return false;
148 }
149 return true;
150}
151
159static bool handle_identifier(const tlv_data_t *data, s_provide_contact_ctx *context)
160{
161 buffer_t buf = {0};
162 if (!get_buffer_from_tlv_data(data, &buf, 1, IDENTIFIER_MAX_LENGTH)) {
163 PRINTF("[Provide Contact] IDENTIFIER: failed to extract\n");
164 return false;
165 }
166 memmove(context->identity->identifier, buf.ptr, buf.size);
167 context->identity->identifier_len = (uint8_t) buf.size;
168 return true;
169}
170
178static bool handle_group_handle(const tlv_data_t *data, s_provide_contact_ctx *context)
179{
180 buffer_t buf = {0};
181 if (!get_buffer_from_tlv_data(data, &buf, GROUP_HANDLE_SIZE, GROUP_HANDLE_SIZE)) {
182 PRINTF("[Provide Contact] GROUP_HANDLE: invalid length (expected %d bytes)\n",
183 GROUP_HANDLE_SIZE);
184 return false;
185 }
186 memmove(context->group_handle, buf.ptr, GROUP_HANDLE_SIZE);
187 return true;
188}
189
197static bool handle_chain_id(const tlv_data_t *data, s_provide_contact_ctx *context)
198{
199 return address_book_handle_chain_id(data, &context->identity->chain_id);
200}
201
209static bool handle_blockchain_family(const tlv_data_t *data, s_provide_contact_ctx *context)
210{
211 return address_book_handle_blockchain_family(data, &context->identity->blockchain_family);
212}
213
221static bool handle_hmac_proof(const tlv_data_t *data, s_provide_contact_ctx *context)
222{
223 buffer_t buf = {0};
224 if (!get_buffer_from_tlv_data(data, &buf, CX_SHA256_SIZE, CX_SHA256_SIZE)) {
225 PRINTF("[Provide Contact] HMAC_PROOF: invalid length (expected %d bytes)\n",
226 CX_SHA256_SIZE);
227 return false;
228 }
229 memmove(context->hmac_proof, buf.ptr, CX_SHA256_SIZE);
230 return true;
231}
232
240static bool handle_hmac_rest(const tlv_data_t *data, s_provide_contact_ctx *context)
241{
242 buffer_t buf = {0};
243 if (!get_buffer_from_tlv_data(data, &buf, CX_SHA256_SIZE, CX_SHA256_SIZE)) {
244 PRINTF("[Provide Contact] HMAC_REST: invalid length (expected %d bytes)\n", CX_SHA256_SIZE);
245 return false;
246 }
247 memmove(context->hmac_rest, buf.ptr, CX_SHA256_SIZE);
248 return true;
249}
250
251DEFINE_TLV_PARSER(PROVIDE_CONTACT_TAGS, NULL, provide_contact_tlv_parser)
252
253
259static bool verify_fields(const s_provide_contact_ctx *context)
260{
261 bool result = TLV_CHECK_RECEIVED_TAGS(context->received_tags,
262 TAG_STRUCTURE_TYPE,
263 TAG_STRUCTURE_VERSION,
264 TAG_CONTACT_NAME,
265 TAG_SCOPE,
266 TAG_ACCOUNT_IDENTIFIER,
267 TAG_GROUP_HANDLE,
268 TAG_BLOCKCHAIN_FAMILY,
269 TAG_HMAC_PROOF,
270 TAG_HMAC_REST);
271 if (!result) {
272 PRINTF("[Provide Contact] Missing mandatory fields!\n");
273 return false;
274 }
275 if (context->identity->blockchain_family == FAMILY_ETHEREUM) {
276 result = TLV_CHECK_RECEIVED_TAGS(context->received_tags, TAG_CHAIN_ID);
277 if (!result) {
278 PRINTF("[Provide Contact] Missing CHAIN_ID for Ethereum family!\n");
279 return false;
280 }
281 }
282 return true;
283}
284
291static void print_payload(const s_provide_contact_ctx *context)
292{
293 UNUSED(context);
294 PRINTF("****************************************************************************\n");
295 PRINTF("[Provide Contact] - Retrieved Descriptor:\n");
296 PRINTF("[Provide Contact] - Contact name: %s\n", context->identity->contact_name);
297 if (context->identity->scope[0] != '\0') {
298 PRINTF("[Provide Contact] - Scope: %s\n", context->identity->scope);
299 }
300 PRINTF("[Provide Contact] - Identifier len: %d\n", context->identity->identifier_len);
301 PRINTF("[Provide Contact] - Blockchain: %s\n",
302 FAMILY_AS_STR(context->identity->blockchain_family));
303}
304
305/* Exported functions --------------------------------------------------------*/
306
317bolos_err_t provide_contact(uint8_t *buffer_in, size_t buffer_in_length)
318{
319 const buffer_t payload = {.ptr = buffer_in, .size = buffer_in_length};
320 s_provide_contact_ctx ctx = {0};
321
322 // Init the structure
323 ctx.identity = &g_ab_payload.provide_contact;
324 memset(&g_ab_payload.provide_contact, 0, sizeof(g_ab_payload.provide_contact));
325
326 // Parse using SDK TLV parser
327 if (!provide_contact_tlv_parser(&payload, &ctx, &ctx.received_tags)) {
328 PRINTF("[Provide Contact] TLV parsing failed\n");
329 return SWO_INCORRECT_DATA;
330 }
331 if (!verify_fields(&ctx)) {
332 return SWO_INCORRECT_DATA;
333 }
334 print_payload(&ctx);
335
336 // Verify the group handle and extract the gid
337 if (!address_book_verify_group_handle(ctx.group_handle, g_ab_payload.provide_contact.gid)) {
338 PRINTF("[Provide Contact] Group handle verification failed\n");
339 return SWO_SECURITY_CONDITION_NOT_SATISFIED;
340 }
341
342 // Verify HMAC_PROOF over (gid, contact_name)
343 if (!address_book_verify_hmac_proof(g_ab_payload.provide_contact.gid,
344 g_ab_payload.provide_contact.contact_name,
345 ctx.hmac_proof)) {
346 PRINTF("[Provide Contact] HMAC_PROOF verification failed\n");
347 return SWO_SECURITY_CONDITION_NOT_SATISFIED;
348 }
349
350 // Verify HMAC_REST over (gid, scope, identifier, family[, chain_id])
351 if (!address_book_verify_hmac_rest(g_ab_payload.provide_contact.gid,
352 g_ab_payload.provide_contact.scope,
353 g_ab_payload.provide_contact.identifier,
354 g_ab_payload.provide_contact.identifier_len,
355 g_ab_payload.provide_contact.blockchain_family,
356 g_ab_payload.provide_contact.chain_id,
357 ctx.hmac_rest)) {
358 PRINTF("[Provide Contact] HMAC_REST verification failed\n");
359 return SWO_SECURITY_CONDITION_NOT_SATISFIED;
360 }
361
362 // Pass contact to the coin app for storage
363 if (!handle_provide_identity(&g_ab_payload.provide_contact)) {
364 PRINTF("[Provide Contact] Rejected by coin application\n");
365 return SWO_WRONG_PARAMETER_VALUE;
366 }
367
368 return SWO_SUCCESS;
369}
370
371#endif // HAVE_ADDRESS_BOOK
@ FAMILY_ETHEREUM
#define FAMILY_AS_STR(x)
Register / Edit Contact Name / Edit Scope / Edit Identifier.
uint8_t * ptr
Definition buffer.h:22
size_t size
Pointer to byte buffer.
Definition buffer.h:23
bool tlv_enforce_u8_value(const tlv_data_t *data, uint8_t expected_value)
Enforce that a TLV data contains the expected uint8 value.
Definition tlv_library.c:38
bool get_buffer_from_tlv_data(const tlv_data_t *data, buffer_t *out, uint16_t min_size, uint16_t max_size)
Extract a sized buffer from TLV data (zero-copy).
#define DEFINE_TLV_PARSER(TAG_LIST, COMMON_HANDLER, PARSE_FUNCTION_NAME)
Creates a parser function for a given TLV use case.
#define TLV_CHECK_RECEIVED_TAGS(received,...)
static bool handle_chain_id(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
static bool handle_blockchain_family(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)