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
51 changes: 44 additions & 7 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ name: CI

jobs:

build_test:
name: Build and test
build:
name: Build workspace
runs-on: ubuntu-latest
strategy:
matrix:
Expand All @@ -24,6 +24,7 @@ jobs:
features:
- --no-default-features
- --features default
- --all-features
steps:
- name: checkout
uses: actions/checkout@v2
Expand All @@ -35,15 +36,51 @@ jobs:
profile: minimal
- name: Rust Cache
uses: Swatinem/rust-cache@v2.2.1
- name: Install libpcsclite-dev
run: sudo apt install libpcsclite-dev
- name: Install PCSC development libraries
run: |
sudo apt-get update
sudo apt-get install -y libpcsclite-dev swig
- name: Use MSRV Cargo.toml # Don't include "cli" in workspace if using MSRV
if: ${{ matrix.rust.msrv }}
run: cp Cargo.toml.MSRV Cargo.toml
- name: Build
run: cargo build ${{ matrix.features }}

test:
name: Test with emulator
runs-on: ubuntu-latest
strategy:
matrix:
rust:
- version: stable
- version: 1.81.0 # MSRV
msrv: true
steps:
- name: checkout
uses: actions/checkout@v2
with:
submodules: 'true'
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust.version }}
override: true
profile: minimal
- name: Rust Cache
uses: Swatinem/rust-cache@v2.2.1
- name: Use MSRV Cargo.toml # Don't include "cli" in workspace if using MSRV
if: ${{ matrix.rust.msrv }}
run: cp Cargo.toml.MSRV Cargo.toml
- name: Install PCSC development libraries
run: |
sudo apt-get update
sudo apt-get install -y libpcsclite-dev swig
- name: Test
run: cargo test ${{ matrix.features }}
run: |
python3 -m venv emulator_env
source emulator_env/bin/activate
pip install -r coinkite/coinkite-tap-proto/emulator/requirements.txt
cargo test -p rust-cktap --features emulator

rust_fmt:
name: Rust fmt
Expand All @@ -54,12 +91,12 @@ jobs:
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
toolchain: nightly
override: true
profile: minimal
components: rustfmt
- name: Check fmt
run: cargo fmt --all -- --config format_code_in_doc_comments=true --check
run: cargo +nightly fmt --all

clippy_check:
name: Clippy check
Expand Down
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,7 @@ DerivedData/
*.xcframework.zip
Info.plist
Sources
lib*.a
lib*.a

# Python ck-tap emulator
/emulator_env/
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "coinkite/coinkite-tap-proto"]
path = coinkite/coinkite-tap-proto
url = https://github.com/coinkite/coinkite-tap-proto.git
27 changes: 27 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
set quiet := true
emulator_dir := 'coinkite/coinkite-tap-proto/emulator'

# list of recipes
default:
just --list

# format the project code
fmt:
cargo +nightly fmt --all

# lint the project
clippy: fmt
cargo clippy --all-features --tests

# build the project
build: fmt
cargo build --all-features --tests

# test the rust-cktap lib with the coinkite cktap card emulator
test: fmt
(test -d emulator_env || python3 -m venv emulator_env) && source emulator_env/bin/activate && pip install -r {{emulator_dir}}/requirements.txt
source emulator_env/bin/activate && cargo test -p rust-cktap --features emulator

# clean the project target directory
clean:
cargo clean
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,8 @@ It is up to the crate user to send and receive the raw cktap APDU messages via N

### Automated Testing with Emulator

1. Install and start [cktap emulator](https://github.com/coinkite/coinkite-tap-proto/blob/master/emulator/README.md)
- TapSigner: `./ecard.py emulate -t --no-init`
- SatsCard: `./ecard.py emulate -s`
2. run tests: `cargo test --features emulator`
1. Install dependencies for [cktap emulator](https://github.com/coinkite/coinkite-tap-proto/blob/master/emulator/README.md)
2. run tests with emulator: `just test`

### Manual Testing with real cards

Expand Down
70 changes: 68 additions & 2 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ use rust_cktap::emulator;
use rust_cktap::pcsc;
use rust_cktap::secp256k1::hashes::Hash as _;
use rust_cktap::secp256k1::rand;
use rust_cktap::tap_signer::TapSignerShared;
use rust_cktap::{apdu::Error, commands::Certificate, rand_chaincode, CkTapCard};
use std::io;
use std::io::Write;
#[cfg(feature = "emulator")]
use std::path::Path;

/// SatsCard CLI
#[derive(Parser)]
Expand Down Expand Up @@ -74,6 +77,38 @@ enum TapSignerCommand {
Sign { to_sign: String },
}

/// TapSigner CLI
#[derive(Parser)]
#[command(author, version = option_env ! ("CARGO_PKG_VERSION").unwrap_or("unknown"), about,
long_about = None, propagate_version = true)]
struct SatsChipCli {
#[command(subcommand)]
command: SatsChipCommand,
}

