diff mbox

[v23,06/11] libcacard: initial commit

Message ID 1300886393-2799-7-git-send-email-alevy@redhat.com
State New
Headers show

Commit Message

Alon Levy March 23, 2011, 1:19 p.m. UTC
From: Robert Relyea <rrelyea@redhat.com>

libcacard emulates a Common Access Card (CAC) which is a standard
for smartcards. It is used by the emulated ccid card introduced in
a following patch. Docs are available in docs/libcacard.txt

Signed-off-by: Alon Levy <alevy@redhat.com>

---

changes from v22->v23:
 * configure fixes: (reported by Stefan Hajnoczi)
  * test a = b, not a == b (second isn't portable)
  * quote $source_path in case it contains spaces
   - this doesn't really help since there are many other places
     that need similar fixes, not introduced by this patch.

changes from v21->v22:
 * fix configure to not link libcacard if nss not found
    (reported by Stefan Hajnoczi)
 * fix vscclient linkage with simpletrace backend
    (reported by Stefan Hajnoczi)
 * card_7816.c: add missing break in ERROR_DATA_NOT_FOUND
    (reported by William van de Velde)

changes from v20->v21: (Jes Sorenson review)
 * use qemu infrastructure: qemu-thread, qemu-common (qemu_malloc
  and qemu_free), error_report
 * assert instead of ASSERT
 * cosmetic fixes
 * use strpbrk and isspace
 * add --disable-nss --enable-nss here, instead of in the final patch.
 * split vscclient, passthru and docs to following patches.

changes from v19->v20:
 * checkpatch.pl

changes from v15->v16:

Build:
 * don't erase self with distclean
 * fix make clean after make distclean
 * Makefile: make vscclient link quiet

Behavioral:
 * vcard_emul_nss: load coolkey in more situations
 * vscclient:
  * use hton,ntoh
  * send init on connect, only start vevent thread on response
  * read payload after header check, before type switch
  * remove Reconnect
  * update for vscard_common changes, empty Flush implementation

Style/Whitespace:
 * fix wrong variable usage
 * remove unused variable
 * use only C style comments
  * add copyright header
  * fix tabulation

Signed-off-by: Alon Levy <alevy@redhat.com>
---
 Makefile                    |    6 +-
 Makefile.objs               |    5 +
 Makefile.target             |    2 +
 configure                   |   49 ++
 libcacard/Makefile          |   17 +
 libcacard/cac.c             |  406 +++++++++++++++
 libcacard/cac.h             |   23 +
 libcacard/card_7816.c       |  764 ++++++++++++++++++++++++++++
 libcacard/card_7816.h       |   62 +++
 libcacard/card_7816t.h      |  165 ++++++
 libcacard/event.c           |  108 ++++
 libcacard/eventt.h          |   29 ++
 libcacard/link_test.c       |   21 +
 libcacard/vcard.c           |  341 +++++++++++++
 libcacard/vcard.h           |   86 ++++
 libcacard/vcard_emul.h      |   65 +++
 libcacard/vcard_emul_nss.c  | 1159 +++++++++++++++++++++++++++++++++++++++++++
 libcacard/vcard_emul_type.c |   57 +++
 libcacard/vcard_emul_type.h |   32 ++
 libcacard/vcardt.h          |   64 +++
 libcacard/vevent.h          |   27 +
 libcacard/vreader.c         |  519 +++++++++++++++++++
 libcacard/vreader.h         |   55 ++
 libcacard/vreadert.h        |   24 +
 24 files changed, 4084 insertions(+), 2 deletions(-)
 create mode 100644 libcacard/Makefile
 create mode 100644 libcacard/cac.c
 create mode 100644 libcacard/cac.h
 create mode 100644 libcacard/card_7816.c
 create mode 100644 libcacard/card_7816.h
 create mode 100644 libcacard/card_7816t.h
 create mode 100644 libcacard/event.c
 create mode 100644 libcacard/eventt.h
 create mode 100644 libcacard/link_test.c
 create mode 100644 libcacard/vcard.c
 create mode 100644 libcacard/vcard.h
 create mode 100644 libcacard/vcard_emul.h
 create mode 100644 libcacard/vcard_emul_nss.c
 create mode 100644 libcacard/vcard_emul_type.c
 create mode 100644 libcacard/vcard_emul_type.h
 create mode 100644 libcacard/vcardt.h
 create mode 100644 libcacard/vevent.h
 create mode 100644 libcacard/vreader.c
 create mode 100644 libcacard/vreader.h
 create mode 100644 libcacard/vreadert.h

Comments

Jes Sorensen March 28, 2011, 12:35 p.m. UTC | #1
On 03/23/11 14:19, Alon Levy wrote:
> From: Robert Relyea <rrelyea@redhat.com>
> 
> libcacard emulates a Common Access Card (CAC) which is a standard
> for smartcards. It is used by the emulated ccid card introduced in
> a following patch. Docs are available in docs/libcacard.txt
> 
> Signed-off-by: Alon Levy <alevy@redhat.com>

A couple of minor nits.

> diff --git a/Makefile.objs b/Makefile.objs
> index 744e1d3..f513ffa 100644
> --- a/Makefile.objs
> +++ b/Makefile.objs
> @@ -352,6 +352,11 @@ user-obj-y += qemu-timer-common.o
>  endif
>  endif
>  
> +######################################################################
> +# smartcard
> +
> +libcacard-y = cac.o event.o vcard.o vreader.o vcard_emul_nss.o vcard_emul_type.o card_7816.o
> +
>  vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
>  
>  vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
> diff --git a/Makefile.target b/Makefile.target
> index 62b102a..7f163e3 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -353,6 +353,8 @@ obj-y += $(addprefix $(HWDIR)/, $(hw-obj-y))
>  
>  endif # CONFIG_SOFTMMU
>  
> +obj-y += $(addprefix ../libcacard/, $(libcacard-$(CONFIG_SMARTCARD_NSS)))
> +
>  obj-y += $(addprefix ../, $(trace-obj-y))
>  obj-$(CONFIG_GDBSTUB_XML) += gdbstub-xml.o

This is a bit backwards, normally we do
foobar-$(CONFIG_FOOBAR) = foo.o bar.o

and then later obj-y = $(foobar-y)

> diff --git a/libcacard/cac.c b/libcacard/cac.c
> new file mode 100644
> index 0000000..7a910d8
> --- /dev/null
> +++ b/libcacard/cac.c
> @@ -0,0 +1,406 @@
> +/*
> + * implement the applets for the CAC card.
> + *
> + * This code is licensed under the GNU LGPL, version 2.1 or later.
> + * See the COPYING.LIB file in the top-level directory.
> + *
> + */
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include "qemu-common.h"

stdlib.h and string.h are both included by qemu-common.h

> diff --git a/libcacard/card_7816.c b/libcacard/card_7816.c
> new file mode 100644
> index 0000000..4c10cae
> --- /dev/null
> +++ b/libcacard/card_7816.c
> @@ -0,0 +1,764 @@
> +/*
> + * Implement the 7816 portion of the card spec
> + *
> + * This code is licensed under the GNU LGPL, version 2.1 or later.
> + * See the COPYING.LIB file in the top-level directory.
> + */
> +
> +#include <stdlib.h>
> +#include <string.h>
> +#include "qemu-common.h"

same here

> diff --git a/libcacard/event.c b/libcacard/event.c
> new file mode 100644
> index 0000000..12722cc
> --- /dev/null
> +++ b/libcacard/event.c
> @@ -0,0 +1,108 @@
> +/*
> + * event queue implementation.
> + *
> + * This code is licensed under the GNU LGPL, version 2.1 or later.
> + * See the COPYING.LIB file in the top-level directory.
> + */
> +
> +#include <stdlib.h>
> +
> +#include "qemu-thread.h"
> +#include "qemu-common.h"

again here


> diff --git a/libcacard/vcard.c b/libcacard/vcard.c
> new file mode 100644
> index 0000000..d7828a2
> --- /dev/null
> +++ b/libcacard/vcard.c
> @@ -0,0 +1,341 @@
> +/*
> + * implement the Java card standard.
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
> + * See the COPYING.LIB file in the top-level directory.
> + */
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include "qemu-common.h"

and here

> diff --git a/libcacard/vcard_emul_nss.c b/libcacard/vcard_emul_nss.c
> new file mode 100644
> index 0000000..d3ab7ea
> --- /dev/null
> +++ b/libcacard/vcard_emul_nss.c
> @@ -0,0 +1,1159 @@
> +/*
> + * This is the actual card emulator.
> + *
> + * These functions can be implemented in different ways on different platforms
> + * using the underlying system primitives. For Linux it uses NSS, though direct
> + * to PKCS #11, openssl+pkcs11, or even gnu crypto libraries+pkcs #11 could be
> + * used. On Windows CAPI could be used.
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
> + * See the COPYING.LIB file in the top-level directory.
> + */
> +
> +/*
> + * system headers
> + */
> +#include <stdlib.h>
> +#include <string.h>
> +
> +/*
> + * NSS headers
> + */
> +#include <nss.h>
> +#include <pk11pub.h>
> +#include <cert.h>
> +#include <key.h>
> +#include <secmod.h>
> +#include <prthread.h>
> +#include <secerr.h>
> +
> +#include "qemu-common.h"

again here

prthread.h do you have a check for it in configure?  I have to admit I
really would prefer QEMU not relying on the NSPR stuff, but I don't know
if it can be avoided with the ccid code?

> diff --git a/libcacard/vreader.c b/libcacard/vreader.c
> new file mode 100644
> index 0000000..0b67c6c
> --- /dev/null
> +++ b/libcacard/vreader.c
> @@ -0,0 +1,519 @@
> +/*
> + * emulate the reader
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
> + * See the COPYING.LIB file in the top-level directory.
> + */
> +
> +/*
> + * System includes
> + */
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include "qemu-thread.h"
> +#include "qemu-common.h"

and a last one....

Cheers,
Jes
Alon Levy March 28, 2011, 12:42 p.m. UTC | #2
On Mon, Mar 28, 2011 at 02:35:23PM +0200, Jes Sorensen wrote:
> On 03/23/11 14:19, Alon Levy wrote:
> > From: Robert Relyea <rrelyea@redhat.com>
> > 
> > libcacard emulates a Common Access Card (CAC) which is a standard
> > for smartcards. It is used by the emulated ccid card introduced in
> > a following patch. Docs are available in docs/libcacard.txt
> > 
> > Signed-off-by: Alon Levy <alevy@redhat.com>
> 
> A couple of minor nits.
> 
> > diff --git a/Makefile.objs b/Makefile.objs
> > index 744e1d3..f513ffa 100644
> > --- a/Makefile.objs
> > +++ b/Makefile.objs
> > @@ -352,6 +352,11 @@ user-obj-y += qemu-timer-common.o
> >  endif
> >  endif
> >  
> > +######################################################################
> > +# smartcard
> > +
> > +libcacard-y = cac.o event.o vcard.o vreader.o vcard_emul_nss.o vcard_emul_type.o card_7816.o
> > +
> >  vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
> >  
> >  vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
> > diff --git a/Makefile.target b/Makefile.target
> > index 62b102a..7f163e3 100644
> > --- a/Makefile.target
> > +++ b/Makefile.target
> > @@ -353,6 +353,8 @@ obj-y += $(addprefix $(HWDIR)/, $(hw-obj-y))
> >  
> >  endif # CONFIG_SOFTMMU
> >  
> > +obj-y += $(addprefix ../libcacard/, $(libcacard-$(CONFIG_SMARTCARD_NSS)))
> > +
> >  obj-y += $(addprefix ../, $(trace-obj-y))
> >  obj-$(CONFIG_GDBSTUB_XML) += gdbstub-xml.o
> 
> This is a bit backwards, normally we do
> foobar-$(CONFIG_FOOBAR) = foo.o bar.o
> 
> and then later obj-y = $(foobar-y)
> 
> > diff --git a/libcacard/cac.c b/libcacard/cac.c
> > new file mode 100644
> > index 0000000..7a910d8
> > --- /dev/null
> > +++ b/libcacard/cac.c
> > @@ -0,0 +1,406 @@
> > +/*
> > + * implement the applets for the CAC card.
> > + *
> > + * This code is licensed under the GNU LGPL, version 2.1 or later.
> > + * See the COPYING.LIB file in the top-level directory.
> > + *
> > + */
> > +#include <stdlib.h>
> > +#include <string.h>
> > +
> > +#include "qemu-common.h"
> 
> stdlib.h and string.h are both included by qemu-common.h
> 
> > diff --git a/libcacard/card_7816.c b/libcacard/card_7816.c
> > new file mode 100644
> > index 0000000..4c10cae
> > --- /dev/null
> > +++ b/libcacard/card_7816.c
> > @@ -0,0 +1,764 @@
> > +/*
> > + * Implement the 7816 portion of the card spec
> > + *
> > + * This code is licensed under the GNU LGPL, version 2.1 or later.
> > + * See the COPYING.LIB file in the top-level directory.
> > + */
> > +
> > +#include <stdlib.h>
> > +#include <string.h>
> > +#include "qemu-common.h"
> 
> same here
> 
> > diff --git a/libcacard/event.c b/libcacard/event.c
> > new file mode 100644
> > index 0000000..12722cc
> > --- /dev/null
> > +++ b/libcacard/event.c
> > @@ -0,0 +1,108 @@
> > +/*
> > + * event queue implementation.
> > + *
> > + * This code is licensed under the GNU LGPL, version 2.1 or later.
> > + * See the COPYING.LIB file in the top-level directory.
> > + */
> > +
> > +#include <stdlib.h>
> > +
> > +#include "qemu-thread.h"
> > +#include "qemu-common.h"
> 
> again here
> 
> 
> > diff --git a/libcacard/vcard.c b/libcacard/vcard.c
> > new file mode 100644
> > index 0000000..d7828a2
> > --- /dev/null
> > +++ b/libcacard/vcard.c
> > @@ -0,0 +1,341 @@
> > +/*
> > + * implement the Java card standard.
> > + *
> > + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
> > + * See the COPYING.LIB file in the top-level directory.
> > + */
> > +#include <stdlib.h>
> > +#include <string.h>
> > +
> > +#include "qemu-common.h"
> 
> and here
> 
> > diff --git a/libcacard/vcard_emul_nss.c b/libcacard/vcard_emul_nss.c
> > new file mode 100644
> > index 0000000..d3ab7ea
> > --- /dev/null
> > +++ b/libcacard/vcard_emul_nss.c
> > @@ -0,0 +1,1159 @@
> > +/*
> > + * This is the actual card emulator.
> > + *
> > + * These functions can be implemented in different ways on different platforms
> > + * using the underlying system primitives. For Linux it uses NSS, though direct
> > + * to PKCS #11, openssl+pkcs11, or even gnu crypto libraries+pkcs #11 could be
> > + * used. On Windows CAPI could be used.
> > + *
> > + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
> > + * See the COPYING.LIB file in the top-level directory.
> > + */
> > +
> > +/*
> > + * system headers
> > + */
> > +#include <stdlib.h>
> > +#include <string.h>
> > +
> > +/*
> > + * NSS headers
> > + */
> > +#include <nss.h>
> > +#include <pk11pub.h>
> > +#include <cert.h>
> > +#include <key.h>
> > +#include <secmod.h>
> > +#include <prthread.h>
> > +#include <secerr.h>
> > +
> > +#include "qemu-common.h"
> 
> again here
> 
> prthread.h do you have a check for it in configure?  I have to admit I
> really would prefer QEMU not relying on the NSPR stuff, but I don't know
> if it can be avoided with the ccid code?
> 

No, unless you mean I should rewrite the emulation to not use NSS, I don't know
how. Or are you saying NSS can be used without using NSPR? I admited and will
repeat that I have not authored this code (that's why I have Robert Relyea as
the author of this patch), so I'm not familiar with NSS/NSPR except superficially.

> > diff --git a/libcacard/vreader.c b/libcacard/vreader.c
> > new file mode 100644
> > index 0000000..0b67c6c
> > --- /dev/null
> > +++ b/libcacard/vreader.c
> > @@ -0,0 +1,519 @@
> > +/*
> > + * emulate the reader
> > + *
> > + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
> > + * See the COPYING.LIB file in the top-level directory.
> > + */
> > +
> > +/*
> > + * System includes
> > + */
> > +#include <stdlib.h>
> > +#include <string.h>
> > +
> > +#include "qemu-thread.h"
> > +#include "qemu-common.h"
> 
> and a last one....

Are these a problem enough that you will without an ack? with respect to your
previous acks do you want me to only send this patch again, and Anthony should
merge the acked patches?

> 
> Cheers,
> Jes
Jes Sorensen March 28, 2011, 1:52 p.m. UTC | #3
On 03/28/11 14:42, Alon Levy wrote:
> On Mon, Mar 28, 2011 at 02:35:23PM +0200, Jes Sorensen wrote:
>>> +/*
>>> + * NSS headers
>>> + */
>>> +#include <nss.h>
>>> +#include <pk11pub.h>
>>> +#include <cert.h>
>>> +#include <key.h>
>>> +#include <secmod.h>
>>> +#include <prthread.h>
>>> +#include <secerr.h>
>>> +
>>> +#include "qemu-common.h"
>>
>> again here
>>
>> prthread.h do you have a check for it in configure?  I have to admit I
>> really would prefer QEMU not relying on the NSPR stuff, but I don't know
>> if it can be avoided with the ccid code?
> 
> No, unless you mean I should rewrite the emulation to not use NSS, I don't know
> how. Or are you saying NSS can be used without using NSPR? I admited and will
> repeat that I have not authored this code (that's why I have Robert Relyea as
> the author of this patch), so I'm not familiar with NSS/NSPR except superficially.

I don't know enough about NSS to say so, so just leave it in. However,
please check that the build doesn't break if one doesn't have the nspr
headers installed.

>>> diff --git a/libcacard/vreader.c b/libcacard/vreader.c
>>> new file mode 100644
>>> index 0000000..0b67c6c
>>> --- /dev/null
>>> +++ b/libcacard/vreader.c
>>> @@ -0,0 +1,519 @@
>>> +/*
>>> + * emulate the reader
>>> + *
>>> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
>>> + * See the COPYING.LIB file in the top-level directory.
>>> + */
>>> +
>>> +/*
>>> + * System includes
>>> + */
>>> +#include <stdlib.h>
>>> +#include <string.h>
>>> +
>>> +#include "qemu-thread.h"
>>> +#include "qemu-common.h"
>>
>> and a last one....
> 
> Are these a problem enough that you will without an ack? with respect to your
> previous acks do you want me to only send this patch again, and Anthony should
> merge the acked patches?

It would be preferred to have it fixed before the patch goes in, it
should be a quick fix. I'll be happy to ack it with that change.

Cheers,
Jes
Alon Levy March 28, 2011, 2:43 p.m. UTC | #4
On Mon, Mar 28, 2011 at 02:35:23PM +0200, Jes Sorensen wrote:
> On 03/23/11 14:19, Alon Levy wrote:
> > From: Robert Relyea <rrelyea@redhat.com>
> > 
> > libcacard emulates a Common Access Card (CAC) which is a standard
> > for smartcards. It is used by the emulated ccid card introduced in
> > a following patch. Docs are available in docs/libcacard.txt
> > 
> > Signed-off-by: Alon Levy <alevy@redhat.com>
> 
> A couple of minor nits.
> 
> > diff --git a/Makefile.objs b/Makefile.objs
> > index 744e1d3..f513ffa 100644
> > --- a/Makefile.objs
> > +++ b/Makefile.objs
> > @@ -352,6 +352,11 @@ user-obj-y += qemu-timer-common.o
> >  endif
> >  endif
> >  
> > +######################################################################
> > +# smartcard
> > +
> > +libcacard-y = cac.o event.o vcard.o vreader.o vcard_emul_nss.o vcard_emul_type.o card_7816.o
> > +
> >  vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
> >  
> >  vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
> > diff --git a/Makefile.target b/Makefile.target
> > index 62b102a..7f163e3 100644
> > --- a/Makefile.target
> > +++ b/Makefile.target
> > @@ -353,6 +353,8 @@ obj-y += $(addprefix $(HWDIR)/, $(hw-obj-y))
> >  
> >  endif # CONFIG_SOFTMMU
> >  
> > +obj-y += $(addprefix ../libcacard/, $(libcacard-$(CONFIG_SMARTCARD_NSS)))
> > +
> >  obj-y += $(addprefix ../, $(trace-obj-y))
> >  obj-$(CONFIG_GDBSTUB_XML) += gdbstub-xml.o
> 
> This is a bit backwards, normally we do
> foobar-$(CONFIG_FOOBAR) = foo.o bar.o
> 
> and then later obj-y = $(foobar-y)
> 
> > diff --git a/libcacard/cac.c b/libcacard/cac.c
> > new file mode 100644
> > index 0000000..7a910d8
> > --- /dev/null
> > +++ b/libcacard/cac.c
> > @@ -0,0 +1,406 @@
> > +/*
> > + * implement the applets for the CAC card.
> > + *
> > + * This code is licensed under the GNU LGPL, version 2.1 or later.
> > + * See the COPYING.LIB file in the top-level directory.
> > + *
> > + */
> > +#include <stdlib.h>
> > +#include <string.h>
> > +
> > +#include "qemu-common.h"
> 
> stdlib.h and string.h are both included by qemu-common.h
> 
> > diff --git a/libcacard/card_7816.c b/libcacard/card_7816.c
> > new file mode 100644
> > index 0000000..4c10cae
> > --- /dev/null
> > +++ b/libcacard/card_7816.c
> > @@ -0,0 +1,764 @@
> > +/*
> > + * Implement the 7816 portion of the card spec
> > + *
> > + * This code is licensed under the GNU LGPL, version 2.1 or later.
> > + * See the COPYING.LIB file in the top-level directory.
> > + */
> > +
> > +#include <stdlib.h>
> > +#include <string.h>
> > +#include "qemu-common.h"
> 
> same here
> 
> > diff --git a/libcacard/event.c b/libcacard/event.c
> > new file mode 100644
> > index 0000000..12722cc
> > --- /dev/null
> > +++ b/libcacard/event.c
> > @@ -0,0 +1,108 @@
> > +/*
> > + * event queue implementation.
> > + *
> > + * This code is licensed under the GNU LGPL, version 2.1 or later.
> > + * See the COPYING.LIB file in the top-level directory.
> > + */
> > +
> > +#include <stdlib.h>
> > +
> > +#include "qemu-thread.h"
> > +#include "qemu-common.h"
> 
> again here
> 
> 
> > diff --git a/libcacard/vcard.c b/libcacard/vcard.c
> > new file mode 100644
> > index 0000000..d7828a2
> > --- /dev/null
> > +++ b/libcacard/vcard.c
> > @@ -0,0 +1,341 @@
> > +/*
> > + * implement the Java card standard.
> > + *
> > + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
> > + * See the COPYING.LIB file in the top-level directory.
> > + */
> > +#include <stdlib.h>
> > +#include <string.h>
> > +
> > +#include "qemu-common.h"
> 
> and here
> 
> > diff --git a/libcacard/vcard_emul_nss.c b/libcacard/vcard_emul_nss.c
> > new file mode 100644
> > index 0000000..d3ab7ea
> > --- /dev/null
> > +++ b/libcacard/vcard_emul_nss.c
> > @@ -0,0 +1,1159 @@
> > +/*
> > + * This is the actual card emulator.
> > + *
> > + * These functions can be implemented in different ways on different platforms
> > + * using the underlying system primitives. For Linux it uses NSS, though direct
> > + * to PKCS #11, openssl+pkcs11, or even gnu crypto libraries+pkcs #11 could be
> > + * used. On Windows CAPI could be used.
> > + *
> > + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
> > + * See the COPYING.LIB file in the top-level directory.
> > + */
> > +
> > +/*
> > + * system headers
> > + */
> > +#include <stdlib.h>
> > +#include <string.h>
> > +
> > +/*
> > + * NSS headers
> > + */
> > +#include <nss.h>
> > +#include <pk11pub.h>
> > +#include <cert.h>
> > +#include <key.h>
> > +#include <secmod.h>
> > +#include <prthread.h>
> > +#include <secerr.h>
> > +
> > +#include "qemu-common.h"
> 
> again here
> 
> prthread.h do you have a check for it in configure?  I have to admit I
> really would prefer QEMU not relying on the NSPR stuff, but I don't know
> if it can be avoided with the ccid code?
> 

Tried removing, we do actually use it, but I'll try to remove it in a later
patch. (not in this series).

> > diff --git a/libcacard/vreader.c b/libcacard/vreader.c
> > new file mode 100644
> > index 0000000..0b67c6c
> > --- /dev/null
> > +++ b/libcacard/vreader.c
> > @@ -0,0 +1,519 @@
> > +/*
> > + * emulate the reader
> > + *
> > + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
> > + * See the COPYING.LIB file in the top-level directory.
> > + */
> > +
> > +/*
> > + * System includes
> > + */
> > +#include <stdlib.h>
> > +#include <string.h>
> > +
> > +#include "qemu-thread.h"
> > +#include "qemu-common.h"
> 
> and a last one....
> 
> Cheers,
> Jes
diff mbox

Patch

diff --git a/Makefile b/Makefile
index 209e14d..4462df0 100644
--- a/Makefile
+++ b/Makefile
@@ -141,6 +141,8 @@  check-qlist: check-qlist.o qlist.o qint.o $(CHECK_PROG_DEPS)
 check-qfloat: check-qfloat.o qfloat.o $(CHECK_PROG_DEPS)
 check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjson.o json-streamer.o json-lexer.o json-parser.o $(CHECK_PROG_DEPS)
 
+QEMULIBS=libhw32 libhw64 libuser libdis libdis-user
+
 clean:
 # avoid old build problems by removing potentially incorrect old files
 	rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
@@ -152,7 +154,7 @@  clean:
 	rm -f trace-dtrace.dtrace trace-dtrace.dtrace-timestamp
 	rm -f trace-dtrace.h trace-dtrace.h-timestamp
 	$(MAKE) -C tests clean
-	for d in $(ALL_SUBDIRS) libhw32 libhw64 libuser libdis libdis-user; do \
+	for d in $(ALL_SUBDIRS) $(QEMULIBS) libcacard; do \
 	if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \
 	rm -f $$d/qemu-options.def; \
         done
@@ -163,7 +165,7 @@  distclean: clean
 	rm -f roms/seabios/config.mak roms/vgabios/config.mak
 	rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.dvi qemu-doc.fn qemu-doc.info qemu-doc.ky qemu-doc.log qemu-doc.pdf qemu-doc.pg qemu-doc.toc qemu-doc.tp qemu-doc.vr
 	rm -f qemu-tech.info qemu-tech.aux qemu-tech.cp qemu-tech.dvi qemu-tech.fn qemu-tech.info qemu-tech.ky qemu-tech.log qemu-tech.pdf qemu-tech.pg qemu-tech.toc qemu-tech.tp qemu-tech.vr
-	for d in $(TARGET_DIRS) libhw32 libhw64 libuser libdis libdis-user; do \
+	for d in $(TARGET_DIRS) $(QEMULIBS); do \
 	rm -rf $$d || exit 1 ; \
         done
 
diff --git a/Makefile.objs b/Makefile.objs
index 744e1d3..f513ffa 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -352,6 +352,11 @@  user-obj-y += qemu-timer-common.o
 endif
 endif
 
+######################################################################
+# smartcard
+
+libcacard-y = cac.o event.o vcard.o vreader.o vcard_emul_nss.o vcard_emul_type.o card_7816.o
+
 vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
 
 vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
diff --git a/Makefile.target b/Makefile.target
index 62b102a..7f163e3 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -353,6 +353,8 @@  obj-y += $(addprefix $(HWDIR)/, $(hw-obj-y))
 
 endif # CONFIG_SOFTMMU
 
+obj-y += $(addprefix ../libcacard/, $(libcacard-$(CONFIG_SMARTCARD_NSS)))
+
 obj-y += $(addprefix ../, $(trace-obj-y))
 obj-$(CONFIG_GDBSTUB_XML) += gdbstub-xml.o
 
diff --git a/configure b/configure
index 159549d..7c4acf8 100755
--- a/configure
+++ b/configure
@@ -176,6 +176,7 @@  trace_file="trace"
 spice=""
 rbd=""
 smartcard=""
+smartcard_nss=""
 
 # parse CC options first
 for opt do
@@ -729,6 +730,10 @@  for opt do
   ;;
   --enable-smartcard) smartcard="yes"
   ;;
