zcash-sync/src/ledger/transport.rs

312 lines
8.4 KiB
Rust

use anyhow::{anyhow, Result};
use byteorder::WriteBytesExt;
use byteorder::LE;
use group::GroupEncoding;
use hex_literal::hex;
use jubjub::Fr;
use jubjub::SubgroupPoint;
use ledger_apdu::APDUCommand;
use ledger_transport_hid::{hidapi::HidApi, TransportNativeHID};
use reqwest::Client;
use serde_json::Value;
use std::io::Write;
use zcash_primitives::sapling::ProofGenerationKey;
use zcash_primitives::zip32::DiversifiableFullViewingKey;
fn handle_error_code(code: u16) -> Result<()> {
match code {
0x9000 => Ok(()),
0x6D02 => Err(anyhow!("Zcash Application NOT OPEN")),
0x6985 => Err(anyhow!("Tx REJECTED by User")),
0x5515 => Err(anyhow!("Ledger is LOCKED")),
_ => Err(anyhow!("Ledger device returned error code {:#06x}", code)),
}
}
async fn apdu_hid(data: &[u8]) -> Result<Vec<u8>> {
let api = HidApi::new()?;
let transport = TransportNativeHID::new(&api)?;
let command = APDUCommand {
cla: data[0],
ins: data[1],
p1: data[2],
p2: data[3],
data: &data[5..],
};
log::info!("ins {}", data[1]);
let response = transport.exchange(&command)?;
let error_code = response.retcode();
log::info!("error_code {}", error_code);
handle_error_code(error_code)?;
Ok(response.data().to_vec())
}
const TEST_SERVER_IP: Option<&'static str> = option_env!("LEDGER_IP");
async fn apdu_http(data: &[u8]) -> Vec<u8> {
let client = Client::new();
.post(&format!("http://{}:5000/apdu", TEST_SERVER_IP.unwrap()))
.body(format!("{{\"data\": \"{}\"}}", hex::encode(data)))
.send()
.await?;
let response_body: Value = response.json().await?;
let data = response_body["data"]
.as_str()
.ok_or(anyhow!("No data field"))?;
let data = hex::decode(data)?;
let error_code = u16::from_be_bytes(data[data.len() - 2..].try_into().unwrap());
handle_error_code(error_code)?;
Ok(data[..data.len() - 2].to_vec())
}
pub async fn ledger_init() -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.clear();
bb.write_all(&hex!("E005000000"))?;
apdu(&bb).await?;
Ok(())
}
pub async fn ledger_get_dfvk() -> Result<DiversifiableFullViewingKey> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E006000000"))?;
let dfvk_vec = apdu(&bb).await?;
let mut dfvk = [0; 128];
dfvk.copy_from_slice(&dfvk_vec);
let dfvk = DiversifiableFullViewingKey::from_bytes(&dfvk)
.ok_or(anyhow!("Invalid diversifiable fvk"))?;
Ok(dfvk)
}
pub async fn ledger_get_pubkey() -> Result<Vec<u8>> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E007000000"))?;
let pk = apdu(&bb).await?;
Ok(pk)
}
pub async fn ledger_get_o_fvk() -> Result<Vec<u8>> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E008000000"))?;
let pk = apdu(&bb).await?;
Ok(pk)
}
pub async fn ledger_init_tx(header_digest: &[u8]) -> Result<Vec<u8>> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E010000020"))?;
bb.write_all(header_digest)?;
let main_seed = apdu(&bb).await?;
Ok(main_seed)
}
pub async fn ledger_set_stage(stage: u8) -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E011"))?;
bb.write_u8(stage)?;
bb.write_all(&hex!("0000"))?;
apdu(&bb).await?;
Ok(())
}
pub async fn ledger_set_transparent_merkle_proof(
prevouts_digest: &[u8],
pubscripts_digest: &[u8],
sequences_digest: &[u8],
) -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E012000060"))?;
bb.write_all(prevouts_digest)?;
bb.write_all(pubscripts_digest)?;
bb.write_all(sequences_digest)?;
apdu(&bb).await?;
Ok(())
}
pub async fn ledger_set_sapling_merkle_proof(
spends_digest: &[u8],
memos_digest: &[u8],
outputs_nc_digest: &[u8],
) -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E013000060"))?;
bb.write_all(spends_digest)?;
bb.write_all(memos_digest)?;
bb.write_all(outputs_nc_digest)?;
apdu(&bb).await?;
Ok(())
}
pub async fn ledger_set_orchard_merkle_proof(
anchor: &[u8],
memos_digest: &[u8],
outputs_nc_digest: &[u8],
) -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E014000060"))?;
bb.write_all(anchor)?;
bb.write_all(memos_digest)?;
bb.write_all(outputs_nc_digest)?;
apdu(&bb).await?;
Ok(())
}
pub async fn ledger_add_t_input(amount: u64) -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E015010008"))?;
bb.write_u64::<LE>(amount)?;
apdu(&bb).await?;
Ok(())
}
pub async fn ledger_add_t_output(amount: u64, address: &[u8]) -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E01601001D"))?;
bb.write_u64::<LE>(amount)?;
bb.write_all(address)?;
apdu(&bb).await?;
Ok(())
}
pub async fn ledger_add_s_output(
amount: u64,
epk: &[u8],
address: &[u8],
enc_compact: &[u8],
) -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E017010087"))?;
bb.write_all(address)?;
bb.write_u64::<LE>(amount)?;
bb.write_all(epk)?;
bb.write_all(enc_compact)?;
apdu(&bb).await?;
Ok(())
}
pub async fn ledger_add_o_action(
nf: &[u8],
amount: u64,
epk: &[u8],
address: &[u8],
enc_compact: &[u8],
) -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E0180100A7"))?;
bb.write_all(nf)?;
bb.write_all(address)?;
bb.write_u64::<LE>(amount)?;
bb.write_all(epk)?;
bb.write_all(enc_compact)?;
apdu(&bb).await?;
Ok(())
}
pub async fn ledger_set_net_sapling(net: i64) -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E019000008"))?;
bb.write_i64::<LE>(net)?;
apdu(&bb).await?;
Ok(())
}
pub async fn ledger_set_net_orchard(net: i64) -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E01A000008"))?;
bb.write_i64::<LE>(net)?;
apdu(&bb).await?;
Ok(())
}
pub async fn ledger_confirm_fee() -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E01B010000"))?;
apdu(&bb).await?;
Ok(())
}
pub async fn ledger_get_sighash() -> Result<Vec<u8>> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E020000000"))?;
let sighash = apdu(&bb).await?;
Ok(sighash)
}
pub async fn ledger_get_proofgen_key() -> Result<ProofGenerationKey> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E021000000"))?;
let proofgen_key = apdu(&bb).await?;
let proofgen_key = ProofGenerationKey {
ak: SubgroupPoint::from_bytes(proofgen_key[0..32].try_into().unwrap()).unwrap(),
nsk: Fr::from_bytes(proofgen_key[32..64].try_into().unwrap()).unwrap(),
};
Ok(proofgen_key)
}
pub async fn ledger_sign_transparent(txin_digest: &[u8]) -> Result<Vec<u8>> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E022000020"))?;
bb.write_all(txin_digest)?;
let signature = apdu(&bb).await?;
Ok(signature)
}
pub async fn ledger_sign_sapling() -> Result<Vec<u8>> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E023000000"))?;
let signature = apdu(&bb).await?;
Ok(signature)
}
pub async fn ledger_sign_orchard() -> Result<Vec<u8>> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E024000000"))?;
let signature = apdu(&bb).await?;
Ok(signature)
}
pub async fn ledger_end_tx() -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E030000000"))?;
apdu(&bb).await?;
Ok(())
}
pub async fn ledger_cmu(data: &[u8]) -> Result<Vec<u8>> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E0800000"))?;
bb.write_u8(data.len() as u8)?;
bb.write_all(data)?;
let cmu = apdu(&bb).await?;
Ok(cmu)
}
pub async fn ledger_jubjub_hash(data: &[u8]) -> Result<Vec<u8>> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E0810000"))?;
bb.write_u8(data.len() as u8)?;
bb.write_all(data)?;
let cmu = apdu(&bb).await?;
Ok(cmu)
}
pub async fn ledger_pedersen_hash(data: &[u8]) -> Result<Vec<u8>> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E0820000"))?;
bb.write_u8(data.len() as u8)?;
bb.write_all(data)?;
let cmu = apdu(&bb).await?;
Ok(cmu)
}
pub async fn ledger_test_math(data: &[u8]) -> Result<Vec<u8>> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E0FF0000"))?;
bb.write_u8(data.len() as u8)?;
bb.write_all(data)?;
let res = apdu(&bb).await?;
Ok(res)
}