ledger_bitcoin_client/
command.rs1use bitcoin::{
4 bip32::{ChildNumber, DerivationPath},
5 consensus::encode::{self, VarInt},
6};
7use core::default::Default;
8
9use super::{
10 apdu::{self, APDUCommand},
11 wallet::WalletPolicy,
12};
13
14pub fn get_version() -> APDUCommand {
16 APDUCommand {
17 ins: apdu::BitcoinCommandCode::GetVersion as u8,
18 p2: 0x00,
19 ..Default::default()
20 }
21}
22
23pub fn get_master_fingerprint() -> APDUCommand {
25 APDUCommand {
26 cla: apdu::Cla::Bitcoin as u8,
27 ins: apdu::BitcoinCommandCode::GetMasterFingerprint as u8,
28 ..Default::default()
29 }
30}
31
32pub fn get_extended_pubkey(path: &DerivationPath, display: bool) -> APDUCommand {
34 let child_numbers: &[ChildNumber] = path.as_ref();
35 let data: Vec<u8> = child_numbers.iter().fold(
36 vec![
37 if display { 1_u8 } else { b'\0' },
38 child_numbers.len() as u8,
39 ],
40 |mut acc, &x| {
41 acc.extend_from_slice(&u32::from(x).to_be_bytes());
42 acc
43 },
44 );
45
46 APDUCommand {
47 cla: apdu::Cla::Bitcoin as u8,
48 ins: apdu::BitcoinCommandCode::GetExtendedPubkey as u8,
49 data,
50 ..Default::default()
51 }
52}
53
54pub fn register_wallet(policy: &WalletPolicy) -> APDUCommand {
56 let bytes = policy.serialize();
57 let mut data = encode::serialize(&VarInt(bytes.len() as u64));
58 data.extend(bytes);
59 APDUCommand {
60 cla: apdu::Cla::Bitcoin as u8,
61 ins: apdu::BitcoinCommandCode::RegisterWallet as u8,
62 data,
63 ..Default::default()
64 }
65}
66
67pub fn get_wallet_address(
69 policy: &WalletPolicy,
70 hmac: Option<&[u8; 32]>,
71 change: bool,
72 address_index: u32,
73 display: bool,
74) -> APDUCommand {
75 let mut data: Vec<u8> = Vec::with_capacity(70);
76 data.push(if display { 1_u8 } else { b'\0' });
77 data.extend_from_slice(&policy.id());
78 data.extend_from_slice(hmac.unwrap_or(&[b'\0'; 32]));
79 data.push(if change { 1_u8 } else { b'\0' });
80 data.extend_from_slice(&address_index.to_be_bytes());
81 APDUCommand {
82 cla: apdu::Cla::Bitcoin as u8,
83 ins: apdu::BitcoinCommandCode::GetWalletAddress as u8,
84 data,
85 ..Default::default()
86 }
87}
88
89pub fn sign_psbt(
91 global_mapping_commitment: &[u8],
92 inputs_number: usize,
93 input_commitments_root: &[u8; 32],
94 outputs_number: usize,
95 output_commitments_root: &[u8; 32],
96 policy: &WalletPolicy,
97 hmac: Option<&[u8; 32]>,
98) -> APDUCommand {
99 let mut data: Vec<u8> = Vec::new();
100 data.extend_from_slice(global_mapping_commitment);
101 data.extend(encode::serialize(&VarInt(inputs_number as u64)));
102 data.extend_from_slice(input_commitments_root);
103 data.extend(encode::serialize(&VarInt(outputs_number as u64)));
104 data.extend_from_slice(output_commitments_root);
105 data.extend_from_slice(&policy.id());
106 data.extend_from_slice(hmac.unwrap_or(&[b'\0'; 32]));
107 APDUCommand {
108 cla: apdu::Cla::Bitcoin as u8,
109 ins: apdu::BitcoinCommandCode::SignPSBT as u8,
110 data,
111 ..Default::default()
112 }
113}
114
115pub fn sign_message(
117 message_length: usize,
118 message_commitment_root: &[u8; 32],
119 path: &DerivationPath,
120) -> APDUCommand {
121 let child_numbers: &[ChildNumber] = path.as_ref();
122 let mut data: Vec<u8> =
123 child_numbers
124 .iter()
125 .fold(vec![child_numbers.len() as u8], |mut acc, &x| {
126 acc.extend_from_slice(&u32::from(x).to_be_bytes());
127 acc
128 });
129 data.extend(encode::serialize(&VarInt(message_length as u64)));
130 data.extend_from_slice(message_commitment_root);
131
132 APDUCommand {
133 cla: apdu::Cla::Bitcoin as u8,
134 ins: apdu::BitcoinCommandCode::SignMessage as u8,
135 data,
136 ..Default::default()
137 }
138}
139
140pub fn continue_interrupted(data: Vec<u8>) -> APDUCommand {
142 APDUCommand {
143 cla: apdu::Cla::Framework as u8,
144 ins: apdu::FrameworkCommandCode::ContinueInterrupted as u8,
145 data,
146 ..Default::default()
147 }
148}