summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt22
-rw-r--r--src/bac.c146
-rw-r--r--src/bac.h36
-rw-r--r--src/bachelper.c355
-rw-r--r--src/bachelper.h48
-rw-r--r--src/crypto-gcrypt.c182
-rw-r--r--src/crypto-tomcrypt.c211
-rw-r--r--src/crypto.h47
-rw-r--r--src/fileread.c307
-rw-r--r--src/fileread.h32
-rw-r--r--src/hardware.c21
-rw-r--r--src/mrtd.h30
-rw-r--r--src/mrtdreader.c298
13 files changed, 1735 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..d88d3e7
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,22 @@
+add_library(mrtd SHARED bac.c bachelper.c fileread.c hardware.c crypto-gcrypt.c)
+target_link_libraries(mrtd nfc)
+target_link_libraries(mrtd usb)
+target_link_libraries(mrtd gcrypt)
+
+add_executable(mrtdreader mrtdreader.c)
+target_link_libraries(mrtdreader mrtd)
+
+set_target_properties(mrtd PROPERTIES VERSION 0.0.1 SOVERSION 0)
+
+install(TARGETS mrtdreader DESTINATION bin)
+if(${CMAKE_INSTALL_PREFIX} STREQUAL "/usr")
+ install(TARGETS mrtd DESTINATION lib/${CMAKE_LIBRARY_ARCHITECTURE})
+else()
+ install(TARGETS mrtd DESTINATION lib)
+endif()
+
+install(FILES mrtd.h DESTINATION include/mrtd)
+install(FILES bac.h DESTINATION include/mrtd)
+install(FILES bachelper.h DESTINATION include/mrtd)
+install(FILES crypto.h DESTINATION include/mrtd)
+install(FILES fileread.h DESTINATION include/mrtd)
diff --git a/src/bac.c b/src/bac.c
new file mode 100644
index 0000000..588de54
--- /dev/null
+++ b/src/bac.c
@@ -0,0 +1,146 @@
+/*
+ * bac.c - Basic Access Control
+ *
+ * Copyright (C) 2014 Ruben Undheim <ruben.undheim@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <nfc/nfc.h>
+#include "bachelper.h"
+#include "bac.h"
+
+static uint8_t rnd_ifd_int[8] = {0x78,0x17,0x23,0x86,0x0c,0x06,0xc2,0x26};
+static uint8_t kifd_int[16] = {0x0b,0x79,0x52,0x40,0xcb,0x70,0x49,0xb0,0x1c,0x19,0xb3,0x3e,0x32,0x80,0x4f,0x0b};
+
+int mrtd_bac_keyhandshake(nfc_device *pnd, const uint8_t *kmrz, uint8_t *ksenc, uint8_t *ksmac, uint64_t *ssc_long)
+{
+ int i;
+ int res;
+ uint8_t txbuffer[300];
+ int txlen;
+ uint8_t rxbuffer[300];
+ int rxlen;
+
+ txlen = 12;
+ memcpy(txbuffer, "\x00\xa4\x04\x0c\x07\xa0\x00\x00\x02\x47\x10\x01", txlen);
+ rxlen = sizeof(rxbuffer);
+ if((res = nfc_initiator_transceive_bytes(pnd,txbuffer,txlen,rxbuffer,rxlen,500)) < 0){
+ fprintf(stderr,"Unable to send");
+ goto failed;
+ }
+ else{
+ rxlen = res;
+ }
+
+ txlen = 5;
+ memcpy(txbuffer, "\x00\x84\x00\x00\x08", txlen);
+ rxlen = sizeof(rxbuffer);
+ if((res = nfc_initiator_transceive_bytes(pnd,txbuffer,txlen,rxbuffer,rxlen,500)) < 0){
+ fprintf(stderr,"Unable to send");
+ goto failed;
+ }
+ else{
+ rxlen = res;
+ }
+
+ uint8_t remotechallenge[8];
+ memcpy(remotechallenge,rxbuffer,8);
+
+
+ uint8_t kenc[16];
+ uint8_t kmac[16];
+
+ mrtd_bac_kmrz_to_kenc_kmac(kmrz,kenc,kmac);
+
+ uint8_t cmd_data[40];
+
+ mrtd_bac_cmd_data(rnd_ifd_int,kifd_int,remotechallenge,kenc,kmac,cmd_data);
+
+ txlen = 46;
+ memcpy(txbuffer, "\x00\x82\x00\x00\x28", 5);
+ memcpy(txbuffer+5,cmd_data,40);
+ txbuffer[45] = 0x28;
+ rxlen = sizeof(rxbuffer);
+ if((res = nfc_initiator_transceive_bytes(pnd,txbuffer,txlen,rxbuffer,rxlen,100000)) < 0){
+ fprintf(stderr,"Unable to send");
+ goto failed;
+ }
+ else{
+ rxlen = res;
+ }
+
+ uint8_t rnd_icc[8];
+ uint8_t kicc[16];
+
+ if(mrtd_bac_challenge_ok(rxbuffer,kenc,rnd_ifd_int,rnd_icc,kicc)){
+ }
+ else {
+ goto challengefailed;
+ }
+ uint8_t xored[16];
+
+ for(i=0;i<16;i++){
+ xored[i] = kifd_int[i] ^ kicc[i];
+ }
+
+
+ mrtd_bac_kenc_kmac(xored,ksenc,ksmac);
+
+ //printhex("ksenc",ksenc,16);
+ //printhex("ksmac",ksmac,16);
+
+ (*ssc_long) = mrtd_bac_get_ssc(remotechallenge,rnd_ifd_int);
+
+ //printf("ssc: %lx\n",ssc_long);
+
+ return 0;
+
+ failed:
+ return -1;
+ challengefailed:
+ return RET_CHALLENGE_FAILED;
+}
+
+void mrtd_bac_set_rndifd_kifd(const uint8_t *rnd_ifd, const uint8_t *kifd)
+{
+ int i;
+ int r = rand();
+ for(i=0;i<8;i++){
+ rnd_ifd_int[i] = rnd_ifd[i];
+ }
+ for(i=0;i<16;i++){
+ kifd_int[i] = kifd[i];
+ }
+}
+
+void mrtd_bac_randomize_rndifd_kifd()
+{
+ int i,r;
+ for(i=0;i<8;i++){
+ r = rand();
+ rnd_ifd_int[i] = (uint8_t)(r & 0xff);
+ }
+ for(i=0;i<16;i++){
+ r = rand();
+ kifd_int[i] = (uint8_t)(r & 0xff);
+ }
+}
+
+
diff --git a/src/bac.h b/src/bac.h
new file mode 100644
index 0000000..1f928a2
--- /dev/null
+++ b/src/bac.h
@@ -0,0 +1,36 @@
+/*
+ * header for bac.c
+ *
+ * Copyright (C) 2014 Ruben Undheim <ruben.undheim@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#ifndef INC_BAC_H
+#define INC_BAC_H
+
+#include <stdint.h>
+
+#define RET_CHALLENGE_FAILED -2
+
+int mrtd_bac_keyhandshake(nfc_device *pnd, const uint8_t *kmrz, uint8_t *ksenc, uint8_t *ksmac, uint64_t *ssc_long);
+
+void mrtd_bac_set_rndifd_kifd(const uint8_t *rnd_ifd, const uint8_t *kifd);
+
+void mrtd_bac_randomize_rndifd_kifd();
+
+#endif /* INC_BAC_H */
+
diff --git a/src/bachelper.c b/src/bachelper.c
new file mode 100644
index 0000000..c06925f
--- /dev/null
+++ b/src/bachelper.c
@@ -0,0 +1,355 @@
+/*
+ * bachelper.c - functions that implement basic algorithms that are
+ * part of the BAC procedure
+ *
+ * Copyright (C) 2014 Ruben Undheim <ruben.undheim@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include "crypto.h"
+
+
+static int endianness()
+{
+ int i = 1;
+ char *p = (char *)&i;
+ if(p[0] == 1)
+ return 0;
+ else
+ return 1;
+}
+
+void mrtd_bac_kenc_kmac(const uint8_t *input, uint8_t *kenc, uint8_t *kmac)
+{
+ uint8_t hash[20];
+ uint8_t k[20];
+ uint8_t kenc_tmp[20];
+ uint8_t kmac_tmp[20];
+ int tmp;
+ memcpy(hash,input,16);
+ memcpy(hash+16,"\x00\x00\x00\x01",4);
+ mrtd_crypto_sha1(hash,20,k);
+ mrtd_crypto_fix_parity(k,kenc_tmp,20,&tmp);
+ memcpy(kenc,kenc_tmp,16);
+
+ hash[19] = 0x02;
+ mrtd_crypto_sha1(hash,20,k);
+ mrtd_crypto_fix_parity(k,kmac_tmp,20,&tmp);
+ memcpy(kmac,kmac_tmp,16);
+}
+
+void mrtd_bac_kmrz_to_kenc_kmac(const uint8_t *kmrz, uint8_t *kenc, uint8_t *kmac)
+{
+ uint8_t hash[20];
+ mrtd_crypto_sha1(kmrz,24,hash);
+ mrtd_bac_kenc_kmac(hash,kenc,kmac);
+}
+
+void mrtd_bac_eifd_mifd(const uint8_t *rnd_ifd, const uint8_t *kifd, const uint8_t *remote_challenge, const uint8_t *kenc, const uint8_t *kmac, uint8_t *eifd, uint8_t *mifd)
+{
+ uint8_t S[32];
+ memcpy(S,rnd_ifd,8);
+ memcpy(S+8,remote_challenge,8);
+ memcpy(S+16,kifd,16);
+
+ mrtd_crypto_encrypt_3des(S,eifd,32,kenc);
+ mrtd_crypto_mac_padding(eifd,mifd,32,kmac);
+}
+
+void mrtd_bac_cmd_data(const uint8_t *rnd_ifd, const uint8_t *kifd, const uint8_t *remote_challenge, const uint8_t *kenc, const uint8_t *kmac, uint8_t *cmd_data)
+{
+ mrtd_bac_eifd_mifd(rnd_ifd,kifd,remote_challenge,kenc,kmac,cmd_data,cmd_data+32);
+}
+
+
+char mrtd_bac_challenge_ok(const uint8_t *rx_data, const uint8_t *kenc, const uint8_t *rnd_ifd, uint8_t *rnd_icc, uint8_t *kicc)
+{
+ int i;
+ uint8_t decryptedresp[32];
+ const uint8_t *resp;
+ const uint8_t *mac_received;
+ uint8_t *rec_ifd;
+
+ resp = rx_data;
+ mac_received = rx_data+32;
+
+ mrtd_crypto_decrypt_3des(resp,decryptedresp,32,kenc);
+
+ if(rnd_icc != NULL)
+ memcpy(rnd_icc,decryptedresp,8);
+ if(kicc != NULL)
+ memcpy(kicc,decryptedresp+16,16);
+ rec_ifd = decryptedresp+8;
+ for(i=0;i<8;i++){
+ if(rec_ifd[i] != rnd_ifd[i]){
+ return 0;
+ }
+ }
+ return 1;
+}
+
+uint64_t mrtd_bac_get_ssc(const uint8_t *remote_challenge, const uint8_t *rnd_ifd)
+{
+ char ssc[8];
+ uint64_t ssc_long;
+
+ memcpy(ssc,remote_challenge+4,4);
+ memcpy(ssc+4,rnd_ifd+4,4);
+
+ if(endianness()){
+ *(((uint8_t*)(&ssc_long))+0) = ssc[0];
+ *(((uint8_t*)(&ssc_long))+1) = ssc[1];
+ *(((uint8_t*)(&ssc_long))+2) = ssc[2];
+ *(((uint8_t*)(&ssc_long))+3) = ssc[3];
+ *(((uint8_t*)(&ssc_long))+4) = ssc[4];
+ *(((uint8_t*)(&ssc_long))+5) = ssc[5];
+ *(((uint8_t*)(&ssc_long))+6) = ssc[6];
+ *(((uint8_t*)(&ssc_long))+7) = ssc[7];
+ }
+ else {
+ *(((uint8_t*)(&ssc_long))+7) = ssc[0];
+ *(((uint8_t*)(&ssc_long))+6) = ssc[1];
+ *(((uint8_t*)(&ssc_long))+5) = ssc[2];
+ *(((uint8_t*)(&ssc_long))+4) = ssc[3];
+ *(((uint8_t*)(&ssc_long))+3) = ssc[4];
+ *(((uint8_t*)(&ssc_long))+2) = ssc[5];
+ *(((uint8_t*)(&ssc_long))+1) = ssc[6];
+ *(((uint8_t*)(&ssc_long))+0) = ssc[7];
+ }
+
+ return ssc_long;
+}
+
+
+void mrtd_bac_protected_apdu(const uint8_t *input, uint8_t *output, const int length, int *outputlength, const uint8_t *ksenc, const uint8_t *ksmac, const uint64_t ssc_long)
+{
+ int datalength;
+ char has_le;
+ uint8_t *do87 = NULL;
+ uint8_t do8e[10];
+ uint8_t padded_command[8];
+ uint8_t *A;
+ int padded_data_length;
+ int do87_length;
+ uint8_t *do97 = NULL;
+ int do97_length;
+ uint8_t le;
+
+ if(length > 5){
+ datalength = (int)input[4];
+ }
+ else{
+ datalength = 0;
+ }
+ if(datalength != 0 ? length > (5+datalength) : length == 5){
+ le = input[length-1];
+ has_le = 1;
+ }
+ else{
+ le = 0;
+ has_le = 0;
+ }
+
+ //printf("datalength: %d\n",datalength);
+ //printf("hasle: %d\n",has_le);
+ int i;
+
+ if(datalength != 0){
+ uint8_t *padded_data;
+ padded_data = malloc(((datalength+8)/8)*8);
+ mrtd_crypto_padding(input+5,padded_data,datalength,&padded_data_length);
+ do87 = malloc(padded_data_length+3);
+ mrtd_crypto_encrypt_3des(padded_data,do87+3,padded_data_length,ksenc);
+ do87[0] = 0x87;
+ do87[1] = 0x09;
+ do87[2] = 0x01;
+ do87_length = padded_data_length+3;
+
+ free(padded_data);
+ }
+ else{
+ do87_length = 0;
+ padded_data_length = 0;
+ }
+
+ if(has_le){
+ do97_length = 3;
+ do97 = malloc(do97_length);
+ do97[0] = 0x97;
+ do97[1] = 0x01;
+ do97[2] = le;
+ }
+ else{
+ do97_length = 0;
+ }
+
+ int padded_command_length;
+ mrtd_crypto_padding(input,padded_command,4,&padded_command_length);
+ padded_command[0] = 0x0c;
+
+
+ A = malloc(16+do87_length+do97_length);
+ if(endianness()){
+ A[0] = *(((uint8_t*)(&ssc_long))+0);
+ A[1] = *(((uint8_t*)(&ssc_long))+1);
+ A[2] = *(((uint8_t*)(&ssc_long))+2);
+ A[3] = *(((uint8_t*)(&ssc_long))+3);
+ A[4] = *(((uint8_t*)(&ssc_long))+4);
+ A[5] = *(((uint8_t*)(&ssc_long))+5);
+ A[6] = *(((uint8_t*)(&ssc_long))+6);
+ A[7] = *(((uint8_t*)(&ssc_long))+7);
+ }
+ else {
+ A[0] = *(((uint8_t*)(&ssc_long))+7);
+ A[1] = *(((uint8_t*)(&ssc_long))+6);
+ A[2] = *(((uint8_t*)(&ssc_long))+5);
+ A[3] = *(((uint8_t*)(&ssc_long))+4);
+ A[4] = *(((uint8_t*)(&ssc_long))+3);
+ A[5] = *(((uint8_t*)(&ssc_long))+2);
+ A[6] = *(((uint8_t*)(&ssc_long))+1);
+ A[7] = *(((uint8_t*)(&ssc_long))+0);
+ }
+ memcpy(A+8,padded_command,8);
+ if(do87 != NULL)
+ memcpy(A+16,do87,do87_length);
+ if(do97 != NULL)
+ memcpy(A+16+do87_length,do97,do97_length);
+
+
+ do8e[0] = 0x8e;
+ do8e[1] = 0x08;
+
+ mrtd_crypto_mac_padding(A,do8e+2,16+do87_length+do97_length,ksmac);
+
+ (*outputlength) = 5+do87_length+do97_length+10+1;
+ memcpy(output,padded_command,4);
+ output[4] = (*outputlength)-6;
+ if(do87 != NULL)
+ memcpy(output+5,do87,do87_length);
+ if(do97 != NULL)
+ memcpy(output+5+do87_length,do97,do97_length);
+ memcpy(output+5+do87_length+do97_length,do8e,10);
+ output[(*outputlength)-1] = 0x00;
+
+ //printf("tot_length: %d\n",*outputlength);
+
+ free(A);
+ if(do87 != NULL)
+ free(do87);
+ if(do97 != NULL)
+ free(do97);
+
+ return ;
+}
+
+void mrtd_bac_decrypt_response(const uint8_t *input, uint8_t *output, const int length, int *outputlength, uint8_t *ksenc)
+{
+ uint8_t *tmp;
+ int tmplength;
+ const uint8_t *tobedecrypted = input+3;
+ tmplength = length-16-3;
+ if(tmplength > 0){
+ tmp = malloc(tmplength);
+
+ mrtd_crypto_decrypt_3des(tobedecrypted,tmp,tmplength,ksenc);
+
+ mrtd_crypto_padding_remove(tmp,output,tmplength,outputlength);
+
+ free(tmp);
+ }
+ else {
+ (*outputlength) = 0;
+ }
+}
+
+static int mrtd_get_mrz_weight(int i)
+{
+ switch(i){
+ case(0): return 7;
+ case(1): return 3;
+ case(2): return 1;
+ }
+}
+
+int mrtd_bac_check_digit(const uint8_t *input, const int length)
+{
+ int i;
+ int tmp;
+ int out_value;
+ int check_digit;
+ check_digit = 0;
+ for(i=0;i<length;i++){
+ if (input[i] >= 'A' && input[i] <= 'Z')
+ tmp = input[i] - 55;
+ else if (input[i] == '<')
+ tmp = 0;
+ else
+ tmp = input[i] - 0x30;
+ check_digit += tmp * mrtd_get_mrz_weight(i % 3);
+ }
+ return (check_digit % 10);
+}
+
+void mrtd_bac_get_kmrz(const uint8_t *pn, const uint8_t *dob, const uint8_t *eov, uint8_t *output)
+{
+ uint8_t tmp[20];
+ int cd;
+ int len;
+
+ len = strlen(pn);
+ if (len < 9){
+ memcpy(output,pn,len);
+ memset(output+len,'<',9-len);
+ }
+ else {
+ memcpy(output,pn,9);
+ }
+
+ cd = mrtd_bac_check_digit(output,9);
+ output[9] = (uint8_t)(cd+0x30);
+
+ memcpy(output+10,dob,6);
+ cd = mrtd_bac_check_digit(dob,6);
+ output[16] = (uint8_t)(cd+0x30);
+
+ memcpy(output+17,eov,6);
+ cd = mrtd_bac_check_digit(eov,6);
+ output[23] = (uint8_t)(cd+0x30);
+ output[24] = 0;
+
+}
+
+void mrtd_bac_get_kmrz_from_mrz(const uint8_t *mrz, uint8_t *kmrz)
+{
+ uint8_t pn[9];
+ uint8_t dob[6];
+ uint8_t eov[6];
+
+ memcpy(pn,mrz,9);
+ memcpy(dob,mrz+13,6);
+ memcpy(eov,mrz+21,6);
+
+ mrtd_bac_get_kmrz(pn,dob,eov,kmrz);
+
+}
+
+
diff --git a/src/bachelper.h b/src/bachelper.h
new file mode 100644
index 0000000..e428723
--- /dev/null
+++ b/src/bachelper.h
@@ -0,0 +1,48 @@
+/*
+ * header for bachelper.c
+ *
+ * Copyright (C) 2014 Ruben Undheim <ruben.undheim@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef INC_BAC_HELPER_H
+#define INC_BAC_HELPER_H
+
+#include <stdint.h>
+
+void mrtd_bac_kmrz_to_kenc_kmac(const uint8_t *kmrz, uint8_t *kenc, uint8_t *kmac);
+
+void mrtd_bac_kenc_kmac(const uint8_t *input, uint8_t *kenc, uint8_t *kmac);
+
+void mrtd_bac_eifd_mifd(const uint8_t *rnd_ifd, const uint8_t *kifd, const uint8_t *remote_challenge, const uint8_t *kenc, const uint8_t *kmac, uint8_t *eifd, uint8_t *mifd);
+
+void mrtd_bac_cmd_data(const uint8_t *rnd_ifd, const uint8_t *kifd, const uint8_t *remote_challenge, const uint8_t *kenc, const uint8_t *kmac, uint8_t *cmd_data);
+
+char mrtd_bac_challenge_ok(const uint8_t *rx_data, const uint8_t *kenc, const uint8_t *rnd_ifd, uint8_t *rnd_icc, uint8_t *kicc);
+
+uint64_t mrtd_bac_get_ssc(const uint8_t *remote_challenge, const uint8_t *rnd_ifd);
+
+void mrtd_bac_protected_apdu(const uint8_t *input, uint8_t *output, const int length, int *outputlength, const uint8_t *ksenc, const uint8_t *ksmac, const uint64_t ssc_long);
+
+void mrtd_bac_decrypt_response(const uint8_t *input, uint8_t *output, const int length, int *outputlength, const uint8_t *ksenc);
+
+int mrtd_bac_check_digit(const uint8_t *input, const int length);
+
+void mrtd_bac_get_kmrz(const uint8_t *pn, const uint8_t *dob, const uint8_t *eov, uint8_t *output);
+
+void mrtd_bac_get_kmrz_from_mrz(const uint8_t *mrz, uint8_t *kmrz);
+
+#endif /* INC_BAC_HELPER_H */
diff --git a/src/crypto-gcrypt.c b/src/crypto-gcrypt.c
new file mode 100644
index 0000000..5973704
--- /dev/null
+++ b/src/crypto-gcrypt.c
@@ -0,0 +1,182 @@
+/*
+ * crypto-gcrypt.c - Cryptography routines needed by mrtreader using
+ * libgcrypt.
+ *
+ * Copyright (C) 2014 Ruben Undheim <ruben.undheim@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <gcrypt.h>
+
+
+void mrtd_crypto_sha1(const uint8_t *input, const int length, uint8_t *output)
+{
+ gcry_md_hash_buffer(GCRY_MD_SHA1, output, input, length);
+}
+
+void mrtd_crypto_crypt_des(const uint8_t *input, uint8_t *output, const int length, const uint8_t *key, const char encrypt)
+{
+ int err;
+ uint8_t iv[8];
+ memset(iv,0,8);
+ gcry_cipher_hd_t handle;
+ err = gcry_cipher_open(&handle, GCRY_CIPHER_DES,GCRY_CIPHER_MODE_CBC,0);
+ err = gcry_cipher_setkey(handle, key,8);
+ err = gcry_cipher_setiv(handle, iv, 8);
+ if(encrypt)
+ err = gcry_cipher_encrypt(handle,output,length,input,length);
+ else
+ err = gcry_cipher_decrypt(handle,output,length,input,length);
+ gcry_cipher_close(handle);
+
+}
+
+void mrtd_crypto_encrypt_des(const uint8_t *input, uint8_t *output, const int length, const uint8_t *key)
+{
+ mrtd_crypto_crypt_des(input,output,length,key,1);
+}
+
+void mrtd_crypto_decrypt_des(const uint8_t *input, uint8_t *output, const int length, const uint8_t *key)
+{
+ mrtd_crypto_crypt_des(input,output,length,key,0);
+}
+
+void mrtd_crypto_crypt_3des(const uint8_t *input, uint8_t *output, const int length, const uint8_t *key, const char encrypt)
+{
+ int err;
+ uint8_t longkey[24];
+ uint8_t iv[8];
+ memset(iv,0,8);
+ memcpy(longkey,key,16);
+ memcpy(longkey+16,key,8);
+ gcry_cipher_hd_t handle;
+ err = gcry_cipher_open(&handle, GCRY_CIPHER_3DES,GCRY_CIPHER_MODE_CBC,0);
+ err = gcry_cipher_setkey(handle, longkey,24);
+ err = gcry_cipher_setiv(handle, iv, 8);
+ if(encrypt)
+ err = gcry_cipher_encrypt(handle,output,length,input,length);
+ else
+ err = gcry_cipher_decrypt(handle,output,length,input,length);
+ gcry_cipher_close(handle);
+}
+
+void mrtd_crypto_encrypt_3des(const uint8_t *input, uint8_t *output, const int length, const uint8_t *key)
+{
+ mrtd_crypto_crypt_3des(input,output,length,key,1);
+}
+
+void mrtd_crypto_decrypt_3des(const uint8_t *input, uint8_t *output, const int length, const uint8_t *key)
+{
+ mrtd_crypto_crypt_3des(input,output,length,key,0);
+}
+
+void mrtd_crypto_fix_parity(const uint8_t *input, uint8_t *output, const int length, int *newlength)
+{
+ int i,j;
+ unsigned char y;
+ int parity;
+ *newlength = length+(length/8);
+ for(i=0;i<length;i++){
+ y = input[i] & 0xfe;
+ parity = 0;
+ for(j=0;j<8;j++){
+ parity += y >> j & 1;
+ }
+ if(parity % 2 == 0){
+ output[i] = (char)(y + 1);
+ }
+ else{
+ output[i] = y;
+ }
+ }
+
+}
+void mrtd_crypto_padding(const uint8_t *input, uint8_t *output, const int length, int *newlength)
+{
+ *newlength = ((length+8)/8)*8;
+ memset(output,0,*newlength);
+ memcpy(output,input,length);
+ output[length] = 0x80;
+}
+void mrtd_crypto_padding_remove(const uint8_t *input, uint8_t *output, const int length, int *newlength)
+{
+ int i;
+ int pos;
+ char found = 0;
+ for(i=length-1;i>=0;i--){
+ if(input[i] == 0x00) {
+ continue;
+ }
+ else if(input[i] == 0x80) {
+ pos = i;
+ found = 1;
+ break;
+ }
+ else {
+ goto failed;
+ }
+ }
+ if(found){
+ *newlength = pos;
+ memcpy(output,input,*newlength);
+ }
+ else{
+ goto failed;
+ }
+
+ return;
+
+ failed:
+ *newlength = 0;
+ return;
+}
+
+void mrtd_crypto_mac(const uint8_t *input, uint8_t *output, const int length, const uint8_t *key)
+{
+ int i,j;
+ uint8_t current[8];
+ uint8_t mac[8];
+ uint8_t xormac[8];
+ uint8_t tmp[8];
+ memset(mac,0,8);
+ for(i=0;i<(length/8);i++){
+ for(j=0;j<8;j++){
+ current[j] = input[i*8+j];
+ }
+ for(j=0;j<8;j++){
+ xormac[j] = mac[j] ^ current[j];
+ }
+ mrtd_crypto_encrypt_des(xormac,mac,8,key);
+ }
+ mrtd_crypto_decrypt_des(mac,tmp,8,key+8);
+ mrtd_crypto_encrypt_des(tmp,output,8,key);
+}
+
+void mrtd_crypto_mac_padding(const uint8_t *input, uint8_t *output, const int length, const uint8_t *key)
+{
+ int newlength;
+ uint8_t *tmp;
+ tmp = malloc(((length+8)/8)*8);
+ mrtd_crypto_padding(input,tmp,length,&newlength);
+ mrtd_crypto_mac(tmp,output,newlength,key);
+
+ free(tmp);
+}
diff --git a/src/crypto-tomcrypt.c b/src/crypto-tomcrypt.c
new file mode 100644
index 0000000..b19c724
--- /dev/null
+++ b/src/crypto-tomcrypt.c
@@ -0,0 +1,211 @@
+/*
+ * crypto-tomgrypt.c - Cryptography routines needed by mrtreader using
+ * libtomcrypt.
+ *
+ * Copyright (C) 2014 Ruben Undheim <ruben.undheim@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <tomcrypt.h>
+
+
+void mrtd_crypto_sha1(const uint8_t *input, const int length, uint8_t *output)
+{
+ hash_state md;
+ sha1_init(&md);
+ sha1_process(&md, (const unsigned char*) input, length);
+ sha1_done(&md, output);
+}
+
+void mrtd_crypto_crypt_des(const uint8_t *input, uint8_t *output, const int length, const uint8_t *key, const char encrypt)
+{
+ int err;
+ symmetric_CBC cbc_state;
+ char IV[8];
+
+ memset(IV,0,8);
+
+ if(register_cipher(&des_desc) == -1){
+ printf("Error registering cipher\n");
+ }
+ if((err = cbc_start(find_cipher("des"),IV,key,8,0,&cbc_state)) != CRYPT_OK){
+ printf("cbc_start error: %s\n", error_to_string(err));
+ }
+ if(encrypt){
+ if((err = cbc_encrypt(input,output,length,&cbc_state)) != CRYPT_OK){
+ printf("cbc_encrypt error: %s\n", error_to_string(err));
+ }
+ }
+ else{
+ if((err = cbc_decrypt(input,output,length,&cbc_state)) != CRYPT_OK){
+ printf("cbc_encrypt error: %s\n", error_to_string(err));
+ }
+ }
+ if((err = cbc_done(&cbc_state)) != CRYPT_OK){
+ printf("cbc_done error: %s\n", error_to_string(err));
+ }
+}
+
+void mrtd_crypto_encrypt_des(const uint8_t *input, uint8_t *output, const int length, const uint8_t *key)
+{
+ mrtd_crypto_crypt_des(input,output,length,key,1);
+}
+
+void mrtd_crypto_decrypt_des(const uint8_t *input, uint8_t *output, const int length, const uint8_t *key)
+{
+ mrtd_crypto_crypt_des(input,output,length,key,0);
+}
+
+void mrtd_crypto_crypt_3des(const uint8_t *input, uint8_t *output, const int length, const uint8_t *key, const char encrypt)
+{
+ int err;
+ symmetric_CBC cbc_state;
+ char longkey[24];
+ char IV[8];
+
+ memcpy(longkey,key,16);
+ memcpy(longkey+16,key,8);
+ memset(IV,0,8);
+
+ if(register_cipher(&des3_desc) == -1){
+ printf("Error registering cipher\n");
+ exit(-1);
+ }
+ if((err = cbc_start(find_cipher("3des"),IV,longkey,24,0,&cbc_state)) != CRYPT_OK){
+ printf("cbc_start error: %s\n", error_to_string(err));
+ exit(-1);
+ }
+ if(encrypt){
+ if((err = cbc_encrypt(input,output,length,&cbc_state)) != CRYPT_OK){
+ printf("cbc_encrypt error: %s\n", error_to_string(err));
+ exit(-1);
+ }
+ }
+ else{
+ if((err = cbc_decrypt(input,output,length,&cbc_state)) != CRYPT_OK){
+ printf("cbc_encrypt error: %s\n", error_to_string(err));
+ exit(-1);
+ }
+ }
+}
+
+void mrtd_crypto_encrypt_3des(const uint8_t *input, uint8_t *output, const int length, const uint8_t *key)
+{
+ mrtd_crypto_crypt_3des(input,output,length,key,1);
+}
+
+void mrtd_crypto_decrypt_3des(const uint8_t *input, uint8_t *output, const int length, const uint8_t *key)
+{
+ mrtd_crypto_crypt_3des(input,output,length,key,0);
+}
+
+void mrtd_crypto_fix_parity(const uint8_t *input, uint8_t *output, const int length, int *newlength)
+{
+ int i,j;
+ unsigned char y;
+ int parity;
+ *newlength = length+(length/8);
+ for(i=0;i<length;i++){
+ y = input[i] & 0xfe;
+ parity = 0;
+ for(j=0;j<8;j++){
+ parity += y >> j & 1;
+ }
+ if(parity % 2 == 0){
+ output[i] = (char)(y + 1);
+ }
+ else{
+ output[i] = y;
+ }
+ }
+
+}
+void mrtd_crypto_padding(const uint8_t *input, uint8_t *output, const int length, int *newlength)
+{
+ *newlength = ((length+8)/8)*8;
+ memset(output,0,*newlength);
+ memcpy(output,input,length);
+ output[length] = 0x80;
+}
+void mrtd_crypto_padding_remove(const uint8_t *input, uint8_t *output, const int length, int *newlength)
+{
+ int i;
+ int pos;
+ char found = 0;
+ for(i=length-1;i>=0;i--){
+ if(input[i] == 0x00) {
+ continue;
+ }
+ else if(input[i] == 0x80) {
+ pos = i;
+ found = 1;
+ break;
+ }
+ else {
+ goto failed;
+ }
+ }
+ if(found){
+ *newlength = pos;
+ memcpy(output,input,*newlength);
+ }
+ else{
+ goto failed;
+ }
+
+ return;
+
+ failed:
+ *newlength = 0;
+ return;
+}
+
+void mrtd_crypto_mac(const uint8_t *input, uint8_t *output, const int length, const uint8_t *key)
+{
+ int i,j;
+ uint8_t current[8];
+ uint8_t mac[8];
+ uint8_t xormac[8];
+ uint8_t tmp[8];
+ memset(mac,0,8);
+ for(i=0;i<(length/8);i++){
+ for(j=0;j<8;j++){
+ current[j] = input[i*8+j];
+ }
+ for(j=0;j<8;j++){
+ xormac[j] = mac[j] ^ current[j];
+ }
+ mrtd_crypto_encrypt_des(xormac,mac,8,key);
+ }
+ mrtd_crypto_decrypt_des(mac,tmp,8,key+8);
+ mrtd_crypto_encrypt_des(tmp,output,8,key);
+}
+
+void mrtd_crypto_mac_padding(const uint8_t *input, uint8_t *output, const int length, const uint8_t *key)
+{
+ int newlength;
+ uint8_t *tmp;
+ tmp = malloc(((length+8)/8)*8);
+ mrtd_crypto_padding(input,tmp,length,&newlength);
+ mrtd_crypto_mac(tmp,output,newlength,key);
+
+ free(tmp);
+}
diff --git a/src/crypto.h b/src/crypto.h
new file mode 100644
index 0000000..764dab9
--- /dev/null
+++ b/src/crypto.h
@@ -0,0 +1,47 @@
+/*
+ * header for crypto_*.c - this header makes the higher-level code
+ * independent of which crypto-library that is used.
+ *
+ * Copyright (C) 2014 Ruben Undheim <ruben.undheim@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef INC_BAC_CRYPTO_H
+#define INC_BAC_CRYPTO_H
+
+#include <stdint.h>
+
+void mrtd_crypto_sha1(const uint8_t *input, const int length, uint8_t *output);
+
+void mrtd_crypto_encrypt_3des(const uint8_t *input, uint8_t *output, const int length, const uint8_t *key);
+
+void mrtd_crypto_decrypt_3des(const uint8_t *input, uint8_t *output, const int length, const uint8_t *key);
+
+void mrtd_crypto_encrypt_des(const uint8_t *input, uint8_t *output, const int length, const uint8_t *key);
+
+void mrtd_crypto_decrypt_des(const uint8_t *input, uint8_t *output, const int length, const uint8_t *key);
+
+void mrtd_crypto_fix_parity(const uint8_t *input, uint8_t *output, const int length, int *newlength);
+
+void mrtd_crypto_mac_padding(const uint8_t *input, uint8_t *output, const int length, const uint8_t *key);
+
+void mrtd_crypto_mac(const uint8_t *input, uint8_t *output, const int length, const uint8_t *key);
+
+void mrtd_crypto_padding(const uint8_t *input, uint8_t *output, const int length, int *newlength);
+
+void mrtd_crypto_padding_remove(const uint8_t *input, uint8_t *output, const int length, int *newlength);
+
+#endif /* INC_BAC_CRYPTO_H */
diff --git a/src/fileread.c b/src/fileread.c
new file mode 100644
index 0000000..beb9931
--- /dev/null
+++ b/src/fileread.c
@@ -0,0 +1,307 @@
+/*
+ * fileread.c - Functions for reading secure files from MRTD
+ *
+ * Copyright (C) 2014 Ruben Undheim <ruben.undheim@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <nfc/nfc.h>
+#include "crypto.h"
+#include "bac.h"
+#include "bachelper.h"
+
+
+#define MAXREAD 100
+
+static int endianness()
+{
+ int i = 1;
+ char *p = (char *)&i;
+ if(p[0] == 1)
+ return 0;
+ else
+ return 1;
+}
+
+int mrtd_fileread_read(nfc_device *pnd, const uint8_t *file_index, uint8_t *output, int *outputlength, const uint8_t *ksenc, const uint8_t *ksmac, uint64_t *ssc_long)
+{
+ int res;
+ uint8_t txbuffer[300];
+ int txlen;
+ uint8_t rxbuffer[300];
+ int rxlen;
+ int already_received;
+
+ uint8_t unprotected[300];
+ int unprotectedlength;
+ unprotectedlength = 7;
+ memcpy(unprotected,"\x00\xa4\x02\x0c\x02\x01\x1e",5);
+ memcpy(unprotected+5,file_index,2);
+ (*ssc_long)++;
+ mrtd_bac_protected_apdu(unprotected,txbuffer,unprotectedlength,&txlen,ksenc,ksmac,*ssc_long);
+
+ //printhex("Transmit",txbuffer,txlen);
+ rxlen = sizeof(rxbuffer);
+ if((res = nfc_initiator_transceive_bytes(pnd,txbuffer,txlen,rxbuffer,rxlen,500)) < 0){
+ fprintf(stderr,"Unable to send");
+ goto failed;
+ }
+ else{
+ rxlen = res;
+ }
+ //printhex("Received (encrypted)",rxbuffer,rxlen);
+ (*ssc_long)++;
+ mrtd_bac_decrypt_response(rxbuffer,unprotected,rxlen,&unprotectedlength,ksenc);
+ //printhex("Received (decrypted)",unprotected,unprotectedlength);
+
+ already_received=0;
+
+ unprotectedlength = 5;
+ memcpy(unprotected,"\x00\xb0\x00\x00\x04",unprotectedlength);
+ (*ssc_long)++;
+ mrtd_bac_protected_apdu(unprotected,txbuffer,unprotectedlength,&txlen,ksenc,ksmac,*ssc_long);
+ //printhex("Transmit",txbuffer,txlen);
+ rxlen = sizeof(rxbuffer);
+ if((res = nfc_initiator_transceive_bytes(pnd,txbuffer,txlen,rxbuffer,rxlen,500)) < 0){
+ fprintf(stderr,"Unable to send");
+ goto failed;
+ }
+ else{
+ rxlen = res;
+ }
+ //printhex("Received (encrypted)",rxbuffer,rxlen);
+ (*ssc_long)++;
+ mrtd_bac_decrypt_response(rxbuffer,unprotected,rxlen,&unprotectedlength,ksenc);
+ //printhex("Received (decrypted)",unprotected,unprotectedlength);
+ memcpy(output+already_received,unprotected,unprotectedlength);
+ already_received += unprotectedlength;
+
+ uint16_t numberbytes;
+ int field_length;
+ if(unprotected[1] <= 0x7f){
+ numberbytes = (uint16_t)unprotected[1];
+ field_length = 1;
+ }
+ else if(unprotected[1] == 0x81){
+ numberbytes = (uint16_t)unprotected[2];
+ field_length = 2;
+ }
+ else if(unprotected[1] == 0x82){
+ if(endianness()){
+ *(((uint8_t*)(&numberbytes))+0) = unprotected[2];
+ *(((uint8_t*)(&numberbytes))+1) = unprotected[3];
+ }
+ else {
+ *(((uint8_t*)(&numberbytes))+1) = unprotected[2];
+ *(((uint8_t*)(&numberbytes))+0) = unprotected[3];
+ }
+ field_length = 3;
+ }
+ else {
+ fprintf(stderr,"Not correct field length");
+ goto failed;
+ }
+ //printf("numberbytes: %d\n",numberbytes);
+
+ int left_to_read;
+ int readnow;
+ left_to_read = numberbytes - (3-field_length);
+
+ while (left_to_read > 0){
+ if(left_to_read > MAXREAD)
+ readnow = MAXREAD;
+ else
+ readnow = left_to_read;
+ unprotectedlength = 5;
+ memcpy(unprotected,"\x00\xb0\x00\x00\x00",unprotectedlength);
+ if(endianness()){
+ unprotected[2] = *(((uint8_t*)&already_received)+0);
+ unprotected[3] = *(((uint8_t*)&already_received)+1);
+ }
+ else {
+ unprotected[2] = *(((uint8_t*)&already_received)+1);
+ unprotected[3] = *(((uint8_t*)&already_received)+0);
+ }
+ unprotected[4] = readnow;
+ (*ssc_long)++;
+ mrtd_bac_protected_apdu(unprotected,txbuffer,unprotectedlength,&txlen,ksenc,ksmac,*ssc_long);
+ //printhex("Transmit",txbuffer,txlen);
+ rxlen = sizeof(rxbuffer);
+ if((res = nfc_initiator_transceive_bytes(pnd,txbuffer,txlen,rxbuffer,rxlen,500)) < 0){
+ fprintf(stderr,"Unable to send");
+ goto failed;
+ }
+ else{
+ rxlen = res;
+ }
+ //printhex("Received (encrypted)",rxbuffer,rxlen);
+ (*ssc_long)++;
+ mrtd_bac_decrypt_response(rxbuffer,unprotected,rxlen,&unprotectedlength,ksenc);
+ //printhex("Received (decrypted)",unprotected,unprotectedlength);
+ memcpy(output+already_received,unprotected,unprotectedlength);
+ already_received += unprotectedlength;
+ left_to_read -= unprotectedlength;
+ }
+ (*outputlength) = already_received;
+
+ return 0;
+
+ failed:
+ return -1;
+}
+
+void mrtd_fileread_write_image_to_file(const uint8_t *file_content, const int file_size, const char *filename)
+{
+ FILE *out;
+ char filetype;
+ char filenamebuf[256];
+ int baselength=0;
+ unsigned char start_sequence_jpeg[10] = {0xff,0xd8,0xff,0xe0,0x00,0x10,0x4a,0x46,0x49,0x46};
+ unsigned char start_sequence_jpeg2000[10] = {0x00,0x00,0x00,0x0c,0x6a,0x50,0x20,0x20,0x0d,0x0a};
+ unsigned char *start_sequence;
+ int comparelen = 0;
+ if(file_size > 84){
+ filetype = file_content[73]; // 0x00: JPG, 0x01: JPEG2000
+ if(strlen(filename) > 3 && filename[strlen(filename)-4] == '.')
+ baselength = strlen(filename)-4;
+ else
+ baselength = strlen(filename);
+ memcpy(filenamebuf, filename, baselength);
+ if(filetype == 0x00) {
+ memcpy(filenamebuf+baselength,".jpg",4);
+ start_sequence = start_sequence_jpeg;
+ comparelen = 2;
+ }
+ else {
+ memcpy(filenamebuf+baselength,".jp2",4);
+ start_sequence = start_sequence_jpeg2000;
+ comparelen = 10;
+ }
+ filenamebuf[baselength+4] = 0;
+
+ int i,j;
+ int offset = 0;
+ char equal = 0;
+ for(i=0;i<120;i++) {
+ j = 0;
+ if(file_content[i] == start_sequence[j]){
+ equal = 1;
+ for(j=1;j<comparelen;j++) {
+ if(file_content[i+j] != start_sequence[j]){
+ equal = 0;
+ break;
+ }
+ }
+ }
+ if(equal) {
+ offset = i;
+ break;
+ }
+ }
+ if(!equal){
+ printf("Couldn't find start of image\n");
+ return;
+ }
+
+ out = fopen(filenamebuf,"w");
+ printf("Saving image to %s...",filenamebuf);
+ fwrite(file_content+offset,1,file_size-offset,out);
+ fclose(out);
+ printf(" done\n");
+ if(filetype == 0x01)
+ printf("\n(Note: .jp2 files are JPEG2000 images which can be opened\n with many different image viewers. If you are unable to\n open it, it can be converted to JPEG with GraphicsMagick:\n gm convert image.jp2 image.jpg )\n\n");
+ }
+
+}
+
+void mrtd_fileread_get_datagroup_name(const uint8_t dg, char *name)
+{
+ switch(dg){
+ case(0x60): sprintf(name,"EF_COM"); break;
+ case(0x61): sprintf(name,"EF_DG1"); break;
+ case(0x75): sprintf(name,"EF_DG2"); break;
+ case(0x63): sprintf(name,"EF_DG3"); break;
+ case(0x76): sprintf(name,"EF_DG4"); break;
+ case(0x65): sprintf(name,"EF_DG5"); break;
+ case(0x66): sprintf(name,"EF_DG6"); break;
+ case(0x67): sprintf(name,"EF_DG7"); break;
+ case(0x68): sprintf(name,"EF_DG8"); break;
+ case(0x69): sprintf(name,"EF_DG9"); break;
+ case(0x6a): sprintf(name,"EF_DG10"); break;
+ case(0x6b): sprintf(name,"EF_DG11"); break;
+ case(0x6c): sprintf(name,"EF_DG12"); break;
+ case(0x6d): sprintf(name,"EF_DG13"); break;
+ case(0x6e): sprintf(name,"EF_DG14"); break;
+ case(0x6f): sprintf(name,"EF_DG15"); break;
+ case(0x70): sprintf(name,"EF_DG16"); break;
+ case(0x77): sprintf(name,"EF_SOD"); break;
+ default: sprintf(name,"not defined"); break;
+ }
+ return;
+}
+
+void mrtd_fileread_decode_ef_com(const uint8_t *file_content, const int file_size, uint8_t *datagroups, int *numdatagroups)
+{
+ int i;
+ int currentlength;
+ int interncounter;
+ int totallength;
+ char longer = 0;
+ char currentkeys = 0;
+ currentlength = 0;
+ *numdatagroups=0;
+ for(i=0;i<file_size;i++){
+ if(i==1){
+ totallength = file_content[i];
+ interncounter=0;
+ }
+ else if(longer == 1){
+ longer = 2;
+ }
+ else if(longer == 2){
+ longer = 0;
+ currentlength = file_content[i];
+ interncounter=0;
+ }
+ else if(interncounter == currentlength && file_content[i] == 0x5f){
+ currentkeys = 0;
+ longer = 1;
+ }
+ else if(interncounter == currentlength){
+ longer = 2;
+ if(file_content[i] == 0x5c)
+ currentkeys = 1;
+ else
+ currentkeys = 0;
+ }
+ else {
+ if(currentkeys) {
+ datagroups[interncounter] = file_content[i];
+ char buffer[30];
+ mrtd_fileread_get_datagroup_name(datagroups[interncounter],buffer);
+ printf("Found: %s\n",buffer);
+ (*numdatagroups)++;
+ }
+ interncounter++;
+ }
+ }
+ printf("\n");
+}
+
diff --git a/src/fileread.h b/src/fileread.h
new file mode 100644
index 0000000..e8a8c7a
--- /dev/null
+++ b/src/fileread.h
@@ -0,0 +1,32 @@
+/*
+ * header for fileread.c
+ *
+ * Copyright (C) 2014 Ruben Undheim <ruben.undheim@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef INC_FILEREAD_H
+#define INC_FILEREAD_H
+
+int mrtd_fileread_read(nfc_device *pnd, const uint8_t *file_index, uint8_t *output, int *outputlength, const uint8_t *ksenc, const uint8_t *ksmac, uint64_t *ssc_long);
+
+void mrtd_fileread_write_image_to_file(const uint8_t *file_content, const int file_size, const char *filename);
+
+void mrtd_fileread_decode_ef_com(const uint8_t *file_content, const int file_size, uint8_t *datagroups, int *numdatagroups);
+
+void mrtd_fileread_get_datagroup_name(const uint8_t dg, char *name);
+
+#endif /* INC_FILEREAD_H */
diff --git a/src/hardware.c b/src/hardware.c
new file mode 100644
index 0000000..06f6f1b
--- /dev/null
+++ b/src/hardware.c
@@ -0,0 +1,21 @@
+/*
+ * hardware.c - sort of a hardware abstraction layer around libnfc
+ *
+ * Copyright (C) 2014 Ruben Undheim <ruben.undheim@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
diff --git a/src/mrtd.h b/src/mrtd.h
new file mode 100644
index 0000000..24d080d
--- /dev/null
+++ b/src/mrtd.h
@@ -0,0 +1,30 @@
+/*
+ * mrtd.h - Header file for libmrtd.so
+ *
+ * Copyright (C) 2014 Ruben Undheim <ruben.undheim@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef INC_MRTD_H
+#define INC_MRTD_H
+
+#include "bac.h"
+#include "bachelper.h"
+#include "crypto.h"
+#include "fileread.h"
+
+#endif /* INC_MRTD_H */
+
diff --git a/src/mrtdreader.c b/src/mrtdreader.c
new file mode 100644
index 0000000..d19944f
--- /dev/null
+++ b/src/mrtdreader.c
@@ -0,0 +1,298 @@
+/*
+ * mrtdreader.c - Main program
+ *
+ * Copyright (C) 2014 Ruben Undheim <ruben.undheim@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <time.h>
+#include <nfc/nfc.h>
+#include <signal.h>
+#include "mrtd.h"
+
+#define MAX_DEVICE_COUNT 5
+
+static nfc_context *context = NULL;
+static nfc_device *pnd = NULL;
+
+static char *pn = NULL;
+static char *dob = NULL;
+static char *eov = NULL;
+static char *extra_argument = NULL;
+
+static char done = 0;
+
+static void closedown(int sig)
+{
+ done = 1;
+ printf("Stopping...\n");
+}
+static void forcestop(int sig)
+{
+ done = 1;
+ printf("Stopping...\n");
+ if(pnd != NULL)
+ nfc_close(pnd);
+ if(context != NULL)
+ nfc_exit(context);
+ exit(-1);
+}
+
+void printhex(char *description, uint8_t *input, int length)
+{
+ int i;
+ printf("%s: ",description);
+ for(i=0;i<length;i++){
+ printf("%02x",input[i]);
+ }
+ printf("\n");
+}
+
+int parse_cmdline(int argc, char **argv)
+{
+ int s;
+ opterr = 0;
+ while((s = getopt(argc, argv, "p:b:e:")) != -1) {
+ switch (s) {
+ case 'p':
+ pn = malloc(strlen(optarg)+1);
+ memcpy(pn,optarg,strlen(optarg));
+ break;
+ case 'b':
+ dob = malloc(strlen(optarg)+1);
+ memcpy(dob,optarg,strlen(optarg));
+ break;
+ case 'e':
+ eov = malloc(strlen(optarg)+1);
+ memcpy(eov,optarg,strlen(optarg));
+ break;
+ case '?':
+ if(optopt == 'p')
+ fprintf(stderr, "Option -%c requires an argument.\n",optopt);
+ else if(optopt == 'b')
+ fprintf(stderr, "Option -%c requires an argument.\n",optopt);
+ else if(optopt == 'e')
+ fprintf(stderr, "Option -%c requires an argument.\n",optopt);
+ else if(isprint(optopt))
+ fprintf(stderr, "Unknown option '-%c'.\n",optopt);
+ return -1;
+ default:
+ abort();
+ }
+ }
+
+ if(argc == (optind + 1)){
+ extra_argument = argv[optind];
+ }
+ if((extra_argument == NULL) && ((pn == NULL) || (dob == NULL) || (eov == NULL))){
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int i,ret,res;
+ if(parse_cmdline(argc,argv) == -1){
+ fprintf(stderr,"Usage: %s [options] <MRZ>\n",argv[0]);
+ goto failed;
+ }
+ uint8_t *kmrz;
+ uint8_t buffer[25];
+ int inlength;
+ if(extra_argument != NULL){
+ inlength = strlen(extra_argument);
+ if(inlength == 24){
+ kmrz = extra_argument;
+ }
+ else if(inlength == 44){
+ mrtd_bac_get_kmrz_from_mrz(extra_argument, buffer);
+ kmrz = buffer;
+ }
+ else{
+ fprintf(stderr,"Did not recognize <MRZ>\n");
+ goto failed;
+ }
+ }
+ else {
+ mrtd_bac_get_kmrz(pn, dob, eov, buffer);
+ kmrz = buffer;
+ }
+ if(pn != NULL) {
+ free(pn);
+ pn = NULL;
+ }
+ if(dob != NULL){
+ free(dob);
+ dob = NULL;
+ }
+ if(eov != NULL){
+ free(eov);
+ eov = NULL;
+ }
+
+
+ signal(SIGINT, forcestop);
+ srand(time(NULL));
+
+ nfc_init(&context);
+ nfc_target ant;
+
+ if(context == NULL){
+ fprintf(stderr,"Unable to init libnfc (malloc)\n");
+ goto failed;
+ }
+ nfc_connstring connstrings[MAX_DEVICE_COUNT];
+ size_t szDeviceFound = nfc_list_devices(context,connstrings,MAX_DEVICE_COUNT);
+
+ if(szDeviceFound == 0){
+ fprintf(stderr,"No NFC device found.\n");
+ goto failed;
+ }
+
+ pnd = nfc_open(context, connstrings[0]);
+ if(pnd == NULL){
+ fprintf(stderr,"Unable to open NFC device: %s\n",connstrings[0]);
+ goto failed;
+ }
+ if(nfc_initiator_init(pnd) < 0){
+ nfc_perror(pnd,"nfc_initiator_init");
+ goto failed;
+ }
+
+ printf("NFC device: %s opened\n",nfc_device_get_name(pnd));
+
+ if (nfc_device_set_property_bool(pnd, NP_INFINITE_SELECT, false) < 0){
+ fprintf(stderr, "error setting property\n");
+ goto failed;
+ }
+
+ nfc_modulation nmA,nmB;
+ nmA.nmt = NMT_ISO14443A;
+ nmA.nbr = NBR_106;
+ nmB.nmt = NMT_ISO14443B;
+ nmB.nbr = NBR_106;
+
+ signal(SIGINT, closedown);
+ while(1){
+ ret = nfc_initiator_select_passive_target(pnd,nmA,NULL,0,&ant);
+ if(ret > 0 || done) break;
+ ret = nfc_initiator_select_passive_target(pnd,nmB,NULL,0,&ant);
+ if(ret > 0 || done) break;
+ }
+ if(done)
+ goto failed;
+ printf("Target found!\n");
+ signal(SIGINT, forcestop);
+
+ uint8_t txbuffer[300];
+ int txlen;
+ uint8_t rxbuffer[300];
+ int rxlen;
+
+ uint64_t ssc_long;
+ uint8_t ksenc[16];
+ uint8_t ksmac[16];
+
+ mrtd_bac_randomize_rndifd_kifd();
+ ret = mrtd_bac_keyhandshake(pnd,kmrz,ksenc,ksmac,&ssc_long);
+
+ if(ret == RET_CHALLENGE_FAILED){
+ fprintf(stderr,"======================\nChallenge failed...\n======================\n");
+ goto failed;
+ }
+ else if(ret < 0){
+ goto failed;
+ }
+ printf("======================\nChallenge successful!\n======================\n");
+
+ printf("\n");
+ uint8_t *filecontent = NULL;
+ filecontent = malloc(50000);
+ int filecontentlength;
+
+
+ printf("Getting EF.COM...");
+ fflush(stdout);
+ mrtd_fileread_read(pnd,"\x01\x1e",filecontent,&filecontentlength,ksenc,ksmac,&ssc_long);
+ printf(" done\n");
+
+ printhex("File content",filecontent,filecontentlength);
+ printf("File size: %d\n",filecontentlength);
+
+ uint8_t datagroups[20];
+ int ndatagroups;
+ mrtd_fileread_decode_ef_com(filecontent,filecontentlength,datagroups,&ndatagroups);
+
+ printf("\n");
+
+ printf("Getting EF.SOD...");
+ fflush(stdout);
+ mrtd_fileread_read(pnd,"\x01\x1d",filecontent,&filecontentlength,ksenc,ksmac,&ssc_long);
+ printf(" done\n");
+
+ printhex("File content",filecontent,filecontentlength);
+ printf("File size: %d\n",filecontentlength);
+
+ printf("\n");
+
+ printf("Getting EF.DG1...");
+ fflush(stdout);
+ mrtd_fileread_read(pnd,"\x01\x01",filecontent,&filecontentlength,ksenc,ksmac,&ssc_long);
+ printf(" done\n");
+
+ filecontent[filecontentlength] = 0;
+ printf("%s\n\n",filecontent);
+ printf("File size: %d\n",filecontentlength);
+
+ printf("\n");
+ printf("Getting EF.DG2 which contains the image...");
+ fflush(stdout);
+ mrtd_fileread_read(pnd,"\x01\x02",filecontent,&filecontentlength,ksenc,ksmac,&ssc_long);
+ printf(" done\n");
+
+
+ mrtd_fileread_write_image_to_file(filecontent, filecontentlength, "image");
+
+ nfc_close(pnd);
+ nfc_exit(context);
+ if(filecontent != NULL)
+ free(filecontent);
+ return 0;
+
+ failed:
+ if(pnd != NULL)
+ nfc_close(pnd);
+ if(context != NULL)
+ nfc_exit(context);
+ if(pn != NULL)
+ free(pn);
+ if(dob != NULL)
+ free(dob);
+ if(eov != NULL)
+ free(eov);
+ if(filecontent != NULL)
+ free(filecontent);
+ return -1;
+}
+