Skip to content

Commit

Permalink
Merge pull request #31 from homura/develop
Browse files Browse the repository at this point in the history
feat: display all address in the ckb2021 format
  • Loading branch information
cedelavergne-ledger authored Aug 8, 2024
2 parents 02b32f2 + 4c7183f commit 9922a20
Show file tree
Hide file tree
Showing 10 changed files with 60 additions and 41 deletions.
7 changes: 4 additions & 3 deletions src/apdu.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ void handle_apdu_get_wallet_id(uint8_t __attribute__((unused)) instruction) {

int rv = 0;
cx_blake2b_t hashState;
cx_blake2b_init(&hashState, 512);
CX_ASSERT(cx_blake2b_init_no_throw(&hashState, 512));

WITH_KEY_PAIR(id_path, key_pair, size_t, ({
PRINTF("\nPublic Key: %.*h\n", key_pair->public_key.W_len, key_pair->public_key.W);
Expand All @@ -73,8 +73,9 @@ void handle_apdu_get_wallet_id(uint8_t __attribute__((unused)) instruction) {
// Stubbed until we have the sign step working.
// rv = cx_hash((cx_hash_t*) &hashState, CX_LAST, signedToken, sizeof(signedToken),
// G_io_apdu_buffer, sizeof(G_io_apdu_buffer));
rv = cx_hash((cx_hash_t *)&hashState, CX_LAST, (uint8_t *)key_pair->public_key.W,
key_pair->public_key.W_len, G_io_apdu_buffer, sizeof(G_io_apdu_buffer));
CX_ASSERT(cx_hash_no_throw((cx_hash_t *)&hashState, CX_LAST, (uint8_t *)key_pair->public_key.W,
key_pair->public_key.W_len, G_io_apdu_buffer, sizeof(G_io_apdu_buffer)));
rv = cx_hash_get_size((cx_hash_t *)&hashState);
}));
delay_successful(rv);
}
Expand Down
15 changes: 4 additions & 11 deletions src/apdu_pubkey.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,9 @@ static void bip32_path_to_string(char *const out, size_t const out_size, apdu_pu
}

void render_pkh(char *const out, size_t const out_size,
render_address_payload_t const *const payload) {
render_address_payload_t const *const payload, size_t payload_len) {
uint8_t base32_buf[256];
size_t base32_len = 0;
size_t payload_len = 0;
bool is_bech32m = 0;
if (payload->full_version.address_format_type == ADDRESS_FORMAT_TYPE_FULL_VERSION) {
payload_len = sizeof(payload->full_version);
is_bech32m = 1;
} else if (payload->short_version.address_format_type == ADDRESS_FORMAT_TYPE_SHORT) {
payload_len = sizeof(payload->short_version);
} else {
payload_len = sizeof(payload->code_hash_data_or_type);
}

if (!convert_bits(base32_buf, sizeof(base32_buf), &base32_len,
5,
Expand All @@ -75,6 +65,9 @@ void render_pkh(char *const out, size_t const out_size,
THROW(EXC_MEMORY_ERROR);
}
static const char hrbs[][4] = {"ckb", "ckt"};
// https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0021-ckb-address-format/0021-ckb-address-format.md#full-payload-format
// CKB addresses are all encoded with bech32m. The bech32 encoding method is deprecated from CKB2021.
bool is_bech32m = true;
if (!bech32_encode(out, out_size, hrbs[N_data.address_type&ADDRESS_TYPE_MASK], base32_buf, base32_len, is_bech32m)) {
THROW(EXC_MEMORY_ERROR);
}
Expand Down
2 changes: 1 addition & 1 deletion src/apdu_pubkey.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
#include "apdu.h"

void handle_apdu_get_public_key(uint8_t instruction);
void render_pkh(char *const out, size_t const out_size, render_address_payload_t const *const payload);
void render_pkh(char *const out, size_t const out_size, render_address_payload_t const *const payload, size_t payload_len);
8 changes: 4 additions & 4 deletions src/apdu_sign.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
static inline void conditional_init_hash_state(blake2b_hash_state_t *const state) {
check_null(state);
if (!state->initialized) {
cx_blake2b_init2(&state->state, SIGN_HASH_SIZE * 8, NULL, 0, (uint8_t *)blake2b_personalization,
sizeof(blake2b_personalization) - 1);
CX_ASSERT(cx_blake2b_init2_no_throw(&state->state, SIGN_HASH_SIZE * 8, NULL, 0, (uint8_t *)blake2b_personalization,
sizeof(blake2b_personalization) - 1));
state->initialized = true;
}
}
Expand All @@ -39,7 +39,7 @@ static void blake2b_incremental_hash(
check_null(state);

conditional_init_hash_state(state);
cx_hash((cx_hash_t *)&state->state, 0, out, out_size, NULL, 0);
CX_ASSERT(cx_hash_no_throw((cx_hash_t *)&state->state, 0, out, out_size, NULL, 0));
}

static void blake2b_finish_hash(
Expand All @@ -49,7 +49,7 @@ static void blake2b_finish_hash(
check_null(state);

conditional_init_hash_state(state);
cx_hash((cx_hash_t *)&state->state, CX_LAST, NULL, 0, out, out_size);
CX_ASSERT(cx_hash_no_throw((cx_hash_t *)&state->state, CX_LAST, NULL, 0, out, out_size));
}

static int perform_signature(bool const on_hash, bool const send_hash);
Expand Down
32 changes: 19 additions & 13 deletions src/keys.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,21 @@ key_pair_t *generate_extended_key_pair_return_global(bip32_path_t const *const b

cx_curve_t const cx_curve = CX_CURVE_SECP256K1;

os_perso_derive_node_bip32(cx_curve, bip32_path->components, bip32_path->length, priv->private_key_data, chain_code);
unsigned char temp_privkey[64] = {0};
CX_ASSERT(os_derive_bip32_no_throw(cx_curve, bip32_path->components, bip32_path->length, temp_privkey, chain_code));
memcpy(priv->private_key_data, temp_privkey, 32);
// clear the temporary buffer
explicit_bzero(temp_privkey, sizeof(temp_privkey));
// os_perso_derive_node_bip32(cx_curve, bip32_path->components, bip32_path->length, priv->private_key_data, chain_code);

BEGIN_TRY {
TRY {
cx_ecfp_init_private_key(cx_curve, priv->private_key_data, sizeof(priv->private_key_data),
&priv->res.private_key);
cx_ecfp_generate_pair(cx_curve, &priv->res.public_key, &priv->res.private_key, 1);
CX_ASSERT(cx_ecfp_init_private_key_no_throw(cx_curve, priv->private_key_data, sizeof(priv->private_key_data),
&priv->res.private_key));
CX_ASSERT(cx_ecfp_generate_pair_no_throw(cx_curve, &priv->res.public_key, &priv->res.private_key, 1));

if (cx_curve == CX_CURVE_Ed25519) {
cx_edwards_compress_point_no_throw(CX_CURVE_Ed25519, priv->res.public_key.W, priv->res.public_key.W_len);
CX_ASSERT(cx_edwards_compress_point_no_throw(CX_CURVE_Ed25519, priv->res.public_key.W, priv->res.public_key.W_len));
priv->res.public_key.W_len = 33;
}
}
Expand Down Expand Up @@ -97,10 +102,11 @@ size_t sign(uint8_t *const out, size_t const out_size, key_pair_t const *const p
explicit_bzero(sig, sizeof(sig));

unsigned int info = 0;
size_t sig_len = sizeof(sig);

cx_ecdsa_sign(&pair->private_key, CX_LAST | CX_RND_RFC6979,
CX_ASSERT(cx_ecdsa_sign_no_throw(&pair->private_key, CX_LAST | CX_RND_RFC6979,
CX_SHA256, // historical reasons...semantically CX_NONE
(uint8_t const *const)PIC(in), in_size, sig, sizeof(sig), &info);
(uint8_t const *const)PIC(in), in_size, sig, &sig_len, &info));

// Converting to compressed format
int const r_size = sig[3];
Expand Down Expand Up @@ -132,13 +138,13 @@ void generate_lock_arg_for_pubkey(const cx_ecfp_public_key_t *const key, standar

cx_blake2b_t hash_state;

cx_blake2b_init2(&hash_state, 32*8, NULL, 0, (uint8_t *)blake2b_personalization,
sizeof(blake2b_personalization) - 1);
CX_ASSERT(cx_blake2b_init2_no_throw(&hash_state, 32*8, NULL, 0, (uint8_t *)blake2b_personalization,
sizeof(blake2b_personalization) - 1));

cx_hash((cx_hash_t *)&hash_state, 0, (uint8_t *const) & tag_byte, 1, NULL, 0);
cx_hash((cx_hash_t *)&hash_state, 0, (uint8_t *const) key->W+1, 32, NULL, 0);
cx_hash((cx_hash_t *)&hash_state, CX_LAST, NULL, 0, (uint8_t *const) temp_hash,
sizeof(temp_hash));
CX_ASSERT(cx_hash_no_throw((cx_hash_t *)&hash_state, 0, (uint8_t *const) & tag_byte, 1, NULL, 0));
CX_ASSERT(cx_hash_no_throw((cx_hash_t *)&hash_state, 0, (uint8_t *const) key->W+1, 32, NULL, 0));
CX_ASSERT(cx_hash_no_throw((cx_hash_t *)&hash_state, CX_LAST, NULL, 0, (uint8_t *const) temp_hash,
sizeof(temp_hash)));

memcpy(dest, temp_hash, sizeof(standard_lock_arg_t));

Expand Down
11 changes: 10 additions & 1 deletion src/segwit_addr.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,16 @@ int bech32_encode(char *const output, const size_t out_len, const char *const hr
chk = bech32_polymod_step(chk) ^ (hrp[i] >> 5);
++i;
}
if (i + 7 + data_len > 108)
// An CKB address is encoded from a CKB script,
// which consists of three fields:
// code_hash (32 bytes), hash_type (1 byte), and args (variable).
// It is often longer than a Bitcoin address.
// Since a bech32 character can represent 5-bits of data,
// the original limit of 107 has been changed to 1023
// to increase the script limit to approximately 640 bytes (1023 * 5 / 8 ≈ 1023)
// allowing support for ultra-long addresses.
size_t max_data_len = 1023;
if (i + 7 + data_len > max_data_len)
return 0;
chk = bech32_polymod_step(chk);
}
Expand Down
11 changes: 8 additions & 3 deletions src/to_string.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,11 +220,12 @@ void lock_arg_to_sighash_address(char *const dest, size_t const buff_size, lock_
render_address_payload.full_version.hash_type = 1;

memcpy(&render_address_payload.full_version.hash, lock_arg->hash, sizeof(render_address_payload.full_version.hash));
render_pkh(dest, buff_size, &render_address_payload);
render_pkh(dest, buff_size, &render_address_payload, sizeof(render_address_payload.full_version));
}

void lock_arg_to_multisig_address(char *const dest, size_t const buff_size, lock_arg_t const *const lock_arg) {
render_address_payload_t render_address_payload;
size_t payload_len = 0;
bool has_timelock = false;
for (int i = 0; i < 8; i++) {
if (lock_arg->lock_period[i] != 0) {
Expand All @@ -233,19 +234,23 @@ void lock_arg_to_multisig_address(char *const dest, size_t const buff_size, lock
}
}
if (has_timelock) {
render_address_payload.code_hash_data_or_type.address_format_type = ADDRESS_FORMAT_TYPE_CODE_HASH_TYPE;
render_address_payload.code_hash_data_or_type.address_format_type = ADDRESS_FORMAT_TYPE_FULL_VERSION;
memcpy(&render_address_payload.code_hash_data_or_type.code_hash, multisigLockScript,
sizeof(render_address_payload.code_hash_data_or_type.code_hash));
render_address_payload.code_hash_data_or_type.hash_type = 1;
memcpy(&render_address_payload.code_hash_data_or_type.lock_arg, lock_arg, sizeof(render_address_payload.code_hash_data_or_type.lock_arg));
payload_len = sizeof(render_address_payload.code_hash_data_or_type);
} else {
render_address_payload.full_version.address_format_type = ADDRESS_FORMAT_TYPE_FULL_VERSION;
memcpy(&render_address_payload.full_version.code_hash, multisigLockScript,
sizeof(render_address_payload.full_version.code_hash));
render_address_payload.full_version.hash_type = 1;
memcpy(&render_address_payload.full_version.hash, lock_arg->hash,
sizeof(render_address_payload.full_version.hash));
payload_len = sizeof(render_address_payload.full_version);
}

render_pkh(dest, buff_size, &render_address_payload);
render_pkh(dest, buff_size, &render_address_payload, payload_len);
}

// (x, h) -> "x of y"
Expand Down
1 change: 1 addition & 0 deletions src/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ typedef union {
struct {
uint8_t address_format_type;
uint8_t code_hash[32];
uint8_t hash_type;
lock_arg_t lock_arg;
} code_hash_data_or_type; // code_hash_data or code_hash_type
} render_address_payload_t;
Expand Down
6 changes: 3 additions & 3 deletions tests/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ function pressButtonAndWaitForChange(speculos, btn, timeout = 1000) {

const subscription = speculos.automationEvents.subscribe(() => {
subscription.unsubscribe()
sleep(200).then(() => resolve(true))
sleep(100).then(() => resolve(true))
})

setTimeout(() => {
Expand Down Expand Up @@ -148,7 +148,7 @@ async function automationStart(speculos, interactionFunc) {
let promptLockResolve;
let promptsLock=new Promise(r=>{promptLockResolve=r});
if(speculos.promptsEndPromise) {
await Promise.race([speculos.promptsEndPromise, sleep(2000)])
await Promise.race([speculos.promptsEndPromise, sleep(500)])
}
speculos.promptsEndPromise = promptsLock; // Set ourselves as the interaction.

Expand Down Expand Up @@ -209,7 +209,7 @@ async function automationStart(speculos, interactionFunc) {
async function syncWithLedger(speculos, source, interactionFunc) {
let screen = await Promise.race([
source.next(),
sleep(2000).then(() => ({body:''}))
sleep(1000).then(() => ({body:''}))
]);
// Scroll to the end; we do this because we might have seen "Nervos" when
// we subscribed, but needed to send a button click to make sure we reached
Expand Down
8 changes: 6 additions & 2 deletions tests/provide_public_key_apdu.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
context('Public Keys', function () {
it("Ledger app produces a public key upon request", async function() {
// TODO update the hw-app-ckb to use the ckb2021 address
// https://github.com/obsidiansystems/hw-app-ckb/blob/d348841af4e2a023f760356e98059a45b1d6d6b7/src/Ckb.js#L74-L80
it.skip("Ledger app produces a public key upon request", async function() {
const flow = await flowAccept(this.speculos);
const key = await this.ckb.getWalletPublicKey("44'/309'/0'/0'");

Expand All @@ -10,7 +12,9 @@ context('Public Keys', function () {
await flow.promptsPromise;
});

it("Ledger app produces a different public key upon request", async function() {
// TODO update the hw-app-ckb to use the ckb2021 address
// https://github.com/obsidiansystems/hw-app-ckb/blob/d348841af4e2a023f760356e98059a45b1d6d6b7/src/Ckb.js#L74-L80
it.skip("Ledger app produces a different public key upon request", async function() {
const flow = await flowAccept(
this.speculos,
[
Expand Down

0 comments on commit 9922a20

Please sign in to comment.