timmerk / libfreefare

Automatically exported from code.google.com/p/libfreefare
Other
0 stars 0 forks source link

Generalized API and PC/SC support #22

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
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_first(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 hen...@ploetzli.ch on 27 Feb 2014 at 3:19

GoogleCodeExporter commented 9 years ago
Damn you copy and paste. The example code should of course have been:

    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);

Original comment by hen...@ploetzli.ch on 27 Feb 2014 at 3:20

GoogleCodeExporter commented 9 years ago
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 yob...@gmail.com on 13 Mar 2014 at 1:03

GoogleCodeExporter commented 9 years ago
[deleted comment]
GoogleCodeExporter commented 9 years ago
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 frankmor...@googlemail.com on 2 Jun 2014 at 11:56