Skip to content

Commit 15592e2

Browse files
committed
test: add test for check_certificate command
1 parent 602a770 commit 15592e2

9 files changed

Lines changed: 65 additions & 280 deletions

File tree

Justfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ build: fmt
1818
cargo build --all-features --tests
1919

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

README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,8 @@ It is up to the crate user to send and receive the raw cktap APDU messages via N
4343

4444
### Automated Testing with Emulator
4545

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

5149
### Manual Testing with real cards
5250

lib/examples/pcsc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ use rust_cktap::commands::{Certificate, Wait};
55
use rust_cktap::{pcsc, rand_chaincode, CkTapCard};
66

77
use bitcoin::secp256k1::rand;
8+
use rust_cktap::tap_signer::TapSignerShared;
89
use std::io;
910
use std::io::Write;
10-
use rust_cktap::tap_signer::TapSignerShared;
1111

1212
fn get_cvc() -> String {
1313
print!("Enter cvc: ");

lib/src/apdu.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ pub enum Error {
2828
CkTap(CkTapError),
2929
#[error("IncorrectSignature: {0}")]
3030
IncorrectSignature(String),
31+
#[error("Root cert is not from Coinkite. Card is counterfeit: {0}")]
32+
InvalidRootCert(String),
3133
#[error("UnknownCardType: {0}")]
3234
UnknownCardType(String),
3335

lib/src/commands.rs

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -277,16 +277,10 @@ mod tests {
277277
async fn test_new_command() {
278278
let rng = &mut rand::thread_rng();
279279
let chain_code = rand_chaincode(rng);
280-
for card_type in [
281-
CardTypeOption::SatsCard,
282-
CardTypeOption::TapSigner,
283-
CardTypeOption::SatsChip,
284-
] {
280+
for card_type in CardTypeOption::values() {
285281
let pipe_path = format!("/tmp/test-new-command-pipe{card_type}");
286282
let pipe_path = Path::new(&pipe_path);
287-
// Only SatsCard is expected to be initialized
288-
let no_init = card_type != CardTypeOption::SatsCard;
289-
let python = EcardSubprocess::new(pipe_path, &card_type, no_init).unwrap();
283+
let python = EcardSubprocess::new(pipe_path, &card_type).unwrap();
290284
let emulator = find_emulator(pipe_path).await.unwrap();
291285
match emulator {
292286
CkTapCard::SatsCard(mut sc) => {
@@ -321,6 +315,39 @@ mod tests {
321315
}
322316
}
323317

318+
#[tokio::test]
319+
async fn test_cert_command() {
320+
for card_type in CardTypeOption::values() {
321+
let emulator_root_pubkey =
322+
"0312d005ca1501b1603c3b00412eefe27c6b20a74c29377263b357b3aff12de6fa".to_string();
323+
let pipe_path = format!("/tmp/test-cert-command-pipe{card_type}");
324+
let pipe_path = Path::new(&pipe_path);
325+
let python = EcardSubprocess::new(pipe_path, &card_type).unwrap();
326+
let emulator = find_emulator(pipe_path).await.unwrap();
327+
match emulator {
328+
CkTapCard::SatsCard(mut sc) => {
329+
assert_eq!(card_type, CardTypeOption::SatsCard);
330+
let response = sc.check_certificate().await;
331+
assert!(response.is_err());
332+
matches!(response, Err(Error::InvalidRootCert(pubkey)) if pubkey == emulator_root_pubkey);
333+
}
334+
CkTapCard::TapSigner(mut ts) => {
335+
assert_eq!(card_type, CardTypeOption::TapSigner);
336+
let response = ts.check_certificate().await;
337+
assert!(response.is_err());
338+
matches!(response, Err(Error::InvalidRootCert(pubkey)) if pubkey == emulator_root_pubkey);
339+
}
340+
CkTapCard::SatsChip(mut sc) => {
341+
assert_eq!(card_type, CardTypeOption::SatsChip);
342+
let response = sc.check_certificate().await;
343+
assert!(response.is_err());
344+
matches!(response, Err(Error::InvalidRootCert(pubkey)) if pubkey == emulator_root_pubkey);
345+
}
346+
};
347+
drop(python);
348+
}
349+
}
350+
324351
// #[test]
325352
// fn test_tapsigner_signature() {
326353
// let card_pubkey = PublicKey::from_slice(

lib/src/emulator.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,16 @@ pub mod test {
6767
SatsChip,
6868
}
6969

70+
impl CardTypeOption {
71+
pub fn values() -> [CardTypeOption; 3] {
72+
[
73+
CardTypeOption::SatsCard,
74+
CardTypeOption::TapSigner,
75+
CardTypeOption::SatsChip,
76+
]
77+
}
78+
}
79+
7080
impl Display for CardTypeOption {
7181
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
7282
let str = match self {
@@ -82,8 +92,10 @@ pub mod test {
8292
pub fn new(
8393
pipe_path: &Path,
8494
card_type: &CardTypeOption,
85-
no_init: bool,
8695
) -> Result<Self, Box<dyn std::error::Error>> {
96+
// Only SatsCard is expected to be initialized
97+
let no_init = *card_type != CardTypeOption::SatsCard;
98+
8799
// execute ecard.py python script
88100
let mut command = Command::new("python3");
89101
command
@@ -100,6 +112,7 @@ pub mod test {
100112
.stdout(Stdio::piped())
101113
.stderr(Stdio::piped());
102114
if no_init {
115+
// Do not initialize the first slot of card
103116
command.arg("--no-init");
104117
}
105118
let child = command.spawn()?;
@@ -120,7 +133,7 @@ pub mod test {
120133
#[tokio::test]
121134
pub async fn test_transmit() {
122135
let pipe_path = Path::new("/tmp/test-transmit-pipe");
123-
let _python = EcardSubprocess::new(pipe_path, &CardTypeOption::SatsCard, false).unwrap();
136+
let _python = EcardSubprocess::new(pipe_path, &CardTypeOption::SatsCard).unwrap();
124137
let emulator = find_emulator(pipe_path).await.unwrap();
125138
dbg!(emulator);
126139
}

lib/src/factory_root_key.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ impl TryFrom<PublicKey> for FactoryRootKey {
2424
match pubkey.serialize().to_lower_hex_string().as_str() {
2525
PUB_FACTORY_ROOT_KEY => Ok(FactoryRootKey::Pub(pubkey)),
2626
DEV_FACTORY_ROOT_KEY => Ok(FactoryRootKey::Dev(pubkey)),
27-
_ => Err(Error::IncorrectSignature(
28-
"Root cert is not from Coinkite. Card is counterfeit.".to_string(),
27+
_ => Err(Error::InvalidRootCert(
28+
pubkey.serialize().to_lower_hex_string(),
2929
)),
3030
}
3131
}

lib/src/sats_chip.rs

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
use bitcoin::secp256k1::{
2-
hashes::{sha256, Hash as _},
3-
All, Message, PublicKey, Secp256k1,
4-
};
1+
use bitcoin::secp256k1::{All, PublicKey, Secp256k1};
52

63
use crate::apdu::{Error, StatusResponse};
74
use crate::commands::{Authentication, Certificate, CkTransport, Read, Wait};
@@ -30,6 +27,10 @@ impl<T: CkTransport> Authentication<T> for SatsChip<T> {
3027
&self.secp
3128
}
3229

30+
fn ver(&self) -> &str {
31+
&self.ver
32+
}
33+
3334
fn pubkey(&self) -> &PublicKey {
3435
&self.pubkey
3536
}
@@ -90,14 +91,8 @@ impl<T: CkTransport> Read<T> for SatsChip<T> {
9091
}
9192

9293
impl<T: CkTransport> Certificate<T> for SatsChip<T> {
93-
fn message_digest(&mut self, card_nonce: [u8; 16], app_nonce: [u8; 16]) -> Message {
94-
let mut message_bytes: Vec<u8> = Vec::new();
95-
message_bytes.extend("OPENDIME".as_bytes());
96-
message_bytes.extend(card_nonce);
97-
message_bytes.extend(app_nonce);
98-
99-
let message_bytes_hash = sha256::Hash::hash(message_bytes.as_slice());
100-
Message::from_digest(message_bytes_hash.to_byte_array())
94+
async fn slot_pubkey(&mut self) -> Result<Option<PublicKey>, Error> {
95+
Ok(None)
10196
}
10297
}
10398

0 commit comments

Comments
 (0)