Skip to content

Commit 5f4bc09

Browse files
author
Jamie C. Driver
committed
http_request pinserver: extract http_request handler
Remove assumptions that payload will always be json
1 parent e5719ec commit 5f4bc09

File tree

3 files changed

+160
-95
lines changed

3 files changed

+160
-95
lines changed

main/process/pinclient.c

Lines changed: 21 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -63,20 +63,23 @@ typedef struct {
6363
uint8_t replay_counter[REPLAY_COUNTER_LEN];
6464
} pin_keys_t;
6565

66-
typedef struct {
67-
const char* document;
68-
const char* on_reply;
69-
const char* data;
70-
} pin_data_t;
71-
72-
// The urls may be overridden in storage, otherwise use the default
73-
static void add_urls(CborEncoder* encoder, const char* document)
66+
// The urls may be overridden in storage, otherwise use the defaults
67+
static void send_http_request_reply(jade_process_t* process, const char* document, const char* data)
7468
{
69+
JADE_ASSERT(process);
70+
JADE_ASSERT(document);
71+
JADE_ASSERT(data);
72+
73+
// Prepare request data
74+
data_request_t pin_data
75+
= { .method = "POST", .accept = "json", .on_reply = "pin", .strdata = data, .rawdata_len = 0, .num_urls = 0 };
76+
77+
// Add urls - bespoke pinserver urls or defaults if not set
7578
char buf[MAX_PINSVR_URL_LENGTH];
7679
char urlA[sizeof(buf) + sizeof(PINSERVER_DOC_GET_PIN)];
7780
char urlB[sizeof(buf) + sizeof(PINSERVER_DOC_GET_PIN)];
7881

79-
// Get first URL (defaults to h/coded url)
82+
// Add first URL (defaults to h/coded url)
8083
size_t urlA_len = 0;
8184
const bool urlASet = storage_get_pinserver_urlA(buf, sizeof(buf), &urlA_len);
8285
if (urlASet && urlA_len <= 1) {
@@ -85,9 +88,10 @@ static void add_urls(CborEncoder* encoder, const char* document)
8588
} else {
8689
urlA_len = snprintf(urlA, sizeof(urlA), "%s/%s", urlASet ? buf : PINSERVER_URL, document);
8790
JADE_ASSERT(urlA_len > 0 && urlA_len < sizeof(urlA));
91+
pin_data.urls[pin_data.num_urls++] = urlA;
8892
}
8993

90-
// Get second URL (defaults to h/coded onion)
94+
// Add second URL (defaults to h/coded onion)
9195
size_t urlB_len = 0;
9296
const bool urlBSet = storage_get_pinserver_urlB(buf, sizeof(buf), &urlB_len);
9397
if (urlBSet && urlB_len <= 1) {
@@ -96,97 +100,20 @@ static void add_urls(CborEncoder* encoder, const char* document)
96100
} else {
97101
urlB_len = snprintf(urlB, sizeof(urlB), "%s/%s", urlBSet ? buf : PINSERVER_ONION, document);
98102
JADE_ASSERT(urlB_len > 0 && urlB_len < sizeof(urlB));
103+
pin_data.urls[pin_data.num_urls++] = urlB;
99104
}
100105

101106
JADE_ASSERT(urlASet == urlBSet);
102-
const char* urls[2] = { urlA, urlB };
103-
add_string_array_to_map(encoder, "urls", urls, 2);
104-
}
105-
106-
// {
107-
// "http_request": {
108-
// // params can be passed directly to gdk.http_request()
109-
// "params": {
110-
// "urls": [],
111-
// "root_certificates": [`certificate`]' ** only present if user has set an additional certificate
112-
// "method": "POST",
113-
// "accept": "json",
114-
// "data": `data`
115-
// }
116-
// "on-reply": `on_reply` ** the result of gdk.http_request(params) should be passed to this method
117-
// }
118-
static void http_post_reply(const void* ctx, CborEncoder* container)
119-
{
120-
JADE_ASSERT(ctx);
121-
JADE_ASSERT(container);
122107

108+
// Add any user certificate
123109
size_t cert_len = 0;
124110
char user_certificate[MAX_PINSVR_CERTIFICATE_LENGTH];
125-
const bool have_certificate = storage_get_pinserver_cert(user_certificate, sizeof(user_certificate), &cert_len);
126-
127-
const pin_data_t* reply_data = (const pin_data_t*)ctx;
128-
JADE_ASSERT(reply_data->document);
129-
JADE_ASSERT(reply_data->on_reply);
130-
JADE_ASSERT(reply_data->data);
131-
132-
CborEncoder root_map;
133-
CborError cberr = cbor_encoder_create_map(container, &root_map, 1);
134-
JADE_ASSERT(cberr == CborNoError);
135-
136-
// Envelope data for http request
137-
cberr = cbor_encode_text_stringz(&root_map, "http_request");
138-
JADE_ASSERT(cberr == CborNoError);
139-
140-
CborEncoder http_encoder;
141-
cberr = cbor_encoder_create_map(&root_map, &http_encoder, 2);
142-
JADE_ASSERT(cberr == CborNoError);
143-
144-
cberr = cbor_encode_text_stringz(&http_encoder, "params");
145-
JADE_ASSERT(cberr == CborNoError);
146-
147-
CborEncoder params_encoder;
148-
const size_t num_params = have_certificate ? 5 : 4;
149-
cberr = cbor_encoder_create_map(&http_encoder, &params_encoder, num_params);
150-
JADE_ASSERT(cberr == CborNoError);
151-
152-
// The urls (tls and onion) and any associated root certificate we may require
153-
// These may be the built-in defaults, or may have been overridden (in storage)
154-
add_urls(&params_encoder, reply_data->document);
155-
156-
if (have_certificate) {
157-
const char* root_certificates[] = { user_certificate };
158-
add_string_array_to_map(&params_encoder, "root_certificates", root_certificates, 1);
111+
if (storage_get_pinserver_cert(user_certificate, sizeof(user_certificate), &cert_len) && cert_len) {
112+
pin_data.certificate = user_certificate;
159113
}
160114

161-
// The method here is always POST and the payload always json
162-
add_string_to_map(&params_encoder, "method", "POST");
163-
add_string_to_map(&params_encoder, "accept", "json");
164-
165-
// Add payload data
166-
cberr = cbor_encode_text_stringz(&params_encoder, "data");
167-
JADE_ASSERT(cberr == CborNoError);
168-
169-
CborEncoder data_encoder;
170-
cberr = cbor_encoder_create_map(&params_encoder, &data_encoder, 1);
171-
JADE_ASSERT(cberr == CborNoError);
172-
173-
// Payload data - one large opaque base64 string
174-
add_string_to_map(&data_encoder, "data", reply_data->data);
175-
176-
cberr = cbor_encoder_close_container(&params_encoder, &data_encoder);
177-
JADE_ASSERT(cberr == CborNoError);
178-
179-
cberr = cbor_encoder_close_container(&http_encoder, &params_encoder);
180-
JADE_ASSERT(cberr == CborNoError);
181-
182-
// Add function to call with server's reply payload
183-
add_string_to_map(&http_encoder, "on-reply", reply_data->on_reply);
184-
185-
cberr = cbor_encoder_close_container(&root_map, &http_encoder);
186-
JADE_ASSERT(cberr == CborNoError);
187-
188-
cberr = cbor_encoder_close_container(container, &root_map);
189-
JADE_ASSERT(cberr == CborNoError);
115+
// Send reply message
116+
jade_process_reply_to_message_result(process->ctx, &pin_data, http_request_reply);
190117
}
191118

192119
// Hepler to tweak the server static key into a session key
@@ -535,8 +462,7 @@ static pinserver_result_t pinserver_interaction(jade_process_t* process, const u
535462
}
536463

537464
// Build and send cbor reply
538-
const pin_data_t pin_data = { .document = document, .on_reply = "pin", .data = data };
539-
jade_process_reply_to_message_result(process->ctx, &pin_data, http_post_reply);
465+
send_http_request_reply(process, document, data);
540466

541467
// Get the server's aes key for the given pin/key data
542468
uint8_t serverkey[AES_KEY_LEN_256];

main/process/process_utils.c

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#include "../ui.h"
99
#include "../utils/cbor_rpc.h"
1010

11+
#include "process_utils.h"
12+
1113
#include <sys/time.h>
1214
#include <wally_anti_exfil.h>
1315
#include <wally_script.h>
@@ -440,3 +442,121 @@ void update_aggregate_scripts_flavour(
440442
*aggregate_scripts_flavour = SCRIPT_FLAVOUR_MIXED;
441443
}
442444
}
445+
446+
// {
447+
// "http_request": {
448+
// //
449+
// "params": {
450+
// "urls": [],
451+
// "root_certificates": [`certificate`]' ** optional
452+
// "method": "POST", ** optional
453+
// "accept": "json", ** optional
454+
// "data": `data` ** optional - can be text or binary
455+
// }
456+
// "on-reply": `on_reply`
457+
// }
458+
void http_request_reply(const void* ctx, CborEncoder* container)
459+
{
460+
JADE_ASSERT(ctx);
461+
JADE_ASSERT(container);
462+
463+
const data_request_t* const request_data = (const data_request_t*)ctx;
464+
JADE_ASSERT(request_data->num_urls);
465+
JADE_ASSERT(request_data->on_reply);
466+
// method, accept and certificate and data fields are optional, but some combinations may be nonsensical
467+
JADE_ASSERT(request_data->rawdata || !request_data->rawdata_len);
468+
469+
const bool has_data_payload = request_data->strdata || (request_data->rawdata && request_data->rawdata_len);
470+
const bool nested_json = request_data->accept
471+
&& (!strcmp(request_data->accept, "json") | !strcmp(request_data->accept, "application/json"));
472+
473+
JADE_ASSERT(!nested_json || !request_data->rawdata_len);
474+
475+
size_t num_params = 1; // urls
476+
if (request_data->method) {
477+
++num_params;
478+
}
479+
if (request_data->accept) {
480+
++num_params;
481+
}
482+
if (request_data->certificate) {
483+
++num_params;
484+
}
485+
if (has_data_payload) {
486+
++num_params;
487+
}
488+
489+
CborEncoder root_map;
490+
CborError cberr = cbor_encoder_create_map(container, &root_map, 1);
491+
JADE_ASSERT(cberr == CborNoError);
492+
493+
// Envelope data for http request
494+
cberr = cbor_encode_text_stringz(&root_map, "http_request");
495+
JADE_ASSERT(cberr == CborNoError);
496+
497+
CborEncoder http_encoder;
498+
cberr = cbor_encoder_create_map(&root_map, &http_encoder, 2);
499+
JADE_ASSERT(cberr == CborNoError);
500+
501+
cberr = cbor_encode_text_stringz(&http_encoder, "params");
502+
JADE_ASSERT(cberr == CborNoError);
503+
504+
CborEncoder params_encoder;
505+
cberr = cbor_encoder_create_map(&http_encoder, &params_encoder, num_params);
506+
JADE_ASSERT(cberr == CborNoError);
507+
508+
// The urls (http/tls/onion)
509+
add_string_array_to_map(&params_encoder, "urls", (const char**)request_data->urls, request_data->num_urls);
510+
511+
// Any additional root certificate that may be required
512+
if (request_data->certificate) {
513+
const char* root_certificates[] = { request_data->certificate };
514+
add_string_array_to_map(&params_encoder, "root_certificates", root_certificates, 1);
515+
}
516+
517+
// The optional method (eg. GET/POST) and accept header (eg. json)
518+
if (request_data->method) {
519+
add_string_to_map(&params_encoder, "method", request_data->method);
520+
}
521+
if (request_data->accept) {
522+
add_string_to_map(&params_encoder, "accept", request_data->accept);
523+
}
524+
525+
// Add payload data if passed
526+
if (has_data_payload) {
527+
if (request_data->rawdata_len) {
528+
// Binary blob
529+
add_bytes_to_map(&params_encoder, "data", request_data->rawdata, request_data->rawdata_len);
530+
} else if (!nested_json) {
531+
// Plain string
532+
add_string_to_map(&params_encoder, "data", request_data->strdata);
533+
} else {
534+
// Add additional layer of json
535+
cberr = cbor_encode_text_stringz(&params_encoder, "data");
536+
JADE_ASSERT(cberr == CborNoError);
537+
538+
CborEncoder data_encoder;
539+
cberr = cbor_encoder_create_map(&params_encoder, &data_encoder, 1);
540+
JADE_ASSERT(cberr == CborNoError);
541+
542+
// Payload data - one large opaque string
543+
// Test if character or binary string is based on length field
544+
add_string_to_map(&data_encoder, "data", request_data->strdata);
545+
546+
cberr = cbor_encoder_close_container(&params_encoder, &data_encoder);
547+
JADE_ASSERT(cberr == CborNoError);
548+
}
549+
}
550+
551+
cberr = cbor_encoder_close_container(&http_encoder, &params_encoder);
552+
JADE_ASSERT(cberr == CborNoError);
553+
554+
// Add function to call with server's reply payload
555+
add_string_to_map(&http_encoder, "on-reply", request_data->on_reply);
556+
557+
cberr = cbor_encoder_close_container(&root_map, &http_encoder);
558+
JADE_ASSERT(cberr == CborNoError);
559+
560+
cberr = cbor_encoder_close_container(container, &root_map);
561+
JADE_ASSERT(cberr == CborNoError);
562+
}

