Skip to content

Commit

Permalink
Merge pull request #54 from input-output-hk/iapyx_get_address_from_qr
Browse files Browse the repository at this point in the history
Iapyx get address from qr
  • Loading branch information
dkijania authored Mar 8, 2021
2 parents b80c0b6 + 40a80bf commit 23be93b
Show file tree
Hide file tree
Showing 5 changed files with 260 additions and 50 deletions.
100 changes: 100 additions & 0 deletions iapyx/src/cli/args/qr/address.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use crate::PinReadMode;
use crate::QrReader;

use crate::cli::args::qr::IapyxQrCommandError;
use chain_addr::AddressReadable;
use chain_addr::{Discrimination, Kind};
use chain_core::mempack::ReadBuf;
use chain_core::mempack::Readable;
use chain_core::property::Deserialize;
use chain_crypto::Ed25519Extended;
use chain_crypto::SecretKey;
use chain_impl_mockchain::block::Block;
use jormungandr_lib::interfaces::{Block0Configuration, Initial};
use std::convert::TryInto;
use std::io::BufReader;
use std::path::Path;
use std::path::PathBuf;
use structopt::StructOpt;
use url::Url;
#[derive(StructOpt, Debug)]
pub struct GetAddressFromQRCommand {
#[structopt(long = "qr")]
pub qr: PathBuf,

#[structopt(short = "p", long = "pin", default_value = "1234")]
pub pin: String,

#[structopt(short = "f", long = "read-from-filename")]
pub read_pin_from_filename: bool,

// if true then testing discrimination would be used
#[structopt(long = "testing")]
pub testing: bool,

// if true then testing discrimination would be used
#[structopt(long = "prefix", default_value = "ca")]
pub prefix: String,

#[structopt(long = "block0")]
pub block0: Option<String>,
}