/// Commands supported by SatsChip cards
#[derive(Subcommand)]
enum SatsChipCommand {
/// Show the card status
Debug,
/// Check this card was made by Coinkite: Verifies a certificate chain up to root factory key.
Certs,
/// Read the pubkey (requires CVC)
Read,
/// This command is used once to initialize a new card.
Init,
/// Derive a public key at the given hardened path
Derive {
/// path, eg. for 84'/0'/0'/* use 84,0,0
#[clap(short, long, value_delimiter = ',', num_args = 1..)]
path: Vec<u32>,
},
/// Change the PIN (CVC) used for card authentication to a new user provided one
Change { new_cvc: String },
/// Sign a digest
Sign { to_sign: String },
}

#[tokio::main]
async fn main() -> Result<(), Error> {
// figure out what type of card we have before parsing cli args
Expand All @@ -82,7 +117,7 @@ async fn main() -> Result<(), Error> {

// if emulator feature enabled override pcsc card
#[cfg(feature = "emulator")]
let mut card = emulator::find_emulator().await?;
let mut card = emulator::find_emulator(Path::new("/tmp/ecard-pipe")).await?;

let rng = &mut rand::thread_rng();

Expand Down Expand Up @@ -112,7 +147,7 @@ async fn main() -> Result<(), Error> {
}
}
}
CkTapCard::TapSigner(ts) | CkTapCard::SatsChip(ts) => {
CkTapCard::TapSigner(ts) => {
let cli = TapSignerCli::parse();
match cli.command {
TapSignerCommand::Debug => {
Expand Down Expand Up @@ -148,6 +183,37 @@ async fn main() -> Result<(), Error> {
}
}
}
CkTapCard::SatsChip(sc) => {
let cli = SatsChipCli::parse();
match cli.command {
SatsChipCommand::Debug => {
dbg!(&sc);
}
SatsChipCommand::Certs => check_cert(sc).await,
SatsChipCommand::Read => read(sc, Some(cvc())).await,
SatsChipCommand::Init => {
let chain_code = rand_chaincode(rng);
let response = &sc.init(chain_code, &cvc()).await;
dbg!(response);
}
SatsChipCommand::Derive { path } => {
dbg!(&sc.derive(&path, &cvc()).await);
}

SatsChipCommand::Change { new_cvc } => {
let response = &sc.change(&new_cvc, &cvc()).await;
println!("{response:?}");
}
SatsChipCommand::Sign { to_sign } => {
let digest: [u8; 32] =
rust_cktap::secp256k1::hashes::sha256::Hash::hash(to_sign.as_bytes())
.to_byte_array();

let response = &sc.sign(digest, vec![], &cvc()).await;
println!("{response:?}");
}
}
}
}

Ok(())
Expand Down
1 change: 1 addition & 0 deletions coinkite/coinkite-tap-proto
Submodule coinkite-tap-proto added at 9d9548
4 changes: 4 additions & 0 deletions lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ log = "0.4"
# pcsc as optional
pcsc = { version = "2", optional = true }

[dev-dependencies]

tokio = { version = "1.44", features = ["rt"] }

[features]
default = []
emulator = []
Expand Down
1 change: 1 addition & 0 deletions lib/examples/pcsc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use rust_cktap::commands::{Certificate, Wait};
use rust_cktap::{pcsc, rand_chaincode, CkTapCard};

use bitcoin::secp256k1::rand;
use rust_cktap::tap_signer::TapSignerShared;
use std::io;
use std::io::Write;

Expand Down
2 changes: 2 additions & 0 deletions lib/src/apdu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ pub enum Error {
CkTap(CkTapError),
#[error("IncorrectSignature: {0}")]
IncorrectSignature(String),
#[error("Root cert is not from Coinkite. Card is counterfeit: {0}")]
InvalidRootCert(String),
#[error("UnknownCardType: {0}")]
UnknownCardType(String),

Expand Down
Loading
Loading