-
Notifications
You must be signed in to change notification settings - Fork 74
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Loading status checks…
Merge pull request #294 from LedgerHQ/musig2
MuSig2 support
Showing
776 changed files
with
6,095 additions
and
950 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
# extracted from the BIP327 reference implementation: https://github.com/bitcoin/bips/blob/b3701faef2bdb98a0d7ace4eedbeefa2da4c89ed/bip-0327/reference.py | ||
|
||
# Only contains the key aggregation part of the library | ||
|
||
# The code in this source file is distributed under the BSD-3-Clause. | ||
|
||
# autopep8: off | ||
|
||
from typing import List, Optional, Tuple, NewType, NamedTuple | ||
import hashlib | ||
|
||
# | ||
# The following helper functions were copied from the BIP-340 reference implementation: | ||
# https://github.com/bitcoin/bips/blob/master/bip-0340/reference.py | ||
# | ||
|
||
p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F | ||
n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 | ||
|
||
# Points are tuples of X and Y coordinates and the point at infinity is | ||
# represented by the None keyword. | ||
G = (0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798, 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8) | ||
|
||
Point = Tuple[int, int] | ||
|
||
# This implementation can be sped up by storing the midstate after hashing | ||
# tag_hash instead of rehashing it all the time. | ||
def tagged_hash(tag: str, msg: bytes) -> bytes: | ||
tag_hash = hashlib.sha256(tag.encode()).digest() | ||
return hashlib.sha256(tag_hash + tag_hash + msg).digest() | ||
|
||
def is_infinite(P: Optional[Point]) -> bool: | ||
return P is None | ||
|
||
def x(P: Point) -> int: | ||
assert not is_infinite(P) | ||
return P[0] | ||
|
||
def y(P: Point) -> int: | ||
assert not is_infinite(P) | ||
return P[1] | ||
|
||
def point_add(P1: Optional[Point], P2: Optional[Point]) -> Optional[Point]: | ||
if P1 is None: | ||
return P2 | ||
if P2 is None: | ||
return P1 | ||
if (x(P1) == x(P2)) and (y(P1) != y(P2)): | ||
return None | ||
if P1 == P2: | ||
lam = (3 * x(P1) * x(P1) * pow(2 * y(P1), p - 2, p)) % p | ||
else: | ||
lam = ((y(P2) - y(P1)) * pow(x(P2) - x(P1), p - 2, p)) % p | ||
x3 = (lam * lam - x(P1) - x(P2)) % p | ||
return (x3, (lam * (x(P1) - x3) - y(P1)) % p) | ||
|
||
def point_mul(P: Optional[Point], n: int) -> Optional[Point]: | ||
R = None | ||
for i in range(256): | ||
if (n >> i) & 1: | ||
R = point_add(R, P) | ||
P = point_add(P, P) | ||
return R | ||
|
||
def bytes_from_int(x: int) -> bytes: | ||
return x.to_bytes(32, byteorder="big") | ||
|
||
def lift_x(b: bytes) -> Optional[Point]: | ||
x = int_from_bytes(b) | ||
if x >= p: | ||
return None | ||
y_sq = (pow(x, 3, p) + 7) % p | ||
y = pow(y_sq, (p + 1) // 4, p) | ||
if pow(y, 2, p) != y_sq: | ||
return None | ||
return (x, y if y & 1 == 0 else p-y) | ||
|
||
def int_from_bytes(b: bytes) -> int: | ||
return int.from_bytes(b, byteorder="big") | ||
|
||
def has_even_y(P: Point) -> bool: | ||
assert not is_infinite(P) | ||
return y(P) % 2 == 0 | ||
|
||
# | ||
# End of helper functions copied from BIP-340 reference implementation. | ||
# | ||
|
||
PlainPk = NewType('PlainPk', bytes) | ||
XonlyPk = NewType('XonlyPk', bytes) | ||
|
||
# There are two types of exceptions that can be raised by this implementation: | ||
# - ValueError for indicating that an input doesn't conform to some function | ||
# precondition (e.g. an input array is the wrong length, a serialized | ||
# representation doesn't have the correct format). | ||
# - InvalidContributionError for indicating that a signer (or the | ||
# aggregator) is misbehaving in the protocol. | ||
# | ||
# Assertions are used to (1) satisfy the type-checking system, and (2) check for | ||
# inconvenient events that can't happen except with negligible probability (e.g. | ||
# output of a hash function is 0) and can't be manually triggered by any | ||
# signer. | ||
|
||
# This exception is raised if a party (signer or nonce aggregator) sends invalid | ||
# values. Actual implementations should not crash when receiving invalid | ||
# contributions. Instead, they should hold the offending party accountable. | ||
class InvalidContributionError(Exception): | ||
def __init__(self, signer, contrib): | ||
self.signer = signer | ||
# contrib is one of "pubkey", "pubnonce", "aggnonce", or "psig". | ||
self.contrib = contrib | ||
|
||
infinity = None | ||
|
||
def xbytes(P: Point) -> bytes: | ||
return bytes_from_int(x(P)) | ||
|
||
def cbytes(P: Point) -> bytes: | ||
a = b'\x02' if has_even_y(P) else b'\x03' | ||
return a + xbytes(P) | ||
|
||
def point_negate(P: Optional[Point]) -> Optional[Point]: | ||
if P is None: | ||
return P | ||
return (x(P), p - y(P)) | ||
|
||
def cpoint(x: bytes) -> Point: | ||
if len(x) != 33: | ||
raise ValueError('x is not a valid compressed point.') | ||
P = lift_x(x[1:33]) | ||
if P is None: | ||
raise ValueError('x is not a valid compressed point.') | ||
if x[0] == 2: | ||
return P | ||
elif x[0] == 3: | ||
P = point_negate(P) | ||
assert P is not None | ||
return P | ||
else: | ||
raise ValueError('x is not a valid compressed point.') | ||
|
||
KeyAggContext = NamedTuple('KeyAggContext', [('Q', Point), | ||
('gacc', int), | ||
('tacc', int)]) | ||
|
||
def key_agg(pubkeys: List[PlainPk]) -> KeyAggContext: | ||
pk2 = get_second_key(pubkeys) | ||
u = len(pubkeys) | ||
Q = infinity | ||
for i in range(u): | ||
try: | ||
P_i = cpoint(pubkeys[i]) | ||
except ValueError: | ||
raise InvalidContributionError(i, "pubkey") | ||
a_i = key_agg_coeff_internal(pubkeys, pubkeys[i], pk2) | ||
Q = point_add(Q, point_mul(P_i, a_i)) | ||
# Q is not the point at infinity except with negligible probability. | ||
assert(Q is not None) | ||
gacc = 1 | ||
tacc = 0 | ||
return KeyAggContext(Q, gacc, tacc) | ||
|
||
def hash_keys(pubkeys: List[PlainPk]) -> bytes: | ||
return tagged_hash('KeyAgg list', b''.join(pubkeys)) | ||
|
||
def get_second_key(pubkeys: List[PlainPk]) -> PlainPk: | ||
u = len(pubkeys) | ||
for j in range(1, u): | ||
if pubkeys[j] != pubkeys[0]: | ||
return pubkeys[j] | ||
return PlainPk(b'\x00'*33) | ||
|
||
def key_agg_coeff_internal(pubkeys: List[PlainPk], pk_: PlainPk, pk2: PlainPk) -> int: | ||
L = hash_keys(pubkeys) | ||
if pk_ == pk2: | ||
return 1 | ||
return int_from_bytes(tagged_hash('KeyAgg coefficient', L + pk_)) % n |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,9 @@ | ||
pytest>=6.1.1,<7.0.0 | ||
pytest-timeout>=2.1.0,<3.0.0 | ||
bip32>=3.4,<4.0 | ||
ledgercomm>=1.1.0,<1.2.0 | ||
ecdsa>=0.16.1,<0.17.0 | ||
typing-extensions>=3.7,<4.0 | ||
embit>=0.7.0,<0.8.0 | ||
mnemonic==0.20 | ||
bip32>=3.4,<4.0 | ||
pytest>=6.1.1,<7.0.0 | ||
pytest-timeout>=2.1.0,<3.0.0 | ||
speculos>=0.12.0,<0.13.0 | ||
typing-extensions>=3.7,<4.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
# MuSig2 | ||
|
||
The Ledger Bitcoin app supports wallet policies with `musig()` key expressions. | ||
|
||
MuSig2 is a 2-round multi-signature scheme compatible with the public keys and signatures used in taproot transactions. The implementation is compliant with [BIP-0327](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki). | ||
|
||
## Specs | ||
|
||
`musig()` key expressions are supported for all taproot policies, including taproot keypaths and miniscript. | ||
|
||
- At most 16 keys are allowed in the musig expression; performance limitations, however, might apply in practice. | ||
- At most 8 parallel MuSig signing sessions are supported, due to the need to persist state in the device's memory. | ||
- Only `musig(...)/**` or `musig(...)/<M;N>/*` key expressions are supported; the public keys must be xpubs aggregated without any further derivation. Schemes where each pubkey is derived prior to aggregation (for example descriptors similar to `musig(xpub1/<0;1>/*,xpub2/<0;1>/*,...)`) are not supported. | ||
|
||
## State minimization | ||
|
||
This section describes implementation details that allow to minimize the amount of statefor each MuSig2 signing session, allowing secure support for multiple parallel MuSig2 on embedded device with limited storage. | ||
|
||
### Introduction | ||
|
||
BIP-0327 discusses at length the necessity to keep some state during a signing session. However, a "signing session" in BIP-0327 only refers to the production of a single signature. | ||
|
||
In the typical signing flow of a wallet, it's more logical to consider a _session_ at the level of an entire transaction. All transaction inputs are likely obtained from the same [descriptor containing musig()](https://github.com/bitcoin/bips/pull/1540), with the signer producing the pubnonce/signature for all the inputs at once. | ||
|
||
Therefore, in the flow of BIP-0327, you would expect at least _one MuSig2 signing session per input_ to be active at the same time. In the context of hardware signing device support, that's somewhat problematic: it would require to persist state for an unbounded number of signing sessions, for example for a wallet that received a large number of small UTXOs. Persistent storage is often a scarce resource in embedded signing devices, and a naive approach would likely impose a maximum limit on the number of inputs of the transactions, depending on the hardware limitations. | ||
|
||
This document describes an approach that is compatible with and builds on top of BIP-0327 to define a _psbt-level session_ with only a small amount of state persisted on the device. Each psbt-level session allows to manage in parallel all the MuSig2 sessions involved in signing a transaction (typically, at least one for each input). Each psbt-level session only requires 64 bytes of storage for the entire transaction, regardless of the amount of inputs. | ||
|
||
### Signing flow with synthetic randomness | ||
|
||
#### Synthetic generation of BIP-0327 state | ||
|
||
This section presents the core idea, while the next section makes it more precise in the context of signing devices. | ||
|
||
In BIP-0327, the internal state that is kept by the signing device is essentially the *secnonce*, which in turn is computed from a random number _rand'_, and optionally from other parameters of _NonceGen_ which depend on the transaction being signed. | ||
|
||
The core idea for state minimization is to compute a global random `rand_root`; then, for the *i*-th input and for the *j*-th `musig()` key that the device is signing for in the [wallet policy](https://github.com/bitcoin/bips/pull/1389), one defines the *rand'* in _NonceGen_ as: | ||
|
||
$\qquad rand_{i,j} = SHA256(rand\_root || i || j)$ | ||
|
||
In the concatenation, a fixed-length encoding of $i$ and $j$ is used in order to avoid collisions. That is used as the *rand'* value in the *NonceGen* algorithm for that input/KEY pair. | ||
|
||
The *j* parameter allows to handle wallet policies that contain more than one `musig()` key expression involving the signing device. | ||
|
||
#### Signing flow in detail | ||
|
||
This section describes the handling of the psbt-level sessions, plugging on top of the default signing flow of BIP-0327. | ||
|
||
We assume that the signing device handles a single psbt-level session; this can be generalized to multiple parallel psbt-level sessions, where each session computes and stores a different `rand_root`. | ||
|
||
In the following, a _session_ always refers to the psbt-level signing session; it contains `rand_root`, and possibly any other auxiliary data that the device wishes to save while signing is in progress. | ||
|
||
The term *persistent memory* refers to secure storage that is not wiped out when the device is turned off. The term *volatile memory* refers to the working memory available while the device is involved in the signing process. In Ledger signing devices, the persistent storage is flash memory, and the volatile memory is the RAM of the app. Both are contained in the Secure Element. | ||
|
||
**Phase 1: pubnonce generation:** A PSBT is sent to the signing device, and it does not contain any pubnonce. | ||
- If a session already exists, it is deleted from the persistent memory. | ||
- A new session is created in volatile memory. | ||
- The device produces a fresh random number $rand\_root$, and saves it in the current session. | ||
- The device generates the randomness for the $i$-th input and for the $j$-th key as: $rand_{i,j} = SHA256(rand\_root || i || j)$. | ||
- Compute each *(secnonce, pubnonce)* as per the `NonceGen` algorithm. | ||
- At completion (after all the pubnonces are returned), the session secret $rand\_root$ is copied into the persistent memory. | ||
|
||
**Phase 2: partial signature generation:** A PSBT containing all the pubnonces is sent to the device. | ||
- *A copy of the session is stored in the volatile memory, and the session is deleted from the persistent memory*. | ||
- For each input/musig-key pair $(i, j)$: | ||
- Recompute the pubnonce/secnonce pair using `NonceGen` with the synthetic randomness $rand_{i,j}$ as above. | ||
- Verify that the pubnonce contained in the PSBT matches the one synthetically recomputed. | ||
- Continue the signing flow as per BIP-0327, generating the partial signature. | ||
|
||
### Security considerations | ||
#### State reuse avoidance | ||
Storing the session in persistent memory only at the end of Phase 1, and deleting it before beginning Phase 2 simplifies auditing and making sure that there is no reuse of state across signing sessions. | ||
|
||
#### Security of synthetic randomness | ||
|
||
Generating $rand_{i, j}$ synthetically is not a problem, since the $rand\_root$ value is kept secret and never leaves the device. This ensures that all the values produced for different $i$ and $j$ are not predictable for an attacker. | ||
|
||
#### Malleability of the PSBT | ||
If the optional parameters are passed to the _NonceGen_ function, they will depend on the transaction data present in the PSBT. Therefore, there is no guarantee that they will be unchanged the next time the PSBT is provided. | ||
|
||
However, that does not constitute a security risk, as those parameters are only used as additional sources of entropy in _NonceGen_. A malicious software wallet can't affect the _secnonce_/_pubnonce_ pairs in any predictable way. Changing any of the parameters used in _NonceGen_ would cause a failure during Phase 2, as the recomputed _pubnonce_ would not match the one in the psbt. | ||
|
||
### Generalization to multiple PSBT signing sessions | ||
|
||
The approach described above assumes that no attempt to sign a PSBT for a wallet policy containing `musig()` keys is initiated while a session is already in progress. | ||
|
||
In order to generalize this to an arbitrary number of parallel signing sessions, one can identify each signing session with a `psbt_session_id`. Such `psbt_session_id` should deterministically depend on the transaction being signed (ignoring all the other PSBT fields), and the wallet policy being signed. In praticular, the computed `psbt_session_id` should be identical between Round 1 and Round 2 of the protocol. Note that malicious collisions of the `psbt_session_id` (for example by tampering with some details of the PSBT, like the SIGHASH flags) _are_ possible, but they do not constitute a security risk. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
#pragma once | ||
|
||
#include "../musig/musig_sessions.h" | ||
#include "../common/merkle.h" | ||
#include "../ui/display.h" | ||
|
||
// common info that applies to either the current input or the current output | ||
typedef struct { | ||
merkleized_map_commitment_t map; | ||
|
||
bool unexpected_pubkey_error; // Set to true if the pubkey in the keydata of | ||
// PSBT_{IN,OUT}_BIP32_DERIVATION or | ||
// PSBT_{IN,OUT}_TAP_BIP32_DERIVATION is not the correct length. | ||
|
||
bool key_expression_found; // Set to true if the input/output info in the psbt was correctly | ||
// matched with the current key expression in the signing flow | ||
|
||
bool is_change; | ||
int address_index; | ||
|
||
// For an output, its scriptPubKey | ||
// for an input, the prevout's scriptPubKey (either from the non-witness-utxo, or from the | ||
// witness-utxo) | ||
|
||
uint8_t scriptPubKey[MAX_OUTPUT_SCRIPTPUBKEY_LEN]; | ||
size_t scriptPubKey_len; | ||
} in_out_info_t; | ||
|
||
typedef struct { | ||
in_out_info_t in_out; | ||
bool has_witnessUtxo; | ||
bool has_nonWitnessUtxo; | ||
bool has_redeemScript; | ||
bool has_sighash_type; | ||
|
||
uint64_t prevout_amount; // the value of the prevout of the current input | ||
|
||
// we no longer need the script when we compute the taptree hash right before a taproot key-path | ||
// spending; therefore, we reuse the same memory | ||
union { | ||
// the script used when signing, either from the witness utxo or the redeem script | ||
uint8_t script[MAX_PREVOUT_SCRIPTPUBKEY_LEN]; | ||
uint8_t taptree_hash[32]; | ||
}; | ||
|
||
size_t script_len; | ||
|
||
uint32_t sighash_type; | ||
} input_info_t; | ||
|
||
typedef struct { | ||
in_out_info_t in_out; | ||
uint64_t value; | ||
} output_info_t; | ||
|
||
typedef struct { | ||
policy_node_keyexpr_t *key_expression_ptr; | ||
// index of this key expression in the descriptor template, in parsing order | ||
int index; | ||
uint32_t fingerprint; | ||
|
||
// we only sign for keys expressions for which we find a matching key derivation in the PSBT, | ||
// at least for one of the inputs | ||
bool to_sign; | ||
|
||
// info about the internal key of this key expression | ||
// used at signing time to derive the correct key | ||
uint32_t key_derivation[MAX_BIP32_PATH_STEPS]; | ||
uint8_t key_derivation_length; | ||
|
||
// same as key_derivation_length for internal key | ||
// expressions; 0 for musig, as the key derivation in | ||
// the PSBT use the aggregate key as the root | ||
// used to identify the correct change/address_index from the psbt | ||
uint8_t psbt_root_key_derivation_length; | ||
|
||
// the root pubkey of this key expression | ||
serialized_extended_pubkey_t pubkey; | ||
// the pubkey of the internal key of this key expression. | ||
// same as `pubkey` for simple key expressions, but it's the actual | ||
// internal key for musig key expressions | ||
serialized_extended_pubkey_t internal_pubkey; | ||
|
||
bool is_tapscript; // true if signing with a BIP342 tapleaf script path spend | ||
// only used for tapscripts | ||
const policy_node_t *tapleaf_ptr; | ||
uint8_t tapleaf_hash[32]; | ||
} keyexpr_info_t; | ||
|
||
// Cache for partial hashes during signing (avoid quadratic hashing for segwit transactions) | ||
typedef struct tx_hashes_s { | ||
uint8_t sha_prevouts[32]; | ||
uint8_t sha_amounts[32]; | ||
uint8_t sha_scriptpubkeys[32]; | ||
uint8_t sha_sequences[32]; | ||
uint8_t sha_outputs[32]; | ||
} tx_hashes_t; | ||
|
||
// the signing state for the current transaction; it does not contain any per-input state | ||
typedef struct signing_state_s { | ||
tx_hashes_t tx_hashes; | ||
musig_signing_state_t musig; | ||
} signing_state_t; | ||
|
||
// We cache the first 2 external outputs; that's needed for the swap checks | ||
// Moreover, this helps the code for the simplified UX for transactions that | ||
// have a single external output. | ||
#define N_CACHED_EXTERNAL_OUTPUTS 2 | ||
|
||
typedef struct { | ||
uint32_t master_key_fingerprint; | ||
uint32_t tx_version; | ||
uint32_t locktime; | ||
|
||
unsigned int n_internal_key_expressions; | ||
keyexpr_info_t internal_key_expressions[MAX_INTERNAL_KEY_EXPRESSIONS]; | ||
|
||
unsigned int n_inputs; | ||
uint8_t inputs_root[32]; // merkle root of the vector of input maps commitments | ||
unsigned int n_outputs; | ||
uint8_t outputs_root[32]; // merkle root of the vector of output maps commitments | ||
|
||
uint64_t inputs_total_amount; | ||
|
||
policy_map_wallet_header_t wallet_header; | ||
|
||
unsigned int n_external_inputs; | ||
unsigned int n_external_outputs; | ||
|
||
// set to true if at least a PSBT_IN_MUSIG2_PUB_NONCE field is present in the PSBT | ||
bool has_musig2_pub_nonces; | ||
|
||
// aggregate info on outputs | ||
struct { | ||
uint64_t total_amount; // amount of all the outputs (external + change) | ||
uint64_t change_total_amount; // total amount of all change outputs | ||
int n_change; // count of outputs compatible with change outputs | ||
size_t output_script_lengths[N_CACHED_EXTERNAL_OUTPUTS]; | ||
uint8_t output_scripts[N_CACHED_EXTERNAL_OUTPUTS][MAX_OUTPUT_SCRIPTPUBKEY_LEN]; | ||
uint64_t output_amounts[N_CACHED_EXTERNAL_OUTPUTS]; | ||
} outputs; | ||
|
||
bool is_wallet_default; | ||
|
||
uint8_t protocol_version; | ||
|
||
__attribute__((aligned(4))) uint8_t wallet_policy_map_bytes[MAX_WALLET_POLICY_BYTES]; | ||
policy_node_t *wallet_policy_map; | ||
|
||
tx_ux_warning_t warnings; | ||
|
||
} sign_psbt_state_t; |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
#include <stdint.h> | ||
#include "../common/wallet.h" | ||
#include "../musig/musig.h" | ||
#include "../boilerplate/dispatcher.h" | ||
#include "../sign_psbt.h" | ||
|
||
// Struct to hold the info computed for a given input in either of the two rounds | ||
typedef struct { | ||
plain_pk_t keys[MAX_PUBKEYS_PER_MUSIG]; | ||
serialized_extended_pubkey_t agg_key_tweaked; | ||
uint8_t psbt_session_id[32]; | ||
uint8_t tweaks[3][32]; // 2 or three tweaks | ||
size_t n_tweaks; // always 2 or 3 for supported BIP-388 wallet policies | ||
bool is_xonly[3]; // 2 or 3 elements | ||
} musig_per_input_info_t; | ||
|
||
/** | ||
* Computes the MuSig2 per-input, per-key-expression information. | ||
* | ||
* This function calculates the necessary information for each input in the MuSig protocol. | ||
* It is the shared logic that is common between both rounds of the MuSig2 protocol. | ||
* | ||
* Returns true if the computation is successful, false otherwise. In case of failure, it already | ||
* sends an error status word. | ||
*/ | ||
bool compute_musig_per_input_info(dispatcher_context_t *dc, | ||
sign_psbt_state_t *st, | ||
signing_state_t *signing_state, | ||
const input_info_t *input, | ||
const keyexpr_info_t *keyexpr_info, | ||
musig_per_input_info_t *out); | ||
|
||
/** | ||
* Computes and yields the pubnonce for the current input and placeholder, during Round 1 of the | ||
* MuSig2 protocol. | ||
* | ||
* Returns true if the computation is successful, false otherwise. In case of failure, it already | ||
* sends an error status word. | ||
*/ | ||
bool produce_and_yield_pubnonce(dispatcher_context_t *dc, | ||
sign_psbt_state_t *st, | ||
signing_state_t *signing_state, | ||
const keyexpr_info_t *keyexpr_info, | ||
const input_info_t *input, | ||
unsigned int cur_input_index); | ||
|
||
/** | ||
* Computes and yields the partial signature for a certain sighash, during Round 2 of the MuSig2 | ||
* protocol. | ||
* | ||
* Returns true if the computation is successful, false otherwise. In case of failure, it already | ||
* sends an error status word. | ||
*/ | ||
bool sign_sighash_musig_and_yield(dispatcher_context_t *dc, | ||
sign_psbt_state_t *st, | ||
signing_state_t *signing_state, | ||
const keyexpr_info_t *keyexpr_info, | ||
const input_info_t *input, | ||
unsigned int cur_input_index, | ||
uint8_t sighash[static 32]); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
#pragma once | ||
|
||
#include <stdint.h> | ||
#include <stddef.h> | ||
|
||
#define MUSIG_PUBNONCE_SIZE 66 | ||
|
||
static uint8_t BIP_328_CHAINCODE[32] = { | ||
0x86, 0x80, 0x87, 0xCA, 0x02, 0xA6, 0xF9, 0x74, 0xC4, 0x59, 0x89, 0x24, 0xC3, 0x6B, 0x57, 0x76, | ||
0x2D, 0x32, 0xCB, 0x45, 0x71, 0x71, 0x67, 0xE3, 0x00, 0x62, 0x2C, 0x71, 0x67, 0xE3, 0x89, 0x65}; | ||
|
||
typedef uint8_t plain_pk_t[33]; | ||
typedef uint8_t xonly_pk_t[32]; | ||
|
||
// An uncompressed pubkey, encoded as 04||x||y, where x and y are 32-byte big-endian coordinates. | ||
// If the first byte (prefix) is 0, encodes the point at infinity. | ||
typedef struct { | ||
union { | ||
uint8_t raw[65]; | ||
struct { | ||
uint8_t prefix; // 0 for the point at infinity, otherwise 4. | ||
uint8_t x[32]; | ||
uint8_t y[32]; | ||
}; | ||
}; | ||
} point_t; | ||
|
||
typedef struct musig_keyagg_context_s { | ||
point_t Q; | ||
uint8_t gacc[32]; | ||
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 { | ||
union { | ||
struct { | ||
uint8_t R_s1[33]; | ||
uint8_t R_s2[33]; | ||
}; | ||
uint8_t raw[66]; | ||
}; | ||
} musig_pubnonce_t; | ||
|
||
typedef struct musig_session_context_s { | ||
musig_pubnonce_t *aggnonce; | ||
size_t n_keys; | ||
plain_pk_t *pubkeys; | ||
size_t n_tweaks; | ||
uint8_t **tweaks; | ||
bool *is_xonly; | ||
uint8_t *msg; | ||
size_t msg_len; | ||
} musig_session_context_t; | ||
|
||
// Comparator for 33-byte compressed public key, in order to sort according to the KeySort | ||
// algorithm of BIP-327. | ||
static inline int compare_plain_pk(const void *a, const void *b) { | ||
return memcmp(a, b, sizeof(plain_pk_t)); | ||
} | ||
|
||
/** | ||
* Computes the KeyAgg Context per BIP-0327. | ||
* | ||
* @param[in] pubkeys | ||
* Pointer to a list of pubkeys. | ||
* @param[in] n_keys | ||
* Number of pubkeys. | ||
* @param[out] musig_keyagg_context_t | ||
* Pointer to receive the musig KeyAgg Context. | ||
* | ||
* @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(const uint8_t rand[32], | ||
const plain_pk_t pk, | ||
const xonly_pk_t aggpk, | ||
musig_secnonce_t *secnonce, | ||
musig_pubnonce_t *pubnonce); | ||
|
||
/** | ||
* Generates the aggregate nonce (nonce_agg in the reference implementation). | ||
* | ||
* @param[in] rand | ||
* A list of musig_pubnonce_t, the pubnonces of all the participants. | ||
* @param[in] n_keys | ||
* Number of pubkeys. | ||
* @param[out] out | ||
* Pointer to receive the aggregate nonce. | ||
* | ||
* @return 0 on success, a negative number in case of error. On error, `-i - 1` is returned if the | ||
* nonce provided by the cosigner with index `i` is invalid, in order to allow blaming for a | ||
* disruptive signer. | ||
*/ | ||
int musig_nonce_agg(const musig_pubnonce_t pubnonces[], size_t n_keys, musig_pubnonce_t *out); | ||
|
||
/** | ||
* Computes the partial signature (round 2 of MuSig per BIP-0327). | ||
* | ||
* @param[in] secnonce | ||
* The secret nonce. | ||
* @param[in] session_ctx | ||
* The session context. | ||
* @param[out] psig | ||
* Pointer to receive the partial signature. | ||
* | ||
* @return 0 on success, a negative number in case of error. | ||
*/ | ||
int musig_sign(musig_secnonce_t *secnonce, | ||
const uint8_t sk[static 32], | ||
const musig_session_context_t *session_ctx, | ||
uint8_t psig[static 32]); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
#include <string.h> | ||
|
||
#include "cx.h" | ||
|
||
#include "musig_sessions.h" | ||
#include "../crypto.h" | ||
|
||
typedef struct { | ||
// Aligning by 4 is necessary due to platform limitations. | ||
// Aligning by 64 further guarantees that each session occupies exactly | ||
// a single NVRAM page, minimizing the number of writes. | ||
__attribute__((aligned(64))) musig_psbt_session_t sessions[MAX_N_MUSIG_SESSIONS]; | ||
} musig_persistent_storage_t; | ||
|
||
const musig_persistent_storage_t N_musig_storage_real; | ||
#define N_musig_storage (*(const volatile musig_persistent_storage_t *) PIC(&N_musig_storage_real)) | ||
|
||
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; | ||
} | ||
|
||
static bool musigsession_pop(const uint8_t psbt_session_id[static 32], musig_psbt_session_t *out) { | ||
for (int i = 0; i < MAX_N_MUSIG_SESSIONS; i++) { | ||
if (memcmp(psbt_session_id, (const void *) N_musig_storage.sessions[i]._id, 32) == 0) { | ||
if (out != NULL) { | ||
memcpy(out, | ||
(const void *) &N_musig_storage.sessions[i], | ||
sizeof(musig_psbt_session_t)); | ||
} | ||
uint8_t zeros[sizeof(musig_psbt_session_t)] = {0}; | ||
nvm_write((void *) &N_musig_storage.sessions[i], | ||
(void *) zeros, | ||
sizeof(musig_psbt_session_t)); | ||
|
||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
static void musigsession_init_randomness(musig_psbt_session_t *session) { | ||
// it is extremely important that the randomness is initialized with a cryptographically strong | ||
// random number generator | ||
cx_get_random_bytes(session->_rand_root, 32); | ||
} | ||
|
||
static void musigsession_store(const uint8_t psbt_session_id[static 32], | ||
const musig_psbt_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 *) &N_musig_storage.sessions[i], sizeof(musig_psbt_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_psbt_session_t | ||
i = 0; | ||
} | ||
// replace the chosen slot | ||
nvm_write((void *) &N_musig_storage.sessions[i], | ||
(void *) session, | ||
sizeof(musig_psbt_session_t)); | ||
} | ||
|
||
void compute_rand_i_j(const musig_psbt_session_t *psbt_session, | ||
int i, | ||
int j, | ||
uint8_t out[static 32]) { | ||
// It is extremely important that different choices of the root of randomness, i and j always | ||
// produce a different result in out. | ||
// Failure would be catastrophic as it would cause nonce reuse, which in MuSig2 allows attackers | ||
// to recover the private key. | ||
|
||
cx_sha256_t hash_context; | ||
cx_sha256_init(&hash_context); | ||
crypto_hash_update(&hash_context.header, psbt_session->_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); | ||
} | ||
|
||
void musigsession_initialize_signing_state(musig_signing_state_t *musig_signing_state) { | ||
memset(musig_signing_state, 0, sizeof(musig_signing_state_t)); | ||
} | ||
|
||
const musig_psbt_session_t *musigsession_round1_initialize( | ||
uint8_t psbt_session_id[static 32], | ||
musig_signing_state_t *musig_signing_state) { | ||
// if an existing session for psbt_session_id exists, delete it | ||
if (musigsession_pop(psbt_session_id, NULL)) { | ||
// We wouldn't expect this: probably the client sent the same psbt for | ||
// round 1 twice, without adding the pubnonces to the psbt after the first round. | ||
// We delete the old session and start a fresh one, but we print a | ||
// warning if in debug mode. | ||
PRINTF("Session with the same id already existing\n"); | ||
} | ||
|
||
if (memcmp(musig_signing_state->_round1._id, psbt_session_id, 32) != 0) { | ||
// first input/placeholder pair using this session: initialize the session | ||
memcpy(musig_signing_state->_round1._id, psbt_session_id, 32); | ||
musigsession_init_randomness(&musig_signing_state->_round1); | ||
} | ||
|
||
return &musig_signing_state->_round1; | ||
} | ||
|
||
const musig_psbt_session_t *musigsession_round2_initialize( | ||
uint8_t psbt_session_id[static 32], | ||
musig_signing_state_t *musig_signing_state) { | ||
if (memcmp(musig_signing_state->_round2._id, psbt_session_id, 32) != 0) { | ||
// get and delete the musig session from permanent storage | ||
if (!musigsession_pop(psbt_session_id, &musig_signing_state->_round2)) { | ||
// The PSBT contains a partial nonce, but we do not have the corresponding psbt | ||
// session in storage. Either it was deleted, or the pubnonces were not real. Either | ||
// way, we cannot continue. | ||
PRINTF("Missing MuSig2 session\n"); | ||
return NULL; | ||
} | ||
} | ||
|
||
return &musig_signing_state->_round2; | ||
} | ||
|
||
void musigsession_commit(musig_signing_state_t *musig_signing_state) { | ||
uint8_t acc = 0; | ||
for (size_t i = 0; i < sizeof(musig_signing_state->_round1); i++) { | ||
acc |= musig_signing_state->_round1._id[i]; | ||
} | ||
// If round 1 was not executed, then there is nothing to store. | ||
// This assumes that musigsession_initialize_signing_state, therefore the field is zeroed out | ||
// if it wasn't used. | ||
if (acc != 0) { | ||
musigsession_store(musig_signing_state->_round1._id, &musig_signing_state->_round1); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
#pragma once | ||
|
||
#include <stdbool.h> | ||
#include "musig.h" | ||
|
||
/** | ||
* This module encapsulates the logic to manage the psbt-level MuSig2 sessions. See the | ||
* documentation in docs/musig.md for more information. | ||
*/ | ||
|
||
// the maximum number of musig sessions that are stored in permanent memory | ||
#define MAX_N_MUSIG_SESSIONS 8 | ||
|
||
// state of a musig_psbt_session. Members are private and must not be accessed directly by any | ||
// code outside of musig_sessions.c. | ||
typedef struct musig_psbt_session_s { | ||
uint8_t _id[32]; | ||
uint8_t _rand_root[32]; | ||
} musig_psbt_session_t; | ||
|
||
// volatile state for musig signing. Members are private and must not be accessed directly by any | ||
// code outside of musig_sessions.c. | ||
typedef struct musig_signing_state_s { | ||
// a session created during round 1; if signing completes (and in no other case), it is moved to | ||
// the persistent storage | ||
musig_psbt_session_t _round1; | ||
// a session that was removed from the persistent storage before any partial signature is | ||
// returned during round 2. It is deleted at the end of signing, and must _never_ be used again. | ||
musig_psbt_session_t _round2; | ||
} musig_signing_state_t; | ||
|
||
/** | ||
* Given a musig psbt session, computes the synthetic randomness for a given | ||
* (input_index, placeholder_index) pair. | ||
*/ | ||
void compute_rand_i_j(const musig_psbt_session_t *psbt_session, | ||
int input_index, | ||
int placeholder_index, | ||
uint8_t out[static 32]); | ||
|
||
/** | ||
* Make sure that the musig signing state is initialized correctly. | ||
* | ||
* This method must be called before musigsession_round1_initialize or | ||
* musigsession_round2_initialize are called in the code. | ||
* | ||
* This allows the calling code to not make any assumption about how | ||
* the inialization of the musig signing state is done. | ||
* | ||
* @param[in] musig_signing_state | ||
* Pointer to the musig signing state. | ||
*/ | ||
void musigsession_initialize_signing_state(musig_signing_state_t *musig_signing_state); | ||
|
||
/** | ||
* Handles the creation of a new musig psbt session into the volatile memory, or its retrieval (if | ||
* the session already exists). | ||
* It must be called when starting MuSig2 round 1 for a fixed input/placeholder pair, during the | ||
* signing process. | ||
* | ||
* @param[in] psbt_session_id | ||
* Pointer to the musig psbt session id. | ||
* @param[in] musig_signing_state | ||
* Pointer to the musig signing state. | ||
* | ||
* @return a musig_psbt_session_t on success, NULL on failure. | ||
*/ | ||
__attribute__((warn_unused_result)) const musig_psbt_session_t *musigsession_round1_initialize( | ||
uint8_t psbt_session_id[static 32], | ||
musig_signing_state_t *musig_signing_state); | ||
|
||
/** | ||
* Handles the retrieval of a musig psbt session from volatile memory (if it exists already) or its | ||
* retrieval from the persistent memory otherwise. The session is guaranteed to be deleted from the | ||
* persistent memory prior to returning. | ||
* It must be called when starting MuSig2 round 2 for a fixed input/placeholder pair, during the | ||
* signing process. | ||
* | ||
* @param[in] psbt_session_id | ||
* Pointer to the musig psbt session id. | ||
* @param[in] musig_signing_state | ||
* Pointer to the musig signing state. | ||
* | ||
* @return a musig_psbt_session_t on success, NULL on failure. | ||
*/ | ||
__attribute__((warn_unused_result)) const musig_psbt_session_t *musigsession_round2_initialize( | ||
uint8_t psbt_session_id[static 32], | ||
musig_signing_state_t *musig_signing_state); | ||
|
||
/** | ||
* If a session produced in round 1 is active in volatile memory, it is stored in the persistent | ||
* memory. | ||
* This must be called at the end of a successful signing flow, after all the public nonces have | ||
* been returned to the client. It must _not_ be called if any error occurs, or if the signing | ||
* process is aborted for any reason. | ||
* | ||
* @param[in] musig_signing_state | ||
* Pointer to the musig signing state. | ||
*/ | ||
void musigsession_commit(musig_signing_state_t *musig_signing_state); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
#include "secp256k1.h" | ||
|
||
// clang-format off | ||
const uint8_t secp256k1_generator[65] = { | ||
0x04, | ||
0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, 0x07, | ||
0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98, | ||
0x48, 0x3A, 0xDA, 0x77, 0x26, 0xA3, 0xC4, 0x65, 0x5D, 0xA4, 0xFB, 0xFC, 0x0E, 0x11, 0x08, 0xA8, | ||
0xFD, 0x17, 0xB4, 0x48, 0xA6, 0x85, 0x54, 0x19, 0x9C, 0x47, 0xD0, 0x8F, 0xFB, 0x10, 0xD4, 0xB8}; | ||
|
||
const uint8_t secp256k1_p[32] = { | ||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}; | ||
|
||
const uint8_t secp256k1_n[32] = { | ||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, | ||
0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41}; | ||
|
||
const uint8_t secp256k1_sqr_exponent[32] = { | ||
0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0x0c}; | ||
|
||
// clang-format on |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
#pragma once | ||
|
||
#include <stdint.h> | ||
|
||
/** | ||
* Generator for secp256k1, value 'g' defined in "Standards for Efficient Cryptography" | ||
* (SEC2) 2.7.1. | ||
*/ | ||
extern const uint8_t secp256k1_generator[65]; | ||
|
||
/** | ||
* Modulo for secp256k1 | ||
*/ | ||
extern const uint8_t secp256k1_p[32]; | ||
|
||
/** | ||
* Curve order for secp256k1 | ||
*/ | ||
extern const uint8_t secp256k1_n[32]; | ||
|
||
/** | ||
* (p + 1)/4, used to calculate square roots in secp256k1 | ||
*/ | ||
extern const uint8_t secp256k1_sqr_exponent[32]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.