main/process/process_utils.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,22 @@ typedef struct {
2929
uint8_t content;
3030
} commitment_t;
3131

32+
#define MAX_REQUEST_URLS 2
33+
34+
typedef struct {
35+
const char* urls[MAX_REQUEST_URLS];
36+
const char* certificate;
37+
const char* method;
38+
const char* accept;
39+
union {
40+
const char* strdata;
41+
const uint8_t* rawdata;
42+
};
43+
const char* on_reply;
44+
size_t rawdata_len; // zero implies may be char data
45+
uint8_t num_urls;
46+
} data_request_t;
47+
3248
#define HAS_NO_CURRENT_MESSAGE(process) \
3349
(process && !process->ctx.cbor && !process->ctx.cbor_len && process->ctx.source == SOURCE_NONE)
3450

@@ -122,4 +138,7 @@ bool params_get_bip85_rsa_key(CborValue* params, size_t* key_bits, size_t* index
122138
script_flavour_t get_script_flavour(const uint8_t* script, const size_t script_len, bool* is_p2tr);
123139
void update_aggregate_scripts_flavour(script_flavour_t new_script_flavour, script_flavour_t* aggregate_scripts_flavour);
124140

141+
// A message reply which is actually a request for more data from a resource
142+
void http_request_reply(const void* ctx, CborEncoder* container);
143+
125144
#endif /* PROCESS_UTILS_H_ */

0 commit comments

Comments
 (0)