Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
223 changes: 1 addition & 222 deletions examples/pcsc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,135 +3,9 @@ extern crate core;
use pcsc::{Card, Context, Protocols, Scope, ShareMode, MAX_BUFFER_SIZE};
use rust_cktap::commands::{AppletSelect, CommandApdu, Error, ResponseApdu, StatusResponse};
use rust_cktap::{rand_chaincode, wait_command, CkTapCard, SatsCard, TapSigner, Transport};

use rust_cktap::pcsc::PcscTransport;
use secp256k1::{rand, All, PublicKey, Secp256k1};

struct PcscTransport {
secp: Secp256k1<All>,
card: Card,
}

impl Transport for PcscTransport {
fn find_first() -> Result<CkTapCard<Self>, Error> {
// Establish a PC/SC context.
let ctx = Context::establish(Scope::User)?;

// List available readers.
let mut readers_buf = [0; 2048];
let mut readers = ctx.list_readers(&mut readers_buf)?;

// Use the first reader.
let reader = match readers.next() {
Some(reader) => Ok(reader),
None => {
//println!("No readers are connected.");
Err(Error::PcSc("No readers are connected.".to_string()))
}
}?;
println!("Using reader: {:?}\n", reader);

// Connect to the card.
let card = ctx.connect(reader, ShareMode::Shared, Protocols::ANY)?;

// Create transport
let secp = Secp256k1::new();
let transport = Self { secp, card };

// Get card status
let applet_select_apdu = AppletSelect::default().apdu_bytes();
let rapdu = transport.transmit(applet_select_apdu)?;
let status_response = StatusResponse::from_cbor(rapdu.to_vec())?;
dbg!(&status_response);

// if auth delay call wait
if status_response.auth_delay.is_some() {
let mut auth_delay = status_response.auth_delay.unwrap();
while auth_delay > 0 {
let wait = wait_command(&transport, None)?;
auth_delay = wait.auth_delay;
}
}

// get common fields
let proto = status_response.proto;
let ver = status_response.ver;
let birth = status_response.birth;
let pubkey = status_response.pubkey.as_slice(); // TODO verify is 33 bytes?
let pubkey = PublicKey::from_slice(pubkey).map_err(|e| Error::CiborValue(e.to_string()))?;
let card_nonce = status_response.card_nonce;

// Return correct card variant
match (status_response.tapsigner, status_response.satschip) {
(Some(true), None) => {
let path = status_response.path;
let num_backups = status_response.num_backups;

Ok(CkTapCard::TapSigner(TapSigner {
transport,
proto,
ver,
birth,
path,
num_backups,
pubkey,
card_nonce,
}))
}
(Some(true), Some(true)) => {
let path = status_response.path;
let num_backups = status_response.num_backups;

Ok(CkTapCard::SatsChip(TapSigner {
transport,
proto,
ver,
birth,
path,
num_backups,
pubkey,
card_nonce,
}))
}
(None, None) => {
let slots = status_response
.slots
.ok_or(Error::CiborValue("Missing slots".to_string()))?;

let addr = status_response
.addr
.ok_or(Error::CiborValue("Missing addr".to_string()))?;

Ok(CkTapCard::SatsCard(SatsCard {
transport,
proto,
ver,
birth,
slots,
addr,
pubkey,
card_nonce,
}))
}
(_, _) => {
// TODO throw error
todo!()
}
}
}

fn secp(&self) -> &Secp256k1<All> {
&self.secp
}

fn transmit(&self, send_buffer: Vec<u8>) -> Result<Vec<u8>, Error> {
let mut receive_buf = vec![0; MAX_BUFFER_SIZE];
let rapdu = self
.card
.transmit(&send_buffer.as_slice(), &mut receive_buf)?;
Ok(rapdu.to_vec())
}
}

fn get_cvc() -> String {
println!("Enter cvc:");
let mut cvc: String = String::new();
Expand Down Expand Up @@ -189,104 +63,9 @@ fn main() -> Result<(), Error> {
}
}