impl GetAddressFromQRCommand {
pub fn exec(&self) -> Result<(), IapyxQrCommandError> {
println!("Decoding qr from file: {:?}...", self.qr);
let pin_read_mode = PinReadMode::new(self.read_pin_from_filename, &self.pin);
let pin_reader = QrReader::new(pin_read_mode);
let secret = pin_reader.read_qr(&self.qr)?;
let bin: [u8; 64] = secret.leak_secret().as_ref().try_into().unwrap();
let secret_key: SecretKey<Ed25519Extended> = SecretKey::from_binary(&bin).unwrap();
let kind = Kind::Single(secret_key.to_public());
let address = chain_addr::Address(Discrimination::Production, kind);

if let Some(block0_path) = &self.block0 {
println!("Reading block0 from location {:?}...", block0_path);

let block = {
if Path::new(block0_path).exists() {
let reader = std::fs::OpenOptions::new()
.create(false)
.write(false)
.read(true)
.append(false)
.open(block0_path)?;
let reader = BufReader::new(reader);
Block::deserialize(reader)?
} else if Url::parse(block0_path).is_ok() {
let response = reqwest::blocking::get(block0_path)?;
let block0_bytes = response.bytes()?.to_vec();
Block::read(&mut ReadBuf::from(&block0_bytes))?
} else {
panic!(" block0 should be either path to filesystem or url ");
}
};
let genesis = Block0Configuration::from_block(&block)?;

for initial in genesis.initial.iter() {
if let Initial::Fund(initial_utxos) = initial {
if let Some(entry) = initial_utxos.iter().find(|x| {
let entry_address: chain_addr::Address = x.address.clone().into();
entry_address == address
}) {
println!(
"Address corresponding to input qr found in block0: '{}' with value: '{}'",
AddressReadable::from_address(&self.prefix, &entry.address.clone().into()).to_string(),entry.value
);
return Ok(());
}
}
}
println!("Address corresponding to input qr not found in block0");
} else {
println!(
"Address: {}",
AddressReadable::from_address(&self.prefix, &address).to_string()
);
}
Ok(())
}
}
83 changes: 34 additions & 49 deletions iapyx/src/cli/args/qr/mod.rs
Original file line number Diff line number Diff line change
@@ -1,59 +1,44 @@
use crate::PinReadMode;
use crate::QrReader;
use crate::Wallet;
use std::convert::TryInto;
use std::path::PathBuf;
mod address;
mod secret;
mod verify;
use crate::cli::args::qr::secret::GetSecretFromQRCommand;
use address::GetAddressFromQRCommand;
use jormungandr_lib::interfaces::Block0ConfigurationError;
use structopt::StructOpt;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum IapyxQrCommandError {
#[error("proxy error")]
ProxyError(#[from] crate::backend::ProxyServerError),
}
use verify::VerifyQrCommand;

#[derive(StructOpt, Debug)]
pub struct IapyxQrCommand {
#[structopt(short = "q", long = "qr-codes-folder")]
pub qr_codes_folder: PathBuf,

#[structopt(short = "g", long = "global-pin", default_value = "1234")]
pub global_pin: String,

#[structopt(short = "f", long = "read-from-filename")]
pub read_pin_from_filename: bool,

#[structopt(short = "s", long = "stop-at-fail")]
pub stop_at_fail: bool,
pub enum IapyxQrCommand {
Verify(VerifyQrCommand),
CheckAddress(GetAddressFromQRCommand),
Secret(GetSecretFromQRCommand),
}

impl IapyxQrCommand {
pub fn exec(&self) -> Result<(), IapyxQrCommandError> {
println!("Decoding...");

let pin_read_mode = PinReadMode::new(self.read_pin_from_filename, &self.global_pin);

let qr_codes: Vec<PathBuf> = std::fs::read_dir(&self.qr_codes_folder)
.unwrap()
.into_iter()
.map(|x| x.unwrap().path())
.collect();

let pin_reader = QrReader::new(pin_read_mode);
let secrets = pin_reader.read_qrs(&qr_codes, self.stop_at_fail);
let wallets: Vec<Wallet> = secrets
.into_iter()
.map(|secret| {
Wallet::recover_from_utxo(secret.leak_secret().as_ref().try_into().unwrap())
.unwrap()
})
.collect();

println!(
"{} QR read. {} succesfull, {} failed",
qr_codes.len(),
wallets.len(),
qr_codes.len() - wallets.len()
);
Ok(())
match self {
Self::Verify(verify) => verify.exec(),
Self::CheckAddress(check_address) => check_address.exec(),
Self::Secret(secret) => secret.exec(),
}
}
}

#[derive(Error, Debug)]
pub enum IapyxQrCommandError {
#[error("proxy error")]
ProxyError(#[from] crate::backend::ProxyServerError),
#[error("pin error")]
PinError(#[from] crate::qr::PinReadError),
#[error("reqwest error")]
IapyxQrCommandError(#[from] reqwest::Error),
#[error("block0 parse error")]
Block0ParseError(#[from] Block0ConfigurationError),
#[error("io error")]
IoError(#[from] std::io::Error),
#[error("read error")]
ReadError(#[from] chain_core::mempack::ReadError),
#[error("bech32 error")]
Bech32Error(#[from] bech32::Error),
}
40 changes: 40 additions & 0 deletions iapyx/src/cli/args/qr/secret.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use crate::PinReadMode;
use crate::QrReader;

use crate::cli::args::qr::IapyxQrCommandError;
use bech32::ToBase32;
use chain_crypto::AsymmetricKey;
use chain_crypto::Ed25519Extended;
use chain_crypto::SecretKey;
use std::convert::TryInto;
use std::path::PathBuf;
use structopt::StructOpt;

#[derive(StructOpt, Debug)]
pub struct GetSecretFromQRCommand {
#[structopt(long = "qr")]
pub qr: PathBuf,

#[structopt(short = "p", long = "pin", default_value = "1234")]
pub pin: String,

#[structopt(short = "f", long = "read-from-filename")]
pub read_pin_from_filename: bool,
}

impl GetSecretFromQRCommand {
pub fn exec(&self) -> Result<(), IapyxQrCommandError> {
println!("Decoding qr from file: {:?}...", self.qr);
let pin_read_mode = PinReadMode::new(self.read_pin_from_filename, &self.pin);
let pin_reader = QrReader::new(pin_read_mode);
let secret = pin_reader.read_qr(&self.qr)?;
let bin: [u8; 64] = secret.leak_secret().as_ref().try_into().unwrap();
let secret_key: SecretKey<Ed25519Extended> = SecretKey::from_binary(&bin).unwrap();
let hrp = Ed25519Extended::SECRET_BECH32_HRP;
println!(
"{}",
bech32::encode(hrp, secret_key.leak_secret().to_base32())?
);
Ok(())
}
}
67 changes: 67 additions & 0 deletions iapyx/src/cli/args/qr/verify.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use crate::cli::args::qr::IapyxQrCommandError;
use crate::PinReadMode;
use crate::QrReader;
use crate::Wallet;
use chain_addr::AddressReadable;
use chain_addr::{Discrimination, Kind};
use chain_crypto::Ed25519Extended;
use chain_crypto::SecretKey;
use std::convert::TryInto;
use std::path::PathBuf;
use structopt::StructOpt;

#[derive(StructOpt, Debug)]
pub struct VerifyQrCommand {
#[structopt(short = "q", long = "qr-codes-folder")]
pub qr_codes_folder: PathBuf,

#[structopt(short = "g", long = "global-pin", default_value = "1234")]
pub global_pin: String,

#[structopt(short = "f", long = "read-from-filename")]
pub read_pin_from_filename: bool,

#[structopt(short = "s", long = "stop-at-fail")]
pub stop_at_fail: bool,
}

impl VerifyQrCommand {
pub fn exec(&self) -> Result<(), IapyxQrCommandError> {
println!("Decoding...");

let pin_read_mode = PinReadMode::new(self.read_pin_from_filename, &self.global_pin);

let qr_codes: Vec<PathBuf> = std::fs::read_dir(&self.qr_codes_folder)
.unwrap()
.into_iter()
.map(|x| x.unwrap().path())
.collect();

let pin_reader = QrReader::new(pin_read_mode);
let secrets = pin_reader.read_qrs(&qr_codes, self.stop_at_fail);
let wallets: Vec<Wallet> = secrets
.into_iter()
.map(|secret| {
let bin: [u8; 64] = secret.leak_secret().as_ref().try_into().unwrap();

let secret_key: SecretKey<Ed25519Extended> = SecretKey::from_binary(&bin).unwrap();
let kind = Kind::Single(secret_key.to_public());
let address = chain_addr::Address(Discrimination::Production, kind);
println!(
"{}",
AddressReadable::from_address("ca", &address).to_string()
);

Wallet::recover_from_utxo(&bin).unwrap()
})
.collect();

println!(
"{} QR read. {} succesfull, {} failed",
qr_codes.len(),
wallets.len(),
qr_codes.len() - wallets.len()
);
Ok(())
}
}
20 changes: 19 additions & 1 deletion iapyx/src/qr/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use jormungandr_testing_utils::qr_code::KeyQrCode;
use jormungandr_testing_utils::qr_code::KeyQrCodeError;
use std::path::Path;
use std::path::PathBuf;
use thiserror::Error;
Expand Down Expand Up @@ -26,8 +27,14 @@ pub enum PinReadError {
UnableToDetectFileName(PathBuf),
#[error("cannot read file name from path {0:?}")]
UnableToReadFileName(PathBuf),
#[error("cannot divie file name from path {0:?}")]
#[error("cannot split file name from path {0:?}")]
UnableToSplitFileName(PathBuf),
#[error("Cannot read qr from file")]
UnableToReadQR(#[from] std::io::Error),
#[error("Cannot decode qr from file")]
UnableToDecodeQR(#[from] KeyQrCodeError),
#[error("cannot open image")]
UnableToOpenImage(#[from] image::ImageError),
}

#[derive(Debug)]
Expand All @@ -40,6 +47,17 @@ impl QrReader {
Self { pin_read_mode }
}

pub fn read_qr<P: AsRef<Path>>(
&self,
qr: P,
) -> Result<chain_crypto::SecretKey<chain_crypto::Ed25519Extended>, PinReadError> {
let pin = get_pin(&self.pin_read_mode, &qr)?;
let pin = pin_to_bytes(&pin);
let img = image::open(qr.as_ref())?;
let secret = KeyQrCode::decode(img, &pin)?;
Ok(secret.first().unwrap().clone())
}

pub fn read_qrs<P: AsRef<Path>>(
&self,
qrs: &[P],
Expand Down

0 comments on commit 23be93b

Please sign in to comment.