+  --disable-smartcard-nss) smartcard_nss="no"
+  ;;
+  --enable-smartcard-nss) smartcard_nss="yes"
+  ;;
   *) echo "ERROR: unknown option $opt"; show_help="yes"
   ;;
   esac
@@ -928,6 +933,8 @@  echo "  --enable-spice           enable spice"
 echo "  --enable-rbd             enable building the rados block device (rbd)"
 echo "  --disable-smartcard      disable smartcard support"
 echo "  --enable-smartcard       enable smartcard support"
+echo "  --disable-smartcard-nss  disable smartcard nss support"
+echo "  --enable-smartcard-nss   enable smartcard nss support"
 echo ""
 echo "NOTE: The object files are built at the place where configure is launched"
 exit 1
@@ -2311,6 +2318,31 @@  EOF
   fi
 fi
 
+# check for libcacard for smartcard support
+if test "$smartcard" != "no" ; then
+    smartcard="yes"
+    smartcard_cflags=""
+    # TODO - what's the minimal nss version we support?
+    if test "$smartcard_nss" != "no"; then
+        if $pkg_config --atleast-version=3.12.8 nss >/dev/null 2>&1 ; then
+            smartcard_nss="yes"
+            smartcard_cflags="-I\$(SRC_PATH)/libcacard"
+            libcacard_libs=$($pkg_config --libs nss 2>/dev/null)
+            libcacard_cflags=$($pkg_config --cflags nss 2>/dev/null)
+            QEMU_CFLAGS="$QEMU_CFLAGS $smartcard_cflags $libcacard_cflags"
+            LIBS="$libcacard_libs $LIBS"
+        else
+            if test "$smartcard_nss" = "yes"; then
+                feature_not_found "nss"
+            fi
+            smartcard_nss="no"
+        fi
+    fi
+fi
+if test "$smartcard" = "no" ; then
+    smartcard_nss="no"
+fi
+
 ##########################################
 
 ##########################################
@@ -2547,6 +2579,7 @@  echo "Trace output file $trace_file-<pid>"
 echo "spice support     $spice"
 echo "rbd support       $rbd"
 echo "xfsctl support    $xfs"
+echo "nss used          $smartcard_nss"
 
 if test $sdl_too_old = "yes"; then
 echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -2833,6 +2866,10 @@  if test "$smartcard" = "yes" ; then
   echo "CONFIG_SMARTCARD=y" >> $config_host_mak
 fi
 
+if test "$smartcard_nss" = "yes" ; then
+  echo "CONFIG_SMARTCARD_NSS=y" >> $config_host_mak
+fi
+
 # XXX: suppress that
 if [ "$bsd" = "yes" ] ; then
   echo "CONFIG_BSD=y" >> $config_host_mak
@@ -3181,6 +3218,11 @@  fi
 if test "$target_darwin_user" = "yes" ; then
   echo "CONFIG_DARWIN_USER=y" >> $config_target_mak
 fi
+if test "$smartcard_nss" = "yes" ; then
+  echo "subdir-$target: subdir-libcacard" >> $config_host_mak
+  echo "libcacard_libs=$libcacard_libs" >> $config_host_mak
+  echo "libcacard_cflags=$libcacard_cflags" >> $config_host_mak
+fi
 list=""
 if test ! -z "$gdb_xml_files" ; then
   for x in $gdb_xml_files; do
@@ -3394,6 +3436,13 @@  for hwlib in 32 64; do
   echo "QEMU_CFLAGS+=-DTARGET_PHYS_ADDR_BITS=$hwlib" > $d/config.mak
 done
 
+if [ "$source_path" != `pwd` ]; then
+    # out of tree build
+    mkdir -p libcacard
+    rm -f libcacard/Makefile
+    ln -s "$source_path/libcacard/Makefile" libcacard/Makefile
+fi
+
 d=libuser
 mkdir -p $d
 symlink $source_path/Makefile.user $d/Makefile
