Browse Source

Now with rustfmt.

master
Thomas Kerber 3 years ago
parent
commit
e5346fc8d9
12 changed files with 553 additions and 255 deletions
  1. +1
    -0
      .gitignore
  2. +48
    -11
      src/apdu.rs
  3. +93
    -40
      src/extract.rs
  4. +18
    -9
      src/fs/data.rs
  5. +15
    -6
      src/fs/dummy.rs
  6. +30
    -15
      src/fs/efdf.rs
  7. +118
    -71
      src/fs/mod.rs
  8. +33
    -26
      src/fs/path.rs
  9. +32
    -16
      src/fs/short.rs
  10. +8
    -4
      src/main.rs
  11. +112
    -28
      src/scan.rs
  12. +45
    -29
      src/util.rs

+ 1
- 0
.gitignore View File

@@ -1 +1,2 @@
target
**/*.rs.bk

+ 48
- 11
src/apdu.rs View File

@@ -17,21 +17,39 @@ impl<'a> InsRewriteHandle<'a> {
pub fn new(handle: pcsc::Handle<'a>, rules: &[[u8; 4]]) -> Self {
let mut map = HashMap::with_capacity(rules.len());
for rule in rules.iter() {
map.insert(
Ins { cla: rule[0], ins: rule[1] },
Ins { cla: rule[2], ins: rule[3] });
map.insert(Ins {
cla: rule[0],
ins: rule[1],
},
Ins {
cla: rule[2],
ins: rule[3],
});
}
InsRewriteHandle {
internal: handle,
rules: map,
}
InsRewriteHandle { internal: handle, rules: map }
}
}

impl<'a> Transmit for InsRewriteHandle<'a> {
fn transmit(&mut self, send: &[u8], buf: &mut[u8]) -> Result<usize, pcsc::Error> {
fn transmit(&mut self, send: &[u8], buf: &mut [u8]) -> Result<usize, pcsc::Error> {
self.internal.transmit(send, buf)
}

fn transmit_apdu(&mut self, cla: u8, ins: u8, p1: u8, p2: u8, data: &[u8], le: usize) -> Result<Vec<u8>, pcsc::Error> {
let mut ins = Ins { cla: cla, ins: ins};
fn transmit_apdu(&mut self,
cla: u8,
ins: u8,
p1: u8,
p2: u8,
data: &[u8],
le: usize)
-> Result<Vec<u8>, pcsc::Error> {
let mut ins = Ins {
cla: cla,
ins: ins,
};
if let Some(ins2) = self.rules.get(&ins) {
ins = *ins2;
}
@@ -49,16 +67,35 @@ pub struct ClaRewriteHandle<'a> {

impl<'a> ClaRewriteHandle<'a> {
pub fn new(handle: pcsc::Handle<'a>, cla: u8) -> Self {
ClaRewriteHandle { internal: handle, cla: cla }
ClaRewriteHandle {
internal: handle,
cla: cla,
}
}
}

impl<'a> Transmit for ClaRewriteHandle<'a> {
fn transmit(&mut self, send: &[u8], buf: &mut[u8]) -> Result<usize, pcsc::Error> {
fn transmit(&mut self, send: &[u8], buf: &mut [u8]) -> Result<usize, pcsc::Error> {
self.internal.transmit(send, buf)
}

fn transmit_apdu(&mut self, cla: u8, ins: u8, p1: u8, p2: u8, data: &[u8], le: usize) -> Result<Vec<u8>, pcsc::Error> {
self.internal.transmit_apdu(if cla == 0x00 { self.cla } else { cla }, ins, p1, p2, data, le)
fn transmit_apdu(&mut self,
cla: u8,
ins: u8,
p1: u8,
p2: u8,
data: &[u8],
le: usize)
-> Result<Vec<u8>, pcsc::Error> {
self.internal.transmit_apdu(if cla == 0x00 {
self.cla
} else {
cla
},
ins,
p1,
p2,
data,
le)
}
}

+ 93
- 40
src/extract.rs View File

@@ -16,7 +16,7 @@ enum FileType {
EF,
DF,
Data,
Short
Short,
}

#[derive(Debug)]
@@ -26,7 +26,10 @@ struct FilePath {
ftype: FileType,
}

fn extract_data_files<T: AsRef<[u8]>, P: AsRef<Path>>(handle: &mut pcsc::Transmit, files: &[T], outdir: P, caps: fs::Capabilities) {
fn extract_data_files<T: AsRef<[u8]>, P: AsRef<Path>>(handle: &mut pcsc::Transmit,
files: &[T],
outdir: P,
caps: fs::Capabilities) {
let mut factory = None;
for fact in fs::DATA_FACTORIES {
if fact.has_required_capabilities(caps) {
@@ -37,7 +40,8 @@ fn extract_data_files<T: AsRef<[u8]>, P: AsRef<Path>>(handle: &mut pcsc::Transmi
let factory = match factory {
Some(v) => v,
None => {
warn!("No recipe for data extract with AF capabilities! ({:?})", caps.list());
warn!("No recipe for data extract with AF capabilities! ({:?})",
caps.list());
return;
}
};
@@ -52,12 +56,17 @@ fn extract_data_files<T: AsRef<[u8]>, P: AsRef<Path>>(handle: &mut pcsc::Transmi
println!(":::: Starting data extraction");
for file in files {
if let Err(e) = handle.extract_data(file.as_ref(), outdir.as_ref()) {
error!("Error during extraction of {}: {}", file.as_ref().to_hex(), e);
error!("Error during extraction of {}: {}",
file.as_ref().to_hex(),
e);
}
}
}

fn extract_full_files<T: AsRef<[u8]>, P: AsRef<Path>>(handle: &mut pcsc::Transmit, files: &[T], outdir: P, caps: fs::Capabilities) {
fn extract_full_files<T: AsRef<[u8]>, P: AsRef<Path>>(handle: &mut pcsc::Transmit,
files: &[T],
outdir: P,
caps: fs::Capabilities) {
let mut factory = None;
for fact in fs::FULL_FACTORIES {
if fact.has_required_capabilities(caps) {
@@ -68,7 +77,8 @@ fn extract_full_files<T: AsRef<[u8]>, P: AsRef<Path>>(handle: &mut pcsc::Transmi
let factory = match factory {
Some(v) => v,
None => {
warn!("No recipe for full id extract with AF capabilities! ({:?})", caps.list());
warn!("No recipe for full id extract with AF capabilities! ({:?})",
caps.list());
return;
}
};
@@ -83,12 +93,17 @@ fn extract_full_files<T: AsRef<[u8]>, P: AsRef<Path>>(handle: &mut pcsc::Transmi
println!(":::: Starting full file identifier extraction");
for file in files {
if let Err(e) = handle.extract(file.as_ref(), outdir.as_ref(), caps) {
error!("Error during extraction of {}: {}", file.as_ref().to_hex(), e);
error!("Error during extraction of {}: {}",
file.as_ref().to_hex(),
e);
}
}
}

fn extract_short_files<T: AsRef<[u8]>, P: AsRef<Path>>(handle: &mut pcsc::Transmit, files: &[T], outdir: P, caps: fs::Capabilities) {
fn extract_short_files<T: AsRef<[u8]>, P: AsRef<Path>>(handle: &mut pcsc::Transmit,
files: &[T],
outdir: P,
caps: fs::Capabilities) {
let mut factory = None;
for fact in fs::SHORT_FACTORIES {
if fact.has_required_capabilities(caps) {
@@ -99,7 +114,8 @@ fn extract_short_files<T: AsRef<[u8]>, P: AsRef<Path>>(handle: &mut pcsc::Transm
let factory = match factory {
Some(v) => v,
None => {
warn!("No recipe for short id extraction with AF capabilities! ({:?})", caps.list());
warn!("No recipe for short id extraction with AF capabilities! ({:?})",
caps.list());
return;
}
};
@@ -114,13 +130,19 @@ fn extract_short_files<T: AsRef<[u8]>, P: AsRef<Path>>(handle: &mut pcsc::Transm
println!(":::: Starting short file identifier extraction");
for file in files {
if let Err(e) = handle.extract(file.as_ref(), outdir.as_ref(), caps) {
error!("Error during extraction of {}: {}", file.as_ref().to_hex(), e);
error!("Error during extraction of {}: {}",
file.as_ref().to_hex(),
e);
}
}
}

fn print_usage(program: &str, command: &str, opts: &Options) -> ! {
let brief = format!("Usage: {0} {1} [options] INFILE OUTDIR\n\nExtracts the files listed in INFILE to OUTDIR from a smartcard. INFILE should have previously been generated using '{0} scan'.", program, command);
let brief = format!("Usage: {0} {1} [options] INFILE OUTDIR\n\nExtracts the files listed in \
INFILE to OUTDIR from a smartcard. INFILE should have previously been \
generated using '{0} scan'.",
program,
command);
print!("{}", opts.usage(&brief));
exit(1);
}
@@ -184,17 +206,23 @@ fn parse_infile<P: AsRef<Path>>(infile: P) -> Result<Vec<FilePath>, ParseError>
"short" => FileType::Short,
"data" => FileType::Data,
// Backwards compatibility with previous notation.
"full" => if parts.len() == 4 && parts[3] == "true" {
FileType::DF
} else {
FileType::EF
},
"full" => {
if parts.len() == 4 && parts[3] == "true" {
FileType::DF
} else {
FileType::EF
}
}
_ => {
warn!("unknown path type: {}", parts[0]);
continue;
},
}
};
ret.push(FilePath{af: af, path: path, ftype: ftype})
ret.push(FilePath {
af: af,
path: path,
ftype: ftype,
})
}
Ok(ret)
}
@@ -204,15 +232,33 @@ pub fn main(args: Vec<String>) {
let command = args[1].clone();

let mut opts = Options::new();
opts.optopt("p", "pin", "sets the verify apdu pin in hex, with the first byte being the PIN id. (e.g. 813132333435 corresponds to the APDU 00200081053132333435)", "PIN");
opts.optopt("c", "cla", "sets the cla byte to use when extracting files. Specified as a single hex byte.", "CLA");
opts.optopt("", "apdumap", "specifies rules to rewrite apdus. Rules are comma sperated, and have the for '<ORIG CLA><ORIG INS>-><NEW CLA><NEW INS>'. Example: --apdumap 00a4->3030,00b0->3031", "RULES");
opts.optopt("r", "reader", "specifies the reader to use. Uses the first reader with the given string as part of its name.", "READER");
opts.optopt("p",
"pin",
"sets the verify apdu pin in hex, with the first byte being the PIN id. (e.g. \
813132333435 corresponds to the APDU 00200081053132333435)",
"PIN");
opts.optopt("c",
"cla",
"sets the cla byte to use when extracting files. Specified as a single hex byte.",
"CLA");
opts.optopt("",
"apdumap",
"specifies rules to rewrite apdus. Rules are comma sperated, and have the for \
'<ORIG CLA><ORIG INS>-><NEW CLA><NEW INS>'. Example: --apdumap \
00a4->3030,00b0->3031",
"RULES");
opts.optopt("r",
"reader",
"specifies the reader to use. Uses the first reader with the given string as \
part of its name.",
"READER");
opts.optflag("h", "help", "prints this help text");
opts.optflag("", "data-each-file", "Try to access every data element for each file.");
opts.optflag("",
"data-each-file",
"Try to access every data element for each file.");
let matches = match opts.parse(&args[2..]) {
Ok(m) => { m }
Err(e) => { panic!(e.to_string()) }
Ok(m) => m,
Err(e) => panic!(e.to_string()),
};
if matches.opt_present("h") {
print_usage(&program, &command, &opts);
@@ -229,18 +275,20 @@ pub fn main(args: Vec<String>) {
Err(e) => {
error!("Error parsing infile: {}", e);
exit(2)
},
}
};
let outdir = &matches.free[1];
// Part 1: Sort by AF.
let mut affiles = HashMap::new();
for file in files {
{
if !affiles.contains_key(&file.af[..]) {
affiles.insert(file.af.clone(), Vec::new());
if !affiles.contains_key(&file.af[..]) {
affiles.insert(file.af.clone(), Vec::new());
}
affiles.get_mut(&file.af[..])
}
affiles.get_mut(&file.af[..])
}.unwrap().push(file);
.unwrap()
.push(file);
}
let caps = match fs::Capabilities::test_for(&mut *handle, true) {
Ok(v) => v,
@@ -250,7 +298,8 @@ pub fn main(args: Vec<String>) {
}
};
if !caps.has(fs::Capability::SelectAF) {
warn!("Card does not appear to have the capability to select AFs. Trying to select AFs anyway.");
warn!("Card does not appear to have the capability to select AFs. Trying to select AFs \
anyway.");
}
// Part 2: For each AF, select it and rescan capabilities, and get a factory for it (if
// possible).
@@ -308,15 +357,19 @@ pub fn main(args: Vec<String>) {
}
if data_files.len() > 0 {
if matches.opt_present("data-each-file") {
data_files = data_files.into_iter().filter(|tag| tag.len() == 2)
.flat_map(|tag|
efs.iter()
.chain(dfs.iter())
.map(|file| {
let mut path = file.to_owned();
path.extend(tag.iter());
path
}).collect::<Vec<_>>()).collect();
data_files = data_files.into_iter()
.filter(|tag| tag.len() == 2)
.flat_map(|tag| {
efs.iter()
.chain(dfs.iter())
.map(|file| {
let mut path = file.to_owned();
path.extend(tag.iter());
path
})
.collect::<Vec<_>>()
})
.collect();
}
data_files.sort();
data_files.dedup();

+ 18
- 9
src/fs/data.rs View File

@@ -1,4 +1,5 @@
use fs::{FileSystemHandleFactory, FileSystemHandle, Capabilities, Error, Capability, FULL_FACTORIES, DUMMY_FACTORY, try_sw, scan_start, scan_update, scan_end, found_data};
use fs::{FileSystemHandleFactory, FileSystemHandle, Capabilities, Error, Capability,
FULL_FACTORIES, DUMMY_FACTORY, try_sw, scan_start, scan_update, scan_end, found_data};
use pcsclite as pcsc;
use pcsclite::Transmit;

@@ -9,7 +10,10 @@ impl FileSystemHandleFactory for DataHandleFactory {
c.has(Capability::ReadData)
}

fn create<'a>(&self, handle: &'a mut Transmit, caps: Capabilities) -> Result<Box<FileSystemHandle<'a> + 'a>, Error> {
fn create<'a>(&self,
handle: &'a mut Transmit,
caps: Capabilities)
-> Result<Box<FileSystemHandle<'a> + 'a>, Error> {
// Find a full path factory that works, and use that. If none are available, us a dummy.
let mut fsh = None;
for factory in FULL_FACTORIES.iter().chain(&[DUMMY_FACTORY]) {
@@ -18,7 +22,7 @@ impl FileSystemHandleFactory for DataHandleFactory {
break;
}
}
Ok(Box::new(DataHandle{internal: fsh.unwrap()}))
Ok(Box::new(DataHandle { internal: fsh.unwrap() }))
}

fn method(&self) -> &str {
@@ -31,7 +35,7 @@ pub struct DataHandle<'a> {
}

impl<'a> Transmit for DataHandle<'a> {
fn transmit(&mut self, send: &[u8], buf: &mut[u8]) -> Result<usize, pcsc::Error> {
fn transmit(&mut self, send: &[u8], buf: &mut [u8]) -> Result<usize, pcsc::Error> {
self.internal.transmit(send, buf)
}
}
@@ -48,11 +52,12 @@ impl<'a> FileSystemHandle<'a> for DataHandle<'a> {
scan_update((i << 8) | j);
}
let res = try!(self.transmit_apdu(0x00, 0xca, i as u8, j as u8, &[], 0x100));
const IGNORE_SW: &'static[&'static[u8]] = &[&[0x6a, 0x88], &[0x6a, 0x86], &[0x6a, 0x82]];
if IGNORE_SW.contains(&&res[res.len()-2..]) {
const IGNORE_SW: &'static [&'static [u8]] =
&[&[0x6a, 0x88], &[0x6a, 0x86], &[0x6a, 0x82]];
if IGNORE_SW.contains(&&res[res.len() - 2..]) {
continue;
}
if res[res.len()-2] != 0x6c {
if res[res.len() - 2] != 0x6c {
if let Err(e) = try_sw(&res[..]) {
warn!("Error during recursive scan: {}. Continuing...", e);
continue;
@@ -79,6 +84,10 @@ impl<'a> FileSystemHandle<'a> for DataHandle<'a> {
self.internal.is_df(file)
}

fn current_df(&self) -> &[u8] { self.internal.current_df() }
fn class(&self, _: Option<bool>) -> &str { "data" }
fn current_df(&self) -> &[u8] {
self.internal.current_df()
}
fn class(&self, _: Option<bool>) -> &str {
"data"
}
}

+ 15
- 6
src/fs/dummy.rs View File

@@ -5,10 +5,15 @@ use pcsclite::Transmit;
pub struct DummyHandleFactory;

impl FileSystemHandleFactory for DummyHandleFactory {
fn has_required_capabilities(&self, _: Capabilities) -> bool { true }
fn has_required_capabilities(&self, _: Capabilities) -> bool {
true
}

fn create<'a>(&self, handle: &'a mut Transmit, _: Capabilities) -> Result<Box<FileSystemHandle<'a> + 'a>, Error> {
Ok(Box::new(DummyHandle{handle: handle}))
fn create<'a>(&self,
handle: &'a mut Transmit,
_: Capabilities)
-> Result<Box<FileSystemHandle<'a> + 'a>, Error> {
Ok(Box::new(DummyHandle { handle: handle }))
}

fn method(&self) -> &str {
@@ -21,7 +26,7 @@ pub struct DummyHandle<'a> {
}

impl<'a> Transmit for DummyHandle<'a> {
fn transmit(&mut self, send: &[u8], buf: &mut[u8]) -> Result<usize, pcsc::Error> {
fn transmit(&mut self, send: &[u8], buf: &mut [u8]) -> Result<usize, pcsc::Error> {
self.handle.transmit(send, buf)
}
}
@@ -47,6 +52,10 @@ impl<'a> FileSystemHandle<'a> for DummyHandle<'a> {
}
}

fn current_df(&self) -> &[u8] { &[] }
fn class(&self, _: Option<bool>) -> &str { "dummy" }
fn current_df(&self) -> &[u8] {
&[]
}
fn class(&self, _: Option<bool>) -> &str {
"dummy"
}
}

+ 30
- 15
src/fs/efdf.rs View File

@@ -1,4 +1,5 @@
use fs::{FileSystemHandleFactory, FileSystemHandle, Capability, Capabilities, Error, try_sw, scan_start, scan_update, scan_end, found_file};
use fs::{FileSystemHandleFactory, FileSystemHandle, Capability, Capabilities, Error, try_sw,
scan_start, scan_update, scan_end, found_file};
use pcsclite as pcsc;
use pcsclite::Transmit;

@@ -9,7 +10,10 @@ impl FileSystemHandleFactory for EFDFHandleFactory {
c.has(Capability::SelectEFDF) && c.has(Capability::SelectDF) && c.has(Capability::SelectMF)
}

fn create<'a>(&self, handle: &'a mut Transmit, caps: Capabilities) -> Result<Box<FileSystemHandle<'a> + 'a>, Error> {
fn create<'a>(&self,
handle: &'a mut Transmit,
caps: Capabilities)
-> Result<Box<FileSystemHandle<'a> + 'a>, Error> {
let p2 = if caps.has(Capability::ReturnNothing) {
0x0c
} else if caps.has(Capability::ReturnFCI) {
@@ -21,7 +25,11 @@ impl FileSystemHandleFactory for EFDFHandleFactory {
} else {
unreachable!()
};
Ok(Box::new(EFDFHandle{handle: handle, current_df: Vec::new(), p2: p2}))
Ok(Box::new(EFDFHandle {
handle: handle,
current_df: Vec::new(),
p2: p2,
}))
}

fn method(&self) -> &str {
@@ -36,7 +44,7 @@ pub struct EFDFHandle<'a> {
}

impl<'a> Transmit for EFDFHandle<'a> {
fn transmit(&mut self, send: &[u8], buf: &mut[u8]) -> Result<usize, pcsc::Error> {
fn transmit(&mut self, send: &[u8], buf: &mut [u8]) -> Result<usize, pcsc::Error> {
self.handle.transmit(send, buf)
}
}
@@ -52,16 +60,14 @@ impl<'a> FileSystemHandle<'a> for EFDFHandle<'a> {
first = true;
scan_update((i << 8) | j);
}
if (i, j) == (0x00, 0x00)
|| (i, j) == (0x3F, 0x00)
|| (i, j) == (0x3F, 0xFF)
|| (i, j) == (0xFF, 0xFF)
|| (i, j) == (0x7F, 0xFF) {
if (i, j) == (0x00, 0x00) || (i, j) == (0x3F, 0x00) || (i, j) == (0x3F, 0xFF) ||
(i, j) == (0xFF, 0xFF) ||
(i, j) == (0x7F, 0xFF) {
continue;
}
let p2 = self.p2;
let res = try!(self.transmit_apdu(0x00, 0xa4, 0x00, p2, &[i as u8, j as u8], 0x00));
if &res[res.len()-2..] == &[0x6a, 0x82] {
if &res[res.len() - 2..] == &[0x6a, 0x82] {
continue;
}
let mut fpath = self.current_df.clone();
@@ -71,7 +77,7 @@ impl<'a> FileSystemHandle<'a> for EFDFHandle<'a> {
}
try!(self.select_path(&fpath[..], true));
let res = try!(self.transmit_apdu(0x00, 0xa4, 0x01, p2, &[i as u8, j as u8], 0x00));
let is_df = &res[res.len()-2..] != &[0x6a, 0x82];
let is_df = &res[res.len() - 2..] != &[0x6a, 0x82];
try!(self.select_path(&fpath[..], true));
fpath.push(i as u8);
fpath.push(j as u8);
@@ -93,7 +99,7 @@ impl<'a> FileSystemHandle<'a> for EFDFHandle<'a> {
self.current_df = if is_dir {
path.to_owned()
} else {
path[..path.len()-2].to_owned()
path[..path.len() - 2].to_owned()
};
Ok(())
}
@@ -103,11 +109,20 @@ impl<'a> FileSystemHandle<'a> for EFDFHandle<'a> {
if path.len() == 0 {
return Ok(true);
}
try!(self.select_path(&path[..path.len()-2], true));
Ok(try_sw(&try!(self.transmit_apdu(0x00, 0xa4, 0x01, p2, &path[path.len()-2..], 0x00))[..]).is_ok())
try!(self.select_path(&path[..path.len() - 2], true));
Ok(try_sw(&try!(self.transmit_apdu(0x00,
0xa4,
0x01,
p2,
&path[path.len() -
2..],
0x00))[..])
.is_ok())
}

fn current_df(&self) -> &[u8] { &self.current_df[..] }
fn current_df(&self) -> &[u8] {
&self.current_df[..]
}
fn class(&self, df: Option<bool>) -> &str {
match df {
Some(true) => "df",

+ 118
- 71
src/fs/mod.rs View File

@@ -14,22 +14,17 @@ pub mod dummy;

/// A list of factories for creating file system handles to run a recursive scan of full file
/// identifiers.
pub const FULL_FACTORIES: &'static[&'static FileSystemHandleFactory] = &[
&path::PathHandleFactory,
&efdf::EFDFHandleFactory,
];
pub const FULL_FACTORIES: &'static [&'static FileSystemHandleFactory] = &[&path::PathHandleFactory,
&efdf::EFDFHandleFactory];

/// A list of factories for creating file system handles to find short EF identifiers.
///
/// These cannot do a full recursive scan, and each path returned has an odd number of bytes, to
/// indicate that a short EF id is used.
pub const SHORT_FACTORIES: &'static[&'static FileSystemHandleFactory] = &[
&short::ShortHandleFactory,
];
pub const SHORT_FACTORIES: &'static [&'static FileSystemHandleFactory] =
&[&short::ShortHandleFactory];

pub const DATA_FACTORIES: &'static[&'static FileSystemHandleFactory] = &[
&data::DataHandleFactory,
];
pub const DATA_FACTORIES: &'static [&'static FileSystemHandleFactory] = &[&data::DataHandleFactory];

pub const DUMMY_FACTORY: &'static FileSystemHandleFactory = &dummy::DummyHandleFactory;

@@ -95,10 +90,9 @@ impl From<io::Error> for Error {
}

fn try_sw(rapdu: &[u8]) -> Result<(&[u8], &[u8]), Error> {
const ERR_SW1: &'static [u16] = &[
0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f];
let sw = &rapdu[rapdu.len()-2..];
const ERR_SW1: &'static [u16] = &[0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
0x6e, 0x6f];
let sw = &rapdu[rapdu.len() - 2..];
let sw = (sw[0] as u16) << 8 | sw[1] as u16;
if sw == 0x6982 {
Err(Error::AccessDenied)
@@ -115,13 +109,16 @@ fn try_sw(rapdu: &[u8]) -> Result<(&[u8], &[u8]), Error> {
} else if ERR_SW1.contains(&(sw >> 8)) {
Err(Error::unknown_error(sw))
} else {
Ok((&rapdu[..rapdu.len()-2], &rapdu[rapdu.len()-2..]))
Ok((&rapdu[..rapdu.len() - 2], &rapdu[rapdu.len() - 2..]))
}
}

pub trait FileSystemHandleFactory {
fn has_required_capabilities(&self, c: Capabilities) -> bool;
fn create<'a>(&self, handle: &'a mut pcsc::Transmit, cap: Capabilities) -> Result<Box<FileSystemHandle<'a> + 'a>, Error>;
fn create<'a>(&self,
handle: &'a mut pcsc::Transmit,
cap: Capabilities)
-> Result<Box<FileSystemHandle<'a> + 'a>, Error>;
fn method(&self) -> &str;
}

@@ -134,21 +131,26 @@ pub trait FileSystemHandle<'a>: pcsc::Transmit {

fn extract_data(&mut self, file: &[u8], outdir: &Path) -> Result<(), Error> {
let mut path = PathBuf::from(outdir);
let (file, obj) = (&file[..file.len()-2], &file[file.len()-2..]);
let (file, obj) = (&file[..file.len() - 2], &file[file.len() - 2..]);
let df = try!(self.is_df(file));
if file.len() > 0 {
for name in file.chunks(2).take(file.len()/2 - 1) {
for name in file.chunks(2).take(file.len() / 2 - 1) {
path.push(format!("{}", name.to_hex()));
}
path.push(format!("{}", &file[file.len()-2..].to_hex()));
path.push(format!("{}", &file[file.len() - 2..].to_hex()));
}
path.push(format!("[{}]", obj.to_hex()));
try!(create_dir_all(path.parent().unwrap()));
let mut f = try!(File::create(path));
try!(self.select_path(file, df));
let mut rapdu = try!(self.transmit_apdu(0x00, 0xca, obj[0], obj[1], &[], 0x100));
if rapdu[rapdu.len()-2] == 0x6c {
rapdu = try!(self.transmit_apdu(0x00, 0xca, obj[0], obj[1], &[], rapdu[rapdu.len()-1] as usize));
if rapdu[rapdu.len() - 2] == 0x6c {
rapdu = try!(self.transmit_apdu(0x00,
0xca,
obj[0],
obj[1],
&[],
rapdu[rapdu.len() - 1] as usize));
}
let res = try!(try_sw(&rapdu[..]));
try!(writeln!(f, "type: data object\n"));
@@ -161,8 +163,8 @@ pub trait FileSystemHandle<'a>: pcsc::Transmit {
try!(self.select_path(file, false));
0x00
} else {
try!(self.select_path(&file[..file.len()-1], true));
file[file.len()-1]
try!(self.select_path(&file[..file.len() - 1], true));
file[file.len() - 1]
};
let mut path = PathBuf::from(outdir);
for name in file.chunks(2) {
@@ -179,7 +181,7 @@ pub trait FileSystemHandle<'a>: pcsc::Transmit {
0x80 | shortef
};
let mut rapdu = try!(self.transmit_apdu(0x00, 0xb0, p1, 0x00, &[], 0xffff));
if rapdu[rapdu.len()-2] == 0x6c {
if rapdu[rapdu.len() - 2] == 0x6c {
rapdu = try!(self.transmit_apdu(0x00, 0xb0, p1, 0x00, &[], rapdu[rapdu.len()-1] as usize));
}
let res = try_sw(&rapdu[..]);
@@ -213,8 +215,13 @@ pub trait FileSystemHandle<'a>: pcsc::Transmit {
// try read record
for i in 1.. {
let mut rapdu = try!(self.transmit_apdu(0x00, 0xb2, i as u8, (shortef << 3) | 0x04, &[], 0xffff));
if rapdu[rapdu.len()-2] == 0x6c {
rapdu = try!(self.transmit_apdu(0x00, 0xb2, i as u8, (shortef << 3) | 0x04, &[], rapdu[rapdu.len()-1] as usize));
if rapdu[rapdu.len() - 2] == 0x6c {
rapdu = try!(self.transmit_apdu(0x00,
0xb2,
i as u8,
(shortef << 3) | 0x04,
&[],
rapdu[rapdu.len() - 1] as usize));
}
let res = try_sw(&rapdu[..]);
match res {
@@ -261,36 +268,66 @@ pub trait FileSystemHandle<'a>: pcsc::Transmit {
Ok(())
}

fn scan_paths(&mut self, paths: &[(&[u8], bool)], af: &[u8], f: &mut Write, flog: &mut Write) -> Result<Vec<(Vec<u8>, bool)>, Error> {
fn scan_paths(&mut self,
paths: &[(&[u8], bool)],
af: &[u8],
f: &mut Write,
flog: &mut Write)
-> Result<Vec<(Vec<u8>, bool)>, Error> {
let mut res = Vec::new();
for (i, &(path, is_df)) in paths.iter().enumerate() {
let _ = writeln!(stderr(), ":: Scanning path {}/{}...", i + 1, paths.len());
try!(self.select_path(path, is_df));
let new = try!(self.scan_current_df());
for &(ref file, df) in new.iter() {
try!(writeln!(f, "{}::{}::{}", self.class(Some(df)), af.to_hex(), file.to_hex()));
try!(writeln!(f,
"{}::{}::{}",
self.class(Some(df)),
af.to_hex(),
file.to_hex()));
}
res.extend(new);
try!(writeln!(flog, "scanned-{} {}::{}", self.class(None), af.to_hex(), path.to_hex()));
try!(writeln!(flog,
"scanned-{} {}::{}",
self.class(None),
af.to_hex(),
path.to_hex()));
}
Ok(res)
}

fn rec_scan(&mut self, filter_root: bool, filter_dup: bool, af: &[u8], paths: Vec<Vec<u8>>, f: &mut Write, flog: &mut Write) -> Result<Vec<(Vec<u8>, bool)>, Error> {
let mut files = try!(paths.into_iter().map(|p| {
let df = try!(self.is_df(&p[..]));
Ok((p, df))
}).collect::<Result<Vec<_>, Error>>());
let mut dirs = files.iter().filter(|&&(_, df)| df)
fn rec_scan(&mut self,
filter_root: bool,
filter_dup: bool,
af: &[u8],
paths: Vec<Vec<u8>>,
f: &mut Write,
flog: &mut Write)
-> Result<Vec<(Vec<u8>, bool)>, Error> {
let mut files = try!(paths.into_iter()
.map(|p| {
let df = try!(self.is_df(&p[..]));
Ok((p, df))
})
.collect::<Result<Vec<_>, Error>>());
let mut dirs = files.iter()
.filter(|&&(_, df)| df)
.map(|&(ref p, _)| p.to_owned())
.collect::<Vec<_>>();
for &(ref file, df) in files.iter() {
try!(writeln!(f, "{}::{}::{}", self.class(Some(df)), af.to_hex(), file.to_hex()));
try!(writeln!(f,
"{}::{}::{}",
self.class(Some(df)),
af.to_hex(),
file.to_hex()));
}
let mut index = 0;
let mut rootdirs = vec![];
while let Some(dir) = dirs.pop() {
let _ = writeln!(stderr(), ":: Scanning DF {}/{}...", index + 1, index + 1 + dirs.len());
let _ = writeln!(stderr(),
":: Scanning DF {}/{}...",
index + 1,
index + 1 + dirs.len());
if dir.len() > 0 {
try!(self.select_path(&dir[..], true));
}
@@ -299,19 +336,28 @@ pub trait FileSystemHandle<'a>: pcsc::Transmit {
if df {
if dir.len() == 0 {
rootdirs.push(file.clone());
} else if filter_root && rootdirs.iter().any(|f| &f[..] == &file[file.len()-2..]) {
} else if filter_root && rootdirs.iter().any(|f| &f[..] == &file[file.len() - 2..]) {
continue;
} else if filter_dup && &file[file.len()-2..] == &file[file.len()-4..file.len()-2] {
} else if filter_dup &&
&file[file.len() - 2..] == &file[file.len() - 4..file.len() - 2] {
continue;
}
try!(writeln!(flog, "dir-found {}::{}", af.to_hex(), file.to_hex()));
dirs.push(file.clone());
}
try!(writeln!(f, "{}::{}::{}", self.class(Some(df)), af.to_hex(), file.to_hex()));
try!(writeln!(f,
"{}::{}::{}",
self.class(Some(df)),
af.to_hex(),
file.to_hex()));
files.push((file, df));
}
index += 1;
try!(writeln!(flog, "scanned-{} {}::{}", self.class(None), af.to_hex(), dir.to_hex()));
try!(writeln!(flog,
"scanned-{} {}::{}",
self.class(None),
af.to_hex(),
dir.to_hex()));
}
Ok(files)
}
@@ -341,23 +387,23 @@ pub enum Capability {
impl Capability {
fn raw(self) -> usize {
match self {
Capability::SelectMF => 0x00000001,
Capability::SelectAF => 0x00000002,
Capability::SelectEFDF => 0x00000004,
Capability::SelectEF => 0x00000008,
Capability::SelectDF => 0x00000010,
Capability::SelectParent => 0x00000020,
Capability::SelectPath => 0x00000040,
Capability::ReturnFCI => 0x00000080,
Capability::ReturnFCP => 0x00000100,
Capability::ReturnFMD => 0x00000200,
Capability::ReturnNothing => 0x00000400,
Capability::SelectMF => 0x00000001,
Capability::SelectAF => 0x00000002,
Capability::SelectEFDF => 0x00000004,
Capability::SelectEF => 0x00000008,
Capability::SelectDF => 0x00000010,
Capability::SelectParent => 0x00000020,
Capability::SelectPath => 0x00000040,
Capability::ReturnFCI => 0x00000080,
Capability::ReturnFCP => 0x00000100,
Capability::ReturnFMD => 0x00000200,
Capability::ReturnNothing => 0x00000400,
Capability::ReadBinaryCurrentEF => 0x00000800,
Capability::ReadBinaryLongEF => 0x00001000,
Capability::ReadBinaryShortEF => 0x00002000,
Capability::ReadBinaryLongEF => 0x00001000,
Capability::ReadBinaryShortEF => 0x00002000,
Capability::ReadRecordCurrentEF => 0x00004000,
Capability::ReadRecordShortEF => 0x00008000,
Capability::ReadData => 0x00010000,
Capability::ReadRecordShortEF => 0x00008000,
Capability::ReadData => 0x00010000,
}
}

@@ -391,16 +437,14 @@ pub struct Capabilities(usize);
/// Tests if a result apdu indicates that the issued command is supported
/// for some input.
fn test_result(res: &[u8]) -> bool {
const COMMAND_NOT_SUPPORTED: &'static [&'static [u8]] = &[
&[0x6f, 0x00],
&[0x6f, 0x00],
&[0x6e, 0x00],
&[0x6d, 0x00],
&[0x6b, 0x00],
&[0x6a, 0x86],
&[0x6a, 0x81],
&[0x67, 0x00],
];
const COMMAND_NOT_SUPPORTED: &'static [&'static [u8]] = &[&[0x6f, 0x00],
&[0x6f, 0x00],
&[0x6e, 0x00],
&[0x6d, 0x00],
&[0x6b, 0x00],
&[0x6a, 0x86],
&[0x6a, 0x81],
&[0x67, 0x00]];

!COMMAND_NOT_SUPPORTED.contains(&res)
}
@@ -414,9 +458,13 @@ impl Capabilities {
let mut ret = Self::new();
// Try all p1 and p2 values for select.
// 0xff as p1 is a dummy value for select mf.
const P1AF: &'static[u8] = &[0x00, 0x01, 0x02, 0x03, 0x04, 0x08, 0xff];
const P1NOAF: &'static[u8] = &[0x00, 0x01, 0x02, 0x03, 0x08, 0xff];
let p1s = if test_af { P1AF } else { P1NOAF };
const P1AF: &'static [u8] = &[0x00, 0x01, 0x02, 0x03, 0x04, 0x08, 0xff];
const P1NOAF: &'static [u8] = &[0x00, 0x01, 0x02, 0x03, 0x08, 0xff];
let p1s = if test_af {
P1AF
} else {
P1NOAF
};
let p2s = &[0x00, 0x04, 0x08, 0x0c];
for p1 in p1s {
for p2 in p2s {
@@ -425,8 +473,7 @@ impl Capabilities {
} else {
try!(handle.transmit_apdu(0x00, 0xa4, *p1, *p2, &[0x74, 0x37], 0x00))
};
if !test_result(&res[..]) ||
(p1 == &0xff && &res[..] == &[0x6a, 0x82][..]) {
if !test_result(&res[..]) || (p1 == &0xff && &res[..] == &[0x6a, 0x82][..]) {
continue;
}
match *p1 {

+ 33
- 26
src/fs/path.rs View File

@@ -1,4 +1,5 @@
use fs::{FileSystemHandleFactory, FileSystemHandle, Capability, Capabilities, Error, try_sw, scan_start, scan_update, scan_end, is_df_fcp, found_file};
use fs::{FileSystemHandleFactory, FileSystemHandle, Capability, Capabilities, Error, try_sw,
scan_start, scan_update, scan_end, is_df_fcp, found_file};
use pcsclite as pcsc;
use pcsclite::Transmit;
use rustc_serialize::hex::ToHex;
@@ -10,8 +11,14 @@ impl FileSystemHandleFactory for PathHandleFactory {
c.has(Capability::SelectPath) && c.has(Capability::ReturnFCP) && c.has(Capability::SelectMF)
}

fn create<'a>(&self, handle: &'a mut Transmit, _: Capabilities) -> Result<Box<FileSystemHandle<'a> + 'a>, Error> {
Ok(Box::new(PathHandle{handle: handle, current_df: Vec::new()}))
fn create<'a>(&self,
handle: &'a mut Transmit,
_: Capabilities)
-> Result<Box<FileSystemHandle<'a> + 'a>, Error> {
Ok(Box::new(PathHandle {
handle: handle,
current_df: Vec::new(),
}))
}

fn method(&self) -> &str {
@@ -25,7 +32,7 @@ pub struct PathHandle<'a> {
}

impl<'a> Transmit for PathHandle<'a> {
fn transmit(&mut self, send: &[u8], buf: &mut[u8]) -> Result<usize, pcsc::Error> {
fn transmit(&mut self, send: &[u8], buf: &mut [u8]) -> Result<usize, pcsc::Error> {
self.handle.transmit(send, buf)
}
}
@@ -46,32 +53,31 @@ impl<'a> FileSystemHandle<'a> for PathHandle<'a> {
first = true;
scan_update((i << 8) | j);
}
if (i, j) == (0x00, 0x00)
|| (i, j) == (0x3F, 0x00)
|| (i, j) == (0x3F, 0xFF)
|| (i, j) == (0xFF, 0xFF)
|| (i, j) == (0x7F, 0xFF) {
if (i, j) == (0x00, 0x00) || (i, j) == (0x3F, 0x00) || (i, j) == (0x3F, 0xFF) ||
(i, j) == (0xFF, 0xFF) ||
(i, j) == (0x7F, 0xFF) {
continue;
}
data[l-2] = i as u8;
data[l-1] = j as u8;
data[l - 2] = i as u8;
data[l - 1] = j as u8;
let res = try!(self.transmit_apdu(0x00, 0xa4, 0x08, 0x04, &data[..], 0x100));
if &res[res.len()-2..] == &[0x6a, 0x82] {
if &res[res.len() - 2..] == &[0x6a, 0x82] {
continue;
} else if [&[0x62, 0x83][..], &[0x69, 0x82]].contains(&&res[res.len()-2..]) {
} else if [&[0x62, 0x83][..], &[0x69, 0x82]].contains(&&res[res.len() - 2..]) {
} else if let Err(e) = try_sw(&res[..]) {
warn!("Error during recursive scan: {}. Continuing...", e);
continue;
}
let df = if &res[res.len()-2..] == &[0x90, 0x00] {
is_df_fcp(&res[..res.len()-2])
} else if res[res.len()-2] != 0x61 {
let df = if &res[res.len() - 2..] == &[0x90, 0x00] {
is_df_fcp(&res[..res.len() - 2])
} else if res[res.len() - 2] != 0x61 {
warn!("Error determining if {} is DF: {}",
data.to_hex(),
Error::unexpected_result("expected response FCP template", res));
true
} else {
let res = try!(self.transmit_apdu(0x00, 0xc0, 0x00, 0x00, &[], res[1] as usize));
let res =
try!(self.transmit_apdu(0x00, 0xc0, 0x00, 0x00, &[], res[1] as usize));
let (fcp, _) = match try_sw(&res[..]) {
Ok(v) => v,
Err(e) => {
@@ -97,10 +103,9 @@ impl<'a> FileSystemHandle<'a> for PathHandle<'a> {
try!(self.transmit_apdu(0x00, 0xa4, 0x08, 0x04, path, 0x00))
};
try!(try_sw(&res[..]));
if &res[res.len()-2..] == &[0x90, 0x00] {
} else if res[res.len()-2] != 0x61 {
return Err(Error::unexpected_result(
"expected response FCP template", res));
if &res[res.len() - 2..] == &[0x90, 0x00] {
} else if res[res.len() - 2] != 0x61 {
return Err(Error::unexpected_result("expected response FCP template", res));
} else {
let res = try!(self.transmit_apdu(0x00, 0xc0, 0x00, 0x00, &[], res[1] as usize));
try!(try_sw(&res[..]));
@@ -108,7 +113,7 @@ impl<'a> FileSystemHandle<'a> for PathHandle<'a> {
if is_dir {
self.current_df = path.to_owned();
} else {
self.current_df = path[..path.len()-2].to_owned();
self.current_df = path[..path.len() - 2].to_owned();
}
Ok(())
}
@@ -119,9 +124,9 @@ impl<'a> FileSystemHandle<'a> for PathHandle<'a> {
}
let res = try!(self.transmit_apdu(0x00, 0xa4, 0x08, 0x04, &path[..], 0x100));
try!(try_sw(&res[..]));
if &res[res.len()-2..] == &[0x90, 0x00] {
Ok(is_df_fcp(&res[..res.len()-2]))
} else if res[res.len()-2] != 0x61 {
if &res[res.len() - 2..] == &[0x90, 0x00] {
Ok(is_df_fcp(&res[..res.len() - 2]))
} else if res[res.len() - 2] != 0x61 {
warn!("Error determining if {} is DF: {}",
path.to_hex(),
Error::unexpected_result("expected response FCP template", res));
@@ -133,7 +138,9 @@ impl<'a> FileSystemHandle<'a> for PathHandle<'a> {
}
}

fn current_df(&self) -> &[u8] { &self.current_df[..] }
fn current_df(&self) -> &[u8] {
&self.current_df[..]
}
fn class(&self, df: Option<bool>) -> &str {
match df {
Some(true) => "df",

+ 32
- 16
src/fs/short.rs View File

@@ -1,4 +1,5 @@
use fs::{FileSystemHandleFactory, FileSystemHandle, Capabilities, Error, Capability, FULL_FACTORIES, DUMMY_FACTORY, try_sw, found_file};
use fs::{FileSystemHandleFactory, FileSystemHandle, Capabilities, Error, Capability,
FULL_FACTORIES, DUMMY_FACTORY, try_sw, found_file};
use pcsclite as pcsc;
use pcsclite::Transmit;

@@ -9,7 +10,10 @@ impl FileSystemHandleFactory for ShortHandleFactory {
c.has(Capability::ReadBinaryShortEF) || c.has(Capability::ReadRecordShortEF)
}

fn create<'a>(&self, handle: &'a mut Transmit, caps: Capabilities) -> Result<Box<FileSystemHandle<'a> + 'a>, Error> {
fn create<'a>(&self,
handle: &'a mut Transmit,
caps: Capabilities)
-> Result<Box<FileSystemHandle<'a> + 'a>, Error> {
// Find a full path factory that works, and use that. If none are available, us a dummy.
let mut fsh = None;
for factory in FULL_FACTORIES.iter().chain(&[DUMMY_FACTORY]) {
@@ -18,7 +22,10 @@ impl FileSystemHandleFactory for ShortHandleFactory {
break;
}
}
Ok(Box::new(ShortHandle{internal: fsh.unwrap(), caps: caps}))
Ok(Box::new(ShortHandle {
internal: fsh.unwrap(),
caps: caps,
}))
}

fn method(&self) -> &str {
@@ -32,7 +39,7 @@ pub struct ShortHandle<'a> {
}

impl<'a> Transmit for ShortHandle<'a> {
fn transmit(&mut self, send: &[u8], buf: &mut[u8]) -> Result<usize, pcsc::Error> {
fn transmit(&mut self, send: &[u8], buf: &mut [u8]) -> Result<usize, pcsc::Error> {
self.internal.transmit(send, buf)
}
}
@@ -44,28 +51,33 @@ impl<'a> FileSystemHandle<'a> for ShortHandle<'a> {
if self.caps.has(Capability::ReadBinaryShortEF) {
let res = try!(self.transmit_apdu(0x00, 0xb0, 0x80 | i, 0x00, &[], 0x00));
match try_sw(&res[..]) {
Ok(_) | Err(Error::WrongLe(_, _)) => {
Ok(_) |
Err(Error::WrongLe(_, _)) => {
ret.push(i);
}
_ => {},
_ => {}
}
}
if self.caps.has(Capability::ReadRecordShortEF) {
let res = try!(self.transmit_apdu(0x00, 0xb2, 0x01, (i << 3) | 0x04, &[], 0x00));
match try_sw(&res[..]) {
Ok(_) | Err(Error::RecordNotFound) | Err(Error::WrongLe(_, _)) => {
Ok(_) |
Err(Error::RecordNotFound) |
Err(Error::WrongLe(_, _)) => {
ret.push(i);
}
_ => {},
_ => {}
}
}
}
Ok(ret.into_iter().map(|i| {
let mut v = self.current_df().to_owned();
v.push(i);
found_file(&v[..], false, false);
(v, false)
}).collect())
Ok(ret.into_iter()
.map(|i| {
let mut v = self.current_df().to_owned();
v.push(i);
found_file(&v[..], false, false);
(v, false)
})
.collect())
}

fn select_path(&mut self, path: &[u8], is_dir: bool) -> Result<(), Error> {
@@ -76,6 +88,10 @@ impl<'a> FileSystemHandle<'a> for ShortHandle<'a> {
self.internal.is_df(file)
}

fn current_df(&self) -> &[u8] { self.internal.current_df() }
fn class(&self, _: Option<bool>) -> &str { "short" }
fn current_df(&self) -> &[u8] {
self.internal.current_df()
}
fn class(&self, _: Option<bool>) -> &str {
"short"
}
}

+ 8
- 4
src/main.rs View File

@@ -18,15 +18,16 @@ fn print_usage(program: &str) -> ! {
println!("Usage: {} COMMAND [...]\n", program);
println!("Supported commands:\n");
println!("extract\nscan\n");
println!("Use '{} help COMMAND' for help on the specified subcommand.", program);
println!("Use '{} help COMMAND' for help on the specified subcommand.",
program);
exit(1);
}

fn log_format(r: &log::LogRecord) -> String {
let prefix = match r.level() {
log::LogLevel::Error => "!!!! ",
log::LogLevel::Warn => "!! ",
log::LogLevel::Info => "",
log::LogLevel::Warn => "!! ",
log::LogLevel::Info => "",
log::LogLevel::Debug => "? ",
log::LogLevel::Trace => ">> ",
};
@@ -34,7 +35,10 @@ fn log_format(r: &log::LogRecord) -> String {
}

fn main() {
if let Err(e) = env_logger::LogBuilder::new().format(log_format).filter(None, log::LogLevelFilter::Info).init() {
if let Err(e) = env_logger::LogBuilder::new()
.format(log_format)
.filter(None, log::LogLevelFilter::Info)
.init() {
panic!("Failed to initialize global logger: {}", e);
}
let args = args().collect::<Vec<_>>();

+ 112
- 28
src/scan.rs View File

@@ -9,12 +9,20 @@ use std::io::Write;
use rustc_serialize::hex::{FromHex, ToHex};

fn print_usage(program: &str, command: &str, opts: &Options) -> ! {
let brief = format!("Usage: {} {} [options] FILE\n\nScans a smartcard for files and stores a list of found files in FILE.", program, command);
let brief = format!("Usage: {} {} [options] FILE\n\nScans a smartcard for files and stores a \
list of found files in FILE.",
program,
command);
print!("{}", opts.usage(&brief));
exit(1);
}

fn do_data_scan<T: AsRef<[u8]>>(handle: &mut pcsc::Transmit, caps: fs::Capabilities, paths: &[(T, bool)], af: &[u8], f: &mut Write, flog: &mut Write) {
fn do_data_scan<T: AsRef<[u8]>>(handle: &mut pcsc::Transmit,
caps: fs::Capabilities,
paths: &[(T, bool)],
af: &[u8],
f: &mut Write,
flog: &mut Write) {
let mut factory = None;
for fact in fs::DATA_FACTORIES {
if fact.has_required_capabilities(caps) {
@@ -25,7 +33,8 @@ fn do_data_scan<T: AsRef<[u8]>>(handle: &mut pcsc::Transmit, caps: fs::Capabilit
let factory = match factory {
Some(v) => v,
None => {
warn!("No recipe for data scan with AF capabilities! ({:?})", caps.list());
warn!("No recipe for data scan with AF capabilities! ({:?})",
caps.list());
return;
}
};
@@ -44,7 +53,15 @@ fn do_data_scan<T: AsRef<[u8]>>(handle: &mut pcsc::Transmit, caps: fs::Capabilit
}
}

fn do_full_id_scan(handle: &mut pcsc::Transmit, caps: fs::Capabilities, filter_root: bool, filter_dup: bool, af: &[u8], root: Vec<u8>, f: &mut Write, flog: &mut Write) -> Vec<(Vec<u8>, bool)> {
fn do_full_id_scan(handle: &mut pcsc::Transmit,
caps: fs::Capabilities,
filter_root: bool,
filter_dup: bool,
af: &[u8],
root: Vec<u8>,
f: &mut Write,
flog: &mut Write)
-> Vec<(Vec<u8>, bool)> {
let mut factory = None;
for fact in fs::FULL_FACTORIES {
if fact.has_required_capabilities(caps) {
@@ -55,7 +72,8 @@ fn do_full_id_scan(handle: &mut pcsc::Transmit, caps: fs::Capabilities, filter_r
let factory = match factory {
Some(v) => v,
None => {
warn!("No recipe for full id scan with AF capabilities! ({:?})", caps.list());
warn!("No recipe for full id scan with AF capabilities! ({:?})",
caps.list());
return vec![(Vec::new(), true)];
}
};
@@ -77,7 +95,12 @@ fn do_full_id_scan(handle: &mut pcsc::Transmit, caps: fs::Capabilities, filter_r
}
}

fn do_short_id_scan<T: AsRef<[u8]>>(handle: &mut pcsc::Transmit, caps: fs::Capabilities, dfs: &[T], af: &[u8], f: &mut Write, flog: &mut Write) {
fn do_short_id_scan<T: AsRef<[u8]>>(handle: &mut pcsc::Transmit,
caps: fs::Capabilities,
dfs: &[T],
af: &[u8],
f: &mut Write,
flog: &mut Write) {
let mut factory = None;
for fact in fs::SHORT_FACTORIES {
if fact.has_required_capabilities(caps) {
@@ -88,7 +111,8 @@ fn do_short_id_scan<T: AsRef<[u8]>>(handle: &mut pcsc::Transmit, caps: fs::Capab
let factory = match factory {
Some(v) => v,
None => {
warn!("No recipe for short id scan with AF capabilities! ({:?})", caps.list());
warn!("No recipe for short id scan with AF capabilities! ({:?})",
caps.list());
return;
}
};
@@ -112,23 +136,52 @@ pub fn main(args: Vec<String>) {
let command = args[1].clone();

let mut opts = Options::new();
opts.optopt("p", "pin", "sets the verify apdu pin in hex, with the first byte being the PIN id. (e.g. 813132333435 corresponds to the APDU 00200081053132333435)", "PIN");
opts.optopt("c", "cla", "sets the cla byte to use when scanning for files. Specified as a single hex byte.", "CLA");
opts.optopt("", "apdumap", "specifies rules to rewrite apdus. Rules are comma sperated, and have the for '<ORIG CLA><ORIG INS>-><NEW CLA><NEW INS>'. Example: --apdumap 00a4->3030,00b0->3031", "RULES");
opts.optopt("r", "reader", "specifies the reader to use. Uses the first reader with the given string as part of its name.", "READER");
opts.optmulti("a", "af", "specifies one available af name in hex, and optionally a root path to start scanning from (specified in the format 'AFNAME/PATH'). This option may appear multiple times, and each indicates an af to scan. The default af is scanned if no other is specified.", "AF");
opts.optopt("p",
"pin",
"sets the verify apdu pin in hex, with the first byte being the PIN id. (e.g. \
813132333435 corresponds to the APDU 00200081053132333435)",
"PIN");
opts.optopt("c",
"cla",
"sets the cla byte to use when scanning for files. Specified as a single hex \
byte.",
"CLA");
opts.optopt("",
"apdumap",
"specifies rules to rewrite apdus. Rules are comma sperated, and have the for \
'<ORIG CLA><ORIG INS>-><NEW CLA><NEW INS>'. Example: --apdumap \
00a4->3030,00b0->3031",
"RULES");
opts.optopt("r",
"reader",
"specifies the reader to use. Uses the first reader with the given string as \
part of its name.",
"READER");
opts.optmulti("a",
"af",
"specifies one available af name in hex, and optionally a root path to start \
scanning from (specified in the format 'AFNAME/PATH'). This option may appear \
multiple times, and each indicates an af to scan. The default af is scanned \
if no other is specified.",
"AF");
// TODO: add support
//opts.optflag("", "ef-dir", "attempt to extract available AFs from EF.DIR");
// opts.optflag("", "ef-dir", "attempt to extract available AFs from EF.DIR");
opts.optflag("h", "help", "prints this help text");
opts.optflag("", "filter-root", "filters DF names from the root of an AF occurring further into the path. Prevents some recursive structures on some cards.");
opts.optflag("", "filter-dup", "filters DF names that are the same as the parent's name. Prevents some recursive structures on some cards.");
opts.optflag("",
"filter-root",
"filters DF names from the root of an AF occurring further into the path. \
Prevents some recursive structures on some cards.");
opts.optflag("",
"filter-dup",
"filters DF names that are the same as the parent's name. Prevents some \
recursive structures on some cards.");
opts.optflag("f", "filter", "alias for --filter-root --filter-dup.");
opts.optflag("", "data-af", "scan for data objects on afs.");
opts.optflag("", "data-df", "scan for data objects on dfs.");
opts.optflag("", "data-ef", "scan for data objects on efs.");
let matches = match opts.parse(&args[2..]) {
Ok(m) => { m }
Err(e) => { panic!(e.to_string()) }
Ok(m) => m,
Err(e) => panic!(e.to_string()),
};
if matches.opt_present("h") {
print_usage(&program, &command, &opts);
@@ -147,7 +200,8 @@ pub fn main(args: Vec<String>) {
exit(2);
}
};
let mut logfile = match File::create((matches.free[0].as_ref() as &Path).with_extension("log")) {
let mut logfile = match File::create((matches.free[0].as_ref() as &Path)
.with_extension("log")) {
Ok(v) => v,
Err(e) => {
error!("Failed to create log file: {}", e);
@@ -178,9 +232,9 @@ pub fn main(args: Vec<String>) {
afs.push((af, root));
}
// TODO: implement.
//if matches.opt_present("ef-dir") {
// if matches.opt_present("ef-dir") {
// unimplemented!();
//}
// }
if afs.len() == 0 {
afs.push((Vec::new(), Vec::new()));
} else {
@@ -194,7 +248,8 @@ pub fn main(args: Vec<String>) {
}
};
if !caps.has(fs::Capability::SelectAF) {
warn!("Card does not appear to have the capability to select AFs. Trying to select AFs anyway.");
warn!("Card does not appear to have the capability to select AFs. Trying to select AFs \
anyway.");
}
for af in afs.into_iter() {
if af.0.len() > 0 {
@@ -216,19 +271,48 @@ pub fn main(args: Vec<String>) {
};
let filter_root = matches.opt_present("filter-root") || matches.opt_present("f");
let filter_dup = matches.opt_present("filter-dup") || matches.opt_present("f");
let res = do_full_id_scan(&mut *handle, caps, filter_root, filter_dup, &af.0[..], af.1, &mut f, &mut logfile);
let res = do_full_id_scan(&mut *handle,
caps,
filter_root,
filter_dup,
&af.0[..],
af.1,
&mut f,
&mut logfile);
let dfs = res.iter().filter(|&&(_, df)| df).map(|&(ref v, _)| v).collect::<Vec<_>>();
do_short_id_scan(&mut *handle, caps, &dfs[..], &af.0[..], &mut f, &mut logfile);
do_short_id_scan(&mut *handle,
caps,
&dfs[..],
&af.0[..],
&mut f,
&mut logfile);
if matches.opt_present("data-df") {
let dfs = res.iter().filter(|&&(_, df)| df).map(|&(ref v, _)| (v, true)).collect::<Vec<_>>();
do_data_scan(&mut *handle, caps, &dfs[..], &af.0[..], &mut f, &mut logfile);
let dfs =
res.iter().filter(|&&(_, df)| df).map(|&(ref v, _)| (v, true)).collect::<Vec<_>>();
do_data_scan(&mut *handle,
caps,
&dfs[..],
&af.0[..],
&mut f,
&mut logfile);
}
if matches.opt_present("data-ef") {
let efs = res.iter().filter(|&&(_, df)| !df).map(|&(ref v, _)| (v, true)).collect::<Vec<_>>();
do_data_scan(&mut *handle, caps, &efs[..], &af.0[..], &mut f, &mut logfile);
let efs =
res.iter().filter(|&&(_, df)| !df).map(|&(ref v, _)| (v, true)).collect::<Vec<_>>();
do_data_scan(&mut *handle,
caps,
&efs[..],
&af.0[..],
&mut f,
&mut logfile);
}
if matches.opt_present("data-af") {
do_data_scan(&mut *handle, caps, &[(&[], true)], &af.0[..], &mut f, &mut logfile);
do_data_scan(&mut *handle,
caps,
&[(&[], true)],
&af.0[..],
&mut f,
&mut logfile);
}
}


+ 45
- 29
src/util.rs View File

@@ -33,29 +33,34 @@ pub fn try_pin<T: AsRef<[u8]>>(handle: &mut pcsc::Transmit, pin: &Option<T>) {

fn rewrites_opt(matches: &Matches) -> Option<Vec<[u8; 4]>> {
if let Some(rulestr) = matches.opt_str("apdumap") {
Some(rulestr.split(",").map(|part| {
let rule = part.split("->").collect::<Vec<_>>();
if rule.len() != 2 {
error!("Error parsing apdu map rule '{}': Expected '->'", part);
exit(2);
}
let bin = rule.iter().map(|p| {
match p.from_hex() {
Ok(v) => v,
Err(e) => {
error!("Error parsing rule segment '{}': {}", p, e);
Some(rulestr.split(",")
.map(|part| {
let rule = part.split("->").collect::<Vec<_>>();
if rule.len() != 2 {
error!("Error parsing apdu map rule '{}': Expected '->'", part);
exit(2);
}
let bin = rule.iter()
.map(|p| {
match p.from_hex() {
Ok(v) => v,
Err(e) => {
error!("Error parsing rule segment '{}': {}", p, e);
exit(2);
}
}
})
.collect::<Vec<_>>();
for item in bin.iter() {
if item.len() != 2 {
error!("Error parsing rule segment '{}': Expected length of 2 bytes!",
item.to_hex());
exit(2);
}
}
}).collect::<Vec<_>>();
for item in bin.iter() {
if item.len() != 2 {
error!("Error parsing rule segment '{}': Expected length of 2 bytes!", item.to_hex());
exit(2);
}
}
[bin[0][0], bin[0][1], bin[1][0], bin[1][1]]
}).collect::<Vec<_>>())
[bin[0][0], bin[0][1], bin[1][0], bin[1][1]]
})
.collect::<Vec<_>>())
} else {
None
}
@@ -71,7 +76,8 @@ fn cla_opt(matches: &Matches) -> Option<u8> {
}
};
if raw.len() != 1 {
error!("Error parsing cla value: expected only 1 byte (received {})", raw.len());
error!("Error parsing cla value: expected only 1 byte (received {})",
raw.len());
exit(2);
}
Some(raw[0])
@@ -90,7 +96,9 @@ pub fn init_context() -> pcsc::Context {
}
}

pub fn init_handle<'a, 'b>(context: &'a pcsc::Context, matches: &'b Matches) -> Box<pcsc::Transmit + 'a> {
pub fn init_handle<'a, 'b>(context: &'a pcsc::Context,
matches: &'b Matches)
-> Box<pcsc::Transmit + 'a> {
let mut readers = match context.list_readers() {
Ok(v) => v,
Err(e) => {
@@ -99,10 +107,13 @@ pub fn init_handle<'a, 'b>(context: &'a pcsc::Context, matches: &'b Matches) ->
}
};
let reader = match matches.opt_str("reader") {
Some(pat) => readers.filter(|r| match r.to_str() {
Ok(s) => s.to_lowercase().contains(&pat),
Err(_) => false
}).next(),
Some(pat) => {
readers.filter(|r| match r.to_str() {
Ok(s) => s.to_lowercase().contains(&pat),
Err(_) => false,
})
.next()
}
None => readers.next(),
};
let reader = match reader {
@@ -112,7 +123,9 @@ pub fn init_handle<'a, 'b>(context: &'a pcsc::Context, matches: &'b Matches) ->
exit(2);
}
};
let hraw = match context.connect(reader, pcsc::ShareMode::Exclusive, &[pcsc::Protocol::T0, pcsc::Protocol::T1]) {
let hraw = match context.connect(reader,
pcsc::ShareMode::Exclusive,
&[pcsc::Protocol::T0, pcsc::Protocol::T1]) {
Ok((v, _)) => v,
Err(e) => {
error!("Failed to initiatialize PCSC reader handle: {}", e);
@@ -126,7 +139,7 @@ pub fn init_handle<'a, 'b>(context: &'a pcsc::Context, matches: &'b Matches) ->
(Some(_), Some(_)) => {
error!("Confliction options --cla and --apdumap supplied.");
exit(2);
},
}
}
}

@@ -147,7 +160,10 @@ pub fn parse_pin(matches: &Matches) -> Option<Vec<u8>> {
})
}

pub fn select_af(handle: &mut pcsc::Transmit, caps: fs::Capabilities, af: &[u8]) -> Result<Vec<u8>, pcsc::Error> {
pub fn select_af(handle: &mut pcsc::Transmit,
caps: fs::Capabilities,
af: &[u8])
-> Result<Vec<u8>, pcsc::Error> {
let p2 = if caps.has(fs::Capability::ReturnNothing) {
0x0c
} else if caps.has(fs::Capability::ReturnFCI) {

Loading…
Cancel
Save