7f695cfbec
Previously, no timeout or an arbitrary timeout (500ms) was used. This usually works but causes some issues when operations take longer: for example, during `mifare_desfire_format_picc`. This changes increases the previous default timeout to two seconds and adds a new function that allows setting custom timeouts.
382 lines
12 KiB
C
382 lines
12 KiB
C
#ifndef __FREEFARE_INTERNAL_H__
|
||
#define __FREEFARE_INTERNAL_H__
|
||
|
||
#if defined(HAVE_CONFIG_H)
|
||
#include "config.h"
|
||
#endif
|
||
|
||
#include <openssl/des.h>
|
||
|
||
/*
|
||
* Endienness macros
|
||
*
|
||
* POSIX does not describe any API for endianness problems, and solutions are
|
||
* mostly vendor-dependant. Some operating systems provide a complete
|
||
* framework for this (FreeBSD, OpenBSD), some provide nothing in the base
|
||
* system (Mac OS), GNU/Linux systems may or may not provide macros to do the
|
||
* conversion depending on the version of the libc.
|
||
*
|
||
* This is a PITA but unfortunately we have no other solution than doing all
|
||
* this gymnastic. Depending of what is defined if one or more of endian.h,
|
||
* sys/endian.h and byteswap.h was included, define a set of macros to stick to
|
||
* the set of macros provided by FreeBSD (it's a historic choice: development
|
||
* was done on this operating system when endianness problems first had to be
|
||
* dealt with).
|
||
*/
|
||
|
||
#if !defined(le32toh) && defined(letoh32)
|
||
#define le32toh(x) letoh32(x)
|
||
#define be32toh(x) betoh32(x)
|
||
#endif
|
||
|
||
#if !defined(le16toh) && defined(letoh16)
|
||
#define le16toh(x) letoh16(x)
|
||
#define be16toh(x) betoh16(x)
|
||
#endif
|
||
|
||
#if !defined(le32toh) && defined(HAVE_COREFOUNDATION_COREFOUNDATION_H)
|
||
#define be32toh(x) CFSwapInt32BigToHost(x)
|
||
#define htobe32(x) CFSwapInt32HostToBig(x)
|
||
#define le32toh(x) CFSwapInt32LittleToHost(x)
|
||
#define htole32(x) CFSwapInt32HostToLittle(x)
|
||
#endif
|
||
|
||
#if !defined(le16toh) && defined(HAVE_COREFOUNDATION_COREFOUNDATION_H)
|
||
#define be16toh(x) CFSwapInt16BigToHost(x)
|
||
#define htobe16(x) CFSwapInt16HostToBig(x)
|
||
#define le16toh(x) CFSwapInt16LittleToHost(x)
|
||
#define htole16(x) CFSwapInt16HostToLittle(x)
|
||
#endif
|
||
|
||
#if !defined(le32toh) && defined(bswap_32)
|
||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||
#define be32toh(x) bswap_32(x)
|
||
#define htobe32(x) bswap_32(x)
|
||
#define le32toh(x) (x)
|
||
#define htole32(x) (x)
|
||
#else
|
||
#define be32toh(x) (x)
|
||
#define htobe32(x) (x)
|
||
#define le32toh(x) bswap_32(x)
|
||
#define htole32(x) bswap_32(x)
|
||
#endif
|
||
#endif
|
||
|
||
#if !defined(htole16) && defined(bswap_16)
|
||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||
#define be16toh(x) (bswap_16(x))
|
||
#define htobe16(x) (bswap_16(x))
|
||
#define htole16(x) (x)
|
||
#define le16toh(x) (x)
|
||
#else
|
||
#define be16toh(x) (x)
|
||
#define htobe16(x) (x)
|
||
#define htole16(x) (bswap_16(x))
|
||
#define le16toh(x) (bswap_16(x))
|
||
#endif
|
||
#endif
|
||
|
||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||
|
||
#define MAX_CRYPTO_BLOCK_SIZE 16
|
||
|
||
void *memdup(const void *p, const size_t n);
|
||
|
||
struct mad_sector_0x00;
|
||
struct mad_sector_0x10;
|
||
|
||
void nxp_crc(uint8_t *crc, const uint8_t value);
|
||
uint8_t sector_0x00_crc8(Mad mad);
|
||
uint8_t sector_0x10_crc8(Mad mad);
|
||
|
||
typedef enum {
|
||
MCD_SEND,
|
||
MCD_RECEIVE
|
||
} MifareCryptoDirection;
|
||
|
||
typedef enum {
|
||
MCO_ENCYPHER,
|
||
MCO_DECYPHER
|
||
} MifareCryptoOperation;
|
||
|
||
#define MDCM_MASK 0x000F
|
||
|
||
#define CMAC_NONE 0
|
||
|
||
// Data send to the PICC is used to update the CMAC
|
||
#define CMAC_COMMAND 0x010
|
||
// Data received from the PICC is used to update the CMAC
|
||
#define CMAC_VERIFY 0x020
|
||
|
||
// MAC the command (when MDCM_MACED)
|
||
#define MAC_COMMAND 0x100
|
||
// The command returns a MAC to verify (when MDCM_MACED)
|
||
#define MAC_VERIFY 0x200
|
||
|
||
#define ENC_COMMAND 0x1000
|
||
#define NO_CRC 0x2000
|
||
|
||
#define MAC_MASK 0x0F0
|
||
#define CMAC_MACK 0xF00
|
||
|
||
void *mifare_cryto_preprocess_data(FreefareTag tag, void *data, size_t *nbytes, off_t offset, int communication_settings);
|
||
void *mifare_cryto_postprocess_data(FreefareTag tag, void *data, ssize_t *nbytes, int communication_settings);
|
||
void mifare_cypher_single_block(MifareDESFireKey key, uint8_t *data, uint8_t *ivect, MifareCryptoDirection direction, MifareCryptoOperation operation, size_t block_size);
|
||
void mifare_cypher_blocks_chained(FreefareTag tag, MifareDESFireKey key, uint8_t *ivect, uint8_t *data, size_t data_size, MifareCryptoDirection direction, MifareCryptoOperation operation);
|
||
void rol(uint8_t *data, const size_t len);
|
||
void desfire_crc32(const uint8_t *data, const size_t len, uint8_t *crc);
|
||
void desfire_crc32_append(uint8_t *data, const size_t len);
|
||
size_t key_block_size(const MifareDESFireKey key);
|
||
size_t padded_data_length(const size_t nbytes, const size_t block_size);
|
||
size_t maced_data_length(const MifareDESFireKey key, const size_t nbytes);
|
||
size_t enciphered_data_length(const FreefareTag tag, const size_t nbytes, int communication_settings);
|
||
|
||
void cmac_generate_subkeys(MifareDESFireKey key);
|
||
void cmac(const MifareDESFireKey key, uint8_t *ivect, const uint8_t *data, size_t len, uint8_t *cmac);
|
||
void cmac_an10922(const MifareDESFireKey key, uint8_t *ivect, const uint8_t *data, size_t len, uint8_t *cmac);
|
||
void *assert_crypto_buffer_size(FreefareTag tag, size_t nbytes);
|
||
|
||
#define MIFARE_ULTRALIGHT_PAGE_COUNT 0x10
|
||
#define MIFARE_ULTRALIGHT_C_PAGE_COUNT 0x30
|
||
#define MIFARE_ULTRALIGHT_C_PAGE_COUNT_READ 0x2C
|
||
// Max PAGE_COUNT of the Ultralight Family:
|
||
#define MIFARE_ULTRALIGHT_MAX_PAGE_COUNT 0x30
|
||
// Default timeout (ms) for tag operations
|
||
#define MIFARE_DEFAULT_TIMEOUT 2000
|
||
|
||
/*
|
||
* This structure is common to all supported MIFARE targets but shall not be
|
||
* used directly (it's some kind of abstract class). All members in this
|
||
* structure are initialized by freefare_get_tags().
|
||
*
|
||
* Extra members in derived classes are initialized in the correpsonding
|
||
* mifare_*_connect() function.
|
||
*/
|
||
struct freefare_tag {
|
||
nfc_device *device;
|
||
nfc_target info;
|
||
int type;
|
||
int active;
|
||
int timeout;
|
||
void (*free_tag)(FreefareTag tag);
|
||
};
|
||
|
||
struct felica_tag {
|
||
struct freefare_tag __tag;
|
||
};
|
||
|
||
struct mifare_classic_tag {
|
||
struct freefare_tag __tag;
|
||
|
||
MifareClassicKeyType last_authentication_key_type;
|
||
|
||
/*
|
||
* The following block numbers are on 2 bytes in order to use invalid
|
||
* address and avoid false cache hit with inconsistent data.
|
||
*/
|
||
struct {
|
||
int16_t sector_trailer_block_number;
|
||
uint16_t sector_access_bits;
|
||
int16_t block_number;
|
||
uint8_t block_access_bits;
|
||
} cached_access_bits;
|
||
};
|
||
|
||
struct mifare_desfire_aid {
|
||
uint8_t data[3];
|
||
};
|
||
|
||
struct mifare_desfire_key {
|
||
uint8_t data[24];
|
||
MifareKeyType type;
|
||
DES_key_schedule ks1;
|
||
DES_key_schedule ks2;
|
||
DES_key_schedule ks3;
|
||
uint8_t cmac_sk1[24];
|
||
uint8_t cmac_sk2[24];
|
||
uint8_t aes_version;
|
||
};
|
||
|
||
struct mifare_desfire_tag {
|
||
struct freefare_tag __tag;
|
||
|
||
uint8_t last_picc_error;
|
||
uint8_t last_internal_error;
|
||
uint8_t last_pcd_error;
|
||
MifareDESFireKey session_key;
|
||
enum { AS_LEGACY, AS_NEW } authentication_scheme;
|
||
uint8_t authenticated_key_no;
|
||
uint8_t ivect[MAX_CRYPTO_BLOCK_SIZE];
|
||
uint8_t cmac[16];
|
||
uint8_t *crypto_buffer;
|
||
size_t crypto_buffer_size;
|
||
uint32_t selected_application;
|
||
};
|
||
|
||
struct mifare_key_deriver {
|
||
MifareDESFireKey master_key;
|
||
MifareKeyType output_key_type;
|
||
uint8_t m[32];
|
||
int len;
|
||
int flags;
|
||
};
|
||
|
||
MifareDESFireKey mifare_desfire_session_key_new(const uint8_t rnda[], const uint8_t rndb[], MifareDESFireKey authentication_key);
|
||
const char *mifare_desfire_error_lookup(uint8_t error);
|
||
|
||
struct mifare_ultralight_tag {
|
||
struct freefare_tag __tag;
|
||
|
||
/* mifare_ultralight_read() reads 4 pages at a time (wrapping) */
|
||
MifareUltralightPage cache[MIFARE_ULTRALIGHT_MAX_PAGE_COUNT + 3];
|
||
uint8_t cached_pages[MIFARE_ULTRALIGHT_MAX_PAGE_COUNT];
|
||
};
|
||
|
||
/*
|
||
NTAG section
|
||
*/
|
||
|
||
struct ntag21x_tag {
|
||
struct freefare_tag __tag;
|
||
|
||
int subtype;
|
||
uint8_t vendor_id;
|
||
uint8_t product_type;
|
||
uint8_t product_subtype;
|
||
uint8_t major_product_version;
|
||
uint8_t minor_product_version;
|
||
uint8_t storage_size;
|
||
uint8_t protocol_type;
|
||
|
||
uint8_t last_error;
|
||
};
|
||
|
||
struct ntag21x_key {
|
||
uint8_t data[4]; // 4B key
|
||
uint8_t pack[2]; // 2B Password Acknowlege
|
||
};
|
||
|
||
const char *ntag21x_error_lookup(uint8_t code);
|
||
|
||
/*
|
||
* FreefareTag assertion macros
|
||
*
|
||
* This macros provide a simple and unified way to perform various tests at the
|
||
* beginning of the different targets functions.
|
||
*/
|
||
#define ASSERT_ACTIVE(tag) do { if (!tag->active) return errno = ENXIO, -1; } while (0)
|
||
#define ASSERT_INACTIVE(tag) do { if (tag->active) return errno = ENXIO, -1; } while (0)
|
||
|
||
/*
|
||
* FreefareTag cast macros
|
||
*
|
||
* This macros are intended to provide a convenient way to cast abstract
|
||
* FreefareTag structures to concrete Tags (e.g. MIFARE Classic tag).
|
||
*/
|
||
#define FELICA(tag) ((struct felica_tag *) tag)
|
||
#define MIFARE_CLASSIC(tag) ((struct mifare_classic_tag *) tag)
|
||
#define MIFARE_DESFIRE(tag) ((struct mifare_desfire_tag *) tag)
|
||
#define MIFARE_ULTRALIGHT(tag) ((struct mifare_ultralight_tag *) tag)
|
||
#define NTAG_21x(tag) ((struct ntag21x_tag *) tag)
|
||
|
||
/*
|
||
* Access bits manipulation macros
|
||
*/
|
||
#define DB_AB(ab) ((ab == C_DEFAULT) ? C_000 : ab)
|
||
#define TB_AB(ab) ((ab == C_DEFAULT) ? C_100 : ab)
|
||
|
||
#ifdef WITH_DEBUG
|
||
#define DEBUG_FUNCTION() do { printf("*** \033[033;1m%s\033[0m ***\n", __FUNCTION__); } while (0)
|
||
#define DEBUG_XFER(data, nbytes, hint) do { hexdump (data, nbytes, hint, 0); } while (0)
|
||
#else
|
||
#define DEBUG_FUNCTION() do {} while (0)
|
||
#define DEBUG_XFER(data, nbytes, hint) do {} while (0)
|
||
#endif
|
||
|
||
|
||
/*
|
||
* Buffer management macros.
|
||
*
|
||
* The following macros ease setting-up and using buffers:
|
||
* BUFFER_INIT (data, 5); // data -> [ xx, xx, xx, xx, xx ]
|
||
* BUFFER_SIZE (data); // size -> 0
|
||
* BUFFER_APPEND (data, 0x12); // data -> [ 12, xx, xx, xx, xx ]
|
||
* BUFFER_SIZE (data); // size -> 1
|
||
* uint16_t x = 0x3456; // We suppose we are little endian
|
||
* BUFFER_APPEND_BYTES (data, x, 2);
|
||
* // data -> [ 12, 56, 34, xx, xx ]
|
||
* BUFFER_SIZE (data); // size -> 3
|
||
* BUFFER_APPEND_LE (data, x, 2, sizeof (x));
|
||
* // data -> [ 12, 56, 34, 34, 56 ]
|
||
* BUFFER_SIZE (data); // size -> 5
|
||
*/
|
||
|
||
/*
|
||
* Initialise a buffer named buffer_name of size bytes.
|
||
*/
|
||
#define BUFFER_INIT(buffer_name, size) \
|
||
uint8_t buffer_name[size]; \
|
||
size_t __##buffer_name##_size = size; \
|
||
size_t __##buffer_name##_n = 0
|
||
|
||
/*
|
||
* Create a wrapper for an existing buffer.
|
||
* BEWARE! It eats children!
|
||
*/
|
||
#define BUFFER_ALIAS(buffer_name, origin, origin_size) \
|
||
uint8_t *buffer_name = (void *)origin; \
|
||
size_t __##buffer_name##_size = origin_size; \
|
||
size_t __##buffer_name##_n = 0;
|
||
|
||
#define BUFFER_SIZE(buffer_name) (__##buffer_name##_n)
|
||
|
||
#define BUFFER_MAXSIZE(buffer_name) (__##buffer_name##_size)
|
||
|
||
#define BUFFER_CLEAR(buffer_name) (__##buffer_name##_n = 0)
|
||
/*
|
||
* Append one byte of data to the buffer buffer_name.
|
||
*/
|
||
#define BUFFER_APPEND(buffer_name, data) \
|
||
do { \
|
||
buffer_name[__##buffer_name##_n++] = data; \
|
||
} while (0)
|
||
|
||
/*
|
||
* Append size bytes of data to the buffer buffer_name.
|
||
*/
|
||
#define BUFFER_APPEND_BYTES(buffer_name, data, size) \
|
||
do { \
|
||
size_t __n = 0; \
|
||
while (__n < size) { \
|
||
buffer_name[__##buffer_name##_n++] = ((uint8_t *)data)[__n++]; \
|
||
} \
|
||
} while (0)
|
||
|
||
/*
|
||
* Append data_size bytes of data at the end of the buffer. Since data is
|
||
* copied as a little endian value, the storage size of the value has to be
|
||
* passed as the field_size parameter.
|
||
*
|
||
* Example: to copy 24 bits of data from a 32 bits value:
|
||
* BUFFER_APPEND_LE (buffer, data, 3, 4);
|
||
*/
|
||
|
||
#if defined(_BYTE_ORDER) && (_BYTE_ORDER != _LITTLE_ENDIAN)
|
||
#define BUFFER_APPEND_LE(buffer, data, data_size, field_size) \
|
||
do { \
|
||
size_t __data_size = data_size; \
|
||
size_t __field_size = field_size; \
|
||
while (__field_size--, __data_size--) { \
|
||
buffer[__##buffer##_n++] = ((uint8_t *)&data)[__field_size]; \
|
||
} \
|
||
} while (0)
|
||
#else
|
||
#define BUFFER_APPEND_LE(buffer, data, data_size, field_size) \
|
||
do { \
|
||
memcpy (buffer + __##buffer##_n, &data, data_size); \
|
||
__##buffer##_n += data_size; \
|
||
} while (0)
|
||
#endif
|
||
|
||
#endif /* !__FREEFARE_INTERNAL_H__ */
|