diff --git a/libfreefare/mifare_desfire_crypto.c b/libfreefare/mifare_desfire_crypto.c index 627c0aa..0e9584d 100644 --- a/libfreefare/mifare_desfire_crypto.c +++ b/libfreefare/mifare_desfire_crypto.c @@ -524,9 +524,32 @@ mifare_cryto_postprocess_data (MifareTag tag, void *data, ssize_t *nbytes, int c case MDCM_ENCIPHERED: (*nbytes)--; bool verified = false; + int crc_pos; int end_crc_pos; - switch (MIFARE_DESFIRE (tag)->authentication_scheme) { - case AS_LEGACY: + uint8_t x; + + /* + * AS_LEGACY: + * ,-----------------+-------------------------------+--------+ + * \ BLOCK n-1 | BLOCK n | STATUS | + * / PAYLOAD | CRC0 | CRC1 | 0x80? | 0x000000000000 | 0x9100 | + * `-----------------+-------------------------------+--------+ + * + * <------------ DATA ------------> + * FRAME = PAYLOAD + CRC(PAYLOAD) + PADDING + * + * AS_NEW: + * ,-------------------------------+-----------------------------------------------+--------+ + * \ BLOCK n-1 | BLOCK n | STATUS | + * / PAYLOAD | CRC0 | CRC1 | CRC2 | CRC3 | 0x80? | 0x0000000000000000000000000000 | 0x9100 | + * `-------------------------------+-----------------------------------------------+--------+ + * <----------------------------------- DATA ------------------------------------->| + * + * <----------------- DATA ----------------> + * FRAME = PAYLOAD + CRC(PAYLOAD + STATUS) + PADDING + STATUS + * `------------------' + */ + mifare_cypher_blocks_chained (tag, NULL, NULL, res, *nbytes, MCD_RECEIVE, MCO_DECYPHER); /* @@ -534,72 +557,41 @@ mifare_cryto_postprocess_data (MifareTag tag, void *data, ssize_t *nbytes, int c * can't start by the end because the CRC is supposed to be 0 when * verified, and accumulating 0's in it should not change it. */ - end_crc_pos = *nbytes - 7; // The CRC can be over two blocks - - do { - uint16_t crc; - iso14443a_crc (res, end_crc_pos, (uint8_t *)&crc); - if (!crc) { - verified = true; - for (int n = end_crc_pos; n < *nbytes - 1; n++) { - uint8_t byte = ((uint8_t *)res)[n]; - if (!( (0x00 == byte) || ((0x80 == byte) && (n == end_crc_pos)) )) - verified = false; - } - } - if (verified) { - *nbytes = end_crc_pos - 2; - ((uint8_t *)data)[(*nbytes)++] = 0x00; - } else { - end_crc_pos++; - } - } while (!verified && (end_crc_pos < *nbytes)); - - if (!verified) { -#if WITH_DEBUG - warnx ("(3)DES not verified"); -#endif - MIFARE_DESFIRE (tag)->last_pcd_error = CRYPTO_ERROR; - *nbytes = -1; - res = NULL; - } - break; - - case AS_NEW: - mifare_cypher_blocks_chained (tag, NULL, NULL, res, *nbytes, MCD_RECEIVE, MCO_DECYPHER); - - /* - * ,----------------------------+-----------------------------------------+-------------+ - * \ BLOCK n-1 | BLOCK n | STATUS| - * / DATA | CRC0 | CRC1 | CRC2 | CRC3 | 0x80? | 0x00 | 0x00 | ... | 0x00 | 0x91 | 0x00 | - * `----------------------------+-----------------------------------------+-------------+ - * <------------------------------ DATA --------------------------------->| - * - * <----------------- DATA ----------------> - * FRAME = PAYLOAD + CRC(PAYLOAD + STATUS) + PADDING + STATUS - * ^ | - * | | - * `------------------' - */ - - /* Move status between payload and CRC */ - res = assert_crypto_buffer_size (tag, (*nbytes) + 1); - memcpy (res, data, *nbytes); - - int crc_pos = (*nbytes) - 16 - 3; - if (crc_pos < 0) { - /* Single block */ - crc_pos = 0; - } - memmove (res + crc_pos + 1, res + crc_pos, *nbytes - crc_pos); - ((uint8_t *)res)[crc_pos] = 0x00; - crc_pos++; - *nbytes += 1; + switch (MIFARE_DESFIRE (tag)->authentication_scheme) { + case AS_LEGACY: + crc_pos = *nbytes - 8 - 1; // The CRC can be over two blocks + break; + case AS_NEW: + /* Move status between payload and CRC */ + res = assert_crypto_buffer_size (tag, (*nbytes) + 1); + memcpy (res, data, *nbytes); + + crc_pos = (*nbytes) - 16 - 3; + if (crc_pos < 0) { + /* Single block */ + crc_pos = 0; + } + memmove (res + crc_pos + 1, res + crc_pos, *nbytes - crc_pos); + ((uint8_t *)res)[crc_pos] = 0x00; + crc_pos++; + *nbytes += 1; + break; + } do { + uint16_t crc16; uint32_t crc; - end_crc_pos = crc_pos + 4; - desfire_crc32 (res, end_crc_pos, (uint8_t *)&crc); + switch (MIFARE_DESFIRE (tag)->authentication_scheme) { + case AS_LEGACY: + end_crc_pos = crc_pos + 2; + iso14443a_crc (res, end_crc_pos, (uint8_t *)&crc16); + crc = crc16; + break; + case AS_NEW: + end_crc_pos = crc_pos + 4; + desfire_crc32 (res, end_crc_pos, (uint8_t *)&crc); + break; + } if (!crc) { verified = true; for (int n = end_crc_pos; n < *nbytes - 1; n++) { @@ -609,26 +601,39 @@ mifare_cryto_postprocess_data (MifareTag tag, void *data, ssize_t *nbytes, int c } } if (verified) { - *nbytes = crc_pos - 1; + *nbytes = crc_pos; + switch (MIFARE_DESFIRE (tag)->authentication_scheme) { + case AS_LEGACY: ((uint8_t *)data)[(*nbytes)++] = 0x00; + break; + case AS_NEW: + /* The status byte was already before the CRC */ + break; + } } else { - uint8_t x = ((uint8_t *)res)[crc_pos - 1]; - ((uint8_t *)res)[crc_pos - 1] = ((uint8_t *)res)[crc_pos]; - ((uint8_t *)res)[crc_pos] = x; + switch (MIFARE_DESFIRE (tag)->authentication_scheme) { + case AS_LEGACY: + break; + case AS_NEW: + x = ((uint8_t *)res)[crc_pos - 1]; + ((uint8_t *)res)[crc_pos - 1] = ((uint8_t *)res)[crc_pos]; + ((uint8_t *)res)[crc_pos] = x; + break; + } crc_pos++; } } while (!verified && (end_crc_pos < *nbytes)); if (!verified) { #if WITH_DEBUG - hexdump (res, *nbytes, "KO ", 0); - warnx ("AES not verified"); + /* FIXME In some configurations, the file is transmitted PLAIN */ + warnx ("CRC not verified in decyphered stream"); #endif MIFARE_DESFIRE (tag)->last_pcd_error = CRYPTO_ERROR; *nbytes = -1; res = NULL; } - } + break; default: warnx ("Unknown communication settings");