Skip to content

Commit 397b948

Browse files
author
Jamie C. Driver
committed
bip85: compute and return a deterministic bip85 rsa pubkey
1 parent fe263ab commit 397b948

File tree

11 files changed

+355
-96
lines changed

11 files changed

+355
-96
lines changed

docs/index.rst

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,6 +1074,49 @@ get_receive_address reply
10741074
"result": "3A9HgJqKS5FZtGykWKuVBKyosFppojPPj5"
10751075
}
10761076
1077+
.. _get_bip85_pubkey_request:
1078+
1079+
get_bip85_pubkey request
1080+
------------------------
1081+
1082+
Request to fetch a bip85-based deterministic key for a given type, key size and index.
1083+
1084+
NOTE: currently this call only supports 'RSA', with sizes 1024, 2048, 3072 and 4096.
1085+
1086+
NOTE: Uses current wallet seed and bip85 specification to produce deterministic entropy to initialise shake256 PRNG, used to create a deterministic key.
1087+
However, since there is no well-specified deterministic recipe for creating keys, this implementation output may differ from other deterministic key-generation algorithms.
1088+
1089+
.. code-block:: cbor
1090+
1091+
{
1092+
"id": "8",
1093+
"method": "get_bip85_pubkey",
1094+
"params": {
1095+
"key_type": "RSA",
1096+
"key_bits": 2048,
1097+
"index": 1
1098+
}
1099+
}
1100+
1101+
* 'key_type' must be 'RSA'
1102+
* 'key_bits' must be one of 1024, 2048, 3072 or 4096.
1103+
1104+
1105+
.. _get_bip85_pubkey_reply:
1106+
1107+
get_bip85_pubkey reply
1108+
----------------------
1109+
1110+
.. code-block:: cbor
1111+
1112+
{
1113+
"id": "8",
1114+
"result": <pem string>
1115+
}
1116+
1117+
* 'result' is a public key pem string
1118+
1119+
10771120
.. _get_identity_pubkey_request:
10781121

10791122
get_identity_pubkey request

jadepy/jade.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,6 +1249,33 @@ def sign_message_file(self, message_file):
12491249
params = {'message_file': message_file}
12501250
return self._jadeRpc('sign_message', params)
12511251

