diff options
author | Ruben Undheim <ruben.undheim@gmail.com> | 2016-02-09 20:12:49 +0100 |
---|---|---|
committer | Ruben Undheim <ruben.undheim@gmail.com> | 2016-02-09 20:12:49 +0100 |
commit | 8eb24cbfcface9379d7f3549c338708ab8232822 (patch) | |
tree | 37290ffd194a296beb34f4e10cc69862af24fa58 /src |
Import mrtdreader_0.1.6.orig.tar.gz
[dgit import orig mrtdreader_0.1.6.orig.tar.gz]
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 22 | ||||
-rw-r--r-- | src/bac.c | 146 | ||||
-rw-r--r-- | src/bac.h | 36 | ||||
-rw-r--r-- | src/bachelper.c | 355 | ||||
-rw-r--r-- | src/bachelper.h | 48 | ||||
-rw-r--r-- | src/crypto-gcrypt.c | 182 | ||||
-rw-r--r-- | src/crypto-tomcrypt.c | 211 | ||||
-rw-r--r-- | src/crypto.h | 47 | ||||
-rw-r--r-- | src/fileread.c | 307 | ||||
-rw-r--r-- | src/fileread.h | 32 | ||||
-rw-r--r-- | src/hardware.c | 21 | ||||
-rw-r--r-- | src/mrtd.h | 30 | ||||
-rw-r--r-- | src/mrtdreader.c | 298 |
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; +} + |