bluetech / pcsc-rust

Rust bindings to PC/SC for smart card communication
https://docs.rs/pcsc
MIT License
111 stars 23 forks source link

ShareMode::Direct is not properly supported #8

Closed kouwasi closed 4 years ago

kouwasi commented 6 years ago

Can't connect during not place the card but can connect using pcsc-sys. I think Protocol::from_raw() have a problem.

pcsc-rust throw the below error. thread 'main' panicked at 'impossible protocol: 0x0', pcsc-1.0.1\src\lib.rs:173:18

I think add to Protocol::UNDEFINED pattern in from_raw function is necessary.

What do you think?

Can't connect example code.

extern crate pcsc;

use pcsc::*;

fn main() {
    let ctx = Context::establish(Scope::User).expect("Failed to establish context");

    let mut readers_buf = [0; 2048];
    let mut readers = ctx.list_readers(&mut readers_buf).expect("Failed to list readers");

    let reader = match readers.next() {
        Some(reader) => reader,
        None => {
            println!("No readers are connected.");
            return;
        }
    };

    println!("{:?}", reader);

    // Connect to the card.
    let card = match ctx.connect(reader, ShareMode::Direct, Protocols::UNDEFINED) {
        Ok(card) => card,
        Err(Error::NoSmartcard) => {
            println!("A smartcard is not present in the reader.");
            return;
        }
        Err(err) => {
            eprintln!("Failed to connect to card: {}", err);
            std::process::exit(1);
        }
    };
}

Can connect example code. *using pcsc_sys

extern crate pcsc;
extern crate pcsc_sys;

use std::os::raw::c_char;
use pcsc_sys::*;
use pcsc::{Scope, ShareMode, Protocols};
use std::ptr::null;

const DUMMY_LONG: LONG = -1;
const DUMMY_DWORD: DWORD = 0xdead_beef;

fn main() {
    unsafe {
        let mut ctx: SCARDCONTEXT = DUMMY_LONG as SCARDCONTEXT;
        SCardEstablishContext(Scope::User as DWORD, null(), null(), &mut ctx);

        let mut buffer = [0; 2048];
        let mut buflen = buffer.len() as DWORD;
        SCardListReaders(ctx, null(), buffer.as_mut_ptr() as *mut c_char, &mut buflen);

        println!("{:?}", String::from_utf8_lossy(&buffer));

        let mut handle: SCARDHANDLE = DUMMY_LONG as SCARDHANDLE;
        let mut raw_active_protocol: DWORD = DUMMY_DWORD;
        let result = SCardConnect(ctx, buffer.as_mut_ptr() as *mut c_char, ShareMode::Direct as DWORD, Protocols::UNDEFINED.bits(), &mut handle, &mut raw_active_protocol);
        println!("{:x}", result);
    }
}
bluetech commented 6 years ago

I have never tried to use DIRECT, and support for that is indeed missing.

When trying to add this, I have hit one problem though, namely the pioSendPci parameter of the SCardTransmit function. In the pcsc crate's wrapper of this function, pcsc::Card::transmit, I have tried to automatically handle this parameter as follows:

let pioSendPci = get_protocol_pci(self.active_protocol);

fn get_protocol_pci(protocol: Protocol) -> &'static ffi::SCARD_IO_REQUEST {
    unsafe {
        match protocol {
            Protocol::T0 => &ffi::g_rgSCardT0Pci,
            Protocol::T1 => &ffi::g_rgSCardT1Pci,
            Protocol::RAW => &ffi::g_rgSCardRawPci,
        }
    }
}

This is no longer possible, or at least requires some refinement, once we have Protocol::Undefined.

I admit to not knowing much about this, so before I look into how to wrap it properly, you can help me understand by answering a few questions:

  1. Are you planning to use SCardTransmit/pcsc::Card::transmit in your application?
  2. If so, how do you use the pioSendPci? Do you pass some special values for the custom protocol? How does it look?
  3. Do you need the pioRecvPci return value?

Thanks!

bluetech commented 6 years ago

I forgot to link to the SCARD_IO_REQUEST struct, which is the type of the pioSendPci and pioRecvPci parameters.

arttuys commented 4 years ago

I'm not sure if this issue is still monitored, but anyway:

I ran into a similar problem, being unable to connect to the reader without a card. This I bypassed by:

1) Adding Protocols::UNDEFINED and Protocol::UNDEFINED as appropriate 2) Changed functionality mentioned above as follows:

let send_pci = get_protocol_pci(self.active_protocol).ok_or(Error::InternalError)?;

fn get_protocol_pci(protocol: Protocol) -> Option<&'static ffi::SCARD_IO_REQUEST> {
    unsafe {
        match protocol {
            Protocol::UNDEFINED => None,
            Protocol::T0 => Some(&ffi::g_rgSCardT0Pci),
            Protocol::T1 => Some(&ffi::g_rgSCardT1Pci),
            Protocol::RAW => Some(&ffi::g_rgSCardRawPci),
        }
    }
}

This may not be ideal and has not been well tested, but it did allow me to connect to the reader without having a card, and to send control commands (SCardControl). At least so far in my current use-case, SCardTransmit is not required in the same connection, meaning that if I need to transmit data by ordinary means, I can reconnect with a standard protocol.

Hopefully this helps!

bluetech commented 4 years ago

@arttuys This is a valid use case and I had use for it myself, to set some parameters (buzzer etc.) of an ACS reader. I've created a PR #15 for it, if you are able to test.

arttuys commented 4 years ago

@arttuys This is a valid use case and I had use for it myself, to set some parameters (buzzer etc.) of an ACS reader. I've created a PR #15 for it, if you are able to test.

Sounds promising! I've been a bit caught up with other work, I'll try to test it when work calms down.

bluetech commented 4 years ago

Support for this was added in pcsc v2.4.0.