diff --git a/libcacard/Makefile b/libcacard/Makefile
new file mode 100644
index 0000000..410fa1e
--- /dev/null
+++ b/libcacard/Makefile
@@ -0,0 +1,17 @@ 
+-include ../config-host.mak
+-include $(SRC_PATH)/Makefile.objs
+-include $(SRC_PATH)/rules.mak
+
+$(call set-vpath, $(SRC_PATH):$(SRC_PATH)/libcacard)
+
+ifeq ($(CONFIG_WIN32),y)
+QEMU_THREAD=qemu-thread-win32.o
+else
+QEMU_THREAD=qemu-thread-posix.o
+endif
+
+QEMU_OBJS=$(QEMU_THREAD) $(oslib-obj-y) $(trace-obj-y) qemu-malloc.o qemu-timer-common.o
+
+clean:
+	rm -f *.o */*.o *.d */*.d *.a */*.a *~ */*~
+
diff --git a/libcacard/cac.c b/libcacard/cac.c
new file mode 100644
index 0000000..7a910d8
--- /dev/null
+++ b/libcacard/cac.c
@@ -0,0 +1,406 @@ 
+/*
+ * implement the applets for the CAC card.
+ *
+ * This code is licensed under the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+#include <stdlib.h>
+#include <string.h>
+
+#include "qemu-common.h"
+
+#include "cac.h"
+#include "vcard.h"
+#include "vcard_emul.h"
+#include "card_7816.h"
+
+#define CAC_GET_PROPERTIES  0x56
+#define CAC_GET_ACR         0x4c
+#define CAC_READ_BUFFER     0x52
+#define CAC_UPDATE_BUFFER   0x58
+#define CAC_SIGN_DECRYPT    0x42
+#define CAC_GET_CERTIFICATE 0x36
+
+/* private data for PKI applets */
+typedef struct CACPKIAppletDataStruct {
+    unsigned char *cert;
+    int cert_len;
+    unsigned char *cert_buffer;
+    int cert_buffer_len;
+    unsigned char *sign_buffer;
+    int sign_buffer_len;
+    VCardKey *key;
+} CACPKIAppletData;
+
+/*
+ * CAC applet private data
+ */
+struct VCardAppletPrivateStruct {
+    union {
+        CACPKIAppletData pki_data;
+        void *reserved;
+    } u;
+};
+
+/*
+ * handle all the APDU's that are common to all CAC applets
+ */
+static VCardStatus
+cac_common_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response)
+{
+    int ef;
+
+    switch (apdu->a_ins) {
+    case VCARD7816_INS_SELECT_FILE:
+        if (apdu->a_p1 != 0x02) {
+            /* let the 7816 code handle applet switches */
+            return VCARD_NEXT;
+        }
+        /* handle file id setting */
+        if (apdu->a_Lc != 2) {
+            *response = vcard_make_response(
+                VCARD7816_STATUS_ERROR_DATA_INVALID);
+            return VCARD_DONE;
+        }
+        /* CAC 1.0 only supports ef = 0 */
+        ef = apdu->a_body[0] | (apdu->a_body[1] << 8);
+        if (ef != 0) {
+            *response = vcard_make_response(
+                VCARD7816_STATUS_ERROR_FILE_NOT_FOUND);
+            return VCARD_DONE;
+        }
+        *response = vcard_make_response(VCARD7816_STATUS_SUCCESS);
+        return VCARD_DONE;
+    case VCARD7816_INS_GET_RESPONSE:
+    case VCARD7816_INS_VERIFY:
+        /* let the 7816 code handle these */
+        return VCARD_NEXT;
+    case CAC_GET_PROPERTIES:
+    case CAC_GET_ACR:
+        /* skip these for now, this will probably be needed */
+        *response = vcard_make_response(VCARD7816_STATUS_ERROR_P1_P2_INCORRECT);
+        return VCARD_DONE;
+    }
+    *response = vcard_make_response(
+        VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+    return VCARD_DONE;
+}
+
+/*
+ *  reset the inter call state between applet selects
+ */
+static VCardStatus
+cac_applet_pki_reset(VCard *card, int channel)
+{
+    VCardAppletPrivate *applet_private = NULL;
+    CACPKIAppletData *pki_applet = NULL;
+    applet_private = vcard_get_current_applet_private(card, channel);
+    assert(applet_private);
+    pki_applet = &(applet_private->u.pki_data);
+
+    pki_applet->cert_buffer = NULL;
+    if (pki_applet->sign_buffer) {
+        qemu_free(pki_applet->sign_buffer);
+        pki_applet->sign_buffer = NULL;
+    }
+    pki_applet->cert_buffer_len = 0;
+    pki_applet->sign_buffer_len = 0;
+    return VCARD_DONE;
+}
+
+static VCardStatus
+cac_applet_pki_process_apdu(VCard *card, VCardAPDU *apdu,
+                            VCardResponse **response)
+{
+    CACPKIAppletData *pki_applet = NULL;
+    VCardAppletPrivate *applet_private = NULL;
+    int size, next;
+    unsigned char *sign_buffer;
+    vcard_7816_status_t status;
+
+    applet_private = vcard_get_current_applet_private(card, apdu->a_channel);
+    assert(applet_private);
+    pki_applet = &(applet_private->u.pki_data);
+
+    switch (apdu->a_ins) {
+    case CAC_UPDATE_BUFFER:
+        *response = vcard_make_response(
+            VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED);
+        return VCARD_DONE;
+    case CAC_GET_CERTIFICATE:
+        if ((apdu->a_p2 != 0) || (apdu->a_p1 != 0)) {
+            *response = vcard_make_response(
+                             VCARD7816_STATUS_ERROR_P1_P2_INCORRECT);
+            break;
+        }
+        assert(pki_applet->cert != NULL);
+        size = apdu->a_Le;
+        if (pki_applet->cert_buffer == NULL) {
+            pki_applet->cert_buffer = pki_applet->cert;
+            pki_applet->cert_buffer_len = pki_applet->cert_len;
+        }
+        size = MIN(size, pki_applet->cert_buffer_len);
+        next = MIN(255, pki_applet->cert_buffer_len - size);
+        *response = vcard_response_new_bytes(
+                        card, pki_applet->cert_buffer, size,
+                        apdu->a_Le, next ?
+                        VCARD7816_SW1_WARNING_CHANGE :
+                        VCARD7816_SW1_SUCCESS,
+                        next);
+        pki_applet->cert_buffer += size;
+        pki_applet->cert_buffer_len -= size;
+        if ((*response == NULL) || (next == 0)) {
+            pki_applet->cert_buffer = NULL;
+        }
+        if (*response == NULL) {
+            *response = vcard_make_response(
+                            VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
+        }
+        return VCARD_DONE;
+    case CAC_SIGN_DECRYPT:
+        if (apdu->a_p2 != 0) {
+            *response = vcard_make_response(
+                             VCARD7816_STATUS_ERROR_P1_P2_INCORRECT);
+            break;
+        }
+        size = apdu->a_Lc;
+
+        sign_buffer = realloc(pki_applet->sign_buffer,
+                      pki_applet->sign_buffer_len+size);
+        if (sign_buffer == NULL) {
+            qemu_free(pki_applet->sign_buffer);
+            pki_applet->sign_buffer = NULL;
+            pki_applet->sign_buffer_len = 0;
+            *response = vcard_make_response(
+                            VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
+            return VCARD_DONE;
+        }
+        memcpy(sign_buffer+pki_applet->sign_buffer_len, apdu->a_body, size);
+        size += pki_applet->sign_buffer_len;
+        switch (apdu->a_p1) {
+        case  0x80:
+            /* p1 == 0x80 means we haven't yet sent the whole buffer, wait for
+             * the rest */
+            pki_applet->sign_buffer = sign_buffer;
+            pki_applet->sign_buffer_len = size;
+            *response = vcard_make_response(VCARD7816_STATUS_SUCCESS);
+            return VCARD_DONE;
+        case 0x00:
+            /* we now have the whole buffer, do the operation, result will be
+             * in the sign_buffer */
+            status = vcard_emul_rsa_op(card, pki_applet->key,
+                                       sign_buffer, size);
+            if (status != VCARD7816_STATUS_SUCCESS) {
+                *response = vcard_make_response(status);
+                break;
+            }
+            *response = vcard_response_new(card, sign_buffer, size, apdu->a_Le,
+                                                     VCARD7816_STATUS_SUCCESS);
+            if (*response == NULL) {
+                *response = vcard_make_response(
+                                VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
+            }
+            break;
+        default:
+           *response = vcard_make_response(
+                                VCARD7816_STATUS_ERROR_P1_P2_INCORRECT);
+            break;
+        }
+        qemu_free(sign_buffer);
+        pki_applet->sign_buffer = NULL;
+        pki_applet->sign_buffer_len = 0;
+        return VCARD_DONE;
+    case CAC_READ_BUFFER:
+        /* new CAC call, go ahead and use the old version for now */
+        /* TODO: implement */
+        *response = vcard_make_response(
+                                VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+        return VCARD_DONE;
+    }
+    return cac_common_process_apdu(card, apdu, response);
+}
+
+
+static VCardStatus
+cac_applet_id_process_apdu(VCard *card, VCardAPDU *apdu,
+                           VCardResponse **response)
+{
+    switch (apdu->a_ins) {
+    case CAC_UPDATE_BUFFER:
+        *response = vcard_make_response(
+                        VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED);
+        return VCARD_DONE;
+    case CAC_READ_BUFFER:
+        /* new CAC call, go ahead and use the old version for now */
+        /* TODO: implement */
+        *response = vcard_make_response(
+                        VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+        return VCARD_DONE;
+    }
+    return cac_common_process_apdu(card, apdu, response);
+}
+
+
+/*
+ * TODO: if we ever want to support general CAC middleware, we will need to
+ * implement the various containers.
+ */
+static VCardStatus
+cac_applet_container_process_apdu(VCard *card, VCardAPDU *apdu,
+                                  VCardResponse **response)
+{
+    switch (apdu->a_ins) {
+    case CAC_READ_BUFFER:
+    case CAC_UPDATE_BUFFER:
+        *response = vcard_make_response(
+                        VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+        return VCARD_DONE;
+    default:
+        break;
+    }
+    return cac_common_process_apdu(card, apdu, response);
+}
+
+/*
+ * utilities for creating and destroying the private applet data
+ */
+static void
+cac_delete_pki_applet_private(VCardAppletPrivate *applet_private)
+{
+    CACPKIAppletData *pki_applet_data = NULL;
+    if (pki_applet_data == NULL) {
+        return;
+    }
+    pki_applet_data = &(applet_private->u.pki_data);
+    if (pki_applet_data->cert != NULL) {
+        qemu_free(pki_applet_data->cert);
+    }
+    if (pki_applet_data->sign_buffer != NULL) {
+        qemu_free(pki_applet_data->sign_buffer);
+    }
+    if (pki_applet_data->key != NULL) {
+        vcard_emul_delete_key(pki_applet_data->key);
+    }
+    qemu_free(applet_private);
+}
+
+static VCardAppletPrivate *
+cac_new_pki_applet_private(const unsigned char *cert,
+                           int cert_len, VCardKey *key)
+{
+    CACPKIAppletData *pki_applet_data = NULL;
+    VCardAppletPrivate *applet_private = NULL;
+    applet_private = (VCardAppletPrivate *)qemu_malloc(sizeof(VCardAppletPrivate));
+
+    pki_applet_data = &(applet_private->u.pki_data);
+    pki_applet_data->cert_buffer = NULL;
+    pki_applet_data->cert_buffer_len = 0;
+    pki_applet_data->sign_buffer = NULL;
+    pki_applet_data->sign_buffer_len = 0;
+    pki_applet_data->key = NULL;
+    pki_applet_data->cert = (unsigned char *)qemu_malloc(cert_len+1);
+    /*
+     * if we want to support compression, then we simply change the 0 to a 1
+     * and compress the cert data with libz
+     */
+    pki_applet_data->cert[0] = 0; /* not compressed */
+    memcpy(&pki_applet_data->cert[1], cert, cert_len);
+    pki_applet_data->cert_len = cert_len+1;
+
+    pki_applet_data->key = key;
+    return applet_private;
+}
+
+
+/*
+ * create a new cac applet which links to a given cert
+ */
+static VCardApplet *
+cac_new_pki_applet(int i, const unsigned char *cert,
+                   int cert_len, VCardKey *key)
+{
+    VCardAppletPrivate *applet_private = NULL;
+    VCardApplet *applet = NULL;
+    unsigned char pki_aid[] = { 0xa0, 0x00, 0x00, 0x00, 0x79, 0x01, 0x00 };
+    int pki_aid_len = sizeof(pki_aid);
+
+    pki_aid[pki_aid_len-1] = i;
+
+    applet_private = cac_new_pki_applet_private(cert, cert_len, key);
+    if (applet_private == NULL) {
+        goto failure;
+    }
+    applet = vcard_new_applet(cac_applet_pki_process_apdu, cac_applet_pki_reset,
+                              pki_aid, pki_aid_len);
+    if (applet == NULL) {
+        goto failure;
+    }
+    vcard_set_applet_private(applet, applet_private,
+                             cac_delete_pki_applet_private);
+    applet_private = NULL;
+
+    return applet;
+
+failure:
+    if (applet_private != NULL) {
+        cac_delete_pki_applet_private(applet_private);
+    }
+    return NULL;
+}
+
+
+static unsigned char cac_default_container_aid[] = {
+    0xa0, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00 };
+static unsigned char cac_id_aid[] = {
+    0xa0, 0x00, 0x00, 0x00, 0x79, 0x03, 0x00 };
+/*
+ * Initialize the cac card. This is the only public function in this file. All
+ * the rest are connected through function pointers.
+ */
+VCardStatus
+cac_card_init(VReader *reader, VCard *card,
+              const char *params,
+              unsigned char * const *cert,
+              int cert_len[],
+              VCardKey *key[] /* adopt the keys*/,
+              int cert_count)
+{
+    int i;
+    VCardApplet *applet;
+
+    /* CAC Cards are VM Cards */
+    vcard_set_type(card, VCARD_VM);
+
+    /* create one PKI applet for each cert */
+    for (i = 0; i < cert_count; i++) {
+        applet = cac_new_pki_applet(i, cert[i], cert_len[i], key[i]);
+        if (applet == NULL) {
+            goto failure;
+        }
+        vcard_add_applet(card, applet);
+    }
+
+    /* create a default blank container applet */
+    applet = vcard_new_applet(cac_applet_container_process_apdu,
+                              NULL, cac_default_container_aid,
+                              sizeof(cac_default_container_aid));
+    if (applet == NULL) {
+        goto failure;
+    }
+    vcard_add_applet(card, applet);
+
+    /* create a default blank container applet */
+    applet = vcard_new_applet(cac_applet_id_process_apdu,
+                              NULL, cac_id_aid,
+                              sizeof(cac_id_aid));
+    if (applet == NULL) {
+        goto failure;
+    }
+    vcard_add_applet(card, applet);
+    return VCARD_DONE;
+
+failure:
+    return VCARD_FAIL;
+}
+
diff --git a/libcacard/cac.h b/libcacard/cac.h
new file mode 100644
index 0000000..15a61be
--- /dev/null
+++ b/libcacard/cac.h
@@ -0,0 +1,23 @@ 
+/*
+ * defines the entry point for the cac card. Only used by cac.c anc
+ * vcard_emul_type.c
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+#ifndef CAC_H
+#define CAC_H 1
+#include "vcard.h"
+#include "vreader.h"
+/*
+ * Initialize the cac card. This is the only public function in this file. All
+ * the rest are connected through function pointers.
+ */
+VCardStatus cac_card_init(VReader *reader, VCard *card, const char *params,
+              unsigned char * const *cert, int cert_len[],
+              VCardKey *key[] /* adopt the keys*/,
+              int cert_count);
+
+/* not yet implemented */
+VCardStatus cac_is_cac_card(VReader *reader);
+#endif
diff --git a/libcacard/card_7816.c b/libcacard/card_7816.c
new file mode 100644
index 0000000..4c10cae
--- /dev/null
+++ b/libcacard/card_7816.c
@@ -0,0 +1,764 @@ 
+/*
+ * Implement the 7816 portion of the card spec
+ *
+ * This code is licensed under the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "qemu-common.h"
+#include "vcard.h"
+#include "vcard_emul.h"
+#include "card_7816.h"
+
+/*
+ * set the status bytes based on the status word
+ */
+static void
+vcard_response_set_status(VCardResponse *response, vcard_7816_status_t status)
+{
+    unsigned char sw1, sw2;
+    response->b_status = status; /* make sure the status and swX representations
+                                  * are consistent */
+    sw1 = (status >> 8) & 0xff;
+    sw2 = status & 0xff;
+    response->b_sw1 = sw1;
+    response->b_sw2 = sw2;
+    response->b_data[response->b_len] = sw1;
+    response->b_data[response->b_len+1] = sw2;
+}
+
+/*
+ * set the status bytes in a response buffer
+ */
+static void
+vcard_response_set_status_bytes(VCardResponse *response,
+                               unsigned char sw1, unsigned char sw2)
+{
+    response->b_status = sw1 << 8 | sw2;
+    response->b_sw1 = sw1;
+    response->b_sw2 = sw2;
+    response->b_data[response->b_len] = sw1;
+    response->b_data[response->b_len+1] = sw2;
+}
+
+/*
+ * allocate a VCardResponse structure, plus space for the data buffer, and
+ * set up everything but the resonse bytes.
+ */
+VCardResponse *
+vcard_response_new_data(unsigned char *buf, int len)
+{
+    VCardResponse *new_response;
+
+    new_response = (VCardResponse *)qemu_malloc(sizeof(VCardResponse));
+    new_response->b_data = qemu_malloc(len + 2);
+    memcpy(new_response->b_data, buf, len);
+    new_response->b_total_len = len+2;
+    new_response->b_len = len;
+    new_response->b_type = VCARD_MALLOC;
+    return new_response;
+}
+
+static VCardResponse *
+vcard_init_buffer_response(VCard *card, unsigned char *buf, int len)
+{
+    VCardResponse *response;
+    VCardBufferResponse *buffer_response;
+
+    buffer_response = vcard_get_buffer_response(card);
+    if (buffer_response) {
+        vcard_set_buffer_response(card, NULL);
+        vcard_buffer_response_delete(buffer_response);
+    }
+    buffer_response = vcard_buffer_response_new(buf, len);
+    if (buffer_response == NULL) {
+        return NULL;
+    }
+    response = vcard_response_new_status_bytes(VCARD7816_SW1_RESPONSE_BYTES,
+                                               len > 255 ? 0 : len);
+    if (response == NULL) {
+        return NULL;
+    }
+    vcard_set_buffer_response(card, buffer_response);
+    return response;
+}
+
+/*
+ * general buffer to hold results from APDU calls
+ */
+VCardResponse *
+vcard_response_new(VCard *card, unsigned char *buf,
+                   int len, int Le, vcard_7816_status_t status)
+{
+    VCardResponse *new_response;
+
+    if (len > Le) {
+        return vcard_init_buffer_response(card, buf, len);
+    }
+    new_response = vcard_response_new_data(buf, len);
+    if (new_response == NULL) {
+        return NULL;
+    }
+    vcard_response_set_status(new_response, status);
+    return new_response;
+}
+
+/*
+ * general buffer to hold results from APDU calls
+ */
+VCardResponse *
+vcard_response_new_bytes(VCard *card, unsigned char *buf, int len, int Le,
+                         unsigned char sw1, unsigned char sw2)
+{
+    VCardResponse *new_response;
+
+    if (len > Le) {
+        return vcard_init_buffer_response(card, buf, len);
+    }
+    new_response = vcard_response_new_data(buf, len);
+    if (new_response == NULL) {
+        return NULL;
+    }
+    vcard_response_set_status_bytes(new_response, sw1, sw2);
+    return new_response;
+}
+
+/*
+ * get a new Reponse buffer that only has a status.
+ */
+static VCardResponse *
+vcard_response_new_status(vcard_7816_status_t status)
+{
+    VCardResponse *new_response;
+
+    new_response = (VCardResponse *)qemu_malloc(sizeof(VCardResponse));
+    new_response->b_data = &new_response->b_sw1;
+    new_response->b_len = 0;
+    new_response->b_total_len = 2;
+    new_response->b_type = VCARD_MALLOC_STRUCT;
+    vcard_response_set_status(new_response, status);
+    return new_response;
+}
+
+/*
+ * same as above, but specify the status as separate bytes
+ */
+VCardResponse *
+vcard_response_new_status_bytes(unsigned char sw1, unsigned char sw2)
+{
+    VCardResponse *new_response;
+
+    new_response = (VCardResponse *)qemu_malloc(sizeof(VCardResponse));
+    new_response->b_data = &new_response->b_sw1;
+    new_response->b_len = 0;
+    new_response->b_total_len = 2;
+    new_response->b_type = VCARD_MALLOC_STRUCT;
+    vcard_response_set_status_bytes(new_response, sw1, sw2);
+    return new_response;
+}
+
+
+/*
+ * free the response buffer. The Buffer has a type to handle the buffer
+ * allocated in other ways than through malloc.
+ */
+void
+vcard_response_delete(VCardResponse *response)
+{
+    if (response == NULL) {
+        return;
+    }
+    switch (response->b_type) {
+    case VCARD_MALLOC:
+        /* everything was malloc'ed */
+        if (response->b_data) {
+            qemu_free(response->b_data);
+        }
+        qemu_free(response);
+        break;
+    case VCARD_MALLOC_DATA:
+        /* only the data buffer was malloc'ed */
+        if (response->b_data) {
+            qemu_free(response->b_data);
+        }
+        break;
+    case VCARD_MALLOC_STRUCT:
+        /* only the structure was malloc'ed */
+        qemu_free(response);
+        break;
+    case VCARD_STATIC:
+        break;
+    }
+}
+
+/*
+ * decode the class bit and set our generic type field, channel, and
+ * secure messaging values.
+ */
+static vcard_7816_status_t
+vcard_apdu_set_class(VCardAPDU *apdu) {
+    apdu->a_channel = 0;
+    apdu->a_secure_messaging = 0;
+    apdu->a_type = apdu->a_cla & 0xf0;
+    apdu->a_gen_type = VCARD_7816_ISO;
+
+    /* parse the class  tables 8 & 9 of the 7816-4 Part 4 spec */
+    switch (apdu->a_type) {
+        /* we only support the basic types */
+    case 0x00:
+    case 0x80:
+    case 0x90:
+    case 0xa0:
+        apdu->a_channel = apdu->a_cla & 3;
+        apdu->a_secure_messaging = apdu->a_cla & 0xe;
+        break;
+    case 0xb0:
+    case 0xc0:
+        break;
+
+    case 0x10:
+    case 0x20:
+    case 0x30:
+    case 0x40:
+    case 0x50:
+    case 0x60:
+    case 0x70:
+        /* Reserved for future use */
+        apdu->a_gen_type = VCARD_7816_RFU;
+        break;
+    case 0xd0:
+    case 0xe0:
+    case 0xf0:
+    default:
+        apdu->a_gen_type =
+            (apdu->a_cla == 0xff) ? VCARD_7816_PTS : VCARD_7816_PROPIETARY;
+        break;
+    }
+    return VCARD7816_STATUS_SUCCESS;
+}
+
+/*
+ * set the Le and Lc fiels according to table 5 of the
+ * 7816-4 part 4 spec
+ */
+static vcard_7816_status_t
+vcard_apdu_set_length(VCardAPDU *apdu)
+{
+    int L, Le;
+
+    /* process according to table 5 of the 7816-4 Part 4 spec.
+     * variable names match the variables in the spec */
+    L = apdu->a_len-4; /* fixed APDU header */
+    apdu->a_Lc = 0;
+    apdu->a_Le = 0;
+    apdu->a_body = NULL;
+    switch (L) {
+    case 0:
+        /* 1 minimal apdu */
+        return VCARD7816_STATUS_SUCCESS;
+    case 1:
+        /* 2S only return values apdu */
+        /*   zero maps to 256 here */
+        apdu->a_Le = apdu->a_header->ah_Le ?
+                         apdu->a_header->ah_Le : 256;
+        return VCARD7816_STATUS_SUCCESS;
+    default:
+        /* if the ah_Le byte is zero and we have more than
+         * 1 byte in the header, then we must be using extended Le and Lc.
+         * process the extended now. */
+        if (apdu->a_header->ah_Le == 0) {
+            if (L < 3) {
+                /* coding error, need at least 3 bytes */
+                return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
+            }
+            /* calculate the first extended value. Could be either Le or Lc */
+            Le = (apdu->a_header->ah_body[0] << 8)
+               || apdu->a_header->ah_body[1];
+            if (L == 3) {
+                /* 2E extended, return data only */
+                /*   zero maps to 65536 */
+                apdu->a_Le = Le ? Le : 65536;
+                return VCARD7816_STATUS_SUCCESS;
+            }
+            if (Le == 0) {
+                /* reserved for future use, probably for next time we need
+                 * to extend the lengths */
+                return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
+            }
+            /* we know that the first extended value is Lc now */
+            apdu->a_Lc = Le;
+            apdu->a_body = &apdu->a_header->ah_body[2];
+            if (L == Le+3) {
+                /* 3E extended, only body parameters */
+                return VCARD7816_STATUS_SUCCESS;
+            }
+            if (L == Le+5) {
+                /* 4E extended, parameters and return data */
+                Le = (apdu->a_data[apdu->a_len-2] << 8)
+                   || apdu->a_data[apdu->a_len-1];
+                apdu->a_Le = Le ? Le : 65536;
+                return VCARD7816_STATUS_SUCCESS;
+            }
+            return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
+        }
+        /* not extended */
+        apdu->a_Lc = apdu->a_header->ah_Le;
+        apdu->a_body = &apdu->a_header->ah_body[0];
+        if (L ==  apdu->a_Lc + 1) {
+            /* 3S only body parameters */
+            return VCARD7816_STATUS_SUCCESS;
+        }
+        if (L ==  apdu->a_Lc + 2) {
+            /* 4S parameters and return data */
+            Le = apdu->a_data[apdu->a_len-1];
+            apdu->a_Le = Le ?  Le : 256;
+            return VCARD7816_STATUS_SUCCESS;
+        }
+        break;
+    }
+    return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
+}
+
+/*
+ * create a new APDU from a raw set of bytes. This will decode all the
+ * above fields. users of VCARDAPDU's can then depend on the already decoded
+ * values.
+ */
+VCardAPDU *
+vcard_apdu_new(unsigned char *raw_apdu, int len, vcard_7816_status_t *status)
+{
+    VCardAPDU *new_apdu;
+
+    *status = VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE;
+    if (len < 4) {
+        *status = VCARD7816_STATUS_ERROR_WRONG_LENGTH;
+        return NULL;
+    }
+
+    new_apdu = (VCardAPDU *)qemu_malloc(sizeof(VCardAPDU));
+    new_apdu->a_data = qemu_malloc(len);
+    memcpy(new_apdu->a_data, raw_apdu, len);
+    new_apdu->a_len = len;
+    *status = vcard_apdu_set_class(new_apdu);
+    if (*status != VCARD7816_STATUS_SUCCESS) {
+        qemu_free(new_apdu);
+        return NULL;
+    }
+    *status = vcard_apdu_set_length(new_apdu);
+    if (*status != VCARD7816_STATUS_SUCCESS) {
+        qemu_free(new_apdu);
+        new_apdu = NULL;
+    }
+    return new_apdu;
+}
+
+void
+vcard_apdu_delete(VCardAPDU *apdu)
+{
+    if (apdu == NULL) {
+        return;
+    }
+    if (apdu->a_data) {
+        qemu_free(apdu->a_data);
+    }
+    qemu_free(apdu);
+}
+
+
+/*
+ * declare response buffers for all the 7816 defined error codes
+ */
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_SUCCESS)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_RET_CORUPT)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_CHANGE)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_FILE_FILLED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR_CHANGE)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_LENGTH)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(
+                    VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_INVALID)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_NO_EF)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_PARAMETERS)
+VCARD_RESPONSE_NEW_STATIC_STATUS(
+                            VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_FILE_NOT_FOUND)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_P1_P2_INCORRECT)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_NOT_FOUND)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_INS_CODE_INVALID)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CLA_INVALID)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_GENERAL)
+
+/*
+ * return a single response code. This function cannot fail. It will always
+ * return a response.
+ */
+VCardResponse *
+vcard_make_response(vcard_7816_status_t status)
+{
+    VCardResponse *response = NULL;
+
+    switch (status) {
+    /* known 7816 response codes */
+    case VCARD7816_STATUS_SUCCESS:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_SUCCESS);
+    case VCARD7816_STATUS_WARNING:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_WARNING);
+    case VCARD7816_STATUS_WARNING_RET_CORUPT:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_WARNING_RET_CORUPT);
+    case VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE);
+    case VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED);
+    case VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID);
+    case VCARD7816_STATUS_WARNING_CHANGE:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_WARNING_CHANGE);
+    case VCARD7816_STATUS_WARNING_FILE_FILLED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_WARNING_FILE_FILLED);
+    case VCARD7816_STATUS_EXC_ERROR:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_EXC_ERROR);
+    case VCARD7816_STATUS_EXC_ERROR_CHANGE:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_EXC_ERROR_CHANGE);
+    case VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
+    case VCARD7816_STATUS_ERROR_WRONG_LENGTH:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_WRONG_LENGTH);
+    case VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED);
+    case VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED);
+    case VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED);
+    case VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+    case VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE);
+    case VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED);
+    case VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED);
+    case VCARD7816_STATUS_ERROR_DATA_INVALID:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_DATA_INVALID);
+    case VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED);
+    case VCARD7816_STATUS_ERROR_DATA_NO_EF:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_DATA_NO_EF);
+    case VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING);
+    case VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT);
+    case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_WRONG_PARAMETERS);
+    case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA);
+    case VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED);
+    case VCARD7816_STATUS_ERROR_FILE_NOT_FOUND:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_FILE_NOT_FOUND);
+    case VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND);
+    case VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE);
+    case VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT);
+    case VCARD7816_STATUS_ERROR_P1_P2_INCORRECT:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_P1_P2_INCORRECT);
+    case VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT);
+    case VCARD7816_STATUS_ERROR_DATA_NOT_FOUND:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_DATA_NOT_FOUND);
+    case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2);
+    case VCARD7816_STATUS_ERROR_INS_CODE_INVALID:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_INS_CODE_INVALID);
+    case VCARD7816_STATUS_ERROR_CLA_INVALID:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_CLA_INVALID);
+    case VCARD7816_STATUS_ERROR_GENERAL:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_GENERAL);
+    default:
+        /* we don't know this status code, create a response buffer to
+         * hold it */
+        response = vcard_response_new_status(status);
+        if (response == NULL) {
+            /* couldn't allocate the buffer, return memmory error */
+            return VCARD_RESPONSE_GET_STATIC(
+                        VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
+        }
+    }
+    assert(response);
+    return response;
+}
+
+/*
+ * Add File card support here if you need it.
+ */
+static VCardStatus
+vcard7816_file_system_process_apdu(VCard *card, VCardAPDU *apdu,
+                                   VCardResponse **response)
+{
+    /* TODO: if we want to support a virtual file system card, we do it here.
+     * It would probably be a pkcs #15 card type */
+    *response = vcard_make_response(
+                    VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+    return VCARD_DONE;
+}
+
+/*
+ * VM card (including java cards)
+ */
+static VCardStatus
+vcard7816_vm_process_apdu(VCard *card, VCardAPDU *apdu,
+                          VCardResponse **response)
+{
+    int bytes_to_copy, next_byte_count, count;
+    VCardApplet *current_applet;
+    VCardBufferResponse *buffer_response;
+    vcard_7816_status_t status;
+
+    /* parse the class first */
+    if (apdu->a_gen_type !=  VCARD_7816_ISO) {
+        *response = vcard_make_response(
+                        VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+        return VCARD_DONE;
+    }
+
+    /* use a switch so that if we need to support secure channel stuff later,
+     * we know where to put it */
+    switch (apdu->a_secure_messaging) {
+    case 0x0: /* no SM */
+        break;
+    case 0x4: /* proprietary SM */
+    case 0x8: /* header not authenticated */
+    case 0xc: /* header authenticated */
+    default:
+        /* for now, don't try to support secure channel stuff in the
+         * virtual card. */
+        *response = vcard_make_response(
+                        VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED);
+        return VCARD_DONE;
+    }
+
+    /* now parse the instruction */
+    switch (apdu->a_ins) {
+    case  VCARD7816_INS_MANAGE_CHANNEL: /* secure channel op */
+    case  VCARD7816_INS_EXTERNAL_AUTHENTICATE: /* secure channel op */
+    case  VCARD7816_INS_GET_CHALLENGE: /* secure channel op */
+    case  VCARD7816_INS_INTERNAL_AUTHENTICATE: /* secure channel op */
+    case  VCARD7816_INS_ERASE_BINARY: /* applet control op */
+    case  VCARD7816_INS_READ_BINARY: /* applet control op */
+    case  VCARD7816_INS_WRITE_BINARY: /* applet control op */
+    case  VCARD7816_INS_UPDATE_BINARY: /* applet control op */
+    case  VCARD7816_INS_READ_RECORD: /* file op */
+    case  VCARD7816_INS_WRITE_RECORD: /* file op */
+    case  VCARD7816_INS_UPDATE_RECORD: /* file op */
+    case  VCARD7816_INS_APPEND_RECORD: /* file op */
+    case  VCARD7816_INS_ENVELOPE:
+    case  VCARD7816_INS_PUT_DATA:
+        *response = vcard_make_response(
+                            VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+        break;
+
+    case  VCARD7816_INS_SELECT_FILE:
+        if (apdu->a_p1 != 0x04) {
+            *response = vcard_make_response(
+                            VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED);
+            break;
+        }
+
+        /* side effect, deselect the current applet if no applet has been found
+         * */
+        current_applet = vcard_find_applet(card, apdu->a_body, apdu->a_Lc);
+        vcard_select_applet(card, apdu->a_channel, current_applet);
+        if (current_applet) {
+            unsigned char *aid;
+            int aid_len;
+            aid = vcard_applet_get_aid(current_applet, &aid_len);
+            *response = vcard_response_new(card, aid, aid_len, apdu->a_Le,
+                                          VCARD7816_STATUS_SUCCESS);
+        } else {
+            *response = vcard_make_response(
+                             VCARD7816_STATUS_ERROR_FILE_NOT_FOUND);
+        }
+        break;
+
+    case  VCARD7816_INS_VERIFY:
+        if ((apdu->a_p1 != 0x00) || (apdu->a_p2 != 0x00)) {
+            *response = vcard_make_response(
+                            VCARD7816_STATUS_ERROR_WRONG_PARAMETERS);
+        } else {
+            if (apdu->a_Lc == 0) {
+                /* handle pin count if possible */
+                count = vcard_emul_get_login_count(card);
+                if (count < 0) {
+                    *response = vcard_make_response(
+                                    VCARD7816_STATUS_ERROR_DATA_NOT_FOUND);
+                } else {
+                    if (count > 0xf) {
+                        count = 0xf;
+                    }
+                    *response = vcard_response_new_status_bytes(
+                                                VCARD7816_SW1_WARNING_CHANGE,
+                                                                0xc0 | count);
+                    if (*response == NULL) {
+                        *response = vcard_make_response(
+                                    VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
+                    }
+                }
+            } else {
+                    status = vcard_emul_login(card, apdu->a_body, apdu->a_Lc);
+                *response = vcard_make_response(status);
+            }
+        }
+        break;
+
+    case VCARD7816_INS_GET_RESPONSE:
+        buffer_response = vcard_get_buffer_response(card);
+        if (!buffer_response) {
+            *response = vcard_make_response(
+                            VCARD7816_STATUS_ERROR_DATA_NOT_FOUND);
+            /* handle error */
+            break;
+        }
+        bytes_to_copy = MIN(buffer_response->len, apdu->a_Le);
+        next_byte_count = MIN(256, buffer_response->len - bytes_to_copy);
+        *response = vcard_response_new_bytes(
+                        card, buffer_response->current, bytes_to_copy,
+                        apdu->a_Le,
+                        next_byte_count ?
+                        VCARD7816_SW1_RESPONSE_BYTES : VCARD7816_SW1_SUCCESS,
+                        next_byte_count);
+        buffer_response->current += bytes_to_copy;
+        buffer_response->len -= bytes_to_copy;
+        if (*response == NULL || (next_byte_count == 0)) {
+            vcard_set_buffer_response(card, NULL);
+            vcard_buffer_response_delete(buffer_response);
+        }
+        if (*response == NULL) {
+            *response =
+                vcard_make_response(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
+        }
+        break;
+
+    case VCARD7816_INS_GET_DATA:
+        *response =
+            vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+        break;
+
+    default:
+        *response =
+            vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+        break;
+    }
+
+    /* response should have been set somewhere */
+    assert(*response != NULL);
+    return VCARD_DONE;
+}
+
+
+/*
+ * APDU processing starts here. This routes the card processing stuff to the
+ * right location.
+ */
+VCardStatus
+vcard_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response)
+{
+    VCardStatus status;
+    VCardBufferResponse *buffer_response;
+
+    /* first handle any PTS commands, which aren't really APDU's */
+    if (apdu->a_type == VCARD_7816_PTS) {
+        /* the PTS responses aren't really responses either */
+        *response = vcard_response_new_data(apdu->a_data, apdu->a_len);
+        /* PTS responses have no status bytes */
+        (*response)->b_total_len = (*response)->b_len;
+        return VCARD_DONE;
+    }
+    buffer_response = vcard_get_buffer_response(card);
+    if (buffer_response && apdu->a_ins != VCARD7816_INS_GET_RESPONSE) {
+        /* clear out buffer_response, return an error */
+        vcard_set_buffer_response(card, NULL);
+        vcard_buffer_response_delete(buffer_response);
+        *response = vcard_make_response(VCARD7816_STATUS_EXC_ERROR);
+        return VCARD_DONE;
+    }
+
+    status = vcard_process_applet_apdu(card, apdu, response);
+    if (status != VCARD_NEXT) {
+        return status;
+    }
+    switch (vcard_get_type(card)) {
+    case VCARD_FILE_SYSTEM:
+        return vcard7816_file_system_process_apdu(card, apdu, response);
+    case VCARD_VM:
+        return vcard7816_vm_process_apdu(card, apdu, response);
+    case VCARD_DIRECT:
+        /* if we are type direct, then the applet should handle everything */
+        assert("VCARD_DIRECT: applet failure");
+        break;
+    }
+    *response =
+        vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+    return VCARD_DONE;
+}
diff --git a/libcacard/card_7816.h b/libcacard/card_7816.h
new file mode 100644
index 0000000..2bb2a0d
--- /dev/null
+++ b/libcacard/card_7816.h
@@ -0,0 +1,62 @@ 
+/*
+ * Implement the 7816 portion of the card spec
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+#ifndef CARD_7816_H
+#define CARD_7816_H  1
+
+#include "card_7816t.h"
+#include "vcardt.h"
+
+/*
+ * constructors for VCardResponse's
+ */
+/* response from a return buffer and a status */
+VCardResponse *vcard_response_new(VCard *card, unsigned char *buf, int len,
+                                  int Le, vcard_7816_status_t status);
+/* response from a return buffer and status bytes */
+VCardResponse *vcard_response_new_bytes(VCard *card, unsigned char *buf,
+                                        int len, int Le,
+                                        unsigned char sw1, unsigned char sw2);
+/* response from just status bytes */
+VCardResponse *vcard_response_new_status_bytes(unsigned char sw1,
+                                               unsigned char sw2);
+/* response from just status: NOTE this cannot fail, it will alwyas return a
+ * valid response, if it can't allocate memory, the response will be
+ * VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE */
+VCardResponse *vcard_make_response(vcard_7816_status_t status);
+
+/* create a raw response (status has already been encoded */
+VCardResponse *vcard_response_new_data(unsigned char *buf, int len);
+
+
+
+
+/*
+ * destructor for VCardResponse.
+ *  Can be called with a NULL response
+ */
+void vcard_response_delete(VCardResponse *response);
+
+/*
+ * constructor for VCardAPDU
+ */
+VCardAPDU *vcard_apdu_new(unsigned char *raw_apdu, int len,
+                          unsigned short *status);
+
+/*
+ * destructor for VCardAPDU
+ *  Can be called with a NULL apdu
+ */
+void vcard_apdu_delete(VCardAPDU *apdu);
+
+/*
+ * APDU processing starts here. This routes the card processing stuff to the
+ * right location. Always returns a valid response.
+ */
+VCardStatus vcard_process_apdu(VCard *card, VCardAPDU *apdu,
+                               VCardResponse **response);
+
+#endif
diff --git a/libcacard/card_7816t.h b/libcacard/card_7816t.h
new file mode 100644
index 0000000..9333285
--- /dev/null
+++ b/libcacard/card_7816t.h
@@ -0,0 +1,165 @@ 
+/*
+ * Implement the 7816 portion of the card spec
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+#ifndef CARD_7816T_H
+#define CARD_7816T_H 1
+
+typedef unsigned short vcard_7816_status_t;
+
+struct VCardResponseStruct {
+    unsigned char *b_data;
+    vcard_7816_status_t b_status;
+    unsigned char b_sw1;
+    unsigned char b_sw2;
+    int b_len;
+    int b_total_len;
+    enum VCardResponseBufferType {
+        VCARD_MALLOC,
+        VCARD_MALLOC_DATA,
+        VCARD_MALLOC_STRUCT,
+        VCARD_STATIC
+    } b_type;
+};
+
+#define VCARD_RESPONSE_NEW_STATIC_STATUS(stat) \
+static const VCardResponse VCardResponse##stat = \
+        {(unsigned char *)&VCardResponse##stat.b_sw1, (stat), ((stat) >> 8), \
+         ((stat) & 0xff), 0, 2, VCARD_STATIC};
+
+#define VCARD_RESPONSE_NEW_STATIC_STATUS_BYTES(sw1, sw2) \
+static const VCardResponse VCARDResponse##sw1 = \
+        {(unsigned char *)&VCardResponse##name.b_sw1, ((sw1) << 8 | (sw2)), \
+         (sw1), (sw2), 0, 2, VCARD_STATIC};
+
+/* cast away the const, callers need may need to 'free' the
+ * result, and const implies that they don't */
+#define VCARD_RESPONSE_GET_STATIC(name) \
+        ((VCardResponse *)(&VCardResponse##name))
+
+typedef enum {
+    VCARD_7816_ISO,
+    VCARD_7816_RFU,
+    VCARD_7816_PTS,
+    VCARD_7816_PROPIETARY
+} VCardAPDUType;
+
+
+/*
+ * 7816 header. All APDU's have this header.
+ * They must be laid out in this order.
+ */
+struct VCardAPDUHeader {
+    unsigned char ah_cla;
+    unsigned char ah_ins;
+    unsigned char ah_p1;
+    unsigned char ah_p2;
+    unsigned char ah_Le;
+    unsigned char ah_body[1]; /* indefinate length */
+};
+
+/*
+ * 7816 APDU structure. The raw bytes are stored in the union and can be
+ * accessed directly through u.data (which is aliased as a_data).
+ *
+ * Names of the fields match the 7816 documentation.
+ */
+struct VCardAPDUStruct {
+    int a_len;                /* length of the whole buffer, including header */
+    int a_Lc;                 /* 7816 Lc (parameter length) value */
+    int a_Le;                 /* 7816 Le (expected result length) value */
+    unsigned char *a_body;    /* pointer to the parameter */
+    int a_channel;            /* decoded channel */
+    int a_secure_messaging;   /* decoded secure messaging type */
+    int a_type;               /* decoded type from cla (top nibble of class) */
+    VCardAPDUType a_gen_type; /* generic type (7816, PROPRIETARY, RFU, etc) */
+    union {
+        struct VCardAPDUHeader *header;
+        unsigned char   *data;
+    } u;
+/* give the subfields a unified look */
+#define a_header u.header
+#define a_data u.data
+#define a_cla a_header->ah_cla /* class */
+#define a_ins a_header->ah_ins /* instruction */
+#define a_p1 a_header->ah_p1   /* parameter 1 */
+#define a_p2 a_header->ah_p2   /* parameter 2 */
+};
+
+/* 7816 status codes */
+#define VCARD7816_STATUS_SUCCESS                              0x9000
+#define VCARD7816_STATUS_WARNING                              0x6200
+#define VCARD7816_STATUS_WARNING_RET_CORUPT                   0x6281
+#define VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE            0x6282
+#define VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED        0x6283
+#define VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID           0x6284
+#define VCARD7816_STATUS_WARNING_CHANGE                       0x6300
+#define VCARD7816_STATUS_WARNING_FILE_FILLED                  0x6381
+#define VCARD7816_STATUS_EXC_ERROR                            0x6400
+#define VCARD7816_STATUS_EXC_ERROR_CHANGE                     0x6500
+#define VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE             0x6581
+#define VCARD7816_STATUS_ERROR_WRONG_LENGTH                   0x6700
+#define VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED              0x6800
+#define VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED          0x6881
+#define VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED           0x6882
+#define VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED          0x6900
+#define VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE 0x6981
+#define VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED         0x6982
+#define VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED         0x6983
+#define VCARD7816_STATUS_ERROR_DATA_INVALID                   0x6984
+#define VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED        0x6985
+#define VCARD7816_STATUS_ERROR_DATA_NO_EF                     0x6986
+#define VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING              0x6987
+#define VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT            0x6988
+#define VCARD7816_STATUS_ERROR_WRONG_PARAMETERS               0x6a00
+#define VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA       0x6a80
+#define VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED         0x6a81
+#define VCARD7816_STATUS_ERROR_FILE_NOT_FOUND                 0x6a82
+#define VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND               0x6a83
+#define VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE              0x6a84
+#define VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT            0x6a85
+#define VCARD7816_STATUS_ERROR_P1_P2_INCORRECT                0x6a86
+#define VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT          0x6a87
+#define VCARD7816_STATUS_ERROR_DATA_NOT_FOUND                 0x6a88
+#define VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2             0x6b00
+#define VCARD7816_STATUS_ERROR_INS_CODE_INVALID               0x6d00
+#define VCARD7816_STATUS_ERROR_CLA_INVALID                    0x6e00
+#define VCARD7816_STATUS_ERROR_GENERAL                        0x6f00
+/* 7816 sw1 codes */
+#define VCARD7816_SW1_SUCCESS               0x90
+#define VCARD7816_SW1_RESPONSE_BYTES        0x61
+#define VCARD7816_SW1_WARNING               0x62
+#define VCARD7816_SW1_WARNING_CHANGE        0x63
+#define VCARD7816_SW1_EXC_ERROR             0x64
+#define VCARD7816_SW1_EXC_ERROR_CHANGE      0x65
+#define VCARD7816_SW1_ERROR_WRONG_LENGTH    0x67
+#define VCARD7816_SW1_CLA_ERROR             0x68
+#define VCARD7816_SW1_COMMAND_ERROR         0x69
+#define VCARD7816_SW1_P1_P2_ERROR           0x6a
+#define VCARD7816_SW1_LE_ERROR              0x6c
+#define VCARD7816_SW1_INS_ERROR             0x6d
+#define VCARD7816_SW1_CLA_NOT_SUPPORTED     0x6e
+
+/* 7816 Instructions */
+#define VCARD7816_INS_MANAGE_CHANNEL        0x70
+#define VCARD7816_INS_EXTERNAL_AUTHENTICATE 0x82
+#define VCARD7816_INS_GET_CHALLENGE         0x84
+#define VCARD7816_INS_INTERNAL_AUTHENTICATE 0x88
+#define VCARD7816_INS_ERASE_BINARY          0x0e
+#define VCARD7816_INS_READ_BINARY           0xb0
+#define VCARD7816_INS_WRITE_BINARY          0xd0
+#define VCARD7816_INS_UPDATE_BINARY         0xd6
+#define VCARD7816_INS_READ_RECORD           0xb2
+#define VCARD7816_INS_WRITE_RECORD          0xd2
+#define VCARD7816_INS_UPDATE_RECORD         0xdc
+#define VCARD7816_INS_APPEND_RECORD         0xe2
+#define VCARD7816_INS_ENVELOPE              0xc2
+#define VCARD7816_INS_PUT_DATA              0xda
+#define VCARD7816_INS_GET_DATA              0xca
+#define VCARD7816_INS_SELECT_FILE           0xa4
+#define VCARD7816_INS_VERIFY                0x20
+#define VCARD7816_INS_GET_RESPONSE          0xc0
+
+#endif
diff --git a/libcacard/event.c b/libcacard/event.c
new file mode 100644
index 0000000..12722cc
--- /dev/null
+++ b/libcacard/event.c
@@ -0,0 +1,108 @@ 
+/*
+ * event queue implementation.
+ *
+ * This code is licensed under the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#include <stdlib.h>
+
+#include "qemu-thread.h"
+#include "qemu-common.h"
+
+#include "vcard.h"
+#include "vreader.h"
+#include "vevent.h"
+
+VEvent *
+vevent_new(VEventType type, VReader *reader, VCard *card)
+{
+    VEvent *new_vevent;
+
+    new_vevent = (VEvent *)qemu_malloc(sizeof(VEvent));
+    new_vevent->next = NULL;
+    new_vevent->type = type;
+    new_vevent->reader = vreader_reference(reader);
+    new_vevent->card = vcard_reference(card);
+
+    return new_vevent;
+}
+
+void
+vevent_delete(VEvent *vevent)
+{
+    if (vevent == NULL) {
+        return;
+    }
+    vreader_free(vevent->reader);
+    vcard_free(vevent->card);
+    qemu_free(vevent);
+}
+
+/*
+ * VEvent queue management
+ */
+
+static VEvent *vevent_queue_head;
+static VEvent *vevent_queue_tail;
+static QemuMutex vevent_queue_lock;
+static QemuCond vevent_queue_condition;
+
+void vevent_queue_init(void)
+{
+    qemu_mutex_init(&vevent_queue_lock);
+    qemu_cond_init(&vevent_queue_condition);
+    vevent_queue_head = vevent_queue_tail = NULL;
+}
+
+void
+vevent_queue_vevent(VEvent *vevent)
+{
+    vevent->next = NULL;
+    qemu_mutex_lock(&vevent_queue_lock);
+    if (vevent_queue_head) {
+        assert(vevent_queue_tail);
+        vevent_queue_tail->next = vevent;
+    } else {
+        vevent_queue_head = vevent;
+    }
+    vevent_queue_tail = vevent;
+    qemu_cond_signal(&vevent_queue_condition);
+    qemu_mutex_unlock(&vevent_queue_lock);
+}
+
+/* must have lock */
+static VEvent *
+vevent_dequeue_vevent(void)
+{
+    VEvent *vevent = NULL;
+    if (vevent_queue_head) {
+        vevent = vevent_queue_head;
+        vevent_queue_head = vevent->next;
+        vevent->next = NULL;
+    }
+    return vevent;
+}
+
+VEvent *vevent_wait_next_vevent(void)
+{
+    VEvent *vevent;
+
+    qemu_mutex_lock(&vevent_queue_lock);
+    while ((vevent = vevent_dequeue_vevent()) == NULL) {
+        qemu_cond_wait(&vevent_queue_condition, &vevent_queue_lock);
+    }
+    qemu_mutex_unlock(&vevent_queue_lock);
+    return vevent;
+}
+
+VEvent *vevent_get_next_vevent(void)
+{
+    VEvent *vevent;
+
+    qemu_mutex_lock(&vevent_queue_lock);
+    vevent = vevent_dequeue_vevent();
+    qemu_mutex_unlock(&vevent_queue_lock);
+    return vevent;
+}
+
diff --git a/libcacard/eventt.h b/libcacard/eventt.h
new file mode 100644
index 0000000..0dc7bd4
--- /dev/null
+++ b/libcacard/eventt.h
@@ -0,0 +1,29 @@ 
+/*
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#ifndef EVENTT_H
+#define EVENTT_H 1
+#include "vreadert.h"
+#include "vcardt.h"
+
+typedef struct VEventStruct VEvent;
+
+typedef enum {
+    VEVENT_READER_INSERT,
+    VEVENT_READER_REMOVE,
+    VEVENT_CARD_INSERT,
+    VEVENT_CARD_REMOVE,
+    VEVENT_LAST,
+} VEventType;
+
+struct VEventStruct {
+    VEvent *next;
+    VEventType type;
+    VReader *reader;
+    VCard *card;
+};
+#endif
+
+
diff --git a/libcacard/link_test.c b/libcacard/link_test.c
new file mode 100644
index 0000000..ca34e4f
--- /dev/null
+++ b/libcacard/link_test.c
@@ -0,0 +1,21 @@ 
+/*
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+#include <stdio.h>
+#include "vcard.h"
+
+VCardStatus cac_card_init(const char *flags, VCard *card,
+                const unsigned char *cert[],
+                int cert_len[], VCardKey *key[] /* adopt the keys*/,
+                int cert_count);
+/*
+ * this will crash... just test the linkage right now
+ */
+
+main(int argc, char **argv)
+{
+    VCard *card; /* no constructor yet */
+    cac_card_init("", card, NULL, 0, NULL, 0);
+}
+
diff --git a/libcacard/vcard.c b/libcacard/vcard.c
new file mode 100644
index 0000000..d7828a2
--- /dev/null
+++ b/libcacard/vcard.c
@@ -0,0 +1,341 @@ 
+/*
+ * implement the Java card standard.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+#include <stdlib.h>
+#include <string.h>
+
+#include "qemu-common.h"
+
+#include "vcard.h"
+#include "vcard_emul.h"
+#include "card_7816t.h"
+
+struct VCardAppletStruct {
+    VCardApplet   *next;
+    VCardProcessAPDU process_apdu;
+    VCardResetApplet reset_applet;
+    unsigned char *aid;
+    int aid_len;
+    void *applet_private;
+    VCardAppletPrivateFree applet_private_free;
+};
+
+struct VCardStruct {
+    int reference_count;
+    VCardApplet *applet_list;
+    VCardApplet *current_applet[MAX_CHANNEL];
+    VCardBufferResponse *vcard_buffer_response;
+    VCardType type;
+    VCardEmul *vcard_private;
+    VCardEmulFree vcard_private_free;
+    VCardGetAtr vcard_get_atr;
+};
+
+VCardBufferResponse *
+vcard_buffer_response_new(unsigned char *buffer, int size)
+{
+    VCardBufferResponse *new_buffer;
+
+    new_buffer = (VCardBufferResponse *)qemu_malloc(sizeof(VCardBufferResponse));
+    new_buffer->buffer = (unsigned char *)qemu_malloc(size);
+    memcpy(new_buffer->buffer, buffer, size);
+    new_buffer->buffer_len = size;
+    new_buffer->current = new_buffer->buffer;
+    new_buffer->len = size;
+    return new_buffer;
+}
+
+void
+vcard_buffer_response_delete(VCardBufferResponse *buffer_response)
+{
+    if (buffer_response == NULL) {
+        return;
+    }
+    if (buffer_response->buffer) {
+        qemu_free(buffer_response->buffer);
+    }
+    qemu_free(buffer_response);
+}
+
+
+/*
+ * clean up state after a reset
+ */
+void
+vcard_reset(VCard *card, VCardPower power)
+{
+    int i;
+    VCardApplet *applet = NULL;
+
+    if (card->type ==  VCARD_DIRECT) {
+        /* select the last applet */
+        VCardApplet *current_applet = NULL;
+        for (current_applet = card->applet_list; current_applet;
+                                       current_applet = current_applet->next) {
+            applet = current_applet;
+        }
+    }
+    for (i = 0; i < MAX_CHANNEL; i++) {
+        card->current_applet[i] = applet;
+    }
+    if (card->vcard_buffer_response) {
+        vcard_buffer_response_delete(card->vcard_buffer_response);
+        card->vcard_buffer_response = NULL;
+    }
+    vcard_emul_reset(card, power);
+    if (applet) {
+        applet->reset_applet(card, 0);
+    }
+}
+
+/* applet utilities */
+
+/*
+ * applet utilities
+ */
+/* constructor */
+VCardApplet *
+vcard_new_applet(VCardProcessAPDU applet_process_function,
+                 VCardResetApplet applet_reset_function,
+                 unsigned char *aid, int aid_len)
+{
+    VCardApplet *applet;
+
+    applet = (VCardApplet *)qemu_malloc(sizeof(VCardApplet));
+    applet->next = NULL;
+    applet->applet_private = NULL;
+    applet->applet_private_free = NULL;
+    applet->process_apdu = applet_process_function;
+    applet->reset_applet = applet_reset_function;
+
+    applet->aid = qemu_malloc(aid_len);
+    memcpy(applet->aid, aid, aid_len);
+    applet->aid_len = aid_len;
+    return applet;
+}
+
+/* destructor */
+void
+vcard_delete_applet(VCardApplet *applet)
+{
+    if (applet == NULL) {
+        return;
+    }
+    if (applet->applet_private_free) {
+        applet->applet_private_free(applet->applet_private);
+        applet->applet_private = NULL;
+    }
+    if (applet->aid) {
+        qemu_free(applet->aid);
+        applet->aid = NULL;
+    }
+    qemu_free(applet);
+}
+
+/* accessor */
+void
+vcard_set_applet_private(VCardApplet *applet, VCardAppletPrivate *private,
+                         VCardAppletPrivateFree private_free)
+{
+    if (applet->applet_private_free) {
+        applet->applet_private_free(applet->applet_private);
+    }
+    applet->applet_private = private;
+    applet->applet_private_free = private_free;
+}
+
+VCard *
+vcard_new(VCardEmul *private, VCardEmulFree private_free)
+{
+    VCard *new_card;
+    int i;
+
+    new_card = (VCard *)qemu_malloc(sizeof(VCard));
+    new_card->applet_list = NULL;
+    for (i = 0; i < MAX_CHANNEL; i++) {
+        new_card->current_applet[i] = NULL;
+    }
+    new_card->vcard_buffer_response = NULL;
+    new_card->type = VCARD_VM;
+    new_card->vcard_private = private;
+    new_card->vcard_private_free = private_free;
+    new_card->vcard_get_atr = NULL;
+    new_card->reference_count = 1;
+    return new_card;
+}
+
+VCard *
+vcard_reference(VCard *vcard)
+{
+    if (vcard == NULL) {
+        return NULL;
+    }
+    vcard->reference_count++;
+    return vcard;
+}
+
+void
+vcard_free(VCard *vcard)
+{
+    VCardApplet *current_applet = NULL;
+    VCardApplet *next_applet = NULL;
+
+    if (vcard == NULL) {
+        return;
+    }
+    vcard->reference_count--;
+    if (vcard->reference_count != 0) {
+        return;
+    }
+    if (vcard->vcard_private_free) {
+        (*vcard->vcard_private_free)(vcard->vcard_private);
+        vcard->vcard_private_free = 0;
+        vcard->vcard_private = 0;
+    }
+    for (current_applet = vcard->applet_list; current_applet;
+                                        current_applet = next_applet) {
+        next_applet = current_applet->next;
+        vcard_delete_applet(current_applet);
+    }
+    vcard_buffer_response_delete(vcard->vcard_buffer_response);
+    qemu_free(vcard);
+    return;
+}
+
+void
+vcard_get_atr(VCard *vcard, unsigned char *atr, int *atr_len)
+{
+    if (vcard->vcard_get_atr) {
+        (*vcard->vcard_get_atr)(vcard, atr, atr_len);
+        return;
+    }
+    vcard_emul_get_atr(vcard, atr, atr_len);
+}
+
+void
+vcard_set_atr_func(VCard *card, VCardGetAtr vcard_get_atr)
+{
+    card->vcard_get_atr = vcard_get_atr;
+}
+
+
+VCardStatus
+vcard_add_applet(VCard *card, VCardApplet *applet)
+{
+    applet->next = card->applet_list;
+    card->applet_list = applet;
+    /* if our card-type is direct, always call the applet */
+    if (card->type ==  VCARD_DIRECT) {
+        int i;
+
+        for (i = 0; i < MAX_CHANNEL; i++) {
+            card->current_applet[i] = applet;
+        }
+    }
+    return VCARD_DONE;
+}
+
+/*
+ * manage applets
+ */
+VCardApplet *
+vcard_find_applet(VCard *card, unsigned char *aid, int aid_len)
+{
+    VCardApplet *current_applet;
+
+    for (current_applet = card->applet_list; current_applet;
+                                        current_applet = current_applet->next) {
+        if (current_applet->aid_len != aid_len) {
+            continue;
+        }
+        if (memcmp(current_applet->aid, aid, aid_len) == 0) {
+            break;
+        }
+    }
+    return current_applet;
+}
+
+unsigned char *
+vcard_applet_get_aid(VCardApplet *applet, int *aid_len)
+{
+    if (applet == NULL) {
+        return NULL;
+    }
+    *aid_len = applet->aid_len;
+    return applet->aid;
+}
+
+
+void
+vcard_select_applet(VCard *card, int channel, VCardApplet *applet)
+{
+    assert(channel < MAX_CHANNEL);
+    card->current_applet[channel] = applet;
+    /* reset the applet */
+    if (applet && applet->reset_applet) {
+        applet->reset_applet(card, channel);
+    }
+}
+
+VCardAppletPrivate *
+vcard_get_current_applet_private(VCard *card, int channel)
+{
+    VCardApplet *applet = card->current_applet[channel];
+
+    if (applet == NULL) {
+        return NULL;
+    }
+    return applet->applet_private;
+}
+
+VCardStatus
+vcard_process_applet_apdu(VCard *card, VCardAPDU *apdu,
+                          VCardResponse **response)
+{
+    if (card->current_applet[apdu->a_channel]) {
+        return card->current_applet[apdu->a_channel]->process_apdu(
+                                                        card, apdu, response);
+    }
+    return VCARD_NEXT;
+}
+
+/*
+ * Accessor functions
+ */
+/* accessor functions for the response buffer */
+VCardBufferResponse *
+vcard_get_buffer_response(VCard *card)
+{
+    return card->vcard_buffer_response;
+}
+
+void
+vcard_set_buffer_response(VCard *card, VCardBufferResponse *buffer)
+{
+    card->vcard_buffer_response = buffer;
+}
+
+
+/* accessor functions for the type */
+VCardType
+vcard_get_type(VCard *card)
+{
+    return card->type;
+}
+
+void
+vcard_set_type(VCard *card, VCardType type)
+{
+    card->type = type;
+}
+
+/* accessor for private data */
+VCardEmul *
+vcard_get_private(VCard *vcard)
+{
+    return vcard->vcard_private;
+}
+
diff --git a/libcacard/vcard.h b/libcacard/vcard.h
new file mode 100644
index 0000000..47dc703
--- /dev/null
+++ b/libcacard/vcard.h
@@ -0,0 +1,86 @@ 
+/*
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+#ifndef VCARD_H
+#define VCARD_H 1
+
+#include "vcardt.h"
+
+/*
+ * response buffer constructors and destructors.
+ *
+ * response buffers are used when we need to return more data than will fit in
+ * a normal APDU response (nominally 254 bytes).
+ */
+VCardBufferResponse *vcard_buffer_response_new(unsigned char *buffer, int size);
+void vcard_buffer_response_delete(VCardBufferResponse *buffer_response);
+
+
+/*
+ * clean up state on reset
+ */
+void vcard_reset(VCard *card, VCardPower power);
+
+/*
+ * applet utilities
+ */
+/*
+ * Constructor for a VCardApplet
+ */
+VCardApplet *vcard_new_applet(VCardProcessAPDU applet_process_function,
+                              VCardResetApplet applet_reset_function,
+                              unsigned char *aid, int aid_len);
+
+/*
+ * destructor for a VCardApplet
+ *  Can be called with a NULL applet
+ */
+void vcard_delete_applet(VCardApplet *applet);
+
+/* accessor - set the card type specific private data */
+void vcard_set_applet_private(VCardApplet *applet, VCardAppletPrivate *_private,
+                              VCardAppletPrivateFree private_free);
+
+/* set type of vcard */
+void vcard_set_type(VCard *card, VCardType type);
+
+/*
+ * utilities interacting with the current applet
+ */
+/* add a new applet to a card */
+VCardStatus vcard_add_applet(VCard *card, VCardApplet *applet);
+/* find the applet on the card with the given aid */
+VCardApplet *vcard_find_applet(VCard *card, unsigned char *aid, int aid_len);
+/* set the following applet to be current on the given channel */
+void vcard_select_applet(VCard *card, int channel, VCardApplet *applet);
+/* get the card type specific private data on the given channel */
+VCardAppletPrivate *vcard_get_current_applet_private(VCard *card, int channel);
+/* fetch the applet's id */
+unsigned char *vcard_applet_get_aid(VCardApplet *applet, int *aid_len);
+
+/* process the apdu for the current selected applet/file */
+VCardStatus vcard_process_applet_apdu(VCard *card, VCardAPDU *apdu,
+                                      VCardResponse **response);
+/*
+ * VCard utilities
+ */
+/* constructor */
+VCard *vcard_new(VCardEmul *_private, VCardEmulFree private_free);
+/* get a reference */
+VCard *vcard_reference(VCard *);
+/* destructor (reference counted) */
+void vcard_free(VCard *);
+/* get the atr from the card */
+void vcard_get_atr(VCard *card, unsigned char *atr, int *atr_len);
+void vcard_set_atr_func(VCard *card, VCardGetAtr vcard_get_atr);
+
+/* accessor functions for the response buffer */
+VCardBufferResponse *vcard_get_buffer_response(VCard *card);
+void vcard_set_buffer_response(VCard *card, VCardBufferResponse *buffer);
+/* accessor functions for the type */
+VCardType vcard_get_type(VCard *card);
+/* get the private data */
+VCardEmul *vcard_get_private(VCard *card);
+
+#endif
diff --git a/libcacard/vcard_emul.h b/libcacard/vcard_emul.h
new file mode 100644
index 0000000..963563f
--- /dev/null
+++ b/libcacard/vcard_emul.h
@@ -0,0 +1,65 @@ 
+/*
+ * This is the actual card emulator.
+ *
+ * These functions can be implemented in different ways on different platforms
+ * using the underlying system primitives. For Linux it uses NSS, though direct
+ * to PKCS #11, openssl+pkcs11, or even gnu crypto libraries+pkcs #11 could be
+ * used. On Windows CAPI could be used.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#ifndef VCARD_EMUL_H
+#define VCARD_EMUL_H 1
+
+#include "card_7816t.h"
+#include "vcard.h"
+#include "vcard_emul_type.h"
+
+/*
+ * types
+ */
+typedef enum {
+    VCARD_EMUL_OK = 0,
+    VCARD_EMUL_FAIL,
+    /* return values by vcard_emul_init */
+    VCARD_EMUL_INIT_ALREADY_INITED,
+} VCardEmulError;
+
+/* options are emul specific. call card_emul_parse_args to change a string
+ * To an options struct */
+typedef struct VCardEmulOptionsStruct VCardEmulOptions;
+
+/*
+ * Login functions
+ */
+/* return the number of login attempts still possible on the card. if unknown,
+ * return -1 */
+int vcard_emul_get_login_count(VCard *card);
+/* login into the card, return the 7816 status word (sw2 || sw1) */
+vcard_7816_status_t vcard_emul_login(VCard *card, unsigned char *pin,
+                                     int pin_len);
+
+/*
+ * key functions
+ */
+/* delete a key */
+void vcard_emul_delete_key(VCardKey *key);
+/* RSA sign/decrypt with the key, signature happens 'in place' */
+vcard_7816_status_t vcard_emul_rsa_op(VCard *card, VCardKey *key,
+                                  unsigned char *buffer, int buffer_size);
+
+void vcard_emul_reset(VCard *card, VCardPower power);
+void vcard_emul_get_atr(VCard *card, unsigned char *atr, int *atr_len);
+
+/* Re-insert of a card that has been removed by force removal */
+VCardEmulError vcard_emul_force_card_insert(VReader *vreader);
+/* Force a card removal even if the card is not physically removed */
+VCardEmulError vcard_emul_force_card_remove(VReader *vreader);
+
+VCardEmulOptions *vcard_emul_options(const char *args);
+VCardEmulError vcard_emul_init(const VCardEmulOptions *options);
+void vcard_emul_replay_insertion_events(void);
+void vcard_emul_usage(void);
+#endif
diff --git a/libcacard/vcard_emul_nss.c b/libcacard/vcard_emul_nss.c
new file mode 100644
index 0000000..d3ab7ea
--- /dev/null
+++ b/libcacard/vcard_emul_nss.c
@@ -0,0 +1,1159 @@ 
+/*
+ * This is the actual card emulator.
+ *
+ * These functions can be implemented in different ways on different platforms
+ * using the underlying system primitives. For Linux it uses NSS, though direct
+ * to PKCS #11, openssl+pkcs11, or even gnu crypto libraries+pkcs #11 could be
+ * used. On Windows CAPI could be used.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+/*
+ * system headers
+ */
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * NSS headers
+ */
+#include <nss.h>
+#include <pk11pub.h>
+#include <cert.h>
+#include <key.h>
+#include <secmod.h>
+#include <prthread.h>
+#include <secerr.h>
+
+#include "qemu-common.h"
+
+#include "vcard.h"
+#include "card_7816t.h"
+#include "vcard_emul.h"
+#include "vreader.h"
+#include "vevent.h"
+
+struct VCardKeyStruct {
+    CERTCertificate *cert;
+    PK11SlotInfo *slot;
+    SECKEYPrivateKey *key;
+};
+
+
+typedef struct VirtualReaderOptionsStruct VirtualReaderOptions;
+
+struct VReaderEmulStruct {
+    PK11SlotInfo *slot;
+    VCardEmulType default_type;
+    char *type_params;
+    PRBool present;
+    int     series;
+    VCard *saved_vcard;
+};
+
+/*
+ *  NSS Specific options
+ */
+struct VirtualReaderOptionsStruct {
+    char *name;
+    char *vname;
+    VCardEmulType card_type;
+    char *type_params;
+    char **cert_name;
+    int cert_count;
+};
+
+struct VCardEmulOptionsStruct {
+    void *nss_db;
+    VirtualReaderOptions *vreader;
+    int vreader_count;
+    VCardEmulType hw_card_type;
+    const char *hw_type_params;
+    PRBool use_hw;
+};
+
+static int nss_emul_init;
+
+/* if we have more that just the slot, define
+ * VCardEmulStruct here */
+
+/*
+ * allocate the set of arrays for certs, cert_len, key
+ */
+static PRBool
+vcard_emul_alloc_arrays(unsigned char ***certsp, int **cert_lenp,
+                        VCardKey ***keysp, int cert_count)
+{
+    *certsp = NULL;
+    *cert_lenp = NULL;
+    *keysp = NULL;
+    *certsp = (unsigned char **)qemu_malloc(sizeof(unsigned char *)*cert_count);
+    *cert_lenp = (int *)qemu_malloc(sizeof(int)*cert_count);
+    *keysp = (VCardKey **)qemu_malloc(sizeof(VCardKey *)*cert_count);
+    return PR_TRUE;
+}
+
+/*
+ * Emulator specific card information
+ */
+typedef struct CardEmulCardStruct CardEmulPrivate;
+
+static VCardEmul *
+vcard_emul_new_card(PK11SlotInfo *slot)
+{
+    PK11_ReferenceSlot(slot);
+    /* currently we don't need anything other than the slot */
+    return (VCardEmul *)slot;
+}
+
+static void
+vcard_emul_delete_card(VCardEmul *vcard_emul)
+{
+    PK11SlotInfo *slot = (PK11SlotInfo *)vcard_emul;
+    if (slot == NULL) {
+        return;
+    }
+    PK11_FreeSlot(slot);
+}
+
+static PK11SlotInfo *
+vcard_emul_card_get_slot(VCard *card)
+{
+    /* note, the card is holding the reference, no need to get another one */
+    return (PK11SlotInfo *)vcard_get_private(card);
+}
+
+
+/*
+ * key functions
+ */
+/* private constructure */
+static VCardKey *
+vcard_emul_make_key(PK11SlotInfo *slot, CERTCertificate *cert)
+{
+    VCardKey *key;
+
+    key = (VCardKey *)qemu_malloc(sizeof(VCardKey));
+    key->slot = PK11_ReferenceSlot(slot);
+    key->cert = CERT_DupCertificate(cert);
+    /* NOTE: if we aren't logged into the token, this could return NULL */
+    /* NOTE: the cert is a temp cert, not necessarily the cert in the token,
+     * use the DER version of this function */
+    key->key = PK11_FindKeyByDERCert(slot, cert, NULL);
+    return key;
+}
+
+/* destructor */
+void
+vcard_emul_delete_key(VCardKey *key)
+{
+    if (!nss_emul_init || (key == NULL)) {
+        return;
+    }
+    if (key->key) {
+        SECKEY_DestroyPrivateKey(key->key);
+        key->key = NULL;
+    }
+    if (key->cert) {
+        CERT_DestroyCertificate(key->cert);
+    }
+    if (key->slot) {
+        PK11_FreeSlot(key->slot);
+    }
+    return;
+}
+
+/*
+ * grab the nss key from a VCardKey. If it doesn't exist, try to look it up
+ */
+static SECKEYPrivateKey *
+vcard_emul_get_nss_key(VCardKey *key)
+{
+    if (key->key) {
+        return key->key;
+    }
+    /* NOTE: if we aren't logged into the token, this could return NULL */
+    key->key = PK11_FindPrivateKeyFromCert(key->slot, key->cert, NULL);
+    return key->key;
+}
+
+/*
+ * Map NSS errors to 7816 errors
+ */
+static vcard_7816_status_t
+vcard_emul_map_error(int error)
+{
+    switch (error) {
+    case SEC_ERROR_TOKEN_NOT_LOGGED_IN:
+        return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED;
+    case SEC_ERROR_BAD_DATA:
+    case SEC_ERROR_OUTPUT_LEN:
+    case SEC_ERROR_INPUT_LEN:
+    case SEC_ERROR_INVALID_ARGS:
+    case SEC_ERROR_INVALID_ALGORITHM:
+    case SEC_ERROR_NO_KEY:
+    case SEC_ERROR_INVALID_KEY:
+    case SEC_ERROR_DECRYPTION_DISALLOWED:
+        return VCARD7816_STATUS_ERROR_DATA_INVALID;
+    case SEC_ERROR_NO_MEMORY:
+        return VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE;
+    }
+    return VCARD7816_STATUS_EXC_ERROR_CHANGE;
+}
+
+/* RSA sign/decrypt with the key, signature happens 'in place' */
+vcard_7816_status_t
+vcard_emul_rsa_op(VCard *card, VCardKey *key,
+                  unsigned char *buffer, int buffer_size)
+{
+    SECKEYPrivateKey *priv_key;
+    unsigned signature_len;
+    SECStatus rv;
+
+    if ((!nss_emul_init) || (key == NULL)) {
+        /* couldn't get the key, indicate that we aren't logged in */
+        return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED;
+    }
+    priv_key = vcard_emul_get_nss_key(key);
+
+    /*
+     * this is only true of the rsa signature
+     */
+    signature_len = PK11_SignatureLen(priv_key);
+    if (buffer_size != signature_len) {
+        return  VCARD7816_STATUS_ERROR_DATA_INVALID;
+    }
+    rv = PK11_PrivDecryptRaw(priv_key, buffer, &signature_len, signature_len,
+                             buffer, buffer_size);
+    if (rv != SECSuccess) {
+        return vcard_emul_map_error(PORT_GetError());
+    }
+    assert(buffer_size == signature_len);
+    return VCARD7816_STATUS_SUCCESS;
+}
+
+/*
+ * Login functions
+ */
+/* return the number of login attempts still possible on the card. if unknown,
+ * return -1 */
+int
+vcard_emul_get_login_count(VCard *card)
+{
+    return -1;
+}
+
+/* login into the card, return the 7816 status word (sw2 || sw1) */
+vcard_7816_status_t
+vcard_emul_login(VCard *card, unsigned char *pin, int pin_len)
+{
+    PK11SlotInfo *slot;
+    unsigned char *pin_string = NULL;
+    int i;
+    SECStatus rv;
+
+    if (!nss_emul_init) {
+        return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED;
+    }
+    slot = vcard_emul_card_get_slot(card);
+     /* We depend on the PKCS #11 module internal login state here because we
+      * create a separate process to handle each guest instance. If we needed
+      * to handle multiple guests from one process, then we would need to keep
+      * a lot of extra state in our card structure
+      * */
+    pin_string = qemu_malloc(pin_len+1);
+    memcpy(pin_string, pin, pin_len);
+    pin_string[pin_len] = 0;
+
+    /* handle CAC expanded pins correctly */
+    for (i = pin_len-1; i >= 0 && (pin_string[i] == 0xff); i--) {
+        pin_string[i] = 0;
+    }
+
+    rv = PK11_Authenticate(slot, PR_FALSE, pin_string);
+    memset(pin_string, 0, pin_len);  /* don't let the pin hang around in memory
+                                        to be snooped */
+    qemu_free(pin_string);
+    if (rv == SECSuccess) {
+        return VCARD7816_STATUS_SUCCESS;
+    }
+    /* map the error from port get error */
+    return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED;
+}
+
+void
+vcard_emul_reset(VCard *card, VCardPower power)
+{
+    PK11SlotInfo *slot;
+
+    if (!nss_emul_init) {
+        return;
+    }
+
+    /*
+     * if we reset the card (either power on or power off), we lose our login
+     * state
+     */
+    /* TODO: we may also need to send insertion/removal events? */
+    slot = vcard_emul_card_get_slot(card);
+    PK11_Logout(slot); /* NOTE: ignoring SECStatus return value */
+    return;
+}
+
+
+static VReader *
+vcard_emul_find_vreader_from_slot(PK11SlotInfo *slot)
+{
+    VReaderList *reader_list = vreader_get_reader_list();
+    VReaderListEntry *current_entry = NULL;
+
+    if (reader_list == NULL) {
+        return NULL;
+    }
+    for (current_entry = vreader_list_get_first(reader_list); current_entry;
+                        current_entry = vreader_list_get_next(current_entry)) {
+        VReader *reader = vreader_list_get_reader(current_entry);
+        VReaderEmul *reader_emul = vreader_get_private(reader);
+        if (reader_emul->slot == slot) {
+            return reader;
+        }
+        vreader_free(reader);
+    }
+
+    return NULL;
+}
+
+/*
+ * create a new reader emul
+ */
+static VReaderEmul *
+vreader_emul_new(PK11SlotInfo *slot, VCardEmulType type, const char *params)
+{
+    VReaderEmul *new_reader_emul;
+
+    new_reader_emul = (VReaderEmul *)qemu_malloc(sizeof(VReaderEmul));
+
+    new_reader_emul->slot = PK11_ReferenceSlot(slot);
+    new_reader_emul->default_type = type;
+    new_reader_emul->type_params = strdup(params);
+    new_reader_emul->present = PR_FALSE;
+    new_reader_emul->series = 0;
+    new_reader_emul->saved_vcard = NULL;
+    return new_reader_emul;
+}
+
+static void
+vreader_emul_delete(VReaderEmul *vreader_emul)
+{
+    if (vreader_emul == NULL) {
+        return;
+    }
+    if (vreader_emul->slot) {
+        PK11_FreeSlot(vreader_emul->slot);
+    }
+    if (vreader_emul->type_params) {
+        qemu_free(vreader_emul->type_params);
+    }
+    qemu_free(vreader_emul);
+}
+
+/*
+ *  TODO: move this to emulater non-specific file
+ */
+static VCardEmulType
+vcard_emul_get_type(VReader *vreader)
+{
+    VReaderEmul *vreader_emul;
+
+    vreader_emul = vreader_get_private(vreader);
+    if (vreader_emul && vreader_emul->default_type != VCARD_EMUL_NONE) {
+        return vreader_emul->default_type;
+    }
+
+    return vcard_emul_type_select(vreader);
+}
+/*
+ *  TODO: move this to emulater non-specific file
+ */
+static const char *
+vcard_emul_get_type_params(VReader *vreader)
+{
+    VReaderEmul *vreader_emul;
+
+    vreader_emul = vreader_get_private(vreader);
+    if (vreader_emul && vreader_emul->type_params) {
+        return vreader_emul->type_params;
+    }
+
+    return "";
+}
+
+/* pull the slot out of the reader private data */
+static PK11SlotInfo *
+vcard_emul_reader_get_slot(VReader *vreader)
+{
+    VReaderEmul *vreader_emul = vreader_get_private(vreader);
+    if (vreader_emul == NULL) {
+        return NULL;
+    }
+    return vreader_emul->slot;
+}
+
+/*
+ *  Card ATR's map to physical cards. VCARD_ATR_PREFIX will set appropriate
+ *  historical bytes for any software emulated card. The remaining bytes can be
+ *  used to indicate the actual emulator
+ */
+static const unsigned char nss_atr[] = { VCARD_ATR_PREFIX(3), 'N', 'S', 'S' };
+
+void
+vcard_emul_get_atr(VCard *card, unsigned char *atr, int *atr_len)
+{
+    int len = MIN(sizeof(nss_atr), *atr_len);
+    assert(atr != NULL);
+
+    memcpy(atr, nss_atr, len);
+    *atr_len = len;
+    return;
+}
+
+/*
+ * create a new card from certs and keys
+ */
+static VCard *
+vcard_emul_make_card(VReader *reader,
+                     unsigned char * const *certs, int *cert_len,
+                     VCardKey *keys[], int cert_count)
+{
+    VCardEmul *vcard_emul;
+    VCard *vcard;
+    PK11SlotInfo *slot;
+    VCardEmulType type;
+    const char *params;
+
+    type = vcard_emul_get_type(reader);
+
+    /* ignore the inserted card */
+    if (type == VCARD_EMUL_NONE) {
+        return NULL;
+    }
+    slot = vcard_emul_reader_get_slot(reader);
+    if (slot == NULL) {
+        return NULL;
+    }
+
+    params = vcard_emul_get_type_params(reader);
+    /* params these can be NULL */
+
+    vcard_emul = vcard_emul_new_card(slot);
+    if (vcard_emul == NULL) {
+        return NULL;
+    }
+    vcard = vcard_new(vcard_emul, vcard_emul_delete_card);
+    if (vcard == NULL) {
+        vcard_emul_delete_card(vcard_emul);
+        return NULL;
+    }
+    vcard_init(reader, vcard, type, params, certs, cert_len, keys, cert_count);
+    return vcard;
+}
+
+
+/*
+ * 'clone' a physical card as a virtual card
+ */
+static VCard *
+vcard_emul_mirror_card(VReader *vreader)
+{
+    /*
+     * lookup certs using the C_FindObjects. The Stan Cert handle won't give
+     * us the real certs until we log in.
+     */
+    PK11GenericObject *firstObj, *thisObj;
+    int cert_count;
+    unsigned char **certs;
+    int *cert_len;
+    VCardKey **keys;
+    PK11SlotInfo *slot;
+    PRBool ret;
+
+    slot = vcard_emul_reader_get_slot(vreader);
+    if (slot == NULL) {
+        return NULL;
+    }
+
+    firstObj = PK11_FindGenericObjects(slot, CKO_CERTIFICATE);
+    if (firstObj == NULL) {
+        return NULL;
+    }
+
+    /* count the certs */
+    cert_count = 0;
+    for (thisObj = firstObj; thisObj;
+                             thisObj = PK11_GetNextGenericObject(thisObj)) {
+        cert_count++;
+    }
+
+    if (cert_count == 0) {
+        PK11_DestroyGenericObjects(firstObj);
+        return NULL;
+    }
+
+    /* allocate the arrays */
+    ret = vcard_emul_alloc_arrays(&certs, &cert_len, &keys, cert_count);
+    if (ret == PR_FALSE) {
+        return NULL;
+    }
+
+    /* fill in the arrays */
+    cert_count = 0;
+    for (thisObj = firstObj; thisObj;
+                             thisObj = PK11_GetNextGenericObject(thisObj)) {
+        SECItem derCert;
+        CERTCertificate *cert;
+        SECStatus rv;
+
+        rv = PK11_ReadRawAttribute(PK11_TypeGeneric, thisObj,
+                                   CKA_VALUE, &derCert);
+        if (rv != SECSuccess) {
+            continue;
+        }
+        /* create floating temp cert. This gives us a cert structure even if
+         * the token isn't logged in */
+        cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &derCert,
+                                       NULL, PR_FALSE, PR_TRUE);
+        SECITEM_FreeItem(&derCert, PR_FALSE);
+        if (cert == NULL) {
+            continue;
+        }
+
+        certs[cert_count] = cert->derCert.data;
+        cert_len[cert_count] = cert->derCert.len;
+        keys[cert_count] = vcard_emul_make_key(slot, cert);
+        cert_count++;
+        CERT_DestroyCertificate(cert); /* key obj still has a reference */
+    }
+
+    /* now create the card */
+    return vcard_emul_make_card(vreader, certs, cert_len, keys, cert_count);
+}
+
+static VCardEmulType default_card_type = VCARD_EMUL_NONE;
+static const char *default_type_params = "";
+
+/*
+ * This thread looks for card and reader insertions and puts events on the
+ * event queue
+ */
+static void
+vcard_emul_event_thread(void *arg)
+{
+    PK11SlotInfo *slot;
+    VReader *vreader;
+    VReaderEmul *vreader_emul;
+    VCard *vcard;
+    SECMODModule *module = (SECMODModule *)arg;
+
+    do {
+        slot = SECMOD_WaitForAnyTokenEvent(module, 0, 500);
+        if (slot == NULL) {
+            break;
+        }
+        vreader = vcard_emul_find_vreader_from_slot(slot);
+        if (vreader == NULL) {
+            /* new vreader */
+            vreader_emul = vreader_emul_new(slot, default_card_type,
+                                            default_type_params);
+            vreader = vreader_new(PK11_GetSlotName(slot), vreader_emul,
+                                  vreader_emul_delete);
+            PK11_FreeSlot(slot);
+            slot = NULL;
+            vreader_add_reader(vreader);
+            vreader_free(vreader);
+            continue;
+        }
+        /* card remove/insert */
+        vreader_emul = vreader_get_private(vreader);
+        if (PK11_IsPresent(slot)) {
+            int series = PK11_GetSlotSeries(slot);
+            if (series != vreader_emul->series) {
+                if (vreader_emul->present) {
+                    vreader_insert_card(vreader, NULL);
+                }
+                vcard = vcard_emul_mirror_card(vreader);
+                vreader_insert_card(vreader, vcard);
+                vcard_free(vcard);
+            }
+            vreader_emul->series = series;
+            vreader_emul->present = 1;
+            vreader_free(vreader);
+            PK11_FreeSlot(slot);
+            continue;
+        }
+        if (vreader_emul->present) {
+            vreader_insert_card(vreader, NULL);
+        }
+        vreader_emul->series = 0;
+        vreader_emul->present = 0;
+        PK11_FreeSlot(slot);
+        vreader_free(vreader);
+    } while (1);
+}
+
+/* if the card is inserted when we start up, make sure our state is correct */
+static void
+vcard_emul_init_series(VReader *vreader, VCard *vcard)
+{
+    VReaderEmul *vreader_emul = vreader_get_private(vreader);
+    PK11SlotInfo *slot = vreader_emul->slot;
+
+    vreader_emul->present = PK11_IsPresent(slot);
+    vreader_emul->series = PK11_GetSlotSeries(slot);
+    if (vreader_emul->present == 0) {
+        vreader_insert_card(vreader, NULL);
+    }
+}
+
+/*
+ * each module has a separate wait call, create a thread for each module that
+ * we are using.
+ */
+static void
+vcard_emul_new_event_thread(SECMODModule *module)
+{
+    PR_CreateThread(PR_SYSTEM_THREAD, vcard_emul_event_thread,
+                     module, PR_PRIORITY_HIGH, PR_GLOBAL_THREAD,
+                     PR_UNJOINABLE_THREAD, 0);
+}
+
+static const VCardEmulOptions default_options = {
+    .nss_db = NULL,
+    .vreader = NULL,
+    .vreader_count = 0,
+    .hw_card_type = VCARD_EMUL_CAC,
+    .hw_type_params = "",
+    .use_hw = PR_TRUE
+};
+
+
+/*
+ *  NSS needs the app to supply a password prompt. In our case the only time
+ *  the password is supplied is as part of the Login APDU. The actual password
+ *  is passed in the pw_arg in that case. In all other cases pw_arg should be
+ *  NULL.
+ */
+static char *
+vcard_emul_get_password(PK11SlotInfo *slot, PRBool retries, void *pw_arg)
+{
+    /* if it didn't work the first time, don't keep trying */
+    if (retries) {
+        return NULL;
+    }
+    /* we are looking up a password when we don't have one in hand */
+    if (pw_arg == NULL) {
+        return NULL;
+    }
+    /* TODO: we really should verify that were are using the right slot */
+    return PORT_Strdup(pw_arg);
+}
+
+/* Force a card removal even if the card is not physically removed */
+VCardEmulError
+vcard_emul_force_card_remove(VReader *vreader)
+{
+    if (!nss_emul_init || (vreader_card_is_present(vreader) != VREADER_OK)) {
+        return VCARD_EMUL_FAIL; /* card is already removed */
+    }
+
+    /* OK, remove it */
+    vreader_insert_card(vreader, NULL);
+    return VCARD_EMUL_OK;
+}
+
+/* Re-insert of a card that has been removed by force removal */
+VCardEmulError
+vcard_emul_force_card_insert(VReader *vreader)
+{
+    VReaderEmul *vreader_emul;
+    VCard *vcard;
+
+    if (!nss_emul_init || (vreader_card_is_present(vreader) == VREADER_OK)) {
+        return VCARD_EMUL_FAIL; /* card is already removed */
+    }
+    vreader_emul = vreader_get_private(vreader);
+
+    /* if it's a softcard, get the saved vcard from the reader emul structure */
+    if (vreader_emul->saved_vcard) {
+        vcard = vcard_reference(vreader_emul->saved_vcard);
+    } else {
+        /* it must be a physical card, rebuild it */
+        if (!PK11_IsPresent(vreader_emul->slot)) {
+            /* physical card has been removed, not way to reinsert it */
+            return VCARD_EMUL_FAIL;
+        }
+        vcard = vcard_emul_mirror_card(vreader);
+    }
+    vreader_insert_card(vreader, vcard);
+    vcard_free(vcard);
+
+    return VCARD_EMUL_OK;
+}
+
+
+static PRBool
+module_has_removable_hw_slots(SECMODModule *mod)
+{
+    int i;
+    PRBool ret = PR_FALSE;
+    SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock();
+
+    if (!moduleLock) {
+        PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+        return ret;
+    }
+    SECMOD_GetReadLock(moduleLock);
+    for (i = 0; i < mod->slotCount; i++) {
+        PK11SlotInfo *slot = mod->slots[i];
+        if (PK11_IsRemovable(slot) && PK11_IsHW(slot)) {
+            ret = PR_TRUE;
+            break;
+        }
+    }
+    SECMOD_ReleaseReadLock(moduleLock);
+    return ret;
+}
+
+/* Previously we returned FAIL if no readers found. This makes
+ * no sense when using hardware, since there may be no readers connected
+ * at the time vcard_emul_init is called, but they will be properly
+ * recognized later. So Instead return FAIL only if no_hw==1 and no
+ * vcards can be created (indicates error with certificates provided
+ * or db), or if any other higher level error (NSS error, missing coolkey). */
+static int vcard_emul_init_called;
+
+VCardEmulError
+vcard_emul_init(const VCardEmulOptions *options)
+{
+    SECStatus rv;
+    PRBool ret, has_readers = PR_FALSE, need_coolkey_module;
+    VReader *vreader;
+    VReaderEmul *vreader_emul;
+    SECMODListLock *module_lock;
+    SECMODModuleList *module_list;
+    SECMODModuleList *mlp;
+    int i;
+
+    if (vcard_emul_init_called) {
+        return VCARD_EMUL_INIT_ALREADY_INITED;
+    }
+    vcard_emul_init_called = 1;
+    vreader_init();
+    vevent_queue_init();
+
+    if (options == NULL) {
+        options = &default_options;
+    }
+
+    /* first initialize NSS */
+    if (options->nss_db) {
+        rv = NSS_Init(options->nss_db);
+    } else {
+        rv = NSS_Init("sql:/etc/pki/nssdb");
+    }
+    if (rv != SECSuccess) {
+        return VCARD_EMUL_FAIL;
+    }
+    /* Set password callback function */
+    PK11_SetPasswordFunc(vcard_emul_get_password);
+
+    /* set up soft cards emulated by software certs rather than physical cards
+     * */
+    for (i = 0; i < options->vreader_count; i++) {
+        int j;
+        int cert_count;
+        unsigned char **certs;
+        int *cert_len;
+        VCardKey **keys;
+        PK11SlotInfo *slot;
+
+        slot = PK11_FindSlotByName(options->vreader[i].name);
+        if (slot == NULL) {
+            continue;
+        }
+        vreader_emul = vreader_emul_new(slot, options->vreader[i].card_type,
+                                        options->vreader[i].type_params);
+        vreader = vreader_new(options->vreader[i].vname, vreader_emul,
+                              vreader_emul_delete);
+        vreader_add_reader(vreader);
+        cert_count = options->vreader[i].cert_count;
+
+        ret = vcard_emul_alloc_arrays(&certs, &cert_len, &keys,
+                                      options->vreader[i].cert_count);
+        if (ret == PR_FALSE) {
+            continue;
+        }
+        cert_count = 0;
+        for (j = 0; j < options->vreader[i].cert_count; j++) {
+            /* we should have a better way of identifying certs than by
+             * nickname here */
+            CERTCertificate *cert = PK11_FindCertFromNickname(
+                                        options->vreader[i].cert_name[j],
+                                        NULL);
+            if (cert == NULL) {
+                continue;
+            }
+            certs[cert_count] = cert->derCert.data;
+            cert_len[cert_count] = cert->derCert.len;
+            keys[cert_count] = vcard_emul_make_key(slot, cert);
+            /* this is safe because the key is still holding a cert reference */
+            CERT_DestroyCertificate(cert);
+            cert_count++;
+        }
+        if (cert_count) {
+            VCard *vcard = vcard_emul_make_card(vreader, certs, cert_len,
+                                                keys, cert_count);
+            vreader_insert_card(vreader, vcard);
+            vcard_emul_init_series(vreader, vcard);
+            /* allow insertion and removal of soft cards */
+            vreader_emul->saved_vcard = vcard_reference(vcard);
+            vcard_free(vcard);
+            vreader_free(vreader);
+            has_readers = PR_TRUE;
+        }
+    }
+
+    /* if we aren't suppose to use hw, skip looking up hardware tokens */
+    if (!options->use_hw) {
+        nss_emul_init = has_readers;
+        return has_readers ? VCARD_EMUL_OK : VCARD_EMUL_FAIL;
+    }
+
+    /* make sure we have some PKCS #11 module loaded */
+    module_lock = SECMOD_GetDefaultModuleListLock();
+    module_list = SECMOD_GetDefaultModuleList();
+    need_coolkey_module = !has_readers;
+    SECMOD_GetReadLock(module_lock);
+    for (mlp = module_list; mlp; mlp = mlp->next) {
+        SECMODModule *module = mlp->module;
+        if (module_has_removable_hw_slots(module)) {
+            need_coolkey_module = PR_FALSE;
+            break;
+        }
+    }
+    SECMOD_ReleaseReadLock(module_lock);
+
+    if (need_coolkey_module) {
+        SECMODModule *module;
+        module = SECMOD_LoadUserModule(
+                    (char *)"library=libcoolkeypk11.so name=Coolkey",
+                    NULL, PR_FALSE);
+        if (module == NULL) {
+            return VCARD_EMUL_FAIL;
+        }
+        SECMOD_DestroyModule(module); /* free our reference, Module will still
+                                       * be on the list.
+                                       * until we destroy it */
+    }
+
+    /* now examine all the slots, finding which should be readers */
+    /* We should control this with options. For now we mirror out any
+     * removable hardware slot */
+    default_card_type = options->hw_card_type;
+    default_type_params = strdup(options->hw_type_params);
+
+    SECMOD_GetReadLock(module_lock);
+    for (mlp = module_list; mlp; mlp = mlp->next) {
+        SECMODModule *module = mlp->module;
+        PRBool has_emul_slots = PR_FALSE;
+
+        if (module == NULL) {
+                continue;
+        }
+
+        for (i = 0; i < module->slotCount; i++) {
+            PK11SlotInfo *slot = module->slots[i];
+
+            /* only map removable HW slots */
+            if (slot == NULL || !PK11_IsRemovable(slot) || !PK11_IsHW(slot)) {
+                continue;
+            }
+            vreader_emul = vreader_emul_new(slot, options->hw_card_type,
+                                            options->hw_type_params);
+            vreader = vreader_new(PK11_GetSlotName(slot), vreader_emul,
+                                  vreader_emul_delete);
+            vreader_add_reader(vreader);
+
+            has_readers = PR_TRUE;
+            has_emul_slots = PR_TRUE;
+
+            if (PK11_IsPresent(slot)) {
+                VCard *vcard;
+                vcard = vcard_emul_mirror_card(vreader);
+                vreader_insert_card(vreader, vcard);
+                vcard_emul_init_series(vreader, vcard);
+                vcard_free(vcard);
+            }
+        }
+        if (has_emul_slots) {
+            vcard_emul_new_event_thread(module);
+        }
+    }
+    SECMOD_ReleaseReadLock(module_lock);
+    nss_emul_init = has_readers;
+
+    return VCARD_EMUL_OK;
+}
+
+/* Recreate card insert events for all readers (user should
+ * deduce implied reader insert. perhaps do a reader insert as well?)
+ */
+void
+vcard_emul_replay_insertion_events(void)
+{
+    VReaderListEntry *current_entry;
+    VReaderListEntry *next_entry = NULL;
+    VReaderList *list = vreader_get_reader_list();
+
+    for (current_entry = vreader_list_get_first(list); current_entry;
+            current_entry = next_entry) {
+        VReader *vreader = vreader_list_get_reader(current_entry);
+        next_entry = vreader_list_get_next(current_entry);
+        vreader_queue_card_event(vreader);
+    }
+}
+
+/*
+ *  Silly little functions to help parsing our argument string
+ */
+static char *
+copy_string(const char *str, int str_len)
+{
+    char *new_str;
+
+    new_str = qemu_malloc(str_len+1);
+    memcpy(new_str, str, str_len);
+    new_str[str_len] = 0;
+    return new_str;
+}
+
+static int
+count_tokens(const char *str, char token, char token_end)
+{
+    int count = 0;
+
+    for (; *str; str++) {
+        if (*str == token) {
+            count++;
+        }
+        if (*str == token_end) {
+            break;
+        }
+    }
+    return count;
+}
+
+static const char *
+strip(const char *str)
+{
+    for (; *str && !isspace(*str); str++) {
+    }
+    return str;
+}
+
+static const char *
+find_blank(const char *str)
+{
+    for (; *str && isspace(*str); str++) {
+    }
+    return str;
+}
+
+
+/*
+ *  We really want to use some existing argument parsing library here. That
+ *  would give us a consistant look */
+static VCardEmulOptions options;
+#define READER_STEP 4
+
+VCardEmulOptions *
+vcard_emul_options(const char *args)
+{
+    int reader_count = 0;
+    VCardEmulOptions *opts;
+    char type_str[100];
+    int type_len;
+
+    /* Allow the future use of allocating the options structure on the fly */
+    memcpy(&options, &default_options, sizeof(options));
+    opts = &options;
+
+    do {
+        args = strip(args); /* strip off the leading spaces */
+        if (*args == ',') {
+            continue;
+        }
+        /* soft=(slot_name,virt_name,emul_type,emul_flags,cert_1, (no eol)
+         *       cert_2,cert_3...) */
+        if (strncmp(args, "soft=", 5) == 0) {
+            const char *name;
+            const char *vname;
+            const char *type_params;
+            VCardEmulType type;
+            int name_length, vname_length, type_params_length, count, i;
+            VirtualReaderOptions *vreaderOpt = NULL;
+
+            args = strip(args + 5);
+            if (*args != '(') {
+                continue;
+            }
+            name = args;
+            args = strpbrk(args + 1, ",)");
+            if (*args == 0) {
+                break;
+            }
+            if (*args == ')') {
+                args++;
+                continue;
+            }
+            args = strip(args+1);
+            name_length = args - name - 2;
+            vname = args;
+            args = strpbrk(args + 1, ",)");
+            if (*args == 0) {
+                break;
+            }
+            if (*args == ')') {
+                args++;
+                continue;
+            }
+            vname_length = args - name - 2;
+            args = strip(args+1);
+            type_len = strpbrk(args, ",)") - args;
+            assert(sizeof(type_str) > type_len);
+            strncpy(type_str, args, type_len);
+            type_str[type_len] = 0;
+            type = vcard_emul_type_from_string(type_str);
+            args = strpbrk(args, ",)");
+            if (*args == 0) {
+                break;
+            }
+            if (*args == ')') {
+                args++;
+                continue;
+            }
+            args = strip(args++);
+            type_params = args;
+            args = strpbrk(args + 1, ",)");
+            if (*args == 0) {
+                break;
+            }
+            if (*args == ')') {
+                args++;
+                continue;
+            }
+            type_params_length = args - name;
+            args = strip(args++);
+            if (*args == 0) {
+                break;
+            }
+
+            if (opts->vreader_count >= reader_count) {
+                reader_count += READER_STEP;
+                vreaderOpt = realloc(opts->vreader,
+                                reader_count * sizeof(*vreaderOpt));
+                if (vreaderOpt == NULL) {
+                    return opts; /* we're done */
+                }
+            }
+            opts->vreader = vreaderOpt;
+            vreaderOpt = &vreaderOpt[opts->vreader_count];
+            vreaderOpt->name = copy_string(name, name_length);
+            vreaderOpt->vname = copy_string(vname, vname_length);
+            vreaderOpt->card_type = type;
+            vreaderOpt->type_params =
+                copy_string(type_params, type_params_length);
+            count = count_tokens(args, ',', ')');
+            vreaderOpt->cert_count = count;
+            vreaderOpt->cert_name = (char **)qemu_malloc(count*sizeof(char *));
+            for (i = 0; i < count; i++) {
+                const char *cert = args + 1;
+                args = strpbrk(args + 1, ",)");
+                vreaderOpt->cert_name[i] = copy_string(cert, args - cert);
+            }
+            if (*args == ')') {
+                args++;
+            }
+            opts->vreader_count++;
+        /* use_hw= */
+        } else if (strncmp(args, "use_hw=", 7) == 0) {
+            args = strip(args+7);
+            if (*args == '0' || *args == 'N' || *args == 'n' || *args == 'F') {
+                opts->use_hw = PR_FALSE;
+            } else {
+                opts->use_hw = PR_TRUE;
+            }
+            args = find_blank(args);
+        /* hw_type= */
+        } else if (strncmp(args, "hw_type=", 8) == 0) {
+            args = strip(args+8);
+            opts->hw_card_type = vcard_emul_type_from_string(args);
+            args = find_blank(args);
+        /* hw_params= */
+        } else if (strncmp(args, "hw_params=", 10) == 0) {
+            const char *params;
+            args = strip(args+10);
+            params = args;
+            args = find_blank(args);
+            opts->hw_type_params = copy_string(params, args-params);
+        /* db="/data/base/path" */
+        } else if (strncmp(args, "db=", 3) == 0) {
+            const char *db;
+            args = strip(args+3);
+            if (*args != '"') {
+                continue;
+            }
+            args++;
+            db = args;
+            args = strpbrk(args, "\"\n");
+            opts->nss_db = copy_string(db, args-db);
+            if (*args != 0) {
+                args++;
+            }
+        } else {
+            args = find_blank(args);
+        }
+    } while (*args != 0);
+
+    return opts;
+}
+
+void
+vcard_emul_usage(void)
+{
+   fprintf(stderr,
+"emul args: comma separated list of the following arguments\n"
+" db={nss_database}               (default sql:/etc/pki/nssdb)\n"
+" use_hw=[yes|no]                 (default yes)\n"
+" hw_type={card_type_to_emulate}  (default CAC)\n"
+" hw_param={param_for_card}       (default \"\")\n"
+" soft=({slot_name},{vreader_name},{card_type_to_emulate},{params_for_card},\n"
+"       {cert1},{cert2},{cert3}    (default none)\n"
+"\n"
+"  {nss_database}          The location of the NSS cert & key database\n"
+"  {card_type_to_emulate}  What card interface to present to the guest\n"
+"  {param_for_card}        Card interface specific parameters\n"
+"  {slot_name}             NSS slot that contains the certs\n"
+"  {vreader_name}          Virutal reader name to present to the guest\n"
+"  {certN}                 Nickname of the certificate n on the virtual card\n"
+"\n"
+"These parameters come as a single string separated by blanks or newlines."
+"\n"
+"Unless use_hw is set to no, all tokens that look like removable hardware\n"
+"tokens will be presented to the guest using the emulator specified by\n"
+"hw_type, and parameters of hw_param.\n"
+"\n"
+"If more one or more soft= parameters are specified, these readers will be\n"
+"presented to the guest\n");
+}
diff --git a/libcacard/vcard_emul_type.c b/libcacard/vcard_emul_type.c
new file mode 100644
index 0000000..59a1458
--- /dev/null
+++ b/libcacard/vcard_emul_type.c
@@ -0,0 +1,57 @@ 
+/*
+ *  This file contains utility functions which abstract the different card
+ *  types.  The goal is that new card types can easily be added by simply
+ *  changing this file and vcard_emul_type.h. It is currently not a requirement
+ *  to dynamically add new card types.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#include <strings.h>
+#include "vcardt.h"
+#include "vcard_emul_type.h"
+#include "cac.h"
+
+VCardStatus vcard_init(VReader *vreader, VCard *vcard,
+                       VCardEmulType type, const char *params,
+                       unsigned char *const *cert, int cert_len[],
+                       VCardKey *key[], int cert_count)
+{
+    switch (type) {
+    case VCARD_EMUL_NONE:
+        break;
+    case VCARD_EMUL_CAC:
+        return cac_card_init(vreader, vcard, params,
+                             cert, cert_len, key,  cert_count);
+    /* add new ones here */
+    default:
+        break;
+    }
+    return VCARD_FAIL;
+}
+
+VCardEmulType vcard_emul_type_select(VReader *vreader)
+{
+#ifdef notdef
+    /* since there is only one emulator no need to call this function */
+    if (cac_is_cac_card(vreader) == VCARD_DONE) {
+        return VCARD_EMUL_CAC;
+    }
+#endif
+    /* return the default */
+    return VCARD_EMUL_CAC;
+}
+
+VCardEmulType vcard_emul_type_from_string(const char *type_string)
+{
+     if (strcasecmp(type_string, "CAC") == 0) {
+        return VCARD_EMUL_CAC;
+     }
+#ifdef USE_PASSTHRU
+     if (strcasecmp(type_string, "PASSTHRU") == 0) {
+        return VCARD_EMUL_PASSTHRU;
+     }
+#endif
+     return VCARD_EMUL_NONE;
+}
diff --git a/libcacard/vcard_emul_type.h b/libcacard/vcard_emul_type.h
new file mode 100644
index 0000000..0242f40
--- /dev/null
+++ b/libcacard/vcard_emul_type.h
@@ -0,0 +1,32 @@ 
+/*
+ *  This header file abstracts the different card types. The goal is new card
+ *  types can easily be added by simply changing this file and
+ *  vcard_emul_type.c. It is currently not a requirement to dynamically add new
+ *  card types.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#ifndef VCARD_EMUL_TYPE_H
+#define VCARD_EMUL_TYPE_H 1
+#include "vcardt.h"
+#include "vreadert.h"
+
+/*
+ * types
+ */
+typedef enum {
+     VCARD_EMUL_NONE = 0,
+     VCARD_EMUL_CAC,
+     VCARD_EMUL_PASSTHRU
+} VCardEmulType;
+
+/* functions used by the rest of the emulator */
+VCardStatus vcard_init(VReader *vreader, VCard *vcard, VCardEmulType type,
+                       const char *params, unsigned char * const *cert,
+                       int cert_len[], VCardKey *key[], int cert_count);
+VCardEmulType vcard_emul_type_select(VReader *vreader);
+VCardEmulType vcard_emul_type_from_string(const char *type_string);
+
+#endif
diff --git a/libcacard/vcardt.h b/libcacard/vcardt.h
new file mode 100644
index 0000000..538bdde
--- /dev/null
+++ b/libcacard/vcardt.h
@@ -0,0 +1,64 @@ 
+/*
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+#ifndef VCARDT_H
+#define VCARDT_H 1
+
+/*
+ * these should come from some common spice header file
+ */
+#include <assert.h>
+#ifndef MIN
+#define MIN(x, y) ((x) > (y) ? (y) : (x))
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+typedef struct VCardStruct VCard;
+typedef struct VCardAPDUStruct VCardAPDU;
+typedef struct VCardResponseStruct VCardResponse;
+typedef struct VCardBufferResponseStruct VCardBufferResponse;
+typedef struct VCardAppletStruct VCardApplet;
+typedef struct VCardAppletPrivateStruct VCardAppletPrivate;
+typedef struct VCardKeyStruct VCardKey;  /* opaque */
+typedef struct VCardEmulStruct VCardEmul;
+
+#define MAX_CHANNEL 4
+
+/* create an ATR with appropriate historical bytes */
+#define VCARD_ATR_PREFIX(size) 0x3b, 0x66+(size), 0x00, 0xff, \
+                               'V', 'C', 'A', 'R', 'D', '_'
+
+
+typedef enum {
+    VCARD_DONE,
+    VCARD_NEXT,
+    VCARD_FAIL
+} VCardStatus;
+
+typedef enum {
+    VCARD_FILE_SYSTEM,
+    VCARD_VM,
+    VCARD_DIRECT
+} VCardType;
+
+typedef enum {
+    VCARD_POWER_ON,
+    VCARD_POWER_OFF
+} VCardPower;
+
+typedef VCardStatus (*VCardProcessAPDU)(VCard *card, VCardAPDU *apdu,
+                                        VCardResponse **response);
+typedef VCardStatus (*VCardResetApplet)(VCard *card, int channel);
+typedef void (*VCardAppletPrivateFree) (VCardAppletPrivate *);
+typedef void (*VCardEmulFree) (VCardEmul *);
+typedef void (*VCardGetAtr) (VCard *, unsigned char *atr, int *atr_len);
+
+struct VCardBufferResponseStruct {
+    unsigned char *buffer;
+    int buffer_len;
+    unsigned char *current;
+    int len;
+};
+
+#endif
diff --git a/libcacard/vevent.h b/libcacard/vevent.h
new file mode 100644
index 0000000..38c3482
--- /dev/null
+++ b/libcacard/vevent.h
@@ -0,0 +1,27 @@ 
+/*
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+#ifndef EVENT_H
+#define EVENT_H 1
+#include "eventt.h"
+#include "vreadert.h"
+#include "vcardt.h"
+
+VEvent *vevent_new(VEventType type, VReader *reader, VCard *card);
+void vevent_delete(VEvent *);
+
+/*
+ * VEvent queueing services
+ */
+void vevent_queue_vevent(VEvent *);
+void vevent_queue_init(void);
+
+/*
+ *  VEvent dequeing services
+ */
+VEvent *vevent_wait_next_vevent(void);
+VEvent *vevent_get_next_vevent(void);
+
+
+#endif
diff --git a/libcacard/vreader.c b/libcacard/vreader.c
new file mode 100644
index 0000000..0b67c6c
--- /dev/null
+++ b/libcacard/vreader.c
@@ -0,0 +1,519 @@ 
+/*
+ * emulate the reader
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+/*
+ * System includes
+ */
+#include <stdlib.h>
+#include <string.h>
+
+#include "qemu-thread.h"
+#include "qemu-common.h"
+
+#include "vcard.h"
+#include "vcard_emul.h"
+#include "card_7816.h"
+#include "vreader.h"
+#include "vevent.h"
+
+struct VReaderStruct {
+    int    reference_count;
+    VCard *card;
+    char *name;
+    vreader_id_t id;
+    QemuMutex lock;
+    VReaderEmul  *reader_private;
+    VReaderEmulFree reader_private_free;
+};
+
+/* manage locking */
+static inline void
+vreader_lock(VReader *reader)
+{
+    qemu_mutex_lock(&reader->lock);
+}
+
+static inline void
+vreader_unlock(VReader *reader)
+{
+    qemu_mutex_unlock(&reader->lock);
+}
+
+/*
+ * vreader constructor
+ */
+VReader *
+vreader_new(const char *name, VReaderEmul *private,
+            VReaderEmulFree private_free)
+{
+    VReader *reader;
+
+    reader = (VReader *)qemu_malloc(sizeof(VReader));
+    qemu_mutex_init(&reader->lock);
+    reader->reference_count = 1;
+    reader->name = name ? strdup(name) : NULL;
+    reader->card = NULL;
+    reader->id = (vreader_id_t)-1;
+    reader->reader_private = private;
+    reader->reader_private_free = private_free;
+    return reader;
+}
+
+/* get a reference */
+VReader*
+vreader_reference(VReader *reader)
+{
+    if (reader == NULL) {
+        return NULL;
+    }
+    vreader_lock(reader);
+    reader->reference_count++;
+    vreader_unlock(reader);
+    return reader;
+}
+
+/* free a reference */
+void
+vreader_free(VReader *reader)
+{
+    if (reader == NULL) {
+        return;
+    }
+    vreader_lock(reader);
+    if (reader->reference_count-- > 1) {
+        vreader_unlock(reader);
+        return;
+    }
+    vreader_unlock(reader);
+    if (reader->card) {
+        vcard_free(reader->card);
+    }
+    if (reader->name) {
+        qemu_free(reader->name);
+    }
+    if (reader->reader_private_free) {
+        reader->reader_private_free(reader->reader_private);
+    }
+    qemu_free(reader);
+    return;
+}
+
+static VCard *
+vreader_get_card(VReader *reader)
+{
+    VCard *card;
+
+    vreader_lock(reader);
+    card = vcard_reference(reader->card);
+    vreader_unlock(reader);
+    return card;
+}
+
+VReaderStatus
+vreader_card_is_present(VReader *reader)
+{
+    VCard *card = vreader_get_card(reader);
+
+    if (card == NULL) {
+        return VREADER_NO_CARD;
+    }
+    vcard_free(card);
+    return VREADER_OK;
+}
+
+vreader_id_t
+vreader_get_id(VReader *reader)
+{
+    if (reader == NULL) {
+        return (vreader_id_t)-1;
+    }
+    return reader->id;
+}
+
+VReaderStatus
+vreader_set_id(VReader *reader, vreader_id_t id)
+{
+    if (reader == NULL) {
+        return VREADER_NO_CARD;
+    }
+    reader->id = id;
+    return VREADER_OK;
+}
+
+const char *
+vreader_get_name(VReader *reader)
+{
+    if (reader == NULL) {
+        return NULL;
+    }
+    return reader->name;
+}
+
+VReaderEmul *
+vreader_get_private(VReader *reader)
+{
+    return reader->reader_private;
+}
+
+static VReaderStatus
+vreader_reset(VReader *reader, VCardPower power, unsigned char *atr, int *len)
+{
+    VCard *card = vreader_get_card(reader);
+
+    if (card == NULL) {
+        return VREADER_NO_CARD;
+    }
+    /*
+     * clean up our state
+     */
+    vcard_reset(card, power);
+    if (atr) {
+        vcard_get_atr(card, atr, len);
+    }
+    vcard_free(card); /* free our reference */
+    return VREADER_OK;
+}
+
+VReaderStatus
+vreader_power_on(VReader *reader, unsigned char *atr, int *len)
+{
+    return vreader_reset(reader, VCARD_POWER_ON, atr, len);
+}
+
+VReaderStatus
+vreader_power_off(VReader *reader)
+{
+    return vreader_reset(reader, VCARD_POWER_OFF, NULL, 0);
+}
+
+
+VReaderStatus
+vreader_xfr_bytes(VReader *reader,
+                  unsigned char *send_buf, int send_buf_len,
+                  unsigned char *receive_buf, int *receive_buf_len)
+{
+    VCardAPDU *apdu;
+    VCardResponse *response = NULL;
+    VCardStatus card_status;
+    unsigned short status;
+    VCard *card = vreader_get_card(reader);
+
+    if (card == NULL) {
+        return VREADER_NO_CARD;
+    }
+
+    apdu = vcard_apdu_new(send_buf, send_buf_len, &status);
+    if (apdu == NULL) {
+        response = vcard_make_response(status);
+        card_status = VCARD_DONE;
+    } else {
+        card_status = vcard_process_apdu(card, apdu, &response);
+    }
+    assert(card_status == VCARD_DONE);
+    if (card_status == VCARD_DONE) {
+        int size = MIN(*receive_buf_len, response->b_total_len);
+        memcpy(receive_buf, response->b_data, size);
+        *receive_buf_len = size;
+    }
+    vcard_response_delete(response);
+    vcard_apdu_delete(apdu);
+    vcard_free(card); /* free our reference */
+    return VREADER_OK;
+}
+
+struct VReaderListStruct {
+    VReaderListEntry *head;
+    VReaderListEntry *tail;
+};
+
+struct VReaderListEntryStruct {
+    VReaderListEntry *next;
+    VReaderListEntry *prev;
+    VReader *reader;
+};
+
+
+static VReaderListEntry *
+vreader_list_entry_new(VReader *reader)
+{
+    VReaderListEntry *new_reader_list_entry;
+
+    new_reader_list_entry = (VReaderListEntry *)
+                               qemu_malloc(sizeof(VReaderListEntry));
+    new_reader_list_entry->next = NULL;
+    new_reader_list_entry->prev = NULL;
+    new_reader_list_entry->reader = vreader_reference(reader);
+    return new_reader_list_entry;
+}
+
+static void
+vreader_list_entry_delete(VReaderListEntry *entry)
+{
+    if (entry == NULL) {
+        return;
+    }
+    vreader_free(entry->reader);
+    qemu_free(entry);
+}
+
+
+static VReaderList *
+vreader_list_new(void)
+{
+    VReaderList *new_reader_list;
+
+    new_reader_list = (VReaderList *)qemu_malloc(sizeof(VReaderList));
+    new_reader_list->head = NULL;
+    new_reader_list->tail = NULL;
+    return new_reader_list;
+}
+
+void
+vreader_list_delete(VReaderList *list)
+{
+    VReaderListEntry *current_entry;
+    VReaderListEntry *next_entry = NULL;
+    for (current_entry = vreader_list_get_first(list); current_entry;
+         current_entry = next_entry) {
+        next_entry = vreader_list_get_next(current_entry);
+        vreader_list_entry_delete(current_entry);
+    }
+    list->head = NULL;
+    list->tail = NULL;
+    qemu_free(list);
+}
+
+
+VReaderListEntry *
+vreader_list_get_first(VReaderList *list)
+{
+    return list ? list->head : NULL;
+}
+
+VReaderListEntry *
+vreader_list_get_next(VReaderListEntry *current)
+{
+    return current ? current->next : NULL;
+}
+
+VReader *
+vreader_list_get_reader(VReaderListEntry *entry)
+{
+    return entry ? vreader_reference(entry->reader) : NULL;
+}
+
+static void
+vreader_queue(VReaderList *list, VReaderListEntry *entry)
+{
+    if (entry == NULL) {
+        return;
+    }
+    entry->next = NULL;
+    entry->prev = list->tail;
+    if (list->head) {
+        list->tail->next = entry;
+    } else {
+        list->head = entry;
+    }
+    list->tail = entry;
+}
+
+static void
+vreader_dequeue(VReaderList *list, VReaderListEntry *entry)
+{
+    if (entry == NULL) {
+        return;
+    }
+    if (entry->next == NULL) {
+        list->tail = entry->prev;
+    } else if (entry->prev == NULL) {
+        list->head = entry->next;
+    } else {
+        entry->prev->next = entry->next;
+        entry->next->prev = entry->prev;
+    }
+    if ((list->tail == NULL) || (list->head == NULL)) {
+        list->head = list->tail = NULL;
+    }
+    entry->next = entry->prev = NULL;
+}
+
+static VReaderList *vreader_list;
+static QemuMutex vreader_list_mutex;
+
+static void
+vreader_list_init(void)
+{
+    vreader_list = vreader_list_new();
+    qemu_mutex_init(&vreader_list_mutex);
+}
+
+static void
+vreader_list_lock(void)
+{
+    qemu_mutex_lock(&vreader_list_mutex);
+}
+
+static void
+vreader_list_unlock(void)
+{
+    qemu_mutex_unlock(&vreader_list_mutex);
+}
+
+static VReaderList *
+vreader_copy_list(VReaderList *list)
+{
+    VReaderList *new_list = NULL;
+    VReaderListEntry *current_entry = NULL;
+
+    new_list = vreader_list_new();
+    if (new_list == NULL) {
+        return NULL;
+    }
+    for (current_entry = vreader_list_get_first(list); current_entry;
+         current_entry = vreader_list_get_next(current_entry)) {
+        VReader *reader = vreader_list_get_reader(current_entry);
+        VReaderListEntry *new_entry = vreader_list_entry_new(reader);
+
+        vreader_free(reader);
+        vreader_queue(new_list, new_entry);
+    }
+    return new_list;
+}
+
+VReaderList *
+vreader_get_reader_list(void)
+{
+    VReaderList *new_reader_list;
+
+    vreader_list_lock();
+    new_reader_list = vreader_copy_list(vreader_list);
+    vreader_list_unlock();
+    return new_reader_list;
+}
+
+VReader *
+vreader_get_reader_by_id(vreader_id_t id)
+{
+    VReader *reader = NULL;
+    VReaderListEntry *current_entry = NULL;
+
+    if (id == (vreader_id_t) -1) {
+        return NULL;
+    }
+
+    vreader_list_lock();
+    for (current_entry = vreader_list_get_first(vreader_list); current_entry;
+            current_entry = vreader_list_get_next(current_entry)) {
+        VReader *creader = vreader_list_get_reader(current_entry);
+        if (creader->id == id) {
+            reader = creader;
+            break;
+        }
+        vreader_free(creader);
+    }
+    vreader_list_unlock();
+    return reader;
+}
+
+VReader *
+vreader_get_reader_by_name(const char *name)
+{
+    VReader *reader = NULL;
+    VReaderListEntry *current_entry = NULL;
+
+    vreader_list_lock();
+    for (current_entry = vreader_list_get_first(vreader_list); current_entry;
+            current_entry = vreader_list_get_next(current_entry)) {
+        VReader *creader = vreader_list_get_reader(current_entry);
+        if (strcmp(creader->name, name) == 0) {
+            reader = creader;
+            break;
+        }
+        vreader_free(creader);
+    }
+    vreader_list_unlock();
+    return reader;
+}
+
+/* called from card_emul to initialize the readers */
+VReaderStatus
+vreader_add_reader(VReader *reader)
+{
+    VReaderListEntry *reader_entry;
+
+    reader_entry = vreader_list_entry_new(reader);
+    if (reader_entry == NULL) {
+        return VREADER_OUT_OF_MEMORY;
+    }
+    vreader_list_lock();
+    vreader_queue(vreader_list, reader_entry);
+    vreader_list_unlock();
+    vevent_queue_vevent(vevent_new(VEVENT_READER_INSERT, reader, NULL));
+    return VREADER_OK;
+}
+
+
+VReaderStatus
+vreader_remove_reader(VReader *reader)
+{
+    VReaderListEntry *current_entry;
+
+    vreader_list_lock();
+    for (current_entry = vreader_list_get_first(vreader_list); current_entry;
+         current_entry = vreader_list_get_next(current_entry)) {
+        if (current_entry->reader == reader) {
+            break;
+        }
+    }
+    vreader_dequeue(vreader_list, current_entry);
+    vreader_list_unlock();
+    vreader_list_entry_delete(current_entry);
+    vevent_queue_vevent(vevent_new(VEVENT_READER_REMOVE, reader, NULL));
+    return VREADER_OK;
+}
+
+/*
+ * Generate VEVENT_CARD_INSERT or VEVENT_CARD_REMOVE based on vreader
+ * state. Separated from vreader_insert_card to allow replaying events
+ * for a given state.
+ */
+void
+vreader_queue_card_event(VReader *reader)
+{
+    vevent_queue_vevent(vevent_new(
+        reader->card ? VEVENT_CARD_INSERT : VEVENT_CARD_REMOVE, reader,
+        reader->card));
+}
+
+/*
+ * insert/remove a new card. for removal, card == NULL
+ */
+VReaderStatus
+vreader_insert_card(VReader *reader, VCard *card)
+{
+    vreader_lock(reader);
+    if (reader->card) {
+        /* decrement reference count */
+        vcard_free(reader->card);
+        reader->card = NULL;
+    }
+    reader->card = vcard_reference(card);
+    vreader_unlock(reader);
+    vreader_queue_card_event(reader);
+    return VREADER_OK;
+}
+
+/*
+ * initialize all the static reader structures
+ */
+void
+vreader_init(void)
+{
+    vreader_list_init();
+}
+
diff --git a/libcacard/vreader.h b/libcacard/vreader.h
new file mode 100644
index 0000000..ec20421
--- /dev/null
+++ b/libcacard/vreader.h
@@ -0,0 +1,55 @@ 
+/*
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#ifndef VREADER_H
+#define VREADER_H 1
+
+#include "eventt.h"
+#include "vreadert.h"
+#include "vcardt.h"
+
+/*
+ * calls for reader front end
+ */
+VReaderStatus vreader_power_on(VReader *reader, unsigned char *atr, int *len);
+VReaderStatus vreader_power_off(VReader *reader);
+VReaderStatus vreader_xfr_bytes(VReader *reader, unsigned char *send_buf,
+                                int send_buf_len, unsigned char *receive_buf,
+                                int *receive_buf_len);
+
+/* constructor */
+VReader *vreader_new(const char *readerName, VReaderEmul *emul_private,
+                     VReaderEmulFree private_free);
+/* get a new reference to a reader */
+VReader *vreader_reference(VReader *reader);
+/* "destructor" (readers are reference counted) */
+void vreader_free(VReader *reader);
+
+/* accessors */
+VReaderEmul *vreader_get_private(VReader *);
+VReaderStatus vreader_card_is_present(VReader *reader);
+void vreader_queue_card_event(VReader *reader);
+const char *vreader_get_name(VReader *reader);
+vreader_id_t vreader_get_id(VReader *reader);
+VReaderStatus vreader_set_id(VReader *reader, vreader_id_t id);
+
+/* list operations */
+VReaderList *vreader_get_reader_list(void);
+void vreader_list_delete(VReaderList *list);
+VReader *vreader_list_get_reader(VReaderListEntry *entry);
+VReaderListEntry *vreader_list_get_first(VReaderList *list);
+VReaderListEntry *vreader_list_get_next(VReaderListEntry *list);
+VReader *vreader_get_reader_by_id(vreader_id_t id);
+VReader *vreader_get_reader_by_name(const char *name);
+
+/*
+ * list tools for vcard_emul
+ */
+void vreader_init(void);
+VReaderStatus vreader_add_reader(VReader *reader);
+VReaderStatus vreader_remove_reader(VReader *reader);
+VReaderStatus vreader_insert_card(VReader *reader, VCard *card);
+
+#endif
diff --git a/libcacard/vreadert.h b/libcacard/vreadert.h
new file mode 100644
index 0000000..f97e0a7
--- /dev/null
+++ b/libcacard/vreadert.h
@@ -0,0 +1,24 @@ 
+/*
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#ifndef VREADERT_H
+#define VREADERT_H 1
+
+typedef enum {
+    VREADER_OK = 0,
+    VREADER_NO_CARD,
+    VREADER_OUT_OF_MEMORY
+} VReaderStatus;
+
+typedef unsigned int vreader_id_t;
+typedef struct VReaderStruct VReader;
+typedef struct VReaderListStruct VReaderList;
+typedef struct VReaderListEntryStruct VReaderListEntry;
+
+typedef struct VReaderEmulStruct VReaderEmul;
+typedef void (*VReaderEmulFree)(VReaderEmul *);
+
+#endif
+