added emv-tools pk files

This commit is contained in:
merlokk 2017-12-04 15:03:27 +02:00
commit 4532932727
11 changed files with 2124 additions and 0 deletions

View file

@ -110,6 +110,10 @@ CMDSRCS = crapto1/crapto1.c\
ui.c \ ui.c \
cmddata.c \ cmddata.c \
lfdemod.c \ lfdemod.c \
emv/crypto.c\
emv/emv_pk.c\
emv/emv_pki.c\
emv/emv_pki_priv.c\
emv/apduinfo.c\ emv/apduinfo.c\
emv/dump.c\ emv/dump.c\
emv/tlv.c\ emv/tlv.c\

181
client/emv/crypto.c Normal file
View file

@ -0,0 +1,181 @@
/*
* libopenemv - a library to work with EMV family of smart cards
* Copyright (C) 2015 Dmitry Eremin-Solenikov
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "crypto.h"
#include "crypto_backend.h"
#include <string.h>
static struct crypto_backend *crypto_backend;
static bool crypto_init(void)
{
//const char *driver;
if (crypto_backend)
return true;
// crypto_backend = crypto_nettle_init(); TODO!!!!!
if (!crypto_backend)
return false;
return true;
}
struct crypto_hash *crypto_hash_open(enum crypto_algo_hash hash)
{
struct crypto_hash *ch;
if (!crypto_init())
return NULL;
ch = crypto_backend->hash_open(hash);
if (ch)
ch->algo = hash;
return ch;
}
void crypto_hash_close(struct crypto_hash *ch)
{
ch->close(ch);
}
void crypto_hash_write(struct crypto_hash *ch, const unsigned char *buf, size_t len)
{
ch->write(ch, buf, len);
}
unsigned char *crypto_hash_read(struct crypto_hash *ch)
{
return ch->read(ch);
}
size_t crypto_hash_get_size(const struct crypto_hash *ch)
{
return ch->get_size(ch);
}
struct crypto_pk *crypto_pk_open(enum crypto_algo_pk pk, ...)
{
struct crypto_pk *cp;
va_list vl;
if (!crypto_init())
return NULL;
va_start(vl, pk);
cp = crypto_backend->pk_open(pk, vl);
va_end(vl);
if (cp)
cp->algo = pk;
return cp;
}
struct crypto_pk *crypto_pk_open_priv(enum crypto_algo_pk pk, ...)
{
struct crypto_pk *cp;
va_list vl;
if (!crypto_init())
return NULL;
if (!crypto_backend->pk_open_priv)
return NULL;
va_start(vl, pk);
cp = crypto_backend->pk_open_priv(pk, vl);
va_end(vl);
if (cp)
cp->algo = pk;
return cp;
}
struct crypto_pk *crypto_pk_genkey(enum crypto_algo_pk pk, ...)
{
struct crypto_pk *cp;
va_list vl;
if (!crypto_init())
return NULL;
if (!crypto_backend->pk_genkey)
return NULL;
va_start(vl, pk);
cp = crypto_backend->pk_genkey(pk, vl);
va_end(vl);
if (cp)
cp->algo = pk;
return cp;
}
void crypto_pk_close(struct crypto_pk *cp)
{
cp->close(cp);
}
unsigned char *crypto_pk_encrypt(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen)
{
return cp->encrypt(cp, buf, len, clen);
}
unsigned char *crypto_pk_decrypt(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen)
{
if (!cp->decrypt) {
*clen = 0;
return NULL;
}
return cp->decrypt(cp, buf, len, clen);
}
enum crypto_algo_pk crypto_pk_get_algo(const struct crypto_pk *cp)
{
if (!cp)
return PK_INVALID;
return cp->algo;
}
size_t crypto_pk_get_nbits(const struct crypto_pk *cp)
{
if (!cp->get_nbits)
return 0;
return cp->get_nbits(cp);
}
unsigned char *crypto_pk_get_parameter(const struct crypto_pk *cp, unsigned param, size_t *plen)
{
*plen = 0;
if (!cp->get_parameter)
return NULL;
return cp->get_parameter(cp, param, plen);
}

48
client/emv/crypto.h Normal file
View file

@ -0,0 +1,48 @@
/*
* libopenemv - a library to work with EMV family of smart cards
* Copyright (C) 2015 Dmitry Eremin-Solenikov
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
#ifndef CRYPTO_H
#define CRYPTO_H
#include <stdbool.h>
#include <stddef.h>
enum crypto_algo_hash {
HASH_INVALID,
HASH_SHA_1,
};
struct crypto_hash *crypto_hash_open(enum crypto_algo_hash hash);
void crypto_hash_close(struct crypto_hash *ch);
void crypto_hash_write(struct crypto_hash *ch, const unsigned char *buf, size_t len);
unsigned char *crypto_hash_read(struct crypto_hash *ch);
size_t crypto_hash_get_size(const struct crypto_hash *ch);
enum crypto_algo_pk {
PK_INVALID,
PK_RSA,
};
struct crypto_pk *crypto_pk_open(enum crypto_algo_pk pk, ...);
struct crypto_pk *crypto_pk_open_priv(enum crypto_algo_pk pk, ...);
struct crypto_pk *crypto_pk_genkey(enum crypto_algo_pk pk, ...);
void crypto_pk_close(struct crypto_pk *cp);
unsigned char *crypto_pk_encrypt(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen);
unsigned char *crypto_pk_decrypt(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen);
enum crypto_algo_pk crypto_pk_get_algo(const struct crypto_pk *cp);
size_t crypto_pk_get_nbits(const struct crypto_pk *cp);
unsigned char *crypto_pk_get_parameter(const struct crypto_pk *cp, unsigned param, size_t *plen);
#endif

View file

@ -0,0 +1,48 @@
/*
* libopenemv - a library to work with EMV family of smart cards
* Copyright (C) 2015 Dmitry Eremin-Solenikov
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
#ifndef CRYPTO_BACKEND_H
#define CRYPTO_BACKEND_H
#include "crypto.h"
#include <stddef.h>
#include <stdarg.h>
struct crypto_hash {
enum crypto_algo_hash algo;
void (*write)(struct crypto_hash *ch, const unsigned char *buf, size_t len);
unsigned char *(*read)(struct crypto_hash *ch);
void (*close)(struct crypto_hash *ch);
size_t (*get_size)(const struct crypto_hash *ch);
};
struct crypto_pk {
enum crypto_algo_pk algo;
unsigned char *(*encrypt)(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen);
unsigned char *(*decrypt)(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen);
unsigned char *(*get_parameter)(const struct crypto_pk *cp, unsigned param, size_t *plen);
size_t (*get_nbits)(const struct crypto_pk *cp);
void (*close)(struct crypto_pk *cp);
};
struct crypto_backend {
struct crypto_hash *(*hash_open)(enum crypto_algo_hash hash);
struct crypto_pk *(*pk_open)(enum crypto_algo_pk pk, va_list vl);
struct crypto_pk *(*pk_open_priv)(enum crypto_algo_pk pk, va_list vl);
struct crypto_pk *(*pk_genkey)(enum crypto_algo_pk pk, va_list vl);
};
#endif

View file

@ -0,0 +1,532 @@
/*
* libopenemv - a library to work with EMV family of smart cards
* Copyright (C) 2015 Dmitry Eremin-Solenikov
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "openemv/crypto.h"
#include "crypto_backend.h"
#include <stdarg.h>
#include <stdio.h>
#define GCRYPT_NO_DEPRECATED
#define GCRYPT_NO_MPI_MACROS
#include <gcrypt.h>
struct crypto_hash_libgcrypt {
struct crypto_hash ch;
gcry_md_hd_t md;
};
static void crypto_hash_libgcrypt_close(struct crypto_hash *_ch)
{
struct crypto_hash_libgcrypt *ch = container_of(_ch, struct crypto_hash_libgcrypt, ch);
gcry_md_close(ch->md);
free(ch);
}
static void crypto_hash_libgcrypt_write(struct crypto_hash *_ch, const unsigned char *buf, size_t len)
{
struct crypto_hash_libgcrypt *ch = container_of(_ch, struct crypto_hash_libgcrypt, ch);
gcry_md_write(ch->md, buf, len);
}
static unsigned char *crypto_hash_libgcrypt_read(struct crypto_hash *_ch)
{
struct crypto_hash_libgcrypt *ch = container_of(_ch, struct crypto_hash_libgcrypt, ch);
return gcry_md_read(ch->md, 0);
}
static size_t crypto_hash_libgcrypt_get_size(const struct crypto_hash *ch)
{
int algo = GCRY_MD_NONE;
if (ch->algo == HASH_SHA_1)
algo = GCRY_MD_SHA1;
return gcry_md_get_algo_dlen(algo);
}
static struct crypto_hash *crypto_hash_libgcrypt_open(enum crypto_algo_hash hash)
{
struct crypto_hash_libgcrypt *ch = malloc(sizeof(*ch));
gcry_error_t err;
int algo = GCRY_MD_NONE;
if (hash == HASH_SHA_1)
algo = GCRY_MD_SHA1;
err = gcry_md_open(&ch->md, algo, 0);
if (err) {
fprintf(stderr, "LibGCrypt error %s/%s\n",
gcry_strsource (err),
gcry_strerror (err));
free(ch);
return NULL;
}
ch->ch.write = crypto_hash_libgcrypt_write;
ch->ch.read = crypto_hash_libgcrypt_read;
ch->ch.close = crypto_hash_libgcrypt_close;
ch->ch.get_size = crypto_hash_libgcrypt_get_size;
return &ch->ch;
}
struct crypto_pk_libgcrypt {
struct crypto_pk cp;
gcry_sexp_t pk;
};
static struct crypto_pk *crypto_pk_libgcrypt_open_rsa(va_list vl)
{
struct crypto_pk_libgcrypt *cp = malloc(sizeof(*cp));
gcry_error_t err;
char *mod = va_arg(vl, char *);
int modlen = va_arg(vl, size_t);
char *exp = va_arg(vl, char *);
int explen = va_arg(vl, size_t);
err = gcry_sexp_build(&cp->pk, NULL, "(public-key (rsa (n %b) (e %b)))",
modlen, mod, explen, exp);
if (err) {
fprintf(stderr, "LibGCrypt error %s/%s\n",
gcry_strsource (err),
gcry_strerror (err));
free(cp);
return NULL;
}
return &cp->cp;
}
static struct crypto_pk *crypto_pk_libgcrypt_open_priv_rsa(va_list vl)
{
struct crypto_pk_libgcrypt *cp = malloc(sizeof(*cp));
gcry_error_t err;
char *mod = va_arg(vl, char *);
int modlen = va_arg(vl, size_t);
char *exp = va_arg(vl, char *);
int explen = va_arg(vl, size_t);
char *d = va_arg(vl, char *);
int dlen = va_arg(vl, size_t);
char *p = va_arg(vl, char *);
int plen = va_arg(vl, size_t);
gcry_mpi_t pmpi;
char *q = va_arg(vl, char *);
int qlen = va_arg(vl, size_t);
gcry_mpi_t qmpi;
(void) va_arg(vl, char *);
(void) va_arg(vl, size_t);
(void) va_arg(vl, char *);
(void) va_arg(vl, size_t);
char *inv = va_arg(vl, char *);
int invlen = va_arg(vl, size_t);
gcry_mpi_t invmpi;
err = gcry_mpi_scan(&pmpi, GCRYMPI_FMT_USG, p, plen, NULL);
if (err)
goto err_p;
err = gcry_mpi_scan(&qmpi, GCRYMPI_FMT_USG, q, qlen, NULL);
if (err)
goto err_q;
err = gcry_mpi_scan(&invmpi, GCRYMPI_FMT_USG, inv, invlen, NULL);
if (err)
goto err_inv;
if (gcry_mpi_cmp (pmpi, qmpi) > 0) {
gcry_mpi_swap (pmpi, qmpi);
gcry_mpi_invm (invmpi, pmpi, qmpi);
}
err = gcry_sexp_build(&cp->pk, NULL, "(private-key (rsa (n %b) (e %b) (d %b) (p %M) (q %M) (u %M)))",
modlen, mod, explen, exp, dlen, d,
pmpi, qmpi, invmpi);
if (err)
goto err_sexp;
err = gcry_pk_testkey(cp->pk);
if (err)
goto err_test;
gcry_mpi_release(invmpi);
gcry_mpi_release(qmpi);
gcry_mpi_release(pmpi);
return &cp->cp;
err_test:
gcry_sexp_release(cp->pk);
err_sexp:
gcry_mpi_release(invmpi);
err_inv:
gcry_mpi_release(qmpi);
err_q:
gcry_mpi_release(pmpi);
err_p:
free(cp);
fprintf(stderr, "LibGCrypt error %s/%s\n",
gcry_strsource (err),
gcry_strerror (err));
return NULL;
}
static struct crypto_pk *crypto_pk_libgcrypt_genkey_rsa(va_list vl)
{
struct crypto_pk_libgcrypt *cp = malloc(sizeof(*cp));
gcry_error_t err;
gcry_sexp_t params;
int transient = va_arg(vl, int);
unsigned int nbits = va_arg(vl, unsigned int);
unsigned int exp = va_arg(vl, unsigned int);
err = gcry_sexp_build(&params, NULL,
transient ?
"(genkey (rsa (nbits %u) (rsa-use-e %u) (flags transient-key)))":
"(genkey (rsa (nbits %u) (rsa-use-e %u)))",
nbits, exp);
if (err) {
fprintf(stderr, "LibGCrypt error %s/%s\n",
gcry_strsource (err),
gcry_strerror (err));
free(cp);
return NULL;
}
err = gcry_pk_genkey(&cp->pk, params);
gcry_sexp_release(params);
if (err) {
fprintf(stderr, "LibGCrypt error %s/%s\n",
gcry_strsource (err),
gcry_strerror (err));
free(cp);
return NULL;
}
return &cp->cp;
}
static void crypto_pk_libgcrypt_close(struct crypto_pk *_cp)
{
struct crypto_pk_libgcrypt *cp = container_of(_cp, struct crypto_pk_libgcrypt, cp);
gcry_sexp_release(cp->pk);
free(cp);
}
static unsigned char *crypto_pk_libgcrypt_encrypt(const struct crypto_pk *_cp, const unsigned char *buf, size_t len, size_t *clen)
{
struct crypto_pk_libgcrypt *cp = container_of(_cp, struct crypto_pk_libgcrypt, cp);
gcry_error_t err;
int blen = len;
gcry_sexp_t dsexp, esexp, asexp;
gcry_mpi_t tmpi;
size_t templen;
size_t keysize;
unsigned char *result;
err = gcry_sexp_build(&dsexp, NULL, "(data (flags raw) (value %b))",
blen, buf);
if (err) {
fprintf(stderr, "LibGCrypt error %s/%s\n",
gcry_strsource (err),
gcry_strerror (err));
return NULL;
}
err = gcry_pk_encrypt(&esexp, dsexp, cp->pk);
gcry_sexp_release(dsexp);
if (err) {
fprintf(stderr, "LibGCrypt error %s/%s\n",
gcry_strsource (err),
gcry_strerror (err));
return NULL;
}
asexp = gcry_sexp_find_token(esexp, "a", 1);
gcry_sexp_release(esexp);
if (!asexp)
return NULL;
tmpi = gcry_sexp_nth_mpi(asexp, 1, GCRYMPI_FMT_USG);
gcry_sexp_release(asexp);
if (!tmpi)
return NULL;
keysize = (gcry_pk_get_nbits(cp->pk) + 7) / 8;
result = malloc(keysize);
if (!result) {
gcry_mpi_release(tmpi);
return NULL;
}
err = gcry_mpi_print(GCRYMPI_FMT_USG, NULL, keysize, &templen, tmpi);
if (err) {
fprintf(stderr, "LibGCrypt error %s/%s\n",
gcry_strsource (err),
gcry_strerror (err));
gcry_mpi_release(tmpi);
free(result);
return NULL;
}
err = gcry_mpi_print(GCRYMPI_FMT_USG, result + keysize - templen, templen, &templen, tmpi);
if (err) {
fprintf(stderr, "LibGCrypt error %s/%s\n",
gcry_strsource (err),
gcry_strerror (err));
gcry_mpi_release(tmpi);
free(result);
return NULL;
}
memset(result, 0, keysize - templen);
*clen = keysize;
gcry_mpi_release(tmpi);
return result;
}
static unsigned char *crypto_pk_libgcrypt_decrypt(const struct crypto_pk *_cp, const unsigned char *buf, size_t len, size_t *clen)
{
struct crypto_pk_libgcrypt *cp = container_of(_cp, struct crypto_pk_libgcrypt, cp);
gcry_error_t err;
int blen = len;
gcry_sexp_t esexp, dsexp;
gcry_mpi_t tmpi;
size_t templen;
size_t keysize;
unsigned char *result;
/* XXX: RSA-only! */
err = gcry_sexp_build(&esexp, NULL, "(enc-val (flags) (rsa (a %b)))",
blen, buf);
if (err) {
fprintf(stderr, "LibGCrypt error %s/%s\n",
gcry_strsource (err),
gcry_strerror (err));
return NULL;
}
err = gcry_pk_decrypt(&dsexp, esexp, cp->pk);
gcry_sexp_release(esexp);
if (err) {
fprintf(stderr, "LibGCrypt error %s/%s\n",
gcry_strsource (err),
gcry_strerror (err));
return NULL;
}
tmpi = gcry_sexp_nth_mpi(dsexp, 1, GCRYMPI_FMT_USG);
gcry_sexp_release(dsexp);
if (!tmpi)
return NULL;
keysize = (gcry_pk_get_nbits(cp->pk) + 7) / 8;
result = malloc(keysize);
if (!result) {
gcry_mpi_release(tmpi);
return NULL;
}
err = gcry_mpi_print(GCRYMPI_FMT_USG, NULL, keysize, &templen, tmpi);
if (err) {
fprintf(stderr, "LibGCrypt error %s/%s\n",
gcry_strsource (err),
gcry_strerror (err));
gcry_mpi_release(tmpi);
free(result);
return NULL;
}
err = gcry_mpi_print(GCRYMPI_FMT_USG, result + keysize - templen, templen, &templen, tmpi);
if (err) {
fprintf(stderr, "LibGCrypt error %s/%s\n",
gcry_strsource (err),
gcry_strerror (err));
gcry_mpi_release(tmpi);
free(result);
return NULL;
}
memset(result, 0, keysize - templen);
*clen = keysize;
gcry_mpi_release(tmpi);
return result;
}
static size_t crypto_pk_libgcrypt_get_nbits(const struct crypto_pk *_cp)
{
struct crypto_pk_libgcrypt *cp = container_of(_cp, struct crypto_pk_libgcrypt, cp);
return gcry_pk_get_nbits(cp->pk);
}
static unsigned char *crypto_pk_libgcrypt_get_parameter(const struct crypto_pk *_cp, unsigned param, size_t *plen)
{
struct crypto_pk_libgcrypt *cp = container_of(_cp, struct crypto_pk_libgcrypt, cp);
gcry_error_t err;
gcry_sexp_t psexp;
gcry_mpi_t tmpi;
size_t parameter_size;
unsigned char *result;
const char *name;
/* XXX: RSA-only! */
if (param == 0)
name = "n";
else if (param == 1)
name = "e";
else
return NULL;
psexp = gcry_sexp_find_token(cp->pk, name, 1);
if (!psexp)
return NULL;
tmpi = gcry_sexp_nth_mpi(psexp, 1, GCRYMPI_FMT_USG);
gcry_sexp_release(psexp);
if (!tmpi)
return NULL;
parameter_size = (gcry_mpi_get_nbits(tmpi) + 7) / 8;
result = malloc(parameter_size);
if (!result) {
gcry_mpi_release(tmpi);
return NULL;
}
err = gcry_mpi_print(GCRYMPI_FMT_USG, result, parameter_size, NULL, tmpi);
if (err) {
fprintf(stderr, "LibGCrypt error %s/%s\n",
gcry_strsource (err),
gcry_strerror (err));
gcry_mpi_release(tmpi);
free(result);
return NULL;
}
*plen = parameter_size;
gcry_mpi_release(tmpi);
return result;
}
static struct crypto_pk *crypto_pk_libgcrypt_open(enum crypto_algo_pk pk, va_list vl)
{
struct crypto_pk *cp;
if (pk == PK_RSA)
cp = crypto_pk_libgcrypt_open_rsa(vl);
else
return NULL;
cp->close = crypto_pk_libgcrypt_close;
cp->encrypt = crypto_pk_libgcrypt_encrypt;
cp->get_parameter = crypto_pk_libgcrypt_get_parameter;
cp->get_nbits = crypto_pk_libgcrypt_get_nbits;
return cp;
}
static struct crypto_pk *crypto_pk_libgcrypt_open_priv(enum crypto_algo_pk pk, va_list vl)
{
struct crypto_pk *cp;
if (pk == PK_RSA)
cp = crypto_pk_libgcrypt_open_priv_rsa(vl);
else
return NULL;
cp->close = crypto_pk_libgcrypt_close;
cp->encrypt = crypto_pk_libgcrypt_encrypt;
cp->decrypt = crypto_pk_libgcrypt_decrypt;
cp->get_parameter = crypto_pk_libgcrypt_get_parameter;
cp->get_nbits = crypto_pk_libgcrypt_get_nbits;
return cp;
}
static struct crypto_pk *crypto_pk_libgcrypt_genkey(enum crypto_algo_pk pk, va_list vl)
{
struct crypto_pk *cp;
if (pk == PK_RSA)
cp = crypto_pk_libgcrypt_genkey_rsa(vl);
else
return NULL;
cp->close = crypto_pk_libgcrypt_close;
cp->encrypt = crypto_pk_libgcrypt_encrypt;
cp->decrypt = crypto_pk_libgcrypt_decrypt;
cp->get_parameter = crypto_pk_libgcrypt_get_parameter;
cp->get_nbits = crypto_pk_libgcrypt_get_nbits;
return cp;
}
static struct crypto_backend crypto_libgcrypt_backend = {
.hash_open = crypto_hash_libgcrypt_open,
.pk_open = crypto_pk_libgcrypt_open,
.pk_open_priv = crypto_pk_libgcrypt_open_priv,
.pk_genkey = crypto_pk_libgcrypt_genkey,
};
struct crypto_backend *crypto_libgcrypt_init(void)
{
/* Version check should be the very first call because it
* makes sure that important subsystems are intialized. */
if (!gcry_check_version (GCRYPT_VERSION)) {
fputs ("libgcrypt version mismatch\n", stderr);
return NULL;
}
/* We don't want to see any warnings, e.g. because we have not yet
* parsed program options which might be used to suppress such
* warnings. */
gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
/* ... If required, other initialization goes here. Note that the
* process might still be running with increased privileges and that
* the secure memory has not been intialized. */
/* Allocate a pool of 16k secure memory. This make the secure memory
* available and also drops privileges where needed. */
gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
/* It is now okay to let Libgcrypt complain when there was/is
* a problem with the secure memory. */
gcry_control (GCRYCTL_RESUME_SECMEM_WARN);
/* ... If required, other initialization goes here. */
/* Tell Libgcrypt that initialization has completed. */
gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
// gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 3u , 0);
// gcry_control (GCRYCTL_PRINT_CONFIG, stdout);
return &crypto_libgcrypt_backend;
}

