Skip to content

Commit

Permalink
Parse more ReadCard replies (#13)
Browse files Browse the repository at this point in the history
Also:

- bug fix: the terminal accepts LLV encoded values only if they have have a `f` has high nibble, so `0xf5` instead of `0x05`.
- better debug logs to figure out what is actually going on.
- enable debug output in tests.
  • Loading branch information
SirVer authored Dec 15, 2023
1 parent 7fc3ec5 commit 649341e
Show file tree
Hide file tree
Showing 11 changed files with 190 additions and 30 deletions.
64 changes: 64 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions zvt/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,6 @@ rust_test(
crate = ":zvt",
data = glob(["data/*.blob"]),
edition = "2021",
proc_macro_deps = all_crate_deps(proc_macro = True),
deps = all_crate_deps(normal_dev = True),
)
4 changes: 4 additions & 0 deletions zvt/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ description = """
A crate to interact with payment terminals (ECRs) that use the ZVT protocol, including stand alone commandline tools to interact with the devices.
"""

[dev-dependencies]
rstest = "0.18.2"
env_logger = "0.10.1"

[dependencies]
anyhow = "1.0.70"
async-stream = "0.3.5"
Expand Down
Binary file added zvt/data/status_information_read_card.blob
Binary file not shown.
17 changes: 11 additions & 6 deletions zvt/src/feig/packets/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,12 @@ mod test {
use crate::ZvtSerializer;
use std::net::Ipv4Addr;

#[test]
#[rstest::fixture]
fn common_setup() {
crate::packets::tests::common_setup();
}

#[rstest::rstest]
fn test_request_for_data() {
let bytes = get_bytes("1682080275.777628000_192.168.0.59_192.168.0.139.blob");
let expected = RequestForData {
Expand All @@ -116,7 +121,7 @@ mod test {
assert_eq!(bytes, expected.zvt_serialize());
}

#[test]
#[rstest::rstest]
fn test_cv_end_functions_enhanced_systems_information_completion() {
let bytes = get_bytes("1680761818.768770000_pt_ecr.blob");
let expected = CVendFunctionsEnhancedSystemInformationCompletion {
Expand Down Expand Up @@ -150,7 +155,7 @@ mod test {
);
}

#[test]
#[rstest::rstest]
fn test_write_file() {
let bytes = get_bytes("1682080275.594788000_192.168.0.139_192.168.0.59.blob");
let expected = WriteFile {
Expand Down Expand Up @@ -206,7 +211,7 @@ mod test {
assert_eq!(bytes, expected.zvt_serialize());
}

#[test]
#[rstest::rstest]
fn test_cvend_functions() {
let bytes = get_bytes("1680761818.690979000_ecr_pt.blob");
let expected = CVendFunctions {
Expand All @@ -217,7 +222,7 @@ mod test {
assert_eq!(bytes, expected.zvt_serialize());
}

#[test]
#[rstest::rstest]
fn test_write_data() {
let bytes = get_bytes("1682080310.907262000_192.168.0.139_192.168.0.59.blob");
let dummy_data = vec![0; 0x042c];
Expand All @@ -243,7 +248,7 @@ mod test {
assert_eq!(actual_bytes[..26], bytes[..26]);
}

#[test]
#[rstest::rstest]
fn test_change_host_config() {
let bytes = get_bytes("change_host_config.blob");
let addr = Ipv4Addr::new(213, 183, 19, 105);
Expand Down
82 changes: 68 additions & 14 deletions zvt/src/packets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ pub struct StatusInformation {
#[zvt_bmp(number = 0x22, length = length::Llv, encoding = encoding::Bcd)]
pub card_number: Option<usize>,

#[zvt_bmp(number = 0x27)]
#[zvt_bmp(number = 0x23, length = length::Llv, encoding= encoding::Hex)]
pub track_2_data: Option<String>,

#[zvt_bmp(number = 0x27, length = length::Fixed<1>)]
pub result_code: Option<u8>,

#[zvt_bmp(number = 0x29, length = length::Fixed<4>, encoding = encoding::Bcd)]
Expand Down Expand Up @@ -249,6 +252,9 @@ pub struct Reservation {
#[zvt_bmp(number = 0x22, length = length::Llv, encoding = encoding::Bcd)]
pub card_number: Option<usize>,

#[zvt_bmp(number = 0x23, length = length::Llv, encoding= encoding::Hex)]
pub track_2_data: Option<String>,

// Unclear how to interpret this.
#[zvt_bmp(number = 0x01)]
pub timeout: Option<u8>,
Expand Down Expand Up @@ -425,6 +431,13 @@ pub mod tests {
use chrono::NaiveDate;
use std::fs;

#[rstest::fixture]
pub fn common_setup() {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("debug"))
.is_test(true)
.init();
}

pub fn get_bytes(name: &str) -> Vec<u8> {
let path_from_root = "zvt/data/".to_string();
let base_dir = match fs::metadata(&path_from_root) {
Expand All @@ -434,7 +447,7 @@ pub mod tests {
fs::read(&format!("{base_dir}/{name}")).unwrap()
}

#[test]
#[rstest::rstest]
fn test_read_card() {
let bytes = get_bytes("1680722649.972316000_ecr_pt.blob");
let expected = ReadCard {
Expand All @@ -452,7 +465,7 @@ pub mod tests {
assert_eq!(expected, output.0);
}

#[test]
#[rstest::rstest]
fn test_status_information() {
let bytes = get_bytes("1680728161.963129000_pt_ecr.blob");
let expected = StatusInformation {
Expand All @@ -461,11 +474,15 @@ pub mod tests {
uuid: Some("000000000000081ca72f".to_string()),
ats: Some("0578807002".to_string()),
card_type: Some(1),
maximum_pre_autorisation: None,
card_identification_item: None,
subs_on_card: None,
sub_type: Some("fe04".to_string()),
atqa: Some("0400".to_string()),
sak: Some(0x20),
subs: vec![tlv::Subs {
application_id: Some("a0000000041010".to_string()),
card_type: None,
}],
}),
..StatusInformation::default()
Expand Down Expand Up @@ -596,7 +613,44 @@ pub mod tests {
assert!(expected.tlv.as_ref().unwrap().subs.is_empty());
}

#[test]
#[rstest::rstest]
fn test_status_information_read_card() {
let bytes = get_bytes("status_information_read_card.blob");
let expected = StatusInformation {
result_code: Some(0),
track_2_data: Some("6725904411001000142d24122012386013860f".to_string()),
tlv: Some(tlv::StatusInformation {
maximum_pre_autorisation: Some(10000),
card_identification_item: Some("3f56a32065cc4dbe8330c37609f91996".to_string()),
uuid: Some("00000000000008b3c880".to_string()),
ats: Some("0c788074038031c073d631c0".to_string()),
card_type: Some(1),
sub_type: Some("fe04".to_string()),
atqa: Some("0400".to_string()),
sak: Some(32),
subs: Vec::new(),
subs_on_card: Some(tlv::SubsOnCard {
subs: vec![
tlv::Subs {
application_id: Some("a0000003591010028001".to_string()),
card_type: Some("0005".to_string()),
},
tlv::Subs {
application_id: Some("a0000000043060".to_string()),
card_type: Some("002e".to_string()),
},
],
}),
}),
..StatusInformation::default()
};
assert_eq!(
expected,
StatusInformation::zvt_deserialize(&bytes).unwrap().0
);
}

#[rstest::rstest]
fn test_receipt_printout_completion() {
let bytes = get_bytes("1680728219.054216000_pt_ecr.blob");
let expected = ReceiptPrintoutCompletion {
Expand Down Expand Up @@ -626,7 +680,7 @@ pub mod tests {
);
}

#[test]
#[rstest::rstest]
fn test_pre_auth_data() {
let bytes = get_bytes("1680728162.033575000_ecr_pt.blob");
let expected = Reservation {
Expand All @@ -644,7 +698,7 @@ pub mod tests {
assert_eq!(Reservation::zvt_deserialize(&bytes).unwrap().0, expected,);
}

#[test]
#[rstest::rstest]
fn test_registration() {
let bytes = get_bytes("1681273860.511128000_ecr_pt.blob");
let expected = Registration {
Expand All @@ -657,7 +711,7 @@ pub mod tests {
assert_eq!(Registration::zvt_deserialize(&bytes).unwrap().0, expected);
}

#[test]
#[rstest::rstest]
fn test_pre_auth_reversal() {
let bytes = get_bytes("1680728213.562478000_ecr_pt.blob");
let expected = PreAuthReversal {
Expand All @@ -671,7 +725,7 @@ pub mod tests {
);
}

#[test]
#[rstest::rstest]
fn test_completion_data() {
let bytes = get_bytes("1680761818.641601000_pt_ecr.blob");
let golden = CompletionData {
Expand All @@ -685,7 +739,7 @@ pub mod tests {
assert_eq!(CompletionData::zvt_deserialize(&bytes).unwrap().0, golden);
}

#[test]
#[rstest::rstest]
fn test_end_of_day() {
let bytes = get_bytes("1681282621.302434000_ecr_pt.blob");
let expected = EndOfDay { password: 123456 };
Expand All @@ -694,7 +748,7 @@ pub mod tests {
assert_eq!(bytes, expected.zvt_serialize());
}

#[test]
#[rstest::rstest]
fn test_partial_reversal() {
let bytes = get_bytes("1681455683.221609000_ecr_pt.blob");
let expected = PartialReversal {
Expand All @@ -715,7 +769,7 @@ pub mod tests {
);
}

#[test]
#[rstest::rstest]
fn test_intermediate_status() {
let bytes = get_bytes("1680728162.647465000_pt_ecr.blob");
let expected = IntermediateStatusInformation {
Expand All @@ -731,7 +785,7 @@ pub mod tests {
assert_eq!(bytes, expected.zvt_serialize());
}

#[test]
#[rstest::rstest]
fn test_print_text_block() {
let bytes = get_bytes("1680728215.585561000_pt_ecr.blob");
let actual = PrintTextBlock::zvt_deserialize(&bytes).unwrap().0;
Expand All @@ -743,7 +797,7 @@ pub mod tests {
assert_eq!(bytes, actual.zvt_serialize());
}

#[test]
#[rstest::rstest]
fn test_text_block_system_information() {
let bytes = get_bytes("print_system_configuration_reply.blob");
let actual = PrintTextBlock::zvt_deserialize(&bytes).unwrap().0;
Expand All @@ -755,7 +809,7 @@ pub mod tests {
assert_eq!(bytes, actual.zvt_serialize());
}

#[test]
#[rstest::rstest]
fn test_partial_reversal_abort() {
let bytes = get_bytes("partial_reversal.blob");
let actual = PartialReversalAbort::zvt_deserialize(&bytes).unwrap().0;
Expand Down
Loading

0 comments on commit 649341e

Please sign in to comment.