diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index d68c2f471..d9468636e 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,20.1,, +Version,+,20.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -829,6 +829,7 @@ Function,-,felica_get_service_name,FuriString*,FelicaService* Function,-,felica_get_system_name,FuriString*,FelicaSystem* Function,-,felica_lite_can_read_without_mac,_Bool,"uint8_t*, uint8_t" Function,-,felica_lite_dump_data,_Bool,"FuriHalNfcTxRxContext*, FelicaReader*, FelicaData*, FelicaSystem*" +Function,-,felica_lite_is_issued,_Bool,FelicaLiteInfo* Function,-,felica_lite_prepare_unencrypted_read,uint8_t,"uint8_t*, const FelicaReader*, _Bool, const uint8_t*, uint8_t" Function,-,felica_lite_prepare_unencrypted_write,uint8_t,"uint8_t*, const FelicaReader*, const uint8_t*, uint8_t, const uint8_t*" Function,-,felica_parse_unencrypted_read,uint16_t,"uint8_t*, uint8_t, FelicaReader*, uint8_t*, uint16_t" diff --git a/lib/nfc/protocols/felica.c b/lib/nfc/protocols/felica.c index 827187564..c5823a221 100644 --- a/lib/nfc/protocols/felica.c +++ b/lib/nfc/protocols/felica.c @@ -1,4 +1,5 @@ #include +#include #include #include "felica.h" #include "nfc_util.h" @@ -151,6 +152,215 @@ FelicaICType felica_get_ic_type(uint8_t* PMm) { return FelicaICType2K; } +static void felica_lite_diversify_key(uint8_t* id_block, uint8_t* master_key, uint8_t* card_key) { + uint8_t ZERO[8] = {0}; + uint8_t L[8]; + mbedtls_des3_context ctx; + mbedtls_des3_init(&ctx); + mbedtls_des3_set3key_enc(&ctx, master_key); + mbedtls_des3_crypt_ecb(&ctx, ZERO, L); + mbedtls_des3_free(&ctx); + + uint8_t K1[8]; + for(int i = 0; i < 8; i++) { + K1[i] = L[i] << 1; + if(i < 7) { + K1[i] |= (L[i + 1] >> 7); + } + } + + if((L[0] ^ 0x80) == 0) { + K1[7] ^= 0x1B; + } + + uint8_t M1[8]; + uint8_t M2[8]; + memcpy(M1, id_block, 8); + memcpy(M2, id_block + 8, 8); + for(int i = 0; i < 8; i++) { + M2[i] ^= K1[i]; + } + + uint8_t C1[8]; + mbedtls_des3_init(&ctx); + mbedtls_des3_set3key_enc(&ctx, master_key); + mbedtls_des3_crypt_ecb(&ctx, M1, C1); + for(int i = 0; i < 8; i++) { + C1[i] ^= M2[i]; + } + + mbedtls_des3_crypt_ecb(&ctx, C1, card_key); // T + + M1[0] ^= 0x80; // M'1 + mbedtls_des3_crypt_ecb(&ctx, M1, C1); // C'1 + for(int i = 0; i < 8; i++) { + C1[i] ^= M2[i]; + } + + mbedtls_des3_crypt_ecb(&ctx, C1, card_key + 8); // T' + mbedtls_des3_free(&ctx); +} + +static void felica_lite_generate_session_key( + uint8_t* random_challenge, + uint8_t* card_key, + uint8_t* session_key) { + uint8_t RC1[8]; + uint8_t RC2[8]; + uint8_t CK[16]; + + for(int i = 0; i < 8; i++) { + RC1[i] = random_challenge[7 - i]; + RC2[i] = random_challenge[i]; + CK[i] = card_key[7 - i]; + CK[i + 8] = card_key[15 - i]; + } + + mbedtls_des3_context ctx; + + uint8_t SK1[8]; + mbedtls_des3_init(&ctx); + mbedtls_des3_set2key_enc(&ctx, CK); + mbedtls_des3_crypt_ecb(&ctx, RC1, SK1); + + uint8_t SK2[8]; + for(int i = 0; i < 8; i++) { + RC2[i] ^= SK1[i]; + } + mbedtls_des3_crypt_ecb(&ctx, RC2, SK2); + mbedtls_des3_free(&ctx); + + for(int i = 0; i < 8; i++) { + session_key[i] = SK1[7 - i]; + session_key[i + 8] = SK2[7 - i]; + } +} + +static void felica_lite_calculate_mac( + uint8_t* random_challenge, + uint8_t* session_key, + uint8_t* block_data, + size_t block_count, + uint8_t* MAC) { + uint8_t SK[16]; + + for(int i = 0; i < 8; i++) { + MAC[i] = random_challenge[7 - i]; + SK[i] = session_key[7 - i]; + SK[i + 8] = session_key[15 - i]; + } + + mbedtls_des3_context ctx; + mbedtls_des3_init(&ctx); + mbedtls_des3_set3key_enc(&ctx, SK); + + for(size_t block_num = 0; block_num < block_count; block_num++) { + for(int i = 0; i < 8; i++) { + MAC[i] ^= block_data[block_num * FELICA_BLOCK_SIZE + 7 - i]; + } + + uint8_t intermediate[8]; + mbedtls_des3_crypt_ecb(&ctx, MAC, intermediate); + for(int i = 0; i < 8; i++) { + intermediate[i] ^= block_data[block_num * FELICA_BLOCK_SIZE + 15 - i]; + } + + mbedtls_des3_crypt_ecb(&ctx, intermediate, MAC); + } + + mbedtls_des3_free(&ctx); +} + +static void felica_lite_calculate_mac_a( + uint8_t* random_challenge, + uint8_t* session_key, + uint8_t* iv, + uint8_t* block_data, + size_t block_count, + uint8_t* MAC_A) { + uint8_t SK[16]; + uint8_t intermediate_a[8]; + uint8_t intermediate_b[8]; + + for(int i = 0; i < 8; i++) { + intermediate_a[i] = iv[7 - 1] ^ random_challenge[7 - i]; + SK[i] = session_key[7 - i]; + SK[i + 8] = session_key[15 - i]; + } + + mbedtls_des3_context ctx; + mbedtls_des3_init(&ctx); + mbedtls_des3_set3key_enc(&ctx, SK); + mbedtls_des3_crypt_ecb(&ctx, intermediate_a, intermediate_b); + + for(size_t block_num = 0; block_num < block_count; block_num++) { + for(int i = 0; i < 8; i++) { + intermediate_b[i] ^= block_data[block_num * FELICA_BLOCK_SIZE + 7 - i]; + } + + mbedtls_des3_crypt_ecb(&ctx, intermediate_b, intermediate_a); + for(int i = 0; i < 8; i++) { + intermediate_a[i] ^= block_data[block_num * FELICA_BLOCK_SIZE + 7 - i]; + } + + mbedtls_des3_crypt_ecb(&ctx, intermediate_a, intermediate_b); + } + + for(int i = 0; i < 8; i++) { + MAC_A[i] = intermediate_b[7 - 1]; + } +} + +static void felica_lite_calculate_mac_a_for_write( + uint8_t* random_challenge, + uint8_t* session_key, + uint32_t write_count, + uint8_t block_number, + uint8_t* block_data, + uint8_t* MAC_A) { + uint8_t iv[8]; + nfc_util_num2bytes(write_count, 3, iv); + iv[3] = 0x00; + iv[4] = block_number; + iv[5] = 0x00; + iv[6] = 0x91; + iv[7] = 0x00; + + uint8_t SK[16]; + for(int i = 0; i < 8; i++) { + SK[i] = session_key[i + 8]; + SK[i + 8] = session_key[i]; + } + + felica_lite_calculate_mac_a(random_challenge, SK, iv, block_data, 1, MAC_A); +} + +static void felica_lite_calculate_mac_a_for_read( + uint8_t* random_challenge, + uint8_t* session_key, + uint8_t* block_list, + uint8_t block_list_count, + uint8_t* block_data, + uint8_t block_count, + uint8_t* MAC_A) { + uint8_t iv[8] = {0}; + + uint8_t block_list_to_write = MIN(block_list_count, 4); + for(int i = 0; i < block_list_to_write; i++) { + iv[i * 2] = block_list[i]; + } + if(block_list_to_write < 4) { + iv[6] = 0xFF; + iv[7] = 0xFF; + } + if(block_list_to_write < 3) { + iv[4] = 0xFF; + iv[5] = 0xFF; + } + + felica_lite_calculate_mac_a(random_challenge, session_key, iv, block_data, block_count, MAC_A); +} + /** Parse common FeliCa response headers. * * This parses and validates the most commonly occurring response header types. @@ -165,7 +375,7 @@ FelicaICType felica_get_ic_type(uint8_t* PMm) { * @param always_succeed When set to true, skip status flags (sf1 and sf2) parsing. * @return The number of bytes parsed, or 0 when response is invalid or status flags are set. */ -static uint8_t felica_consume_header( +static uint8_t felica_consume_unencrypted_header( uint8_t* buf, uint8_t len, FelicaReader* reader, @@ -261,7 +471,8 @@ uint16_t felica_parse_unencrypted_read( FelicaReader* reader, uint8_t* out, uint16_t out_len) { - uint8_t consumed = felica_consume_header(buf, len, reader, FELICA_UNENCRYPTED_READ_RES, false); + uint8_t consumed = + felica_consume_unencrypted_header(buf, len, reader, FELICA_UNENCRYPTED_READ_RES, false); if(!consumed) { return 0; } @@ -345,7 +556,7 @@ uint8_t felica_lite_prepare_unencrypted_write( bool felica_parse_unencrypted_write(uint8_t* buf, uint8_t len, FelicaReader* reader) { uint8_t consumed = - felica_consume_header(buf, len, reader, FELICA_UNENCRYPTED_WRITE_RES, false); + felica_consume_unencrypted_header(buf, len, reader, FELICA_UNENCRYPTED_WRITE_RES, false); if(!consumed) { return false; } @@ -364,7 +575,7 @@ bool felica_parse_request_system_code( FelicaReader* reader, FelicaSystemArray_t* systems) { uint8_t consumed = - felica_consume_header(buf, len, reader, FELICA_REQUEST_SYSTEM_CODE_RES, true); + felica_consume_unencrypted_header(buf, len, reader, FELICA_REQUEST_SYSTEM_CODE_RES, true); if(consumed == 0) { return false; } diff --git a/lib/nfc/protocols/felica_util.c b/lib/nfc/protocols/felica_util.c index 481ea5ed7..47f66fcc5 100644 --- a/lib/nfc/protocols/felica_util.c +++ b/lib/nfc/protocols/felica_util.c @@ -11,6 +11,20 @@ uint_least32_t felica_estimate_timing_us(uint_least8_t timing, uint_least8_t uni return TIME_CONSTANT_US * scale * (base_cost_factor + unit_cost_factor * units); } +bool felica_lite_is_issued(FelicaLiteInfo* lite_info) { + // System blocks aren't writable? + if(lite_info->memory_config[2] == 0x00) { + return false; + } + + // MC is not writable? + if(lite_info->memory_config[1] & 0x80) { + return false; + } + + return true; +} + FuriString* felica_get_system_name(FelicaSystem* system) { uint16_t code = system->code; diff --git a/lib/nfc/protocols/felica_util.h b/lib/nfc/protocols/felica_util.h index e53d66805..de7ff884a 100644 --- a/lib/nfc/protocols/felica_util.h +++ b/lib/nfc/protocols/felica_util.h @@ -1,5 +1,6 @@ #include "./felica.h" uint_least32_t felica_estimate_timing_us(uint_least8_t timing, uint_least8_t units); +bool felica_lite_is_issued(FelicaLiteInfo* lite_info); FuriString* felica_get_system_name(FelicaSystem* system); FuriString* felica_get_service_name(FelicaService* service); \ No newline at end of file