521
client/emv/emv_pk.c Normal file
View file

@ -0,0 +1,521 @@
/*
* libopenemv - a library to work with EMV family of smart cards
* Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "emv_pk.h"
#include "crypto.h"
/* For asprintf */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#define BCD(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : \
-1)
#define HEX(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : \
((c) >= 'A' && (c) <= 'F') ? ((c) - 'A' + 10) : \
((c) >= 'a' && (c) <= 'f') ? ((c) - 'a' + 10) : \
-1)
#define TOHEX(v) ((v) < 10 ? (v) + '0' : (v) - 10 + 'a')
static ssize_t emv_pk_read_bin(char *buf, unsigned char *bin, size_t size, size_t *read)
{
size_t left = size;
char *p = buf;
while (*p && *p == ' ')
p++;
while (left > 0) {
int c1, c2;
c1 = HEX(*p);
if (c1 == -1)
return -(p - buf);
p++;
c2 = HEX(*p);
if (c2 == -1)
return -(p - buf);
p++;
*bin = (c1 * 16 + c2);
bin ++;
left --;
if (*p == ':')
p++;
else if (read) {
*read = (size - left);
break;
} else if (left == 0)
break;
else
return -(p - buf);
}
while (*p && *p == ' ')
p++;
p--;
return (p - buf);
}
static ssize_t emv_pk_read_ymv(char *buf, unsigned *ymv)
{
int i;
unsigned char temp[3];
char *p = buf;
*ymv = 0;
while (*p && *p == ' ')
p++;
for (i = 0; i < 3; i++) {
int c1, c2;
c1 = BCD(*p);
if (c1 == -1)
return -(p - buf);
p++;
c2 = BCD(*p);
if (c2 == -1)
return -(p - buf);
p++;
temp[i] = (c1 * 16 + c2);
}
while (*p && *p == ' ')
p++;
p--;
if (temp[1] > 0x12 || temp[2] > 0x31)
return -(p - buf);
*ymv = (temp[0] * 0x10000 + temp[1] * 0x100 + temp[2]);
return (p - buf);
}
static ssize_t emv_pk_read_string(char *buf, char *str, size_t size)
{
char *p = buf;
while (*p && *p == ' ')
p++;
while (size > 1) {
if (*p == ' ')
break;
else if (*p < 0x20 || *p >= 0x7f)
return -(p - buf);
*str = *p;
p++;
str ++;
size --;
}
*str = 0;
while (*p && *p == ' ')
p++;
p--;
return (p - buf);
}
struct emv_pk *emv_pk_parse_pk(char *buf)
{
struct emv_pk *r = calloc(1, sizeof(*r));
ssize_t l;
char temp[10];
l = emv_pk_read_bin(buf, r->rid, 5, NULL);
if (l <= 0)
goto out;
buf += l;
l = emv_pk_read_bin(buf, &r->index, 1, NULL);
if (l <= 0)
goto out;
buf += l;
l = emv_pk_read_ymv(buf, &r->expire);
if (l <= 0)
goto out;
buf += l;
l = emv_pk_read_string(buf, temp, sizeof(temp));
if (l <= 0)
goto out;
buf += l;
if (!strcmp(temp, "rsa"))
r->pk_algo = PK_RSA;
else
goto out;
l = emv_pk_read_bin(buf, r->exp, sizeof(r->exp), &r->elen);
if (l <= 0)
goto out;
buf += l;
r->modulus = malloc(2048/8);
l = emv_pk_read_bin(buf, r->modulus, 2048/8, &r->mlen);
if (l <= 0)
goto out2;
buf += l;
l = emv_pk_read_string(buf, temp, sizeof(temp));
if (l <= 0)
goto out2;
buf += l;
if (!strcmp(temp, "sha1"))
r->hash_algo = HASH_SHA_1;
else
goto out2;
l = emv_pk_read_bin(buf, r->hash, 20, NULL);
if (l <= 0)
goto out2;
return r;
out2:
free(r->modulus);
out:
free(r);
return NULL;
}
static size_t emv_pk_write_bin(char *out, size_t outlen, const unsigned char *buf, size_t len)
{
int i;
size_t pos = 0;
if (len == 0)
return 0;
if (outlen < len * 3)
return 0;
out[pos++] = TOHEX(buf[0] >> 4);
out[pos++] = TOHEX(buf[0] & 0xf);
for (i = 1; i < len; i++) {
out[pos++] = ':';
out[pos++] = TOHEX(buf[i] >> 4);
out[pos++] = TOHEX(buf[i] & 0xf);
}
out[pos++] = ' ';
return pos;
}
static size_t emv_pk_write_str(char *out, size_t outlen, const char *str)
{
size_t len = strlen(str);
if (len == 0)
return 0;
if (outlen < len)
return 0;
memcpy(out, str, len);
return len;
}
char *emv_pk_dump_pk(const struct emv_pk *pk)
{
size_t outsize = 1024; /* should be enough */
char *out = malloc(outsize); /* should be enough */
size_t outpos = 0;
size_t rc;
if (!out)
return NULL;
rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->rid, 5);
if (rc == 0)
goto err;
outpos += rc;
rc = emv_pk_write_bin(out + outpos, outsize - outpos, &pk->index, 1);
if (rc == 0)
goto err;
outpos += rc;
if (outpos + 7 > outsize)
goto err;
out[outpos++] = TOHEX((pk->expire >> 20) & 0xf);
out[outpos++] = TOHEX((pk->expire >> 16) & 0xf);
out[outpos++] = TOHEX((pk->expire >> 12) & 0xf);
out[outpos++] = TOHEX((pk->expire >> 8 ) & 0xf);
out[outpos++] = TOHEX((pk->expire >> 4 ) & 0xf);
out[outpos++] = TOHEX((pk->expire >> 0 ) & 0xf);
out[outpos++] = ' ';
if (pk->pk_algo == PK_RSA) {
rc = emv_pk_write_str(out + outpos, outsize - outpos, "rsa");
if (rc == 0)
goto err;
outpos += rc;
out[outpos++] = ' ';
} else {
if (outpos + 4 > outsize)
goto err;
out[outpos++] = '?';
out[outpos++] = '?';
out[outpos++] = TOHEX(pk->pk_algo >> 4);
out[outpos++] = TOHEX(pk->pk_algo & 0xf);
}
rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->exp, pk->elen);
if (rc == 0)
goto err;
outpos += rc;
rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->modulus, pk->mlen);
if (rc == 0)
goto err;
outpos += rc;
if (pk->hash_algo == HASH_SHA_1) {
rc = emv_pk_write_str(out + outpos, outsize - outpos, "sha1");
if (rc == 0)
goto err;
outpos += rc;
out[outpos++] = ' ';
} else {
if (outpos + 4 > outsize)
goto err;
out[outpos++] = '?';
out[outpos++] = '?';
out[outpos++] = TOHEX(pk->pk_algo >> 4);
out[outpos++] = TOHEX(pk->pk_algo & 0xf);
}
rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->hash, 20);
if (rc == 0)
goto err;
outpos += rc;
out[outpos-1] = '\0';
return out;
err:
free(out);
return NULL;
}
bool emv_pk_verify(const struct emv_pk *pk)
{
struct crypto_hash *ch = crypto_hash_open(pk->hash_algo);
if (!ch)
return false;
crypto_hash_write(ch, pk->rid, sizeof(pk->rid));
crypto_hash_write(ch, &pk->index, 1);
crypto_hash_write(ch, pk->modulus, pk->mlen);
crypto_hash_write(ch, pk->exp, pk->elen);
unsigned char *h = crypto_hash_read(ch);
if (!h) {
crypto_hash_close(ch);
return false;
}
size_t hsize = crypto_hash_get_size(ch);
bool r = hsize && !memcmp(h, pk->hash, hsize) ? true : false;
crypto_hash_close(ch);
return r;
}
struct emv_pk *emv_pk_new(size_t modlen, size_t explen)
{
struct emv_pk *pk;
/* Not supported ATM */
if (explen > 3)
return NULL;
pk = calloc(1, sizeof(*pk));
if (!pk)
return NULL;
pk->mlen = modlen;
pk->elen = explen;
pk->modulus = calloc(modlen, 1);
if (!pk->modulus) {
free(pk);
pk = NULL;
}
return pk;
}
void emv_pk_free(struct emv_pk *pk)
{
if (!pk)
return;
free(pk->modulus);
free(pk);
}
static struct emv_pk *emv_pk_get_ca_pk_from_file(const char *fname,
const unsigned char *rid,
unsigned char idx)
{
if (!fname)
return NULL;
FILE *f = fopen(fname, "r");
if (!f) {
perror("fopen");
return NULL;
}
while (!feof(f)) {
char buf[BUFSIZ];
if (fgets(buf, sizeof(buf), f) == NULL)
break;
struct emv_pk *pk = emv_pk_parse_pk(buf);
if (!pk)
continue;
if (memcmp(pk->rid, rid, 5) || pk->index != idx) {
emv_pk_free(pk);
continue;
}
fclose(f);
return pk;
}
fclose(f);
return NULL;
}
char *emv_pk_get_ca_pk_file(const char *dirname, const unsigned char *rid, unsigned char idx)
{
if (!dirname)
dirname = ".";//openemv_config_get_str("capk.dir", NULL);
if (!dirname)
return NULL;
char *filename;
int ret = asprintf(&filename, "%s/%02hhx%02hhx%02hhx%02hhx%02hhx_%02hhx.0",
dirname,
rid[0],
rid[1],
rid[2],
rid[3],
rid[4],
idx);
if (ret <= 0)
return NULL;
return filename;
}
char *emv_pk_get_ca_pk_rid_file(const char *dirname, const unsigned char *rid)
{
if (!dirname)
dirname = "."; //openemv_config_get_str("capk.dir", NULL);
if (!dirname)
return NULL;
char *filename;
int ret = asprintf(&filename, "%s/%02hhx%02hhx%02hhx%02hhx%02hhx.pks",
dirname,
rid[0],
rid[1],
rid[2],
rid[3],
rid[4]);
if (ret <= 0)
return NULL;
return filename;
}
struct emv_pk *emv_pk_get_ca_pk(const unsigned char *rid, unsigned char idx)
{
struct emv_pk *pk = NULL;
if (!pk) {
char *fname = emv_pk_get_ca_pk_file(NULL, rid, idx);
if (fname) {
pk = emv_pk_get_ca_pk_from_file(fname, rid, idx);
free(fname);
}
}
if (!pk) {
char *fname = emv_pk_get_ca_pk_rid_file(NULL, rid);
if (fname) {
pk = emv_pk_get_ca_pk_from_file(fname, rid, idx);
free(fname);
}
}
if (!pk) {
const char *fname = "capk.txt"; //openemv_config_get_str("capk.file", NULL);
if (!fname) {
fprintf(stderr, "No CA PK file specified!\n");
return NULL;
}
pk = emv_pk_get_ca_pk_from_file(fname, rid, idx);
}
if (!pk)
return NULL;
printf("Verifying CA PK for %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx %zd bits...",
pk->rid[0],
pk->rid[1],
pk->rid[2],
pk->rid[3],
pk->rid[4],
pk->index,
pk->mlen * 8);
if (emv_pk_verify(pk)) {
printf("OK\n");
return pk;
}
printf("Failed!\n");
emv_pk_free(pk);
return NULL;
}