1252+
def get_bip85_pubkey(self, key_type, key_bits, index):
1253+
"""
1254+
RPC call to fetch a bip85-derived pubkey.
1255+
1256+
Parameters
1257+
----------
1258+
key_type : string
1259+
The type of key to be derived.
1260+
At this time only 'RSA' is supported.
1261+
1262+
key_bits : int
1263+
The number of bits in the desired key. Must be valid for the key type.
1264+
At this time must be 1024, 2048, 3096 or 4092
1265+
1266+
index : int
1267+
The index to use in the bip32 path to calculate the entropy to generate the key.
1268+
1269+
Returns
1270+
-------
1271+
string
1272+
PEM file of the public key derived.
1273+
"""
1274+
params = {'key_type': key_type,
1275+
'key_bits': key_bits,
1276+
'index': index}
1277+
return self._jadeRpc('get_bip85_pubkey', params)
1278+
12521279
def get_identity_pubkey(self, identity, curve, key_type, index=0):
12531280
"""
12541281
RPC call to fetch a pubkey for the given identity (slip13/slip17).

main/process/dashboard.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ void get_shared_nonce_process(void* process_ptr);
148148
void get_commitments_process(void* process_ptr);
149149
void get_blinding_factor_process(void* process_ptr);
150150
void sign_liquid_tx_process(void* process_ptr);
151+
void get_bip85_pubkey_process(void* process_ptr);
151152
#ifdef CONFIG_DEBUG_MODE
152153
void get_bip85_bip39_entropy_process(void* process_ptr);
153154
void get_bip85_rsa_entropy_process(void* process_ptr);
@@ -595,6 +596,8 @@ static void dispatch_message(jade_process_t* process)
595596
task_function = get_blinding_key_process;
596597
} else if (IS_METHOD("get_shared_nonce")) {
597598
task_function = get_shared_nonce_process;
599+
} else if (IS_METHOD("get_bip85_pubkey")) {
600+
task_function = get_bip85_pubkey_process;
598601
} else if (IS_METHOD("ota_data") || IS_METHOD("ota_complete") || IS_METHOD("tx_input")
599602
|| IS_METHOD("get_extended_data") || IS_METHOD("get_signature") || IS_METHOD("pin")) {
600603
// Method we only expect as part of a multi-message protocol

main/process/get_bip85_entropy.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
#include "../jade_wally_verify.h"
55
#include "../process.h"
66
#include "../random.h"
7+
#include "../rsa.h"
78
#include "../sensitive.h"
89
#include "../ui.h"
9-
#include "../utils/cbor_rpc.h"
1010
#include "../wallet.h"
1111

1212
#include "process_utils.h"
@@ -16,8 +16,6 @@ static const unsigned char HMAC_BIP39_MESSAGE[]
1616
static const unsigned char HMAC_RSA_MESSAGE[]
1717
= { 'b', 'i', 'p', '8', '5', '_', 'r', 's', 'a', '_', 'e', 'n', 't', 'r', 'o', 'p', 'y' };
1818

19-
#define RSA_KEY_SIZE_VALID(bits) (bits == 1024 || bits == 2048 || bits == 3072 || bits == 4096 || bits == 8192)
20-
2119
typedef struct {
2220
uint8_t encrypted[AES_ENCRYPTED_LEN(HMAC_SHA512_LEN) + HMAC_SHA256_LEN];
2321
uint8_t pubkey[EC_PUBLIC_KEY_LEN];

main/process/get_bip85_pubkey.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
2+
#include "../jade_assert.h"
3+
#include "../process.h"
4+
#include "../rsa.h"
5+
#include "../ui.h"
6+
#include "../utils/cbor_rpc.h"
7+
8+
#include "process_utils.h"
9+
10+
void get_bip85_pubkey_process(void* process_ptr)
11+
{
12+
JADE_LOGI("Starting: %d", xPortGetFreeHeapSize());
13+
jade_process_t* process = process_ptr;
14+
15+
// We expect a current message to be present
16+
ASSERT_CURRENT_MESSAGE(process, "get_bip85_pubkey");
17+
ASSERT_KEYCHAIN_UNLOCKED_BY_MESSAGE_SOURCE(process);
18+
GET_MSG_PARAMS(process);
19+
20+
const char* errmsg = NULL;
21+
size_t key_bits = 0;
22+
size_t index = 0;
23+
24+
if (!params_get_bip85_rsa_key(&params, &key_bits, &index, &errmsg)) {
25+
jade_process_reject_message(process, CBOR_RPC_BAD_PARAMETERS, errmsg, NULL);
26+
goto cleanup;
27+
}
28+
29+
display_processing_message_activity();
30+
31+
char pubkey_pem[1024];
32+
if (!rsa_get_bip85_pubkey_pem(key_bits, index, pubkey_pem, sizeof(pubkey_pem))) {
33+
jade_process_reject_message(process, CBOR_RPC_INTERNAL_ERROR, "Failed to generate RSA key", NULL);
34+
goto cleanup;
35+
}
36+
37+
// Reply with the pubkey pem
38+
jade_process_reply_to_message_result(process->ctx, pubkey_pem, cbor_result_string_cb);
39+
JADE_LOGI("Success");
40+
41+
cleanup:
42+
return;
43+
}

main/process/process_utils.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "../jade_wally_verify.h"
55
#include "../keychain.h"
66
#include "../multisig.h"
7+
#include "../rsa.h"
78
#include "../ui.h"
89
#include "../utils/cbor_rpc.h"
910

@@ -13,6 +14,8 @@
1314

1415
#include "process_utils.h"
1516

17+
static const char KEY_TYPE_RSA[] = { 'R', 'S', 'A' };
18+
1619
// Sanity check extended-data payload fields
1720
bool check_extended_data_fields(CborValue* params, const char* expected_origid, const char* expected_orig,
1821
const size_t expected_seqnum, const size_t expected_seqlen)
@@ -345,6 +348,38 @@ bool params_tx_input_signing_data(const bool use_ae_signatures, CborValue* param
345348
return true;
346349
}
347350

351+
// Bip85 RSA key parameters (key size and index)
352+
bool params_get_bip85_rsa_key(CborValue* params, size_t* key_bits, size_t* index, const char** errmsg)
353+
{
354+
JADE_ASSERT(params);
355+
JADE_ASSERT(key_bits);
356+
JADE_ASSERT(index);
357+
JADE_INIT_OUT_PPTR(errmsg);
358+
359+
// Only handle 'RSA' atm
360+
char key_type[8];
361+
size_t written = 0;
362+
rpc_get_string("key_type", sizeof(key_type), params, key_type, &written);
363+
if (written != sizeof(KEY_TYPE_RSA) || memcmp(key_type, KEY_TYPE_RSA, sizeof(KEY_TYPE_RSA))) {
364+
*errmsg = "Cannot extract valid key_type from parameters";
365+
return false;
366+
}
367+
368+
// Get number of key_bits and final index
369+
if (!rpc_get_sizet("key_bits", params, key_bits) || *key_bits > MAX_RSA_GEN_KEY_LEN
370+
|| !RSA_KEY_SIZE_VALID(*key_bits)) {
371+
*errmsg = "Failed to fetch valid key length from message";
372+
return false;
373+
}
374+
375+
if (!rpc_get_sizet("index", params, index)) {
376+
*errmsg = "Failed to fetch valid index from message";
377+
return false;
378+
}
379+
380+
return true;
381+
}
382+
348383
// For now just return 'single-sig' or 'other'.
349384
// In future may extend to inlcude eg. 'green', 'other-multisig', etc.
350385
script_flavour_t get_script_flavour(const uint8_t* script, const size_t script_len)

main/process/process_utils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ bool params_tx_input_signing_data(const bool use_ae_signatures, CborValue* param
125125
signing_data_t* sig_data, const uint8_t** ae_host_commitment, size_t* ae_host_commitment_len,
126126
const uint8_t** script, size_t* script_len, script_flavour_t* aggregate_script_flavour, const char** errmsg);
127127

128+
bool params_get_bip85_rsa_key(CborValue* params, size_t* key_bits, size_t* index, const char** errmsg);
129+
128130
// Track the types of the input prevout scripts
129131
script_flavour_t get_script_flavour(const uint8_t* script, const size_t script_len);
130132
void update_aggregate_scripts_flavour(script_flavour_t new_script_flavour, script_flavour_t* aggregate_scripts_flavour);

main/rsa.c

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
2+
#include "rsa.h"
3+
#include "jade_assert.h"
4+
#include "keychain.h"
5+
#include "sensitive.h"
6+
#include "utils/shake256.h"
7+
#include "wallet.h"
8+
9+
#include <mbedtls/pk.h>
10+
#include <mbedtls/rsa.h>
11+
12+
// Function to get bip85-generated rsa context
13+
// See: https://github.com/bitcoin/bips/blob/master/bip-0085.mediawiki
14+
static bool get_bip85_rsa_ctx(const size_t key_bits, const size_t index, mbedtls_rsa_context* output)
15+
{
16+
JADE_ASSERT(key_bits <= MAX_RSA_GEN_KEY_LEN);
17+
JADE_ASSERT(RSA_KEY_SIZE_VALID(key_bits));
18+
// index can be 0
19+
JADE_ASSERT(output);
20+
21+
JADE_ASSERT(keychain_get());
22+
JADE_LOGI("Deriving BIP85 RSA context for index %u, key length: %u", index, key_bits);
23+
24+
uint8_t entropy[HMAC_SHA512_LEN];
25+
size_t entropy_len = 0;
26+
wallet_get_bip85_rsa_entropy(key_bits, index, entropy, sizeof(entropy), &entropy_len);
27+
JADE_ASSERT(entropy_len == 64);
28+
29+
struct shake256_ctx sctx = {};
30+
shake256_init(&sctx, entropy, entropy_len);
31+
if (mbedtls_rsa_gen_key(output, shake256_mbedtls_rnd_cb, &sctx, key_bits, 65537) != 0) {
32+
JADE_LOGE("Failed to create/setup key from rsa context");
33+
return false;
34+
}
35+
36+
return true;
37+
}
38+
39+
// Function to get bip85-generated rsa key pem
40+
bool rsa_get_bip85_pubkey_pem(const size_t key_bits, const size_t index, char* output, const size_t output_len)
41+
{
42+
JADE_ASSERT(key_bits <= MAX_RSA_GEN_KEY_LEN);
43+
JADE_ASSERT(RSA_KEY_SIZE_VALID(key_bits));
44+
// index can be 0
45+
JADE_ASSERT(output);
46+
JADE_ASSERT(output_len);
47+
48+
JADE_ASSERT(keychain_get());
49+
bool retval = false;
50+
51+
mbedtls_rsa_context rsa = {};
52+
mbedtls_rsa_init(&rsa);
53+
SENSITIVE_PUSH(&rsa, sizeof(rsa));
54+
55+
mbedtls_pk_context pk;
56+
mbedtls_pk_init(&pk);
57+
SENSITIVE_PUSH(&pk, sizeof(pk));
58+
59+
if (mbedtls_pk_setup(&pk, mbedtls_pk_info_from_type(MBEDTLS_PK_RSA)) != 0) {
60+
JADE_LOGE("Failed to create/setup key from rsa context");
61+
goto cleanup;
62+
}
63+
64+
if (!get_bip85_rsa_ctx(key_bits, index, &rsa)) {
65+
JADE_LOGE("Failed to generate bip85 rsa ctx");
66+
goto cleanup;
67+
}
68+
69+
if (mbedtls_rsa_copy(mbedtls_pk_rsa(pk), &rsa) != 0) {
70+
JADE_LOGE("Failed to copy key from rsa context");
71+
goto cleanup;
72+
}
73+
74+
if (mbedtls_pk_write_pubkey_pem(&pk, (uint8_t*)output, output_len) != 0) {
75+
JADE_LOGE("Failed to write pubkey to buffer of length %u", output_len);
76+
goto cleanup;
77+
}
78+
79+
retval = true;
80+
81+
cleanup:
82+
mbedtls_pk_free(&pk);
83+
SENSITIVE_POP(&pk);
84+
85+
mbedtls_rsa_free(&rsa);
86+
SENSITIVE_POP(&rsa);
87+
88+
return retval;
89+
}

main/rsa.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#ifndef JADE_RSA_H_
2+
#define JADE_RSA_H_
3+
4+
#include <stdbool.h>
5+
#include <stddef.h>
6+
7+
#define RSA_KEY_SIZE_VALID(bits) (bits == 1024 || bits == 2048 || bits == 3072 || bits == 4096 || bits == 8192)
8+
#define MAX_RSA_GEN_KEY_LEN 4096
9+
10+
// Function to get bip85-generated rsa key pem
11+
bool rsa_get_bip85_pubkey_pem(size_t key_bits, size_t index, char* output, size_t output_len);
12+
13+
#endif /* JADE_RSA_H_ */

0 commit comments

Comments
 (0)