// let reader = CardReader::find_first()?;
//
// let status = applet_select(&reader)?;
// dbg!(&status);
//
// // TODO validate certs auth_sig
//
// // if auth delay call wait
// if status.auth_delay.is_some() {
// let mut auth_delay = status.auth_delay.unwrap();
// while auth_delay > 0 {
// let wait = wait_command(&reader, None)?;
// auth_delay = wait.auth_delay;
// }
// }
//
// match CardType::from_status(&status) {
// CardType::SatsCard => {
// let rng = &mut rand::thread_rng();
// let nonce = rand_nonce(rng).to_vec();
// // SatsCard.read() // nonce generated in method
// let read_response = read_command(&reader, nonce)?;
// dbg!(read_response);
// // TODO validate read response sig
// }
// CardType::TapSigner => {
// let mut tapsigner = TapSigner::new(reader, &status);
//
// if tapsigner.cvc.is_none() {
// println!("Enter cvc:");
// let mut cvc: String = String::new();
// let _btye_count = std::io::stdin().read_line(&mut cvc).unwrap();
// tapsigner.set_cvc(cvc.trim().to_owned());
// }
//
// let read_resp = tapsigner.read()?;
// dbg!(&read_resp);
// // TODO validate read response sig
//
// let xpub_resp = tapsigner.xpub(true);
// dbg!(&xpub_resp);
//
// let xpub_resp = tapsigner.xpub(false);
// dbg!(&xpub_resp);
//
// // sample pulled from ref impl: https://github.com/coinkite/coinkite-tap-proto/blob/0ab18dd1446c1e21e30d04ab99c2201ccc0197f8/testing/test_crypto.py
// let md = b"3\xa7=Q\x1f\xb3\xfa)>i\x8f\xb2\x8f6\xd2\x97\x9eW\r5\x0b\x82\x0e\xd3\xd6?\xf4G]\x14Fd";
// let sign_resp = tapsigner.sign(md.to_vec(), Some([0, 0]))?;
// dbg!(&sign_resp);
// // TODO validate response sig
// }
// }

Ok(())
}

// struct CardReader {
// card: Card,
// }
//
// impl CardReader {
// fn find_first() -> Result<CardReader, Error> {
// // Establish a PC/SC context.
// let ctx = Context::establish(Scope::User)?;
//
// // List available readers.
// let mut readers_buf = [0; 2048];
// let mut readers = ctx.list_readers(&mut readers_buf)?;
//
// // Use the first reader.
// let reader = match readers.next() {
// Some(reader) => Ok(reader),
// None => {
// //println!("No readers are connected.");
// Err(Error::PcSc("No readers are connected.".to_string()))
// }
// }?;
// println!("Using reader: {:?}\n", reader);
//
// // Connect to the card.
// let card = ctx.connect(reader, ShareMode::Shared, Protocols::ANY)?;
//
// Ok(Self { card })
// }
//
// // fn get_card(&self) -> Card {
// // self.card
// // }
// }

// impl NfcTransmitter for CardReader {
// fn transmit(&self, send_buffer: Vec<u8>) -> Result<Vec<u8>, Error> {
// let mut receive_buf = vec![0; MAX_BUFFER_SIZE];
// let rapdu = self.card.transmit(&send_buffer.as_slice(), &mut receive_buf)?;
// Ok(rapdu.to_vec())
// }
// }

// fn rand_chaincode(rng: &mut ThreadRng) -> [u8; 32] {
// let mut chain_code = [0u8; 32];
// rng.fill(&mut chain_code);
Expand Down
33 changes: 16 additions & 17 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,18 +138,23 @@ pub struct ReadCommand {
}