48
client/emv/emv_pk.h Normal file
View file

@ -0,0 +1,48 @@
/*
* libopenemv - a library to work with EMV family of smart cards
* Copyright (C) 2015 Dmitry Eremin-Solenikov
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
#ifndef EMV_PK_H
#define EMV_PK_H
#include <stdbool.h>
#include <stddef.h>
struct emv_pk {
unsigned char rid[5];
unsigned char index;
unsigned char serial[3];
unsigned char pan[10];
unsigned char hash_algo;
unsigned char pk_algo;
unsigned char hash[20];
unsigned char exp[3];
size_t elen;
size_t mlen;
unsigned char *modulus;
unsigned int expire;
};
#define EXPIRE(yy, mm, dd) 0x ## yy ## mm ## dd
struct emv_pk *emv_pk_parse_pk(char *buf);
struct emv_pk *emv_pk_new(size_t modlen, size_t explen);
void emv_pk_free(struct emv_pk *pk);
char *emv_pk_dump_pk(const struct emv_pk *pk);
bool emv_pk_verify(const struct emv_pk *pk);
char *emv_pk_get_ca_pk_file(const char *dirname, const unsigned char *rid, unsigned char idx);
char *emv_pk_get_ca_pk_rid_file(const char *dirname, const unsigned char *rid);
struct emv_pk *emv_pk_get_ca_pk(const unsigned char *rid, unsigned char idx);
#endif

390
client/emv/emv_pki.c Normal file
View file

@ -0,0 +1,390 @@
/*
* libopenemv - a library to work with EMV family of smart cards
* Copyright (C) 2015 Dmitry Eremin-Solenikov
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "emv_pki.h"
#include "crypto.h"
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
static const unsigned char empty_tlv_value[] = {};
static const struct tlv empty_tlv = {.tag = 0x0, .len = 0, .value = empty_tlv_value};
static size_t emv_pki_hash_psn[256] = { 0, 0, 11, 2, 17, 2, };
static unsigned char *emv_pki_decode_message(const struct emv_pk *enc_pk,
uint8_t msgtype,
size_t *len,
const struct tlv *cert_tlv,
... /* A list of tlv pointers, end with NULL */
)
{
struct crypto_pk *kcp;
unsigned char *data;
size_t data_len;
va_list vl;
if (!enc_pk)
return NULL;
if (!cert_tlv)
return NULL;
if (cert_tlv->len != enc_pk->mlen)
return NULL;
kcp = crypto_pk_open(enc_pk->pk_algo,
enc_pk->modulus, enc_pk->mlen,
enc_pk->exp, enc_pk->elen);
if (!kcp)
return NULL;
data = crypto_pk_encrypt(kcp, cert_tlv->value, cert_tlv->len, &data_len);
crypto_pk_close(kcp);
if (data[data_len-1] != 0xbc || data[0] != 0x6a || data[1] != msgtype) {
free(data);
return NULL;
}
size_t hash_pos = emv_pki_hash_psn[msgtype];
if (hash_pos == 0 || hash_pos > data_len){
free(data);
return NULL;
}
struct crypto_hash *ch;
ch = crypto_hash_open(data[hash_pos]);
if (!ch) {
free(data);
return NULL;
}
size_t hash_len = crypto_hash_get_size(ch);
crypto_hash_write(ch, data + 1, data_len - 2 - hash_len);
va_start(vl, cert_tlv);
while (true) {
const struct tlv *add_tlv = va_arg(vl, const struct tlv *);
if (!add_tlv)
break;
crypto_hash_write(ch, add_tlv->value, add_tlv->len);
}
va_end(vl);
if (memcmp(data + data_len - 1 - hash_len, crypto_hash_read(ch), hash_len)) {
crypto_hash_close(ch);
free(data);
return NULL;
}
crypto_hash_close(ch);
*len = data_len - hash_len - 1;
return data;
}
static unsigned emv_cn_length(const struct tlv *tlv)
{
int i;
for (i = 0; i < tlv->len; i++) {
unsigned char c = tlv->value[i];
if (c >> 4 == 0xf)
return 2 * i;
if ((c & 0xf) == 0xf)
return 2 * i + 1;
}
return 2 * tlv->len;
}
static unsigned char emv_cn_get(const struct tlv *tlv, unsigned pos)
{
if (pos > tlv->len * 2)
return 0xf;
unsigned char c = tlv->value[pos / 2];
if (pos % 2)
return c & 0xf;
else
return c >> 4;
}
static struct emv_pk *emv_pki_decode_key(const struct emv_pk *enc_pk,
unsigned char msgtype,
const struct tlv *pan_tlv,
const struct tlv *cert_tlv,
const struct tlv *exp_tlv,
const struct tlv *rem_tlv,
const struct tlv *add_tlv
)
{
size_t pan_length;
unsigned char *data;
size_t data_len;
size_t pk_len;
if (!cert_tlv || !exp_tlv || !pan_tlv)
return NULL;
if (!rem_tlv)
rem_tlv = &empty_tlv;
if (msgtype == 2)
pan_length = 4;
else if (msgtype == 4)
pan_length = 10;
else
return NULL;
data = emv_pki_decode_message(enc_pk, msgtype, &data_len,
cert_tlv,
rem_tlv,
exp_tlv,
add_tlv,
NULL);
if (!data || data_len < 11 + pan_length)
return NULL;
/* Perform the rest of checks here */
struct tlv pan2_tlv = {
.tag = 0x5a,
.len = pan_length,
.value = &data[2],
};
unsigned pan_len = emv_cn_length(pan_tlv);
unsigned pan2_len = emv_cn_length(&pan2_tlv);
if (((msgtype == 2) && (pan2_len < 4 || pan2_len > pan_len)) ||
((msgtype == 4) && (pan2_len != pan_len))) {
free(data);
return NULL;
}
unsigned i;
for (i = 0; i < pan2_len; i++)
if (emv_cn_get(pan_tlv, i) != emv_cn_get(&pan2_tlv, i)) {
free(data);
return NULL;
}
pk_len = data[9 + pan_length];
if (pk_len > data_len - 11 - pan_length + rem_tlv->len) {
free(data);
return NULL;
}
if (exp_tlv->len != data[10 + pan_length]) {
free(data);
return NULL;
}
struct emv_pk *pk = emv_pk_new(pk_len, exp_tlv->len);
memcpy(pk->rid, enc_pk->rid, 5);
pk->index = enc_pk->index;
pk->hash_algo = data[7 + pan_length];
pk->pk_algo = data[8 + pan_length];
pk->expire = (data[3 + pan_length] << 16) | (data[2 + pan_length] << 8) | 0x31;
memcpy(pk->serial, data + 4 + pan_length, 3);
memcpy(pk->pan, data + 2, pan_length);
memset(pk->pan + pan_length, 0xff, 10 - pan_length);
memcpy(pk->modulus, data + 11 + pan_length,
pk_len < data_len - (11 + pan_length) ?
pk_len :
data_len - (11 + pan_length));
memcpy(pk->modulus + data_len - (11 + pan_length), rem_tlv->value, rem_tlv->len);
memcpy(pk->exp, exp_tlv->value, exp_tlv->len);
free(data);
return pk;
}
struct emv_pk *emv_pki_recover_issuer_cert(const struct emv_pk *pk, struct tlvdb *db)
{
return emv_pki_decode_key(pk, 2,
tlvdb_get(db, 0x5a, NULL),
tlvdb_get(db, 0x90, NULL),
tlvdb_get(db, 0x9f32, NULL),
tlvdb_get(db, 0x92, NULL),
NULL);
}
struct emv_pk *emv_pki_recover_icc_cert(const struct emv_pk *pk, struct tlvdb *db, const struct tlv *sda_tlv)
{
return emv_pki_decode_key(pk, 4,
tlvdb_get(db, 0x5a, NULL),
tlvdb_get(db, 0x9f46, NULL),
tlvdb_get(db, 0x9f47, NULL),
tlvdb_get(db, 0x9f48, NULL),
sda_tlv);
}
struct emv_pk *emv_pki_recover_icc_pe_cert(const struct emv_pk *pk, struct tlvdb *db)
{
return emv_pki_decode_key(pk, 4,
tlvdb_get(db, 0x5a, NULL),
tlvdb_get(db, 0x9f2d, NULL),
tlvdb_get(db, 0x9f2e, NULL),
tlvdb_get(db, 0x9f2f, NULL),
NULL);
}
struct tlvdb *emv_pki_recover_dac(const struct emv_pk *enc_pk, const struct tlvdb *db, const struct tlv *sda_tlv)
{
size_t data_len;
unsigned char *data = emv_pki_decode_message(enc_pk, 3, &data_len,
tlvdb_get(db, 0x93, NULL),
sda_tlv,
NULL);
if (!data || data_len < 5)
return NULL;
struct tlvdb *dac_db = tlvdb_fixed(0x9f45, 2, data+3);
free(data);
return dac_db;
}
struct tlvdb *emv_pki_recover_idn(const struct emv_pk *enc_pk, const struct tlvdb *db, const struct tlv *dyn_tlv)
{
size_t data_len;
unsigned char *data = emv_pki_decode_message(enc_pk, 5, &data_len,
tlvdb_get(db, 0x9f4b, NULL),
dyn_tlv,
NULL);
if (!data || data_len < 3)
return NULL;
if (data[3] < 2 || data[3] > data_len - 3) {
free(data);
return NULL;
}
size_t idn_len = data[4];
if (idn_len > data[3] - 1) {
free(data);
return NULL;
}
struct tlvdb *idn_db = tlvdb_fixed(0x9f4c, idn_len, data + 5);
free(data);
return idn_db;
}
static bool tlv_hash(void *data, const struct tlv *tlv, int level, bool is_leaf)
{
struct crypto_hash *ch = data;
size_t tag_len;
unsigned char *tag;
if (tlv_is_constructed(tlv))
return true;
if (tlv->tag == 0x9f4b)
return true;
tag = tlv_encode(tlv, &tag_len);
crypto_hash_write(ch, tag, tag_len);
free(tag);
return true;
}
struct tlvdb *emv_pki_perform_cda(const struct emv_pk *enc_pk, const struct tlvdb *db,
const struct tlvdb *this_db,
const struct tlv *pdol_data_tlv,
const struct tlv *crm1_tlv,
const struct tlv *crm2_tlv)
{
const struct tlv *un_tlv = tlvdb_get(db, 0x9f37, NULL);
const struct tlv *cid_tlv = tlvdb_get(this_db, 0x9f27, NULL);
if (!un_tlv || !cid_tlv)
return NULL;
size_t data_len;
unsigned char *data = emv_pki_decode_message(enc_pk, 5, &data_len,
tlvdb_get(this_db, 0x9f4b, NULL),
un_tlv,
NULL);
if (!data || data_len < 3)
return NULL;
if (data[3] < 30 || data[3] > data_len - 4) {
free(data);
return NULL;
}
if (!cid_tlv || cid_tlv->len != 1 || cid_tlv->value[0] != data[5 + data[4]]) {
free(data);
return NULL;
}
struct crypto_hash *ch;
ch = crypto_hash_open(enc_pk->hash_algo);
if (!ch) {
free(data);
return NULL;
}
if (pdol_data_tlv)
crypto_hash_write(ch, pdol_data_tlv->value, pdol_data_tlv->len);
if (crm1_tlv)
crypto_hash_write(ch, crm1_tlv->value, crm1_tlv->len);
if (crm2_tlv)
crypto_hash_write(ch, crm2_tlv->value, crm2_tlv->len);
tlvdb_visit(this_db, tlv_hash, ch, 0);
if (memcmp(data + 5 + data[4] + 1 + 8, crypto_hash_read(ch), 20)) {
crypto_hash_close(ch);
free(data);
return NULL;
}
crypto_hash_close(ch);
size_t idn_len = data[4];
if (idn_len > data[3] - 1) {
free(data);
return NULL;
}
struct tlvdb *idn_db = tlvdb_fixed(0x9f4c, idn_len, data + 5);
free(data);
return idn_db;
}

