Skip to content

Commit

Permalink
[WIP] Round 1
Browse files Browse the repository at this point in the history
  • Loading branch information
bigspider committed May 20, 2024
1 parent ffa7d02 commit 9089b94
Show file tree
Hide file tree
Showing 9 changed files with 727 additions and 67 deletions.
4 changes: 4 additions & 0 deletions src/commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ typedef enum {
GET_MASTER_FINGERPRINT = 0x05,
SIGN_MESSAGE = 0x10,
} command_e;

// Tags used when yielding different objects with the YIELD client command.
#define CCMD_YIELD_MUSIG_PUBNONCE_TAG 0xffffffff
#define CCMD_YIELD_MUSIG_PARTIALSIGNATURE_TAG 0xfffffffe
2 changes: 1 addition & 1 deletion src/handler/lib/policy.c
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ execute_processor(policy_parser_state_t *state, policy_parser_processor_t proc,

// convenience function, split from get_derived_pubkey only to improve stack usage
// returns -1 on error, 0 if the returned key info has no wildcard (**), 1 if it has the wildcard
__attribute__((noinline, warn_unused_result)) static int get_extended_pubkey(
__attribute__((noinline, warn_unused_result)) int get_extended_pubkey(
dispatcher_context_t *dispatcher_context,
const wallet_derivation_info_t *wdi,
int key_index,
Expand Down
23 changes: 23 additions & 0 deletions src/handler/lib/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,29 @@ typedef struct {
bool change; // whether a change address or a receive address is derived
} wallet_derivation_info_t;

/**
* Computes the a derived compressed pubkey for one of the key of the wallet policy,
* for a given change/address_index combination.
*
* This function computes the extended public key (xpub) based on the provided
* BIP32 derivation path. It supports both standard BIP32 derivation and
* the derivation of Musig (multi-signature) keys.
*
* @param[in] dispatcher_context Pointer to the dispatcher content
* @param[in] wdi Pointer to a `wallet_derivation_info_t` struct with the details of the
* necessary details of the wallet policy, and the desired change/address_index pair.
* @param[in] key_index Index of the pubkey in the vector of keys of the wallet policy.
* @param[out] out Pointer to a `serialized_extended_pubkey_t` that will contain the requested
* extended pubkey.
*
* @return -1 on error, 0 if the returned key info has no wildcard (**), 1 if it has the wildcard.
*/
__attribute__((warn_unused_result)) int get_extended_pubkey(
dispatcher_context_t *dispatcher_context,
const wallet_derivation_info_t *wdi,
int key_index,
serialized_extended_pubkey_t *out);

/**
* Computes the hash of a taptree, to be used as tweak for the internal key per BIP-0341;
* The returned hash is the second value in the tuple returned by taproot_tree_helper in
Expand Down
433 changes: 367 additions & 66 deletions src/handler/sign_psbt.c

Large diffs are not rendered by default.

62 changes: 62 additions & 0 deletions src/musig/musig.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ static const uint8_t BIP0327_keyagg_coeff_tag[] =
{'K', 'e', 'y', 'A', 'g', 'g', ' ', 'c', 'o', 'e', 'f', 'f', 'i', 'c', 'i', 'e', 'n', 't'};
static const uint8_t BIP0327_keyagg_list_tag[] =
{'K', 'e', 'y', 'A', 'g', 'g', ' ', 'l', 'i', 's', 't'};
static const uint8_t BIP0327_nonce_tag[] = {'M', 'u', 'S', 'i', 'g', '/', 'n', 'o', 'n', 'c', 'e'};

static inline bool is_point_infinite(const point_t *P) {
return P->prefix == 0;
Expand Down Expand Up @@ -144,3 +145,64 @@ int musig_key_agg(const plain_pk_t pubkeys[], size_t n_keys, musig_keyagg_contex
ctx->gacc[31] = 1;
return 0;
}

static void nonce_hash(const uint8_t *rand,
const plain_pk_t pk,
const xonly_pk_t aggpk,
uint8_t i,
const uint8_t *msg_prefixed,
size_t msg_prefixed_len,
const uint8_t *extra_in,
size_t extra_in_len,
uint8_t out[static CX_SHA256_SIZE]) {
cx_sha256_t hash_context;
crypto_tr_tagged_hash_init(&hash_context, BIP0327_nonce_tag, sizeof(BIP0327_nonce_tag));

crypto_hash_update(&hash_context.header, rand, 32);

crypto_hash_update_u8(&hash_context.header, sizeof(plain_pk_t));
crypto_hash_update(&hash_context.header, pk, sizeof(plain_pk_t));

crypto_hash_update_u8(&hash_context.header, sizeof(xonly_pk_t));
crypto_hash_update(&hash_context.header, aggpk, sizeof(xonly_pk_t));

crypto_hash_update(&hash_context.header, msg_prefixed, msg_prefixed_len);

crypto_hash_update(&hash_context.header, extra_in, extra_in_len);

crypto_hash_update_u8(&hash_context.header, i);

crypto_hash_digest(&hash_context.header, out, CX_SHA256_SIZE);
}

// same as nonce_gen_internal from the reference, removing the optional arguments sk, msg and
// extra_in, and making aggpk compulsory
// TODO: handle errors
int musig_nonce_gen(uint8_t rand[32],
const plain_pk_t pk,
const xonly_pk_t aggpk,
musig_secnonce_t *secnonce,
musig_pubnonce_t *pubnonce) {
uint8_t msg[] = {0x00};

nonce_hash(rand, pk, aggpk, 0, msg, 1, NULL, 0, secnonce->k_1);
if (CX_OK != cx_math_modm_no_throw(secnonce->k_1, 32, secp256k1_n, 32)) return -1;
nonce_hash(rand, pk, aggpk, 1, msg, 1, NULL, 0, secnonce->k_2);
if (CX_OK != cx_math_modm_no_throw(secnonce->k_2, 32, secp256k1_n, 32)) return -1;

memcpy(secnonce->pk, pk, 33);

point_t R1, R2;

memcpy(R1.raw, secp256k1_generator, sizeof(secp256k1_generator));
if (CX_OK != cx_ecfp_scalar_mult_no_throw(CX_CURVE_SECP256K1, R1.raw, secnonce->k_1, 32))
return -1;
memcpy(R2.raw, secp256k1_generator, sizeof(secp256k1_generator));
if (CX_OK != cx_ecfp_scalar_mult_no_throw(CX_CURVE_SECP256K1, R2.raw, secnonce->k_2, 32))
return -1;

crypto_get_compressed_pubkey(R1.raw, pubnonce->R_s1);
crypto_get_compressed_pubkey(R2.raw, pubnonce->R_s2);

return 0;
}
33 changes: 33 additions & 0 deletions src/musig/musig.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ typedef struct musig_keyagg_context_s {
uint8_t tacc[32];
} musig_keyagg_context_t;

typedef struct musig_secnonce_s {
uint8_t k_1[32];
uint8_t k_2[32];
uint8_t pk[33];
} musig_secnonce_t;

typedef struct musig_pubnonce_s {
uint8_t R_s1[33];
uint8_t R_s2[33];
} musig_pubnonce_t;

/**
* Computes the KeyAgg Context per BIP-0327.
*
Expand All @@ -43,3 +54,25 @@ typedef struct musig_keyagg_context_s {
* @return 0 on success, a negative number in case of error.
*/
int musig_key_agg(const plain_pk_t pubkeys[], size_t n_keys, musig_keyagg_context_t *ctx);

/**
* Generates secret and public nonces (round 1 of MuSig per BIP-0327).
*
* @param[in] rand
* The randomness to use.
* @param[in] pk
* The 33-byte public key of the signer.
* @param[in] aggpk
* The 32-byte x-only aggregate public key.
* @param[out] secnonce
* Pointer to receive the secret nonce.
* @param[out] pubnonce
* Pointer to receive the public nonce.
*
* @return 0 on success, a negative number in case of error.
*/
int musig_nonce_gen(uint8_t rand[32],
const plain_pk_t pk,
const xonly_pk_t aggpk,
musig_secnonce_t *secnonce,
musig_pubnonce_t *pubnonce);
64 changes: 64 additions & 0 deletions src/musig/musig_sessions.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#include <string.h>

#include "cx.h"

#include "musig_sessions.h"
#include "../crypto.h"

// TODO: persist in NVRAM instead
musig_session_t musig_sessions[MAX_N_MUSIG_SESSIONS];

bool musigsession_pop(uint8_t psbt_session_id[static 32], musig_session_t *out) {
for (int i = 0; i < MAX_N_MUSIG_SESSIONS; i++) {
if (memcmp(psbt_session_id, musig_sessions[i].id, 32) == 0) {
if (out != NULL) {
memcpy(out, &musig_sessions[i], sizeof(musig_session_t));
}
explicit_bzero(&musig_sessions[i], sizeof(musig_session_t));
return true;
}
}
return false;
}

static bool is_all_zeros(const uint8_t *array, size_t size) {
for (size_t i = 0; i < size; ++i) {
if (array[i] != 0) {
return false;
}
}
return true;
}

void musigsession_init_randomness(musig_session_t *session) {
cx_get_random_bytes(session->rand_root, 32);
}

void musigsession_store(uint8_t psbt_session_id[static 32], const musig_session_t *session) {
// make sure that no session with the same id exists; delete it otherwise
musigsession_pop(psbt_session_id, NULL);

int i;
for (i = 0; i < MAX_N_MUSIG_SESSIONS; i++) {
if (is_all_zeros((uint8_t *) &musig_sessions[i], sizeof(musig_session_t))) {
break;
}
}
if (i >= MAX_N_MUSIG_SESSIONS) {
// no free slot found, delete the first by default
// TODO: should we use a LIFO structure? Could add a counter to musig_session_t
i = 0;
}
// no free slot; replace the first slot
explicit_bzero(&musig_sessions[i], sizeof(musig_session_t));
memcpy(&musig_sessions[i], session, sizeof(musig_session_t));
}

void compute_rand_i_j(const uint8_t rand_root[static 32], int i, int j, uint8_t out[static 32]) {
cx_sha256_t hash_context;
cx_sha256_init(&hash_context);
crypto_hash_update(&hash_context.header, rand_root, CX_SHA256_SIZE);
crypto_hash_update_u32(&hash_context.header, (uint32_t) i);
crypto_hash_update_u32(&hash_context.header, (uint32_t) j);
crypto_hash_digest(&hash_context.header, out, 32);
}
20 changes: 20 additions & 0 deletions src/musig/musig_sessions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#pragma once

#include <stdbool.h>
#include "musig.h"

#define MAX_N_MUSIG_SESSIONS 8

typedef struct {
uint8_t id[32];
uint8_t rand_root[32];
} musig_session_t;

extern musig_session_t musig_sessions[MAX_N_MUSIG_SESSIONS];

// TODO: docs
bool musigsession_pop(uint8_t psbt_session_id[static 32], musig_session_t *out);
void musigsession_init_randomness(musig_session_t *session);
void musigsession_store(uint8_t psbt_session_id[static 32], const musig_session_t *session);

void compute_rand_i_j(const uint8_t rand_root[static 32], int i, int j, uint8_t out[static 32]);
Loading

0 comments on commit 9089b94

Please sign in to comment.