libfreefare
libfreefare copied to clipboard
Generalized API and PC/SC support
Thanks to time set apart for this purpose by my employer, I've been working in the month of January on enhancing libfreefare and solving a couple of the existing issues (some of which I previously opened myself :).
The primary goal was decoupling libfreefare from libnfc and allowing other reader subsystems to be used. I implemented that for PC/SC as a first step, other projects that one could think of would be librfid and the new "Linux NFC" stack (https://01.org/linux-nfc).
A secondary goal was making the API easier to use and removing unnecessary -- and libnfc-version specific -- boilerplate code. To this end the new API that I built is reader subsystem agnostic and I spent some effort into making it future-compatible with no API or ABI changes.
I have looked at the work that has been done on the "pcsc" branch and decided that that is not the way to go forward. Creating "-pcsc" versions of everything and replicating a second flurry of boilerplate code among the code examples is exactly what I strive to avoid.
The new API is centered around a new FreefareContext object that holds a library context and references to either automatically acquired or externally provided reader subsystem contexts (e.g. nfc_context) or devices (e.g. nfc_device) and provides an easy to use and unified way to retrieve MifareTag objects. The freefare_get_tags() and freefare_tag_new() methods are still there but marked deprecated (since they are libnfc specific). The latter is superseded by a new freefare_tag_new_ex() function that works with all supported reader subsystems using unions as a way to allow it to accept both libnfc or PC/SC arguments.
The new freefare_init() takes a flags argument that can be a combination of FREEFARE_FLAG_READER_LIBNFC or FREEFARE_FLAG_READER_PCSC to automatically establish a context to the given reader subsystems, or the special value FREEFARE_FLAG_READER_ALL to establish a context with all supported subsystems, even future ones. The latter is the recommended value, which means that a program using the library needs not to be updated to gain access to a future reader subsystem support by the library. The flags can also include FREEFARE_FLAG_DISABLE_ISO14443_4 to disable 14443-4 support where possible (currently only libnfc) to select the -3 version on hybrid cards.
Alternatively, freefare_context/device_add/remove are available to manually manage associating or dissociating reader subsystem contexts or devices with the libfreefare library. A FREEFARE_FLAG_AUTOCLOSE in _add will have libfreefare take over responsibility of the passed in object and automatically call the respective close function (nfc_close()/nfc_exit()) when it is no longer needed.
Once a library context has been retrieved, the freefare_tags_get() method enumerates all supported tags on all on readers on all contexts and returns a list. Alternatively freefare_tag_first() and freefare_tag_next() can be used to iterate over the same set without creating the entire list at once. Both accept an enum mifare_tag_type argument to limit the type of tags eligible (the new type NO_TAG_TYPE means no limit).
The boilerplate code can thus (without error checking) be reduced to:
FreefareContext ctx = freefare_init(FREEFARE_FLAG_READER_ALL);
for(MifareTag tag = freefare_tag_first(ctx, NO_TAG_TYPE); tag; tag = freefare_tag_next(ctx)) {
/* Actual code here... */
}
freefare_exit(ctx);
The PC/SC support currently is not complete, only DESfire is working. No functional changes to libnfc support should be present (I even added a working FREEFARE_FLAG_DISABLE_ISO14443_4).
Late in the process I added freefare_tag_wait_* which allows non-busy polling for tags (e.g. like the pcsc_scan tool does). This is only implemented for PC/SC and only works on a single context. libnfc has a similar function in nfc_initiator_poll_target() which only works for a single device, implementing support for that is possible. Anything further (waiting for multiple devices/contexts) would require a new threaded approach.
Before I could prepare my code for publication, freefare_selected_tag_is_present() was added in the main libfreefare branch. This was another libnfc specific function, which I choose not to continue supporting (it hasn't been out long, there should be minimal code breakage). Instead I added freefare_tag_is_present() as a reader subsystem agnostic alternative.
I pushed all the code changes to https://code.google.com/r/henryk-libfreefare-extended-reader-support for easy pulling and request inclusion in libfreefare (sadly Google has no pull request support).
Original issue reported on code.google.com by [email protected] on 27 Feb 2014 at 3:19
Thanks a lot Henryk for your very valuable contribution! Sorry we didn't have time yet to review your code, that's not like it can be done in 5 mins ;-) Thanks for your comprehension.
Original comment by [email protected] on 13 Mar 2014 at 1:03
- Changed state: Accepted
- Added labels: Type-Enhancement
- Removed labels: Type-Defect
Henryks PC/SC interface work fine for me. Migrating existing code was quite easy due to the simplifications made.
I want to extend supported_tags from freefare.c with a tag that is recognized by calling its function check_tag_on_reader. I have done this for plain libfreefare by connecting, transceiving bytes and disconnecting from the tag (similar to is_mifare_ultralightc_on_reader). With Henryks implementation, however, I did not find out how to connect to the tag in a non-reader-specific way. AFAICS, the supplied FreefareReaderTag object does not allow to create a usable MifareTag obect (Henryks wrapper mifare_ultralightc_is_on_reader is implemented for libnfc only). Henryk, do you have any hints on that?
Original comment by [email protected] on 2 Jun 2014 at 11:56
any updates on this?
I saved Henryk's initial approach, which I used, here https://github.com/frankmorgner/libfreefare/tree/pcsc-henryk
Any recommended way to move forward with this ?
I'd like to add support for JCOP 2.4.2r2 card, which comes with DESFire and ISO 14443-4 layer APDUs. The current detection code inspects the ATR as returned by the PC/SC subsystem, but does not consider the JCOP ATR as a suitable candidate.
Is this related to #71?
Yes and No. It has the same goal (i.e. run libfreefare over PC/SC), but it's a completely different implementation.
I never got #71 running, whereas https://github.com/frankmorgner/libfreefare/tree/pcsc-henryk worked as expected.