mirror of
https://github.com/RfidResearchGroup/ChameleonUltra.git
synced 2026-05-12 11:22:59 -07:00
Merge remote-tracking branch 'origin/wip_protocol_refresh' into wip_protocol_refresh
This commit is contained in:
@@ -3,6 +3,8 @@ All notable changes to this project will be documented in this file.
|
||||
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
|
||||
|
||||
## [unreleased][unreleased]
|
||||
- Removed MF1_DETECT_DARKSIDE (@doegox)
|
||||
- Added MF1_STATIC_NESTED_ACQUIRE and its support in `hf mf nested` (@xianglin1998)
|
||||
- Changed `hf 14a scan`: Automatically send RATS to 14443-4a tags (@augustozanellato)
|
||||
- Changed Darkside: use LEDs for visual feedback of attack progression (@doegox)
|
||||
- Changed Darkside: longer RF field off for reset and longer CLI timeout (@doegox)
|
||||
|
||||
+7
-7
@@ -211,19 +211,19 @@ Notes:
|
||||
* Command: no data
|
||||
* Response: 1 byte, according to `mf1_nested_type_t` enum
|
||||
* CLI: cf `hf 14a info`
|
||||
### 2003: MF1_DETECT_DARKSIDE
|
||||
* Command: no data
|
||||
* Response: 1 byte, according to `mf1_darkside_status_t` enum
|
||||
* CLI: unused
|
||||
### 2003: MF1_STATIC_NESTED_ACQUIRE
|
||||
* Command: 10 bytes: `type_known|block_known|key_known[6]|type_target|block_target`. Key as 6 bytes.
|
||||
* Response: 4+N*8 bytes: `uid[4]` followed by N tuples of `nt[4]|nt_enc[4]`. All values as U32.
|
||||
* CLI: cf `hf mf nested` on static nonce tag
|
||||
### 2004: MF1_DARKSIDE_ACQUIRE
|
||||
* Command: 4 bytes: `type_target|block_target|first_recover|sync_max`
|
||||
* Response: 1 byte if Darkside failed, according to `mf1_darkside_status_t` enum,
|
||||
else 25 bytes `darkside_status|uid[4]|nt1[4]|par[4]|ks1[4]|nr[4]|ar[4]`
|
||||
else 33 bytes `darkside_status|uid[4]|nt1[4]|par[8]|ks1[8]|nr[4]|ar[4]`
|
||||
* `darkside_status`
|
||||
* `uid[4]` U32 (format expected by `darkside` tool)
|
||||
* `nt1[4]` U32
|
||||
* `par[4]` U32
|
||||
* `ks1[4]` U32
|
||||
* `par[8]` U64
|
||||
* `ks1[8]` U64
|
||||
* `nr[4]` U32
|
||||
* `ar[4]` U32
|
||||
* CLI: cf `hf mf darkside`
|
||||
|
||||
@@ -256,15 +256,6 @@ static data_frame_tx_t *cmd_processor_mf1_detect_prng(uint16_t cmd, uint16_t sta
|
||||
return data_frame_make(cmd, HF_TAG_OK, sizeof(type), &type);
|
||||
}
|
||||
|
||||
static data_frame_tx_t *cmd_processor_mf1_detect_darkside(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
||||
uint8_t darkside_status;
|
||||
status = check_darkside_support((mf1_darkside_status_t *)&darkside_status);
|
||||
if (status != HF_TAG_OK) {
|
||||
return data_frame_make(cmd, status, 0, NULL);
|
||||
}
|
||||
return data_frame_make(cmd, HF_TAG_OK, sizeof(darkside_status), &darkside_status);
|
||||
}
|
||||
|
||||
static data_frame_tx_t *cmd_processor_mf1_darkside_acquire(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
||||
if (length != 4) {
|
||||
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
||||
@@ -938,8 +929,7 @@ static cmd_data_map_t m_data_cmd_map[] = {
|
||||
{ DATA_CMD_HF14A_SCAN, before_hf_reader_run, cmd_processor_hf14a_scan, after_hf_reader_run },
|
||||
{ DATA_CMD_MF1_DETECT_SUPPORT, before_hf_reader_run, cmd_processor_mf1_detect_support, after_hf_reader_run },
|
||||
{ DATA_CMD_MF1_DETECT_PRNG, before_hf_reader_run, cmd_processor_mf1_detect_prng, after_hf_reader_run },
|
||||
{ DATA_CMD_MF1_DETECT_DARKSIDE, before_hf_reader_run, cmd_processor_mf1_detect_darkside, after_hf_reader_run },
|
||||
|
||||
{ DATA_CMD_MF1_STATIC_NESTED_ACQUIRE, before_hf_reader_run, cmd_processor_mf1_static_nested_acquire, after_hf_reader_run },
|
||||
{ DATA_CMD_MF1_DARKSIDE_ACQUIRE, before_hf_reader_run, cmd_processor_mf1_darkside_acquire, after_hf_reader_run },
|
||||
{ DATA_CMD_MF1_DETECT_NT_DIST, before_hf_reader_run, cmd_processor_mf1_detect_nt_dist, after_hf_reader_run },
|
||||
{ DATA_CMD_MF1_NESTED_ACQUIRE, before_hf_reader_run, cmd_processor_mf1_nested_acquire, after_hf_reader_run },
|
||||
@@ -947,7 +937,6 @@ static cmd_data_map_t m_data_cmd_map[] = {
|
||||
{ DATA_CMD_MF1_AUTH_ONE_KEY_BLOCK, before_hf_reader_run, cmd_processor_mf1_auth_one_key_block, after_hf_reader_run },
|
||||
{ DATA_CMD_MF1_READ_ONE_BLOCK, before_hf_reader_run, cmd_processor_mf1_read_one_block, after_hf_reader_run },
|
||||
{ DATA_CMD_MF1_WRITE_ONE_BLOCK, before_hf_reader_run, cmd_processor_mf1_write_one_block, after_hf_reader_run },
|
||||
{ DATA_CMD_MF1_STATIC_NESTED_ACQUIRE, before_hf_reader_run, cmd_processor_mf1_static_nested_acquire, after_hf_reader_run },
|
||||
|
||||
{ DATA_CMD_EM410X_SCAN, before_reader_run, cmd_processor_em410x_scan, NULL },
|
||||
{ DATA_CMD_EM410X_WRITE_TO_T55XX, before_reader_run, cmd_processor_em410x_write_to_t55XX, NULL },
|
||||
|
||||
@@ -57,14 +57,13 @@
|
||||
#define DATA_CMD_HF14A_SCAN (2000)
|
||||
#define DATA_CMD_MF1_DETECT_SUPPORT (2001)
|
||||
#define DATA_CMD_MF1_DETECT_PRNG (2002)
|
||||
#define DATA_CMD_MF1_DETECT_DARKSIDE (2003)
|
||||
#define DATA_CMD_MF1_STATIC_NESTED_ACQUIRE (2003)
|
||||
#define DATA_CMD_MF1_DARKSIDE_ACQUIRE (2004)
|
||||
#define DATA_CMD_MF1_DETECT_NT_DIST (2005)
|
||||
#define DATA_CMD_MF1_NESTED_ACQUIRE (2006)
|
||||
#define DATA_CMD_MF1_AUTH_ONE_KEY_BLOCK (2007)
|
||||
#define DATA_CMD_MF1_READ_ONE_BLOCK (2008)
|
||||
#define DATA_CMD_MF1_WRITE_ONE_BLOCK (2009)
|
||||
#define DATA_CMD_MF1_STATIC_NESTED_ACQUIRE (2010)
|
||||
//
|
||||
// ******************************************************************
|
||||
|
||||
|
||||
@@ -273,7 +273,7 @@ static uint8_t darkside_select_nonces(picc_14a_tag_t *tag, uint8_t block, uint8_
|
||||
//Random number collection
|
||||
for (i = 0; i < NT_COUNT; i++) {
|
||||
bsp_wdt_feed();
|
||||
while(NRF_LOG_PROCESS());
|
||||
while (NRF_LOG_PROCESS());
|
||||
//When the antenna is reset, we must make sure
|
||||
// 1. The antenna is powered off for a long time to ensure that the card is completely powered off, otherwise the pseudo -random number generator of the card cannot be reset
|
||||
// 2. Moderate power -off time, don't be too long, it will affect efficiency, and don't be too short.
|
||||
@@ -419,7 +419,7 @@ uint8_t darkside_recover_key(uint8_t targetBlk, uint8_t targetTyp,
|
||||
// Always collect different NACK under a large cycle
|
||||
do {
|
||||
bsp_wdt_feed();
|
||||
while(NRF_LOG_PROCESS());
|
||||
while (NRF_LOG_PROCESS());
|
||||
// update LEDs
|
||||
led_toggle ^= 1;
|
||||
if (led_toggle) {
|
||||
@@ -492,7 +492,7 @@ uint8_t darkside_recover_key(uint8_t targetBlk, uint8_t targetTyp,
|
||||
resync_count = 0;
|
||||
|
||||
if (len == 4) {
|
||||
NRF_LOG_INFO("NACK acquired (%i/8)", nt_diff+1);
|
||||
NRF_LOG_INFO("NACK acquired (%i/8)", nt_diff + 1);
|
||||
received_nack = 1;
|
||||
} else if (len == 32) {
|
||||
// did we get lucky and got our dummy key to be valid?
|
||||
@@ -569,20 +569,6 @@ void antenna_switch_delay(uint32_t delay_ms) {
|
||||
g_ant_reset_delay = delay_ms;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief :Determine whether this card supports DARKSIDE attack
|
||||
* @retval : If support, return hf_tag_ok and darkside_status = OK. If it is not supported,
|
||||
* Return to the results of abnormal results during the detection process in darkside_status
|
||||
* Or other card -related communication errors, the most common is loss card HF_TAG_NO
|
||||
*
|
||||
*/
|
||||
uint8_t check_darkside_support(mf1_darkside_status_t *darkside_status) {
|
||||
// Instantiated parameter
|
||||
DarksideCore_t dc;
|
||||
//Determine and return the result directly
|
||||
return darkside_recover_key(0x03, PICC_AUTHENT1A, true, 0x15, &dc, darkside_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief : Determine whether this card supports M1 verification steps
|
||||
* @retval : If support, it will return hf_tag_ok,
|
||||
@@ -942,80 +928,80 @@ uint8_t nested_distance_detect(uint8_t block, uint8_t type, uint8_t *key, uint8_
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief : StaticNested core, used to collect NT.
|
||||
* @brief : StaticNested core, used to collect NT.
|
||||
* This function is only responsible for collection and is not responsible for converting and parsing to KS.
|
||||
* @param :p_nt1 : NT1, non encrypted.
|
||||
* @param :p_nt2 : NT2, encrypted.
|
||||
* @param :keyKnown : U64 value of the known key of the card
|
||||
* @param :blkKnown : The sector to which the card's known secret key belongs
|
||||
* @param :typKnown : The known key type of the card, 0x60 (A key) or 0x61 (B key)
|
||||
* @param :targetBlock : Target sectors that require nested attacks
|
||||
* @param :targetType : Target key types that require nested attacks
|
||||
* @param :nestedAgain : StaticNested enhanced vulnerability, which can obtain two sets of encrypted random numbers based on nested verification of known keys
|
||||
* @retval : Successfully collected and returned to HF_TAG_OK, otherwise an error code will be returned.
|
||||
* @param :p_nt1 : NT1, non encrypted.
|
||||
* @param :p_nt2 : NT2, encrypted.
|
||||
* @param :keyKnown : U64 value of the known key of the card
|
||||
* @param :blkKnown : The sector to which the card's known secret key belongs
|
||||
* @param :typKnown : The known key type of the card, 0x60 (A key) or 0x61 (B key)
|
||||
* @param :targetBlock : Target sectors that require nested attacks
|
||||
* @param :targetType : Target key types that require nested attacks
|
||||
* @param :nestedAgain : StaticNested enhanced vulnerability, which can obtain two sets of encrypted random numbers based on nested verification of known keys
|
||||
* @retval : Successfully collected and returned to HF_TAG_OK, otherwise an error code will be returned.
|
||||
*
|
||||
*/
|
||||
uint8_t static_nested_recover_core(uint8_t *p_nt1, uint8_t *p_nt2, uint64_t keyKnown, uint8_t blkKnown, uint8_t typKnown, uint8_t targetBlock, uint8_t targetType, uint8_t nestedAgain) {
|
||||
struct Crypto1State mpcs = {0, 0};
|
||||
struct Crypto1State *pcs = &mpcs;
|
||||
uint8_t status, len;
|
||||
uint8_t parity[4] = {0x00};
|
||||
uint8_t answer[4] = {0x00};
|
||||
uint32_t uid, nt1, nt2;
|
||||
uid = get_u32_tag_uid(p_tag_info);
|
||||
pcd_14a_reader_halt_tag();
|
||||
if (pcd_14a_reader_scan_auto(p_tag_info) != HF_TAG_OK) {
|
||||
return HF_TAG_NO;
|
||||
}
|
||||
status = authex(pcs, uid, blkKnown, typKnown, keyKnown, AUTH_FIRST, &nt1);
|
||||
if (status != HF_TAG_OK) {
|
||||
return MF_ERR_AUTH;
|
||||
}
|
||||
if (nestedAgain) {
|
||||
status = authex(pcs, uid, blkKnown, typKnown, keyKnown, AUTH_NESTED, NULL);
|
||||
if (status != HF_TAG_OK) {
|
||||
return MF_ERR_AUTH;
|
||||
}
|
||||
}
|
||||
len = send_cmd(pcs, AUTH_NESTED, targetType, targetBlock, &status, answer, parity, U8ARR_BIT_LEN(answer));
|
||||
if (len != 32) {
|
||||
NRF_LOG_INFO("No 32 data recv on sendcmd: %d\r\n", len);
|
||||
return HF_ERR_STAT;
|
||||
}
|
||||
nt2 = bytes_to_num(answer, 4);
|
||||
num_to_bytes(nt1, 4, p_nt1);
|
||||
num_to_bytes(nt2, 4, p_nt2);
|
||||
return HF_TAG_OK;
|
||||
struct Crypto1State *pcs = &mpcs;
|
||||
uint8_t status, len;
|
||||
uint8_t parity[4] = {0x00};
|
||||
uint8_t answer[4] = {0x00};
|
||||
uint32_t uid, nt1, nt2;
|
||||
uid = get_u32_tag_uid(p_tag_info);
|
||||
pcd_14a_reader_halt_tag();
|
||||
if (pcd_14a_reader_scan_auto(p_tag_info) != HF_TAG_OK) {
|
||||
return HF_TAG_NO;
|
||||
}
|
||||
status = authex(pcs, uid, blkKnown, typKnown, keyKnown, AUTH_FIRST, &nt1);
|
||||
if (status != HF_TAG_OK) {
|
||||
return MF_ERR_AUTH;
|
||||
}
|
||||
if (nestedAgain) {
|
||||
status = authex(pcs, uid, blkKnown, typKnown, keyKnown, AUTH_NESTED, NULL);
|
||||
if (status != HF_TAG_OK) {
|
||||
return MF_ERR_AUTH;
|
||||
}
|
||||
}
|
||||
len = send_cmd(pcs, AUTH_NESTED, targetType, targetBlock, &status, answer, parity, U8ARR_BIT_LEN(answer));
|
||||
if (len != 32) {
|
||||
NRF_LOG_INFO("No 32 data recv on sendcmd: %d\r\n", len);
|
||||
return HF_ERR_STAT;
|
||||
}
|
||||
nt2 = bytes_to_num(answer, 4);
|
||||
num_to_bytes(nt1, 4, p_nt1);
|
||||
num_to_bytes(nt2, 4, p_nt2);
|
||||
return HF_TAG_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief : StaticNested encapsulates and calls the functions implemented by the core to collect 2 sets of random numbers.
|
||||
* @brief : StaticNested encapsulates and calls the functions implemented by the core to collect 2 sets of random numbers.
|
||||
* This function is only responsible for collection and is not responsible for converting and parsing to KS.
|
||||
* @param :keyKnown : U64 value of the known key of the card
|
||||
* @param :blkKnown : The sector to which the card's known secret key belongs
|
||||
* @param :typKnown : The known key type of the card, 0x60 (A key) or 0x61 (B key)
|
||||
* @param :targetBlock : Target sectors that require nested attacks
|
||||
* @param :targetType : Target key type that require nested attacks
|
||||
* @param :sncs : StaticNested Decrypting Core Structure Array
|
||||
* @retval : Successfully collected and returned to HF_TAG_OK, otherwise an error code will be returned.
|
||||
* @param :keyKnown : U64 value of the known key of the card
|
||||
* @param :blkKnown : The sector to which the card's known secret key belongs
|
||||
* @param :typKnown : The known key type of the card, 0x60 (A key) or 0x61 (B key)
|
||||
* @param :targetBlock : Target sectors that require nested attacks
|
||||
* @param :targetType : Target key type that require nested attacks
|
||||
* @param :sncs : StaticNested Decrypting Core Structure Array
|
||||
* @retval : Successfully collected and returned to HF_TAG_OK, otherwise an error code will be returned.
|
||||
*
|
||||
*/
|
||||
uint8_t static_nested_recover_key(uint64_t keyKnown, uint8_t blkKnown, uint8_t typKnown, uint8_t targetBlock, uint8_t targetType, mf1_static_nested_core_t* sncs) {
|
||||
uint8_t res;
|
||||
res = pcd_14a_reader_scan_auto(p_tag_info);
|
||||
if (res!= HF_TAG_OK) {
|
||||
return res;
|
||||
}
|
||||
get_4byte_tag_uid(p_tag_info, sncs->uid);
|
||||
res = static_nested_recover_core(sncs->core[0].nt1, sncs->core[0].nt2, keyKnown, blkKnown, typKnown, targetBlock, targetType, false);
|
||||
if (res != HF_TAG_OK) {
|
||||
return res;
|
||||
}
|
||||
res = static_nested_recover_core(sncs->core[1].nt1, sncs->core[1].nt2, keyKnown, blkKnown, typKnown, targetBlock, targetType, true);
|
||||
if (res != HF_TAG_OK) {
|
||||
return res;
|
||||
}
|
||||
return HF_TAG_OK;
|
||||
uint8_t static_nested_recover_key(uint64_t keyKnown, uint8_t blkKnown, uint8_t typKnown, uint8_t targetBlock, uint8_t targetType, mf1_static_nested_core_t *sncs) {
|
||||
uint8_t res;
|
||||
res = pcd_14a_reader_scan_auto(p_tag_info);
|
||||
if (res != HF_TAG_OK) {
|
||||
return res;
|
||||
}
|
||||
get_4byte_tag_uid(p_tag_info, sncs->uid);
|
||||
res = static_nested_recover_core(sncs->core[0].nt1, sncs->core[0].nt2, keyKnown, blkKnown, typKnown, targetBlock, targetType, false);
|
||||
if (res != HF_TAG_OK) {
|
||||
return res;
|
||||
}
|
||||
res = static_nested_recover_core(sncs->core[1].nt1, sncs->core[1].nt2, keyKnown, blkKnown, typKnown, targetBlock, targetType, true);
|
||||
if (res != HF_TAG_OK) {
|
||||
return res;
|
||||
}
|
||||
return HF_TAG_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,7 +32,7 @@ typedef struct { //Answer the random number parameters required for N
|
||||
} mf1_nested_core_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t uid[4];
|
||||
uint8_t uid[4];
|
||||
struct {
|
||||
uint8_t nt1[4];
|
||||
uint8_t nt2[4];
|
||||
@@ -88,9 +88,8 @@ uint8_t nested_distance_detect(
|
||||
uint8_t targetType \
|
||||
|
||||
uint8_t nested_recover_key(NESTED_CORE_PARAM_DEF, mf1_nested_core_t ncs[SETS_NR]);
|
||||
uint8_t static_nested_recover_key(NESTED_CORE_PARAM_DEF, mf1_static_nested_core_t* sncs);
|
||||
uint8_t static_nested_recover_key(NESTED_CORE_PARAM_DEF, mf1_static_nested_core_t *sncs);
|
||||
|
||||
uint8_t check_darkside_support(mf1_darkside_status_t *darkside_status);
|
||||
uint8_t check_prng_type(mf1_prng_type_t *type);
|
||||
uint8_t check_std_mifare_nt_support(bool *support);
|
||||
void antenna_switch_delay(uint32_t delay_ms);
|
||||
|
||||
@@ -583,7 +583,7 @@ uint8_t pcd_14a_reader_scan_once(picc_14a_tag_t *tag) {
|
||||
if (tag->sak & 0x20) {
|
||||
// Tag supports 14443-4, sending RATS
|
||||
uint16_t ats_size;
|
||||
status = pcd_14a_reader_ats_request(tag->ats, &ats_size, 0xFF*8);
|
||||
status = pcd_14a_reader_ats_request(tag->ats, &ats_size, 0xFF * 8);
|
||||
ats_size -= 2; // size returned by pcd_14a_reader_ats_request includes CRC
|
||||
if (ats_size > 254) {
|
||||
NRF_LOG_INFO("Invalid ATS > 254!");
|
||||
@@ -600,7 +600,7 @@ uint8_t pcd_14a_reader_scan_once(picc_14a_tag_t *tag) {
|
||||
// return HF_ERR_ATS;
|
||||
}
|
||||
/*
|
||||
* FIXME: If there is an issue here, it will cause the label to lose its selected state.
|
||||
* FIXME: If there is an issue here, it will cause the label to lose its selected state.
|
||||
* It is necessary to reselect the card after the issue occurs here.
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -382,7 +382,7 @@ class HFMFNested(ReaderRequiredUnit):
|
||||
help="The type of the target block to recover")
|
||||
# hf mf nested -o --block-known 0 --type-known A --key FFFFFFFFFFFF --block-target 4 --type-target A
|
||||
return parser
|
||||
|
||||
|
||||
def from_nt_level_code_to_str(self, nt_level):
|
||||
if nt_level == 0:
|
||||
return 'StaticNested'
|
||||
@@ -409,9 +409,10 @@ class HFMFNested(ReaderRequiredUnit):
|
||||
return None
|
||||
|
||||
# acquire
|
||||
if nt_level == 0: # It's a staticnested tag?
|
||||
nt_uid_obj = self.cmd.mf1_static_nested_acquire(block_known, type_known, key_known, block_target, type_target)
|
||||
cmd_param = f"{nt_uid_obj['uid']} {str(type_target)}"
|
||||
if nt_level == 0: # It's a staticnested tag?
|
||||
nt_uid_obj = self.cmd.mf1_static_nested_acquire(
|
||||
block_known, type_known, key_known, block_target, type_target)
|
||||
cmd_param = f"{nt_uid_obj['uid']} {str(type_target)}"
|
||||
for nt_item in nt_uid_obj['nts']:
|
||||
cmd_param += f" {nt_item['nt']} {nt_item['nt_enc']}"
|
||||
decryptor_name = "staticnested"
|
||||
@@ -419,11 +420,11 @@ class HFMFNested(ReaderRequiredUnit):
|
||||
dist_obj = self.cmd.mf1_detect_nt_dist(block_known, type_known, key_known)
|
||||
nt_obj = self.cmd.mf1_nested_acquire(block_known, type_known, key_known, block_target, type_target)
|
||||
# create cmd
|
||||
cmd_param = f"{dist_obj['uid']} {dist_obj['dist']}"
|
||||
cmd_param = f"{dist_obj['uid']} {dist_obj['dist']}"
|
||||
for nt_item in nt_obj:
|
||||
cmd_param += f" {nt_item['nt']} {nt_item['nt_enc']} {nt_item['par']}"
|
||||
decryptor_name = "nested"
|
||||
|
||||
|
||||
# Cross-platform compatibility
|
||||
if sys.platform == "win32":
|
||||
cmd_recover = f"{decryptor_name}.exe {cmd_param}"
|
||||
@@ -517,7 +518,7 @@ class HFMFDarkside(ReaderRequiredUnit):
|
||||
print(f"Darkside error: {chameleon_cmd.MifareClassicDarksideStatus(darkside_resp[0])}")
|
||||
break
|
||||
darkside_obj = darkside_resp[1]
|
||||
|
||||
|
||||
if darkside_obj['par'] != 0: # NXP tag workaround.
|
||||
self.darkside_list.clear()
|
||||
|
||||
@@ -1054,12 +1055,12 @@ class HWSlotList(DeviceRequiredUnit):
|
||||
def get_slot_name(self, slot, sense):
|
||||
try:
|
||||
name = self.cmd.get_slot_tag_nick(slot, sense).decode(encoding="utf8")
|
||||
return len(name),len(CC+C0),f'{CC}{name}{C0}'
|
||||
return len(name), len(CC+C0), f'{CC}{name}{C0}'
|
||||
except UnexpectedResponseError:
|
||||
return 0,0,''
|
||||
return 0, 0, ''
|
||||
except UnicodeDecodeError:
|
||||
name = "UTF8 Err"
|
||||
return len(name),len(CR+C0),f'{CR}{name}{C0}'
|
||||
return len(name), len(CR+C0), f'{CR}{name}{C0}'
|
||||
|
||||
# hw slot list
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
@@ -1086,20 +1087,24 @@ class HWSlotList(DeviceRequiredUnit):
|
||||
f'{(slotnames[fwslot][0][2] if args.extend else ""):{maxnamelength+slotnames[fwslot][0][1]+1 if args.extend else maxnamelength+1}}'
|
||||
f'{f"{CY if enabled[fwslot] else C0}{hf_tag_type}{C0}" if hf_tag_type != chameleon_cmd.TagSpecificType.TAG_TYPE_UNKNOWN else "undef"}')
|
||||
if args.extend == 1 and \
|
||||
enabled[fwslot] and \
|
||||
slot == selected and \
|
||||
hf_tag_type in [
|
||||
chameleon_cmd.TagSpecificType.TAG_TYPE_MIFARE_Mini,
|
||||
chameleon_cmd.TagSpecificType.TAG_TYPE_MIFARE_1024,
|
||||
chameleon_cmd.TagSpecificType.TAG_TYPE_MIFARE_2048,
|
||||
chameleon_cmd.TagSpecificType.TAG_TYPE_MIFARE_4096,
|
||||
]:
|
||||
enabled[fwslot] and \
|
||||
slot == selected and \
|
||||
hf_tag_type in [
|
||||
chameleon_cmd.TagSpecificType.TAG_TYPE_MIFARE_Mini,
|
||||
chameleon_cmd.TagSpecificType.TAG_TYPE_MIFARE_1024,
|
||||
chameleon_cmd.TagSpecificType.TAG_TYPE_MIFARE_2048,
|
||||
chameleon_cmd.TagSpecificType.TAG_TYPE_MIFARE_4096,
|
||||
]:
|
||||
config = self.cmd.mf1_get_emulator_config()
|
||||
print(' - Mifare Classic emulator settings:')
|
||||
print(f' {"Detection (mfkey32) mode:":40}{f"{CG}enabled{C0}" if config["detection"] else f"{CR}disabled{C0}"}')
|
||||
print(f' {"Gen1A magic mode:":40}{f"{CG}enabled{C0}" if config["gen1a_mode"] else f"{CR}disabled{C0}"}')
|
||||
print(f' {"Gen2 magic mode:":40}{f"{CG}enabled{C0}" if config["gen2_mode"] else f"{CR}disabled{C0}"}')
|
||||
print(f' {"Use anti-collision data from block 0:":40}{f"{CG}enabled{C0}" if config["block_anti_coll_mode"] else f"{CR}disabled{C0}"}')
|
||||
print(
|
||||
f' {"Detection (mfkey32) mode:":40}{f"{CG}enabled{C0}" if config["detection"] else f"{CR}disabled{C0}"}')
|
||||
print(
|
||||
f' {"Gen1A magic mode:":40}{f"{CG}enabled{C0}" if config["gen1a_mode"] else f"{CR}disabled{C0}"}')
|
||||
print(
|
||||
f' {"Gen2 magic mode:":40}{f"{CG}enabled{C0}" if config["gen2_mode"] else f"{CR}disabled{C0}"}')
|
||||
print(
|
||||
f' {"Use anti-collision data from block 0:":40}{f"{CG}enabled{C0}" if config["block_anti_coll_mode"] else f"{CR}disabled{C0}"}')
|
||||
print(f' {"Write mode:":40}{CY}{chameleon_cmd.MifareClassicWriteMode(config["write_mode"])}{C0}')
|
||||
print(f' LF: '
|
||||
f'{(slotnames[fwslot][1][2] if args.extend else ""):{maxnamelength+slotnames[fwslot][1][1]+1 if args.extend else maxnamelength+1}}'
|
||||
@@ -1555,7 +1560,8 @@ class HWRaw(DeviceRequiredUnit):
|
||||
return parser
|
||||
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
response = self.cmd.device.send_cmd_sync(args.command, data=bytes.fromhex(args.data), status=0x0, timeout=args.timeout)
|
||||
response = self.cmd.device.send_cmd_sync(
|
||||
args.command, data=bytes.fromhex(args.data), status=0x0, timeout=args.timeout)
|
||||
print(" - Received:")
|
||||
print(f" Command: {response.cmd}")
|
||||
status_string = f" Status: {response.status:#02x}"
|
||||
|
||||
@@ -61,15 +61,13 @@ DATA_CMD_SET_BLE_PAIRING_ENABLE = 1037
|
||||
DATA_CMD_HF14A_SCAN = 2000
|
||||
DATA_CMD_MF1_DETECT_SUPPORT = 2001
|
||||
DATA_CMD_MF1_DETECT_PRNG = 2002
|
||||
# FIXME: implemented but unused in CLI commands
|
||||
DATA_CMD_MF1_DETECT_DARKSIDE = 2003
|
||||
DATA_CMD_MF1_STATIC_NESTED_ACQUIRE = 2003
|
||||
DATA_CMD_MF1_DARKSIDE_ACQUIRE = 2004
|
||||
DATA_CMD_MF1_DETECT_NT_DIST = 2005
|
||||
DATA_CMD_MF1_NESTED_ACQUIRE = 2006
|
||||
DATA_CMD_MF1_AUTH_ONE_KEY_BLOCK = 2007
|
||||
DATA_CMD_MF1_READ_ONE_BLOCK = 2008
|
||||
DATA_CMD_MF1_WRITE_ONE_BLOCK = 2009
|
||||
DATA_CMD_MF1_STATIC_NESTED_ACQUIRE = 2010
|
||||
|
||||
DATA_CMD_EM410X_SCAN = 3000
|
||||
DATA_CMD_EM410X_WRITE_TO_T55XX = 3001
|
||||
@@ -465,17 +463,6 @@ class ChameleonCMD:
|
||||
resp.data = resp.data[0]
|
||||
return resp
|
||||
|
||||
@expect_response(chameleon_status.Device.HF_TAG_OK)
|
||||
def mf1_detect_darkside_support(self):
|
||||
"""
|
||||
Check if the card is vulnerable to mifare classic darkside attack
|
||||
:return:
|
||||
"""
|
||||
resp = self.device.send_cmd_sync(DATA_CMD_MF1_DETECT_DARKSIDE, timeout=20)
|
||||
if resp.status == chameleon_status.Device.HF_TAG_OK:
|
||||
resp.data = resp.data[0]
|
||||
return resp
|
||||
|
||||
@expect_response(chameleon_status.Device.HF_TAG_OK)
|
||||
def mf1_detect_nt_dist(self, block_known, type_known, key_known):
|
||||
"""
|
||||
@@ -502,7 +489,6 @@ class ChameleonCMD:
|
||||
for nt, nt_enc, par in struct.iter_unpack('!IIB', resp.data)]
|
||||
return resp
|
||||
|
||||
|
||||
@expect_response(chameleon_status.Device.HF_TAG_OK)
|
||||
def mf1_darkside_acquire(self, block_target, type_target, first_recover: int or bool, sync_max):
|
||||
"""
|
||||
@@ -563,7 +549,7 @@ class ChameleonCMD:
|
||||
resp = self.device.send_cmd_sync(DATA_CMD_MF1_WRITE_ONE_BLOCK, data)
|
||||
resp.data = resp.status == chameleon_status.Device.HF_TAG_OK
|
||||
return resp
|
||||
|
||||
|
||||
@expect_response(chameleon_status.Device.HF_TAG_OK)
|
||||
def mf1_static_nested_acquire(self, block_known, type_known, key_known, block_target, type_target):
|
||||
"""
|
||||
@@ -577,7 +563,7 @@ class ChameleonCMD:
|
||||
'uid': struct.unpack('!I', resp.data[0:4])[0],
|
||||
'nts': [
|
||||
{
|
||||
'nt': nt,
|
||||
'nt': nt,
|
||||
'nt_enc': nt_enc
|
||||
} for nt, nt_enc in struct.iter_unpack('!II', resp.data[4:])
|
||||
]
|
||||
@@ -1060,20 +1046,20 @@ class ChameleonCMD:
|
||||
if resp.status == chameleon_status.Device.STATUS_DEVICE_SUCCESS:
|
||||
if resp.data[0] > CURRENT_VERSION_SETTINGS:
|
||||
raise ValueError("Settings version in app older than Chameleon. "
|
||||
"Please upgrade client")
|
||||
"Please upgrade client")
|
||||
if resp.data[0] < CURRENT_VERSION_SETTINGS:
|
||||
raise ValueError("Settings version in app newer than Chameleon. "
|
||||
"Please upgrade Chameleon firmware")
|
||||
"Please upgrade Chameleon firmware")
|
||||
settings_version, animation_mode, btn_press_A, btn_press_B, btn_long_press_A, btn_long_press_B, ble_pairing_enable, ble_pairing_key = struct.unpack(
|
||||
'!BBBBBBB6s', resp.data)
|
||||
resp.data = {'settings_version': settings_version,
|
||||
'animation_mode': animation_mode,
|
||||
'btn_press_A': btn_press_A,
|
||||
'btn_press_B': btn_press_B,
|
||||
'btn_long_press_A': btn_long_press_A,
|
||||
'btn_long_press_B': btn_long_press_B,
|
||||
'ble_pairing_enable': ble_pairing_enable,
|
||||
'ble_pairing_key': ble_pairing_key}
|
||||
'animation_mode': animation_mode,
|
||||
'btn_press_A': btn_press_A,
|
||||
'btn_press_B': btn_press_B,
|
||||
'btn_long_press_A': btn_long_press_A,
|
||||
'btn_long_press_B': btn_long_press_B,
|
||||
'ble_pairing_enable': ble_pairing_enable,
|
||||
'ble_pairing_key': ble_pairing_key}
|
||||
return resp
|
||||
|
||||
@expect_response(chameleon_status.Device.STATUS_DEVICE_SUCCESS)
|
||||
|
||||
@@ -61,7 +61,7 @@ def expect_response(accepted_responses: Union[int, list[int]]):
|
||||
else:
|
||||
raise UnexpectedResponseError(
|
||||
f"Unexpected response and unknown status {ret.status}")
|
||||
|
||||
|
||||
return ret.data
|
||||
|
||||
return error_throwing_func
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
uint64_t atoui(const char* str) {
|
||||
uint64_t atoui(const char *str) {
|
||||
|
||||
uint64_t result = 0;
|
||||
for (int i = 0; str[i] != '\0'; ++i) {
|
||||
@@ -12,7 +12,7 @@ uint64_t atoui(const char* str) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void num_to_bytes(uint64_t n, uint32_t len, uint8_t* dest) {
|
||||
void num_to_bytes(uint64_t n, uint32_t len, uint8_t *dest) {
|
||||
while (len--) {
|
||||
dest[len] = (uint8_t)n;
|
||||
n >>= 8;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef NESTED_H__
|
||||
#define NESTED_H__
|
||||
#ifndef COMMON_H__
|
||||
#define COMMON_H__
|
||||
|
||||
uint64_t atoui(const char* str);
|
||||
void num_to_bytes(uint64_t n, uint32_t len, uint8_t* dest);
|
||||
uint64_t atoui(const char *str);
|
||||
void num_to_bytes(uint64_t n, uint32_t len, uint8_t *dest);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "nested_util.h"
|
||||
|
||||
int main(int argc, char *const argv[]) {
|
||||
|
||||
+27
-30
@@ -25,10 +25,10 @@ typedef struct {
|
||||
} countKeys;
|
||||
|
||||
typedef struct {
|
||||
NtpKs1* pNK;
|
||||
NtpKs1 *pNK;
|
||||
uint32_t authuid;
|
||||
|
||||
uint64_t* keys;
|
||||
uint64_t *keys;
|
||||
uint32_t keyCount;
|
||||
|
||||
uint32_t startPos;
|
||||
@@ -36,20 +36,20 @@ typedef struct {
|
||||
} RecPar;
|
||||
|
||||
|
||||
int compar_int(const void* a, const void* b) {
|
||||
return (*(uint64_t*)b - *(uint64_t*)a);
|
||||
int compar_int(const void *a, const void *b) {
|
||||
return (*(uint64_t *)b - * (uint64_t *)a);
|
||||
}
|
||||
|
||||
// Compare countKeys structure
|
||||
int compar_special_int(const void* a, const void* b) {
|
||||
return (((countKeys*)b)->count - ((countKeys*)a)->count);
|
||||
int compar_special_int(const void *a, const void *b) {
|
||||
return (((countKeys *)b)->count - ((countKeys *)a)->count);
|
||||
}
|
||||
|
||||
// keys qsort and unique.
|
||||
countKeys* uniqsort(uint64_t* possibleKeys, uint32_t size) {
|
||||
countKeys *uniqsort(uint64_t *possibleKeys, uint32_t size) {
|
||||
unsigned int i, j = 0;
|
||||
int count = 0;
|
||||
countKeys* our_counts;
|
||||
countKeys *our_counts;
|
||||
|
||||
qsort(possibleKeys, size, sizeof(uint64_t), compar_int);
|
||||
|
||||
@@ -62,8 +62,7 @@ countKeys* uniqsort(uint64_t* possibleKeys, uint32_t size) {
|
||||
for (i = 0; i < size; i++) {
|
||||
if (possibleKeys[i + 1] == possibleKeys[i]) {
|
||||
count++;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
our_counts[j].key = possibleKeys[i];
|
||||
our_counts[j].count = count;
|
||||
j++;
|
||||
@@ -75,8 +74,8 @@ countKeys* uniqsort(uint64_t* possibleKeys, uint32_t size) {
|
||||
}
|
||||
|
||||
// nested decrypt
|
||||
static void nested_revover(RecPar* rp) {
|
||||
struct Crypto1State* revstate, * revstate_start = NULL;
|
||||
static void nested_revover(RecPar *rp) {
|
||||
struct Crypto1State *revstate, * revstate_start = NULL;
|
||||
uint64_t lfsr = 0;
|
||||
uint32_t i, kcount = 0;
|
||||
|
||||
@@ -98,14 +97,14 @@ static void nested_revover(RecPar* rp) {
|
||||
if (((kcount % MEM_CHUNK) == 0) || (kcount >= rp->keyCount)) {
|
||||
rp->keyCount += MEM_CHUNK;
|
||||
// printf("New chunk by %d, sizeof %lu\n", kcount, key_count * sizeof(uint64_t));
|
||||
void* tmp = realloc(rp->keys, rp->keyCount * sizeof(uint64_t));
|
||||
void *tmp = realloc(rp->keys, rp->keyCount * sizeof(uint64_t));
|
||||
if (tmp == NULL) {
|
||||
printf("Memory allocation error for pk->possibleKeys");
|
||||
// exit(EXIT_FAILURE);
|
||||
rp->keyCount = 0;
|
||||
return;
|
||||
}
|
||||
rp->keys = (uint64_t*)tmp;
|
||||
rp->keys = (uint64_t *)tmp;
|
||||
}
|
||||
rp->keys[kcount] = lfsr;
|
||||
kcount++;
|
||||
@@ -117,7 +116,7 @@ static void nested_revover(RecPar* rp) {
|
||||
// Truncate
|
||||
if (kcount != 0) {
|
||||
rp->keyCount = --kcount;
|
||||
void* tmp = (uint64_t*)realloc(rp->keys, rp->keyCount * sizeof(uint64_t));
|
||||
void *tmp = (uint64_t *)realloc(rp->keys, rp->keyCount * sizeof(uint64_t));
|
||||
if (tmp == NULL) {
|
||||
printf("Memory allocation error for pk->possibleKeys");
|
||||
// exit(EXIT_FAILURE);
|
||||
@@ -131,11 +130,11 @@ static void nested_revover(RecPar* rp) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t* nested(NtpKs1* pNK, uint32_t sizePNK, uint32_t authuid, uint32_t* keyCount) {
|
||||
uint64_t *nested(NtpKs1 *pNK, uint32_t sizePNK, uint32_t authuid, uint32_t *keyCount) {
|
||||
*keyCount = 0;
|
||||
uint32_t i;
|
||||
|
||||
RecPar* pRPs = malloc(sizeof(RecPar));
|
||||
RecPar *pRPs = malloc(sizeof(RecPar));
|
||||
if (pRPs == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -149,7 +148,7 @@ uint64_t* nested(NtpKs1* pNK, uint32_t sizePNK, uint32_t authuid, uint32_t* keyC
|
||||
nested_revover(pRPs);
|
||||
*keyCount = pRPs->keyCount;
|
||||
|
||||
uint64_t* keys = NULL;
|
||||
uint64_t *keys = NULL;
|
||||
if (*keyCount != 0) {
|
||||
keys = malloc(*keyCount * sizeof(uint64_t));
|
||||
if (keys != NULL) {
|
||||
@@ -159,9 +158,9 @@ uint64_t* nested(NtpKs1* pNK, uint32_t sizePNK, uint32_t authuid, uint32_t* keyC
|
||||
}
|
||||
free(pRPs);
|
||||
|
||||
countKeys* ck = uniqsort(keys, *keyCount);
|
||||
countKeys *ck = uniqsort(keys, *keyCount);
|
||||
free(keys);
|
||||
keys = (uint64_t*)NULL;
|
||||
keys = (uint64_t *)NULL;
|
||||
*keyCount = 0;
|
||||
|
||||
if (ck != NULL) {
|
||||
@@ -170,30 +169,28 @@ uint64_t* nested(NtpKs1* pNK, uint32_t sizePNK, uint32_t authuid, uint32_t* keyC
|
||||
// This key can be found here two or more times
|
||||
if (ck[i].count > 0) {
|
||||
*keyCount += 1;
|
||||
void* tmp = realloc(keys, sizeof(uint64_t) * (*keyCount));
|
||||
void *tmp = realloc(keys, sizeof(uint64_t) * (*keyCount));
|
||||
if (tmp != NULL) {
|
||||
keys = tmp;
|
||||
keys[*keyCount - 1] = ck[i].key;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
printf("Cannot allocate memory for keys on merge.");
|
||||
free(keys);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
printf("Cannot allocate memory for ck on uniqsort.");
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
// Return 1 if the nonce is invalid else return 0
|
||||
uint8_t valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, uint8_t* parity) {
|
||||
uint8_t valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, uint8_t *parity) {
|
||||
return (
|
||||
(oddparity8((Nt >> 24) & 0xFF) == ((parity[0]) ^ oddparity8((NtEnc >> 24) & 0xFF) ^ BIT(Ks1, 16))) && \
|
||||
(oddparity8((Nt >> 16) & 0xFF) == ((parity[1]) ^ oddparity8((NtEnc >> 16) & 0xFF) ^ BIT(Ks1, 8))) && \
|
||||
(oddparity8((Nt >> 8) & 0xFF) == ((parity[2]) ^ oddparity8((NtEnc >> 8) & 0xFF) ^ BIT(Ks1, 0)))
|
||||
) ? 1 : 0;
|
||||
(oddparity8((Nt >> 24) & 0xFF) == ((parity[0]) ^ oddparity8((NtEnc >> 24) & 0xFF) ^ BIT(Ks1, 16))) && \
|
||||
(oddparity8((Nt >> 16) & 0xFF) == ((parity[1]) ^ oddparity8((NtEnc >> 16) & 0xFF) ^ BIT(Ks1, 8))) && \
|
||||
(oddparity8((Nt >> 8) & 0xFF) == ((parity[2]) ^ oddparity8((NtEnc >> 8) & 0xFF) ^ BIT(Ks1, 0)))
|
||||
) ? 1 : 0;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ typedef struct {
|
||||
uint32_t ks1;
|
||||
} NtpKs1;
|
||||
|
||||
uint8_t valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, uint8_t* parity);
|
||||
uint64_t* nested(NtpKs1* pNK, uint32_t sizePNK, uint32_t authuid, uint32_t* keyCount);
|
||||
uint8_t valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, uint8_t *parity);
|
||||
uint64_t *nested(NtpKs1 *pNK, uint32_t sizePNK, uint32_t authuid, uint32_t *keyCount);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
+15
-18
@@ -2,7 +2,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "nested_util.h"
|
||||
|
||||
int main(int argc, char *const argv[]) {
|
||||
@@ -14,36 +14,33 @@ int main(int argc, char *const argv[]) {
|
||||
uint8_t type = (uint8_t)atoui(argv[2]); // target key type
|
||||
|
||||
// process all args.
|
||||
bool check_st_level_at_sirst_run = false;
|
||||
bool check_st_level_at_first_run = false;
|
||||
for (i = 3, j = 0; i < argc; i += 2) {
|
||||
// nt + par
|
||||
nt1 = atoui(argv[i]);
|
||||
nt2 = atoui(argv[i + 1]);
|
||||
|
||||
// Which generation of ST tags is detected.
|
||||
if (!check_st_level_at_sirst_run) {
|
||||
// Which generation of static tag is detected.
|
||||
if (!check_st_level_at_first_run) {
|
||||
if (nt1 == 0x01200145) {
|
||||
// 发现一代无漏洞,此标签全卡可用默认160的参数进行解密!
|
||||
// There is no loophole in this generation.
|
||||
// This tag can be decrypted with the default parameter value 160!
|
||||
dist = 160; // st gen1
|
||||
}
|
||||
else if (nt1 == 0x009080A2) { // st gen2
|
||||
// 发现无漏洞二代,我们如果确认目前需要攻击的时B密钥,那么就需要更换攻击参数
|
||||
} else if (nt1 == 0x009080A2) { // st gen2
|
||||
// We found that the gen2 tag is vulnerable too but parameter must be adapted depending on the attacked key
|
||||
if (type == 0x61) {
|
||||
dist = 161;
|
||||
}
|
||||
else if (type == 0x60) {
|
||||
} else if (type == 0x60) {
|
||||
dist = 160;
|
||||
}
|
||||
else {
|
||||
// can't to here!!!
|
||||
} else {
|
||||
// can't be here!!!
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// can't to here!!!
|
||||
} else {
|
||||
// can't be here!!!
|
||||
goto error;
|
||||
}
|
||||
check_st_level_at_sirst_run = true;
|
||||
check_st_level_at_first_run = true;
|
||||
}
|
||||
|
||||
nttest = prng_successor(nt1, dist);
|
||||
@@ -51,7 +48,7 @@ int main(int argc, char *const argv[]) {
|
||||
++j;
|
||||
dist += 160;
|
||||
|
||||
void* tmp = realloc(pNK, sizeof(NtpKs1) * j);
|
||||
void *tmp = realloc(pNK, sizeof(NtpKs1) * j);
|
||||
if (tmp == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user