impl ReadCommand {
// epubkey_xcvc is None for SatsCard
pub fn new(nonce: Vec<u8>, epubkey_xcvc: Option<(PublicKey, Vec<u8>)>) -> Self {
let (epubkey, xcvc) = epubkey_xcvc
.map(|(pk, xcvc)| (Some(pk.serialize().to_vec()), Some(xcvc)))
.unwrap_or((None, None));
pub fn for_tapsigner(nonce: Vec<u8>, epubkey: PublicKey, xcvc: Vec<u8>) -> Self {
ReadCommand {
cmd: "read".to_string(),
nonce,
epubkey,
xcvc,
epubkey: Some(epubkey.serialize().to_vec()),
xcvc: Some(xcvc),
}
}

pub fn for_satscard(nonce: Vec<u8>) -> Self {
ReadCommand {
cmd: "read".to_string(),
nonce,
epubkey: None,
xcvc: None,
}
}
}

impl CommandApdu for ReadCommand {}
Expand Down Expand Up @@ -400,20 +405,14 @@ impl DumpCommand {
impl CommandApdu for DumpCommand {}

// TAPSIGNER only - Provides the current XPUB (BIP-32 serialized), either at the top level (master) or the derived key in use (see 'path' value in status response)
// {
// 'cmd': 'xpub', # command
// 'master': (boolean), # give master (`m`) XPUB, otherwise derived XPUB
// 'epubkey': (33 bytes), # app's ephemeral public key (required)
// 'xcvc': (6 bytes) # encrypted CVC value (required)
// }
#[derive(Serialize, Clone, Debug, PartialEq, Eq)]
pub struct XpubCommand {
cmd: String,
master: bool,
cmd: String, // always "xpub"
master: bool, // give master (`m`) XPUB, otherwise derived XPUB
#[serde(with = "serde_bytes")]
epubkey: Vec<u8>,
epubkey: Vec<u8>, // app's ephemeral public key (required)
#[serde(with = "serde_bytes")]
xcvc: Vec<u8>,
xcvc: Vec<u8>, //encrypted CVC value (required)
}

impl CommandApdu for XpubCommand {}
Expand Down
44 changes: 12 additions & 32 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ pub trait Transport {
//fn find_cards() -> Vec<CkTapCard<Self>>;
fn secp(&self) -> &Secp256k1<All>;
fn transmit(&self, send_buffer: Vec<u8>) -> Result<Vec<u8>, Error>;
fn transmit_read(&self, cmd: ReadCommand) -> Result<ReadResponse, Error> {
let read_apdu = cmd.apdu_bytes();
println!("Sending 'Read' APDU: {:?}\n", &read_apdu);
let rapdu = self.transmit(read_apdu)?;
let read_response = ReadResponse::from_cbor(rapdu.to_vec())?;
println!("Received 'Read' APDU: {:?}\n", &read_response);
Ok(read_response)
}
}

pub enum CkTapCard<T: Transport + Sized> {
Expand Down Expand Up @@ -92,8 +100,8 @@ impl<T: Transport + Sized> TapSigner<T> {
let nonce = self.card_nonce.to_vec();
let command = "read".to_string();
let ekeys_xcvc = calc_ekeys_xcvc(secp, pubkey, &nonce, cvc, command);
let epubkey_xcvc = Some((ekeys_xcvc.1, ekeys_xcvc.2));
let response = read_command(&self.transport, nonce, epubkey_xcvc);
let read_cmd = ReadCommand::for_tapsigner(self.card_nonce.clone(), ekeys_xcvc.1, ekeys_xcvc.2);
let response = self.transport.transmit_read(read_cmd);
if let Ok(read_response) = &response {
self.card_nonce = read_response.card_nonce.clone();
}
Expand Down Expand Up @@ -152,9 +160,8 @@ impl<T: Transport + Sized> SatsCard<T> {
}

pub fn read(&mut self) -> Result<ReadResponse, Error> {
let epubkey_xcvc = None; // Not required for SatsCard
let nonce = self.card_nonce.to_vec();
let response = read_command(&self.transport, nonce, epubkey_xcvc);
let read_cmd = ReadCommand::for_satscard(self.card_nonce.clone());
let response = self.transport.transmit_read(read_cmd);
if let Ok(read_response) = &response {
self.card_nonce = read_response.card_nonce.clone();
}
Expand Down Expand Up @@ -222,20 +229,6 @@ fn new_command<T: Transport>(
Ok(new_response)
}

fn read_command<T: Transport>(
transport: &T,
nonce: Vec<u8>,
epubkey_xcvc: Option<(PublicKey, Vec<u8>)>,
) -> Result<ReadResponse, Error> {
// Send 'read' command.
let read_apdu = ReadCommand::new(nonce, epubkey_xcvc).apdu_bytes();
println!("Sending 'Read' APDU: {:?}\n", &read_apdu);
let rapdu = transport.transmit(read_apdu)?;
let read_response = ReadResponse::from_cbor(rapdu.to_vec())?;
println!("Received 'Read' APDU: {:?}\n", &read_response);
Ok(read_response)
}

pub fn wait_command<T: Transport>(
transport: &T,
epubkey_xcvc: Option<(PublicKey, Vec<u8>)>,
Expand Down Expand Up @@ -447,19 +440,6 @@ pub fn rand_nonce(rng: &mut ThreadRng) -> [u8; 16] {
// Ok(check_response)
// }

// TODO - move to satscard struct
// pub fn read_command<T: NfcTransmitter>(
// card: &T,
// card_nonce: Vec<u8>
// ) -> Result<ReadResponse, Error> {
// // Send 'read' command.
// let read_struct = ReadCommand::for_satscard(card_nonce);
// let read_apdu = read_struct.apdu_bytes();
// let rapdu = card.transmit(read_apdu)?;
// let read_response = ReadResponse::from_cbor(rapdu.to_vec())?;
// Ok(read_response)
// }

// fn sign_command<T: NfcTransmittor>(
// card: &T,
// digest: Vec<u8>,
Expand Down
Loading