36
client/emv/emv_pki.h Normal file
View file

@ -0,0 +1,36 @@
/*
* libopenemv - a library to work with EMV family of smart cards
* Copyright (C) 2015 Dmitry Eremin-Solenikov
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
#ifndef EMV_PKI_H
#define EMV_PKI_H
#include "emv_pk.h"
#include "tlv.h"
#include <stddef.h>
struct emv_pk *emv_pki_recover_issuer_cert(const struct emv_pk *pk, struct tlvdb *db);
struct emv_pk *emv_pki_recover_icc_cert(const struct emv_pk *pk, struct tlvdb *db, const struct tlv *sda_tlv);
struct emv_pk *emv_pki_recover_icc_pe_cert(const struct emv_pk *pk, struct tlvdb *db);
struct tlvdb *emv_pki_recover_dac(const struct emv_pk *pk, const struct tlvdb *db, const struct tlv *sda_tlv);
struct tlvdb *emv_pki_recover_idn(const struct emv_pk *pk, const struct tlvdb *db, const struct tlv *dyn_tlv);
struct tlvdb *emv_pki_perform_cda(const struct emv_pk *enc_pk, const struct tlvdb *db,
const struct tlvdb *this_db,
const struct tlv *pdol_data_tlv,
const struct tlv *crm1_tlv,
const struct tlv *crm2_tlv);
#endif

281
client/emv/emv_pki_priv.c Normal file
View file

@ -0,0 +1,281 @@
/*
* libopenemv - a library to work with EMV family of smart cards
* Copyright (C) 2015 Dmitry Eremin-Solenikov
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "emv_pki_priv.h"
#include "crypto.h"
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
struct emv_pk *emv_pki_make_ca(const struct crypto_pk *cp,
const unsigned char *rid, unsigned char index,
unsigned int expire, enum crypto_algo_hash hash_algo)
{
size_t modlen, explen;
unsigned char *mod, *exp;
if (!rid)
return NULL;
mod = crypto_pk_get_parameter(cp, 0, &modlen);
exp = crypto_pk_get_parameter(cp, 1, &explen);
if (!mod || !modlen || !exp || !explen) {
free(mod);
free(exp);
return NULL;
}
struct emv_pk *pk = emv_pk_new(modlen, explen);
memcpy(pk->rid, rid, 5);
pk->index = index;
pk->expire = expire;
pk->pk_algo = crypto_pk_get_algo(cp);
pk->hash_algo = hash_algo;
memcpy(pk->modulus, mod, modlen);
memcpy(pk->exp, exp, explen);
free(mod);
free(exp);
struct crypto_hash *ch = crypto_hash_open(pk->hash_algo);
if (!ch)
return false;
crypto_hash_write(ch, pk->rid, sizeof(pk->rid));
crypto_hash_write(ch, &pk->index, 1);
crypto_hash_write(ch, pk->modulus, pk->mlen);
crypto_hash_write(ch, pk->exp, pk->elen);
unsigned char *h = crypto_hash_read(ch);
if (!h) {
crypto_hash_close(ch);
emv_pk_free(pk);
return NULL;
}
memcpy(pk->hash, h, crypto_hash_get_size(ch));
crypto_hash_close(ch);
return pk;
}
static struct tlvdb *emv_pki_sign_message(const struct crypto_pk *cp,
tlv_tag_t cert_tag, tlv_tag_t rem_tag,
const unsigned char *msg, size_t msg_len,
... /* A list of tlv pointers, end with NULL */
)
{
size_t tmp_len = (crypto_pk_get_nbits(cp) + 7) / 8;
unsigned char *tmp = malloc(tmp_len);
if (!tmp)
return NULL;
// XXX
struct crypto_hash *ch = crypto_hash_open(HASH_SHA_1);
if (!ch) {
free(tmp);
return NULL;
}
tmp[0] = 0x6a;
tmp[tmp_len - 1] = 0xbc;
const unsigned char *rem;
size_t rem_len;
size_t hash_len = crypto_hash_get_size(ch);
size_t part_len = tmp_len - 2 - hash_len;
if (part_len < msg_len) {
memcpy(tmp + 1, msg, part_len);
rem = msg + part_len;
rem_len = msg_len - part_len;
} else {
memcpy(tmp + 1, msg, msg_len);
memset(tmp + 1 + msg_len, 0xbb, part_len - msg_len);
rem = NULL;
rem_len = 0;
}
crypto_hash_write(ch, tmp + 1, part_len);
crypto_hash_write(ch, rem, rem_len);
va_list vl;
va_start(vl, msg_len);
while (true) {
const struct tlv *add_tlv = va_arg(vl, const struct tlv *);
if (!add_tlv)
break;
crypto_hash_write(ch, add_tlv->value, add_tlv->len);
}
va_end(vl);
unsigned char *h = crypto_hash_read(ch);
if (!h) {
crypto_hash_close(ch);
free(tmp);
return NULL;
}
memcpy(tmp + 1 + part_len, h, hash_len);
crypto_hash_close(ch);
size_t cert_len;
unsigned char *cert = crypto_pk_decrypt(cp, tmp, tmp_len, &cert_len);
free(tmp);
if (!cert)
return NULL;
struct tlvdb *db = tlvdb_fixed(cert_tag, cert_len, cert);
free(cert);
if (!db)
return NULL;
if (rem) {
struct tlvdb *rdb = tlvdb_fixed(rem_tag, rem_len, rem);
if (!rdb) {
tlvdb_free(db);
return NULL;
}
tlvdb_add(db, rdb);
}
return db;
}
static struct tlvdb *emv_pki_sign_key(const struct crypto_pk *cp,
struct emv_pk *ipk,
unsigned char msgtype,
size_t pan_len,
tlv_tag_t cert_tag,
tlv_tag_t exp_tag,
tlv_tag_t rem_tag,
const struct tlv *add_tlv
)
{
unsigned pos = 0;
unsigned char *msg = malloc(1 + pan_len + 2 + 3 + 1 + 1 + 1 + 1 + ipk->mlen);
if (!msg)
return NULL;
msg[pos++] = msgtype;
memcpy(msg + pos, ipk->pan, pan_len); pos += pan_len;
msg[pos++] = (ipk->expire >> 8) & 0xff;
msg[pos++] = (ipk->expire >> 16) & 0xff;
memcpy(msg + pos, ipk->serial, 3); pos += 3;
msg[pos++] = ipk->hash_algo;
msg[pos++] = ipk->pk_algo;
msg[pos++] = ipk->mlen;
msg[pos++] = ipk->elen;
memcpy(msg + pos, ipk->modulus, ipk->mlen);
pos += ipk->mlen;
struct tlvdb *exp_db = tlvdb_fixed(exp_tag, ipk->elen, ipk->exp);
if (!exp_db) {
free(msg);
return NULL;
}
struct tlvdb *db = emv_pki_sign_message(cp,
cert_tag, rem_tag,
msg, pos,
tlvdb_get(exp_db, exp_tag, NULL),
add_tlv,
NULL);
free(msg);
if (!db)
return NULL;
tlvdb_add(db, exp_db);
return db;
}
struct tlvdb *emv_pki_sign_issuer_cert(const struct crypto_pk *cp, struct emv_pk *issuer_pk)
{
return emv_pki_sign_key(cp, issuer_pk, 2, 4, 0x90, 0x9f32, 0x92, NULL);
}
struct tlvdb *emv_pki_sign_icc_cert(const struct crypto_pk *cp, struct emv_pk *icc_pk, const struct tlv *sda_tlv)
{
return emv_pki_sign_key(cp, icc_pk, 4, 10, 0x9f46, 0x9f47, 0x9f48, sda_tlv);
}
struct tlvdb *emv_pki_sign_icc_pe_cert(const struct crypto_pk *cp, struct emv_pk *icc_pe_pk)
{
return emv_pki_sign_key(cp, icc_pe_pk, 4, 10, 0x9f2d, 0x9f2e, 0x9f2f, NULL);
}
struct tlvdb *emv_pki_sign_dac(const struct crypto_pk *cp, const struct tlv *dac_tlv, const struct tlv *sda_tlv)
{
unsigned pos = 0;
unsigned char *msg = malloc(1+1+dac_tlv->len);
if (!msg)
return NULL;
msg[pos++] = 3;
msg[pos++] = HASH_SHA_1;
memcpy(msg+pos, dac_tlv->value, dac_tlv->len);
pos += dac_tlv->len;
struct tlvdb *db = emv_pki_sign_message(cp,
0x93, 0,
msg, pos,
sda_tlv,
NULL);
free(msg);
return db;
}
struct tlvdb *emv_pki_sign_idn(const struct crypto_pk *cp, const struct tlv *idn_tlv, const struct tlv *dyn_tlv)
{
unsigned pos = 0;
unsigned char *msg = malloc(1+1+1+1+idn_tlv->len);
if (!msg)
return NULL;
msg[pos++] = 5;
msg[pos++] = HASH_SHA_1;
msg[pos++] = idn_tlv->len + 1;
msg[pos++] = idn_tlv->len;
memcpy(msg+pos, idn_tlv->value, idn_tlv->len);
pos += idn_tlv->len;
struct tlvdb *db = emv_pki_sign_message(cp,
0x9f4b, 0,
msg, pos,
dyn_tlv,
NULL);
free(msg);
return db;
}

35
client/emv/emv_pki_priv.h Normal file
View file

@ -0,0 +1,35 @@
/*
* libopenemv - a library to work with EMV family of smart cards
* Copyright (C) 2015 Dmitry Eremin-Solenikov
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
#ifndef EMV_PKI_PRIV_H
#define EMV_PKI_PRIV_H
#include "crypto.h"
#include "emv_pk.h"
#include "tlv.h"
#include <stddef.h>
struct emv_pk *emv_pki_make_ca(const struct crypto_pk *cp,
const unsigned char *rid, unsigned char index,
unsigned int expire, enum crypto_algo_hash hash_algo);
struct tlvdb *emv_pki_sign_issuer_cert(const struct crypto_pk *cp, struct emv_pk *issuer_pk);
struct tlvdb *emv_pki_sign_icc_cert(const struct crypto_pk *cp, struct emv_pk *icc_pk, const struct tlv *sda_tlv);
struct tlvdb *emv_pki_sign_icc_pe_cert(const struct crypto_pk *cp, struct emv_pk *icc_pe_pk);
struct tlvdb *emv_pki_sign_dac(const struct crypto_pk *cp, const struct tlv *dac_tlv, const struct tlv *sda_tlv);
struct tlvdb *emv_pki_sign_idn(const struct crypto_pk *cp, const struct tlv *idn_tlv, const struct tlv *dyn_tlv);
#endif