mirror of
https://github.com/RfidResearchGroup/ChameleonUltra.git
synced 2026-05-12 11:22:59 -07:00
Added support for StaticNested(FastDecrypt).
This commit is contained in:
@@ -309,25 +309,27 @@ static data_frame_tx_t *cmd_processor_mf1_detect_nt_dist(uint16_t cmd, uint16_t
|
||||
return data_frame_make(cmd, HF_TAG_OK, sizeof(payload_resp), (uint8_t *)&payload_resp);
|
||||
}
|
||||
|
||||
// We have a reusable payload structure.
|
||||
typedef struct {
|
||||
uint8_t type_known;
|
||||
uint8_t block_known;
|
||||
uint8_t key_known[6];
|
||||
uint8_t type_target;
|
||||
uint8_t block_target;
|
||||
} PACKED nested_common_payload_t;
|
||||
|
||||
static data_frame_tx_t *cmd_processor_mf1_nested_acquire(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
||||
NestedCore_t ncs[SETS_NR];
|
||||
typedef struct {
|
||||
uint8_t type_known;
|
||||
uint8_t block_known;
|
||||
uint8_t key_known[6];
|
||||
uint8_t type_target;
|
||||
uint8_t block_target;
|
||||
} PACKED payload_t;
|
||||
if (length != sizeof(payload_t)) {
|
||||
mf1_nested_core_t ncs[SETS_NR];
|
||||
if (length != sizeof(nested_common_payload_t)) {
|
||||
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
||||
}
|
||||
|
||||
payload_t *payload = (payload_t *)data;
|
||||
nested_common_payload_t *payload = (nested_common_payload_t *)data;
|
||||
status = nested_recover_key(bytes_to_num(payload->key_known, 6), payload->block_known, payload->type_known, payload->block_target, payload->type_target, ncs);
|
||||
if (status != HF_TAG_OK) {
|
||||
return data_frame_make(cmd, status, 0, NULL);
|
||||
}
|
||||
// NestedCore_t is PACKED and comprises only bytes so we can use it directly
|
||||
// mf1_nested_core_t is PACKED and comprises only bytes so we can use it directly
|
||||
return data_frame_make(cmd, HF_TAG_OK, sizeof(ncs), (uint8_t *)(&ncs));
|
||||
}
|
||||
|
||||
@@ -390,6 +392,21 @@ static data_frame_tx_t *cmd_processor_mf1_write_one_block(uint16_t cmd, uint16_t
|
||||
return data_frame_make(cmd, status, 0, NULL);
|
||||
}
|
||||
|
||||
static data_frame_tx_t *cmd_processor_mf1_static_nested_acquire(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
||||
mf1_static_nested_core_t sncs;
|
||||
if (length != sizeof(nested_common_payload_t)) {
|
||||
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
||||
}
|
||||
|
||||
nested_common_payload_t *payload = (nested_common_payload_t *)data;
|
||||
status = static_nested_recover_key(bytes_to_num(payload->key_known, 6), payload->block_known, payload->type_known, payload->block_target, payload->type_target, &sncs);
|
||||
if (status != HF_TAG_OK) {
|
||||
return data_frame_make(cmd, status, 0, NULL);
|
||||
}
|
||||
// mf1_static_nested_core_t is PACKED and comprises only bytes so we can use it directly
|
||||
return data_frame_make(cmd, HF_TAG_OK, sizeof(sncs), (uint8_t *)(&sncs));
|
||||
}
|
||||
|
||||
static data_frame_tx_t *cmd_processor_em410x_scan(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
||||
uint8_t id_buffer[5] = { 0x00 };
|
||||
status = PcdScanEM410X(id_buffer);
|
||||
@@ -930,6 +947,7 @@ 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 },
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
#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)
|
||||
//
|
||||
// ******************************************************************
|
||||
|
||||
|
||||
@@ -608,7 +608,7 @@ uint8_t check_tag_response_nt(picc_14a_tag_t *tag, uint32_t *nt) {
|
||||
// Send instructions and get NT return
|
||||
*nt = send_cmd(pcs, AUTH_FIRST, PICC_AUTHENT1A, 0x03, &status, dat_recv, par_recv, U8ARR_BIT_LEN(dat_recv));
|
||||
if (*nt != 32) {
|
||||
// dbg_block_printf("No 32 data recv on send_cmd: %d\n", *nt);
|
||||
// NRF_LOG_INFO("No 32 data recv on send_cmd: %d\n", *nt);
|
||||
return HF_ERR_STAT;
|
||||
}
|
||||
*nt = bytes_to_num(dat_recv, 4);
|
||||
@@ -827,7 +827,7 @@ static uint8_t measure_distance(uint64_t u64Key, uint8_t block, uint8_t type, ui
|
||||
}
|
||||
// After the measurement is completed, store in the buffer
|
||||
distances[index++] = measure_nonces(nt1, nt2);
|
||||
// dbg_block_printf("dist = %"PRIu32"\n\n", distances[index - 1]);
|
||||
// NRF_LOG_INFO("dist = %"PRIu32"\n\n", distances[index - 1]);
|
||||
} while (index < DIST_NR);
|
||||
|
||||
//The final calculation of the distance between the two NTs and spread it directly
|
||||
@@ -847,7 +847,7 @@ static uint8_t measure_distance(uint64_t u64Key, uint8_t block, uint8_t type, ui
|
||||
* @retval : Successfully return hf_tag_ok, verify the unsuccessful return of the non -hf_tag_ok value
|
||||
*
|
||||
*/
|
||||
static uint8_t nested_recover_core(NestedCore_t *pnc, uint64_t keyKnown, uint8_t blkKnown, uint8_t typKnown, uint8_t targetBlock, uint8_t targetType) {
|
||||
static uint8_t nested_recover_core(mf1_nested_core_t *pnc, uint64_t keyKnown, uint8_t blkKnown, uint8_t typKnown, uint8_t targetBlock, uint8_t targetType) {
|
||||
struct Crypto1State mpcs = {0, 0};
|
||||
struct Crypto1State *pcs = &mpcs;
|
||||
uint8_t status;
|
||||
@@ -884,16 +884,16 @@ static uint8_t nested_recover_core(NestedCore_t *pnc, uint64_t keyKnown, uint8_t
|
||||
|
||||
/**
|
||||
* @brief :NESTED is implemented by default to collect random numbers of the sets_nr group. This function is only responsible for collecting, not responsible for conversion and analysis as KS
|
||||
* @param :ncs : Nested core structure array, save related communication data
|
||||
* @param :keyKnown : The U64 value of the known secret key of the card
|
||||
* @param :blkKnown :The owner of the known secret key of the card
|
||||
* @param :typKnown : Types of the known secret key of the card, 0x60 (A secret) or 0x61 (B secret)
|
||||
* @param :targetBlock : The target sector that requires a Nested attack
|
||||
* @param :targetType : The target key type requires the Nested attack
|
||||
* @retval :The attack returns hf_tag_ok, the attack is unsuccessful to return the non -hf_tag_ok value
|
||||
* @param :ncs : Nested core structure array, save related communication data
|
||||
* @retval :The attack success return HF_TAG_OK, else return the error code
|
||||
*
|
||||
*/
|
||||
uint8_t nested_recover_key(uint64_t keyKnown, uint8_t blkKnown, uint8_t typKnown, uint8_t targetBlock, uint8_t targetType, NestedCore_t ncs[SETS_NR]) {
|
||||
uint8_t nested_recover_key(uint64_t keyKnown, uint8_t blkKnown, uint8_t typKnown, uint8_t targetBlock, uint8_t targetType, mf1_nested_core_t ncs[SETS_NR]) {
|
||||
uint8_t m, res;
|
||||
// all operations must be based on the card
|
||||
res = pcd_14a_reader_scan_auto(p_tag_info);
|
||||
@@ -941,6 +941,83 @@ uint8_t nested_distance_detect(uint8_t block, uint8_t type, uint8_t *key, uint8_
|
||||
return measure_distance(bytes_to_num(key, 6), block, type, distance);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief : Use the RC522 M1 algorithm module to verify the key
|
||||
* @retval : validationResults
|
||||
|
||||
@@ -29,14 +29,22 @@ typedef struct { //Answer the random number parameters required for N
|
||||
uint8_t nt1[4]; //Unblocked explicitly random number
|
||||
uint8_t nt2[4]; //Random number of nested verification encryption
|
||||
uint8_t par; //The puppet test of the communication process of nested verification encryption, only the "low 3 digits', that is, the right 3
|
||||
} NestedCore_t;
|
||||
} mf1_nested_core_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t uid[4];
|
||||
struct {
|
||||
uint8_t nt1[4];
|
||||
uint8_t nt2[4];
|
||||
} core[2];
|
||||
} mf1_static_nested_core_t;
|
||||
|
||||
typedef enum {
|
||||
DARKSIDE_OK = 0u, // normal process
|
||||
DARKSIDE_CANT_FIX_NT = 1u, // the random number cannot be fixed, this situation may appear on some UID card
|
||||
DARKSIDE_LUCKY_AUTH_OK = 2u, // the direct authentification is successful, maybe the key is just the default one
|
||||
DARKSIDE_NO_NAK_SENT = 3u, // the card does not respond to NACK, it may be a card that fixes Nack logic vulnerabilities
|
||||
DARKSIDE_TAG_CHANGED = 4u, // card swap while running DARKSIDE
|
||||
DARKSIDE_TAG_CHANGED = 4u, // card change while running DARKSIDE
|
||||
} mf1_darkside_status_t;
|
||||
|
||||
// this struct is also used in the fw/cli protocol, therefore PACKED
|
||||
@@ -63,6 +71,7 @@ uint8_t darkside_recover_key(
|
||||
DarksideCore_t *dc,
|
||||
mf1_darkside_status_t *darkside_status
|
||||
);
|
||||
|
||||
uint8_t nested_distance_detect(
|
||||
uint8_t block,
|
||||
uint8_t type,
|
||||
@@ -70,14 +79,17 @@ uint8_t nested_distance_detect(
|
||||
uint8_t *uid,
|
||||
uint32_t *distance
|
||||
);
|
||||
uint8_t nested_recover_key(
|
||||
uint64_t keyKnown,
|
||||
uint8_t blkKnown,
|
||||
uint8_t typKnown,
|
||||
uint8_t targetBlock,
|
||||
uint8_t targetType,
|
||||
NestedCore_t ncs[SETS_NR]
|
||||
);
|
||||
|
||||
#define NESTED_CORE_PARAM_DEF \
|
||||
uint64_t keyKnown, \
|
||||
uint8_t blkKnown, \
|
||||
uint8_t typKnown, \
|
||||
uint8_t targetBlock, \
|
||||
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 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);
|
||||
|
||||
@@ -8,6 +8,7 @@ import timeit
|
||||
import sys
|
||||
import time
|
||||
import serial.tools.list_ports
|
||||
import threading
|
||||
from platform import uname
|
||||
|
||||
import chameleon_com
|
||||
@@ -82,9 +83,17 @@ class BaseCLIUnit:
|
||||
def sub_process(cmd, cwd=os.path.abspath("bin/")):
|
||||
class ShadowProcess:
|
||||
def __init__(self):
|
||||
self.output = ""
|
||||
self.time_start = timeit.default_timer()
|
||||
self._process = subprocess.Popen(cmd, cwd=cwd, shell=True, stderr=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE)
|
||||
threading.Thread(target=self.thread_read_output).start()
|
||||
|
||||
def thread_read_output(self):
|
||||
while self._process.poll() is None:
|
||||
data = self._process.stdout.read(1024)
|
||||
if len(data) > 0:
|
||||
self.output += data.decode(encoding="utf-8")
|
||||
|
||||
def get_time_distance(self, ms=True):
|
||||
if ms:
|
||||
@@ -101,15 +110,8 @@ class BaseCLIUnit:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_output_sync(self, encoding='utf-8'):
|
||||
buffer = bytearray()
|
||||
while True:
|
||||
data = self._process.stdout.read(1024)
|
||||
if len(data) > 0:
|
||||
buffer.extend(data)
|
||||
else:
|
||||
break
|
||||
return buffer.decode(encoding)
|
||||
def get_output_sync(self):
|
||||
return self.output
|
||||
|
||||
def get_ret_code(self):
|
||||
return self._process.poll()
|
||||
@@ -380,6 +382,14 @@ 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'
|
||||
if nt_level == 1:
|
||||
return 'Nested'
|
||||
if nt_level == 2:
|
||||
return 'HardNested'
|
||||
|
||||
def recover_a_key(self, block_known, type_known, key_known, block_target, type_target) -> str or None:
|
||||
"""
|
||||
@@ -391,17 +401,35 @@ class HFMFNested(ReaderRequiredUnit):
|
||||
:param type_target:
|
||||
:return:
|
||||
"""
|
||||
# check nt level, we can run static or nested auto...
|
||||
nt_level = self.cmd.mf1_detect_prng()
|
||||
print(f" - NT vulnerable: {CY}{ self.from_nt_level_code_to_str(nt_level) }{C0}")
|
||||
if nt_level == 2:
|
||||
print(" [!] HardNested has not been implemented yet.")
|
||||
return None
|
||||
|
||||
# acquire
|
||||
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']}"
|
||||
for nt_item in nt_obj:
|
||||
cmd_param += f" {nt_item['nt']} {nt_item['nt_enc']} {nt_item['par']}"
|
||||
if sys.platform == "win32":
|
||||
cmd_recover = f"nested.exe {cmd_param}"
|
||||
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"
|
||||
else:
|
||||
cmd_recover = f"./nested {cmd_param}"
|
||||
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']}"
|
||||
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}"
|
||||
else:
|
||||
cmd_recover = f"./{decryptor_name} {cmd_param}"
|
||||
|
||||
print(f" Executing {cmd_recover}")
|
||||
# start a decrypt process
|
||||
process = self.sub_process(cmd_recover)
|
||||
@@ -489,6 +517,10 @@ 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()
|
||||
|
||||
self.darkside_list.append(darkside_obj)
|
||||
recover_params = f"{darkside_obj['uid']}"
|
||||
for darkside_item in self.darkside_list:
|
||||
|
||||
@@ -69,6 +69,7 @@ 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
|
||||
@@ -445,7 +446,7 @@ class ChameleonCMD:
|
||||
@expect_response(chameleon_status.Device.HF_TAG_OK)
|
||||
def mf1_detect_support(self):
|
||||
"""
|
||||
Detect whether it is mifare classic label
|
||||
Detect whether it is mifare classic tag
|
||||
:return:
|
||||
"""
|
||||
resp = self.device.send_cmd_sync(DATA_CMD_MF1_DETECT_SUPPORT)
|
||||
@@ -501,6 +502,7 @@ 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):
|
||||
"""
|
||||
@@ -561,6 +563,26 @@ 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):
|
||||
"""
|
||||
Collect the key NT parameters needed for StaticNested decryption
|
||||
:return:
|
||||
"""
|
||||
data = struct.pack('!BB6sBB', type_known, block_known, key_known, type_target, block_target)
|
||||
resp = self.device.send_cmd_sync(DATA_CMD_MF1_STATIC_NESTED_ACQUIRE, data)
|
||||
if resp.status == chameleon_status.Device.HF_TAG_OK:
|
||||
resp.data = {
|
||||
'uid': struct.unpack('!I', resp.data[0:4])[0],
|
||||
'nts': [
|
||||
{
|
||||
'nt': nt,
|
||||
'nt_enc': nt_enc
|
||||
} for nt, nt_enc in struct.iter_unpack('!II', resp.data[4:])
|
||||
]
|
||||
}
|
||||
return resp
|
||||
|
||||
@expect_response(chameleon_status.Device.LF_TAG_OK)
|
||||
def em410x_scan(self):
|
||||
|
||||
@@ -6,10 +6,12 @@ set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../bin)
|
||||
set(SRC_DIR ./)
|
||||
|
||||
set(COMMON_FILES
|
||||
${SRC_DIR}/common.c
|
||||
${SRC_DIR}/crapto1.c
|
||||
${SRC_DIR}/crypto1.c
|
||||
${SRC_DIR}/bucketsort.c
|
||||
${SRC_DIR}/mfkey.c
|
||||
${SRC_DIR}/nested_util.c
|
||||
${SRC_DIR}/parity.c)
|
||||
|
||||
include_directories(
|
||||
@@ -19,6 +21,7 @@ include_directories(
|
||||
|
||||
# tools
|
||||
add_executable(nested ${COMMON_FILES} nested.c)
|
||||
add_executable(staticnested ${COMMON_FILES} staticnested.c)
|
||||
add_executable(darkside ${COMMON_FILES} darkside.c)
|
||||
add_executable(mfkey32 ${COMMON_FILES} mfkey32.c)
|
||||
add_executable(mfkey32v2 ${COMMON_FILES} mfkey32v2.c)
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
uint64_t atoui(const char* str) {
|
||||
|
||||
uint64_t result = 0;
|
||||
for (int i = 0; str[i] != '\0'; ++i) {
|
||||
if (str[i] >= '0' && str[i] <= '9') {
|
||||
result = result * 10 + str[i] - '0';
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void num_to_bytes(uint64_t n, uint32_t len, uint8_t* dest) {
|
||||
while (len--) {
|
||||
dest[len] = (uint8_t)n;
|
||||
n >>= 8;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef NESTED_H__
|
||||
#define NESTED_H__
|
||||
|
||||
uint64_t atoui(const char* str);
|
||||
void num_to_bytes(uint64_t n, uint32_t len, uint8_t* dest);
|
||||
|
||||
#endif
|
||||
+1
-19
@@ -7,6 +7,7 @@
|
||||
#include "parity.h"
|
||||
#include "crapto1.h"
|
||||
#include "mfkey.h"
|
||||
#include "common.h"
|
||||
|
||||
typedef struct {
|
||||
uint32_t nt;
|
||||
@@ -17,25 +18,6 @@ typedef struct {
|
||||
uint64_t ks_list;
|
||||
} DarksideParam;
|
||||
|
||||
// Convert string to U32 type
|
||||
uint64_t atoui(const char *str) {
|
||||
|
||||
uint64_t result = 0;
|
||||
for (int i = 0; str[i] != '\0'; ++i) {
|
||||
if (str[i] >= '0' && str[i] <= '9') {
|
||||
result = result * 10 + str[i] - '0';
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void num_to_bytes(uint64_t n, uint32_t len, uint8_t *dest) {
|
||||
while (len--) {
|
||||
dest[len] = (uint8_t)n;
|
||||
n >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
if (((argc - 2) % 5) != 0) {
|
||||
|
||||
+1
-206
@@ -1,214 +1,9 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <inttypes.h>
|
||||
#include <ctype.h>
|
||||
#include "crapto1.h"
|
||||
#include "parity.h"
|
||||
|
||||
#if WIN32
|
||||
#include "windows.h"
|
||||
#else
|
||||
#include "unistd.h"
|
||||
#endif
|
||||
|
||||
|
||||
#define MEM_CHUNK 10000
|
||||
#define TRY_KEYS 50
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint64_t key;
|
||||
int count;
|
||||
} countKeys;
|
||||
|
||||
typedef struct {
|
||||
uint32_t ntp;
|
||||
uint32_t ks1;
|
||||
} NtpKs1;
|
||||
|
||||
typedef struct {
|
||||
NtpKs1 *pNK;
|
||||
uint32_t authuid;
|
||||
|
||||
uint64_t *keys;
|
||||
uint32_t keyCount;
|
||||
|
||||
uint32_t startPos;
|
||||
uint32_t endPos;
|
||||
} RecPar;
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// keys qsort and unique.
|
||||
countKeys *uniqsort(uint64_t *possibleKeys, uint32_t size) {
|
||||
unsigned int i, j = 0;
|
||||
int count = 0;
|
||||
countKeys *our_counts;
|
||||
|
||||
qsort(possibleKeys, size, sizeof(uint64_t), compar_int);
|
||||
|
||||
our_counts = calloc(size, sizeof(countKeys));
|
||||
if (our_counts == NULL) {
|
||||
printf("Memory allocation error for our_counts");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
if (possibleKeys[i + 1] == possibleKeys[i]) {
|
||||
count++;
|
||||
} else {
|
||||
our_counts[j].key = possibleKeys[i];
|
||||
our_counts[j].count = count;
|
||||
j++;
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
qsort(our_counts, j, sizeof(countKeys), compar_special_int);
|
||||
return (our_counts);
|
||||
}
|
||||
|
||||
uint32_t atoui(const char *str) {
|
||||
|
||||
uint32_t result = 0;
|
||||
for (int i = 0; str[i] != '\0'; ++i) {
|
||||
if (str[i] >= '0' && str[i] <= '9') {
|
||||
result = result * 10 + str[i] - '0';
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// nested decrypt
|
||||
static void nested_revover(RecPar *rp) {
|
||||
struct Crypto1State *revstate, * revstate_start = NULL;
|
||||
uint64_t lfsr = 0;
|
||||
uint32_t i, kcount = 0;
|
||||
|
||||
rp->keyCount = 0;
|
||||
rp->keys = NULL;
|
||||
|
||||
for (i = rp->startPos; i < rp->endPos; i++) {
|
||||
uint32_t nt_probe = rp->pNK[i].ntp;
|
||||
uint32_t ks1 = rp->pNK[i].ks1;
|
||||
// And finally recover the first 32 bits of the key
|
||||
revstate = lfsr_recovery32(ks1, nt_probe ^ rp->authuid);
|
||||
if (revstate_start == NULL) {
|
||||
revstate_start = revstate;
|
||||
}
|
||||
while ((revstate->odd != 0x0) || (revstate->even != 0x0)) {
|
||||
lfsr_rollback_word(revstate, nt_probe ^ rp->authuid, 0);
|
||||
crypto1_get_lfsr(revstate, &lfsr);
|
||||
// Allocate a new space for keys
|
||||
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));
|
||||
if (tmp == NULL) {
|
||||
printf("Memory allocation error for pk->possibleKeys");
|
||||
// exit(EXIT_FAILURE);
|
||||
rp->keyCount = 0;
|
||||
return;
|
||||
}
|
||||
rp->keys = (uint64_t *)tmp;
|
||||
}
|
||||
rp->keys[kcount] = lfsr;
|
||||
kcount++;
|
||||
revstate++;
|
||||
}
|
||||
free(revstate_start);
|
||||
revstate_start = NULL;
|
||||
}
|
||||
// Truncate
|
||||
if (kcount != 0) {
|
||||
rp->keyCount = --kcount;
|
||||
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);
|
||||
rp->keyCount = 0;
|
||||
return;
|
||||
}
|
||||
rp->keys = tmp;
|
||||
return;
|
||||
}
|
||||
rp->keyCount = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t *nested(NtpKs1 *pNK, uint32_t sizePNK, uint32_t authuid, uint32_t *keyCount) {
|
||||
*keyCount = 0;
|
||||
uint32_t i;
|
||||
|
||||
RecPar *pRPs = malloc(sizeof(RecPar));
|
||||
if (pRPs == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pRPs->pNK = pNK;
|
||||
pRPs->authuid = authuid;
|
||||
pRPs->startPos = 0;
|
||||
pRPs->endPos = sizePNK;
|
||||
|
||||
// start recover
|
||||
nested_revover(pRPs);
|
||||
*keyCount = pRPs->keyCount;
|
||||
|
||||
uint64_t *keys = NULL;
|
||||
if (*keyCount != 0) {
|
||||
keys = malloc(*keyCount * sizeof(uint64_t));
|
||||
if (keys != NULL) {
|
||||
memcpy(keys, pRPs->keys, pRPs->keyCount * sizeof(uint64_t));
|
||||
free(pRPs->keys);
|
||||
}
|
||||
}
|
||||
free(pRPs);
|
||||
|
||||
countKeys *ck = uniqsort(keys, *keyCount);
|
||||
free(keys);
|
||||
keys = (uint64_t *)NULL;
|
||||
*keyCount = 0;
|
||||
|
||||
if (ck != NULL) {
|
||||
for (i = 0; i < TRY_KEYS; i++) {
|
||||
// We don't known this key, try to break it
|
||||
// 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));
|
||||
if (tmp != NULL) {
|
||||
keys = tmp;
|
||||
keys[*keyCount - 1] = ck[i].key;
|
||||
} else {
|
||||
printf("Cannot allocate memory for keys on merge.");
|
||||
free(keys);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printf("Cannot allocate memory for ck on uniqsort.");
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
// Return 1 if the nonce is invalid else return 0
|
||||
static 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;
|
||||
}
|
||||
#include "nested_util.h"
|
||||
|
||||
int main(int argc, char *const argv[]) {
|
||||
NtpKs1 *pNK = NULL;
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <inttypes.h>
|
||||
#include <ctype.h>
|
||||
#include "parity.h"
|
||||
|
||||
#if WIN32
|
||||
#include "windows.h"
|
||||
#else
|
||||
#include "unistd.h"
|
||||
#endif
|
||||
|
||||
#include "nested_util.h"
|
||||
|
||||
|
||||
#define MEM_CHUNK 10000
|
||||
#define TRY_KEYS 50
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint64_t key;
|
||||
int count;
|
||||
} countKeys;
|
||||
|
||||
typedef struct {
|
||||
NtpKs1* pNK;
|
||||
uint32_t authuid;
|
||||
|
||||
uint64_t* keys;
|
||||
uint32_t keyCount;
|
||||
|
||||
uint32_t startPos;
|
||||
uint32_t endPos;
|
||||
} RecPar;
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// keys qsort and unique.
|
||||
countKeys* uniqsort(uint64_t* possibleKeys, uint32_t size) {
|
||||
unsigned int i, j = 0;
|
||||
int count = 0;
|
||||
countKeys* our_counts;
|
||||
|
||||
qsort(possibleKeys, size, sizeof(uint64_t), compar_int);
|
||||
|
||||
our_counts = calloc(size, sizeof(countKeys));
|
||||
if (our_counts == NULL) {
|
||||
printf("Memory allocation error for our_counts");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
if (possibleKeys[i + 1] == possibleKeys[i]) {
|
||||
count++;
|
||||
}
|
||||
else {
|
||||
our_counts[j].key = possibleKeys[i];
|
||||
our_counts[j].count = count;
|
||||
j++;
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
qsort(our_counts, j, sizeof(countKeys), compar_special_int);
|
||||
return (our_counts);
|
||||
}
|
||||
|
||||
// nested decrypt
|
||||
static void nested_revover(RecPar* rp) {
|
||||
struct Crypto1State* revstate, * revstate_start = NULL;
|
||||
uint64_t lfsr = 0;
|
||||
uint32_t i, kcount = 0;
|
||||
|
||||
rp->keyCount = 0;
|
||||
rp->keys = NULL;
|
||||
|
||||
for (i = rp->startPos; i < rp->endPos; i++) {
|
||||
uint32_t nt_probe = rp->pNK[i].ntp;
|
||||
uint32_t ks1 = rp->pNK[i].ks1;
|
||||
// And finally recover the first 32 bits of the key
|
||||
revstate = lfsr_recovery32(ks1, nt_probe ^ rp->authuid);
|
||||
if (revstate_start == NULL) {
|
||||
revstate_start = revstate;
|
||||
}
|
||||
while ((revstate->odd != 0x0) || (revstate->even != 0x0)) {
|
||||
lfsr_rollback_word(revstate, nt_probe ^ rp->authuid, 0);
|
||||
crypto1_get_lfsr(revstate, &lfsr);
|
||||
// Allocate a new space for keys
|
||||
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));
|
||||
if (tmp == NULL) {
|
||||
printf("Memory allocation error for pk->possibleKeys");
|
||||
// exit(EXIT_FAILURE);
|
||||
rp->keyCount = 0;
|
||||
return;
|
||||
}
|
||||
rp->keys = (uint64_t*)tmp;
|
||||
}
|
||||
rp->keys[kcount] = lfsr;
|
||||
kcount++;
|
||||
revstate++;
|
||||
}
|
||||
free(revstate_start);
|
||||
revstate_start = NULL;
|
||||
}
|
||||
// Truncate
|
||||
if (kcount != 0) {
|
||||
rp->keyCount = --kcount;
|
||||
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);
|
||||
rp->keyCount = 0;
|
||||
return;
|
||||
}
|
||||
rp->keys = tmp;
|
||||
return;
|
||||
}
|
||||
rp->keyCount = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t* nested(NtpKs1* pNK, uint32_t sizePNK, uint32_t authuid, uint32_t* keyCount) {
|
||||
*keyCount = 0;
|
||||
uint32_t i;
|
||||
|
||||
RecPar* pRPs = malloc(sizeof(RecPar));
|
||||
if (pRPs == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pRPs->pNK = pNK;
|
||||
pRPs->authuid = authuid;
|
||||
pRPs->startPos = 0;
|
||||
pRPs->endPos = sizePNK;
|
||||
|
||||
// start recover
|
||||
nested_revover(pRPs);
|
||||
*keyCount = pRPs->keyCount;
|
||||
|
||||
uint64_t* keys = NULL;
|
||||
if (*keyCount != 0) {
|
||||
keys = malloc(*keyCount * sizeof(uint64_t));
|
||||
if (keys != NULL) {
|
||||
memcpy(keys, pRPs->keys, pRPs->keyCount * sizeof(uint64_t));
|
||||
free(pRPs->keys);
|
||||
}
|
||||
}
|
||||
free(pRPs);
|
||||
|
||||
countKeys* ck = uniqsort(keys, *keyCount);
|
||||
free(keys);
|
||||
keys = (uint64_t*)NULL;
|
||||
*keyCount = 0;
|
||||
|
||||
if (ck != NULL) {
|
||||
for (i = 0; i < TRY_KEYS; i++) {
|
||||
// We don't known this key, try to break it
|
||||
// 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));
|
||||
if (tmp != NULL) {
|
||||
keys = tmp;
|
||||
keys[*keyCount - 1] = ck[i].key;
|
||||
}
|
||||
else {
|
||||
printf("Cannot allocate memory for keys on merge.");
|
||||
free(keys);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef NESTED_H__
|
||||
#define NESTED_H__
|
||||
|
||||
#include "crapto1.h"
|
||||
|
||||
typedef struct {
|
||||
uint32_t ntp;
|
||||
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);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,77 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "nested_util.h"
|
||||
|
||||
int main(int argc, char *const argv[]) {
|
||||
NtpKs1 *pNK = NULL;
|
||||
uint32_t i, j, m;
|
||||
uint32_t nt1, nt2, nttest, ks1, dist;
|
||||
|
||||
uint32_t authuid = atoui(argv[1]); // uid
|
||||
uint8_t type = (uint8_t)atoui(argv[2]); // target key type
|
||||
|
||||
// process all args.
|
||||
bool check_st_level_at_sirst_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) {
|
||||
if (nt1 == 0x01200145) {
|
||||
// 发现一代无漏洞,此标签全卡可用默认160的参数进行解密!
|
||||
dist = 160; // st gen1
|
||||
}
|
||||
else if (nt1 == 0x009080A2) { // st gen2
|
||||
// 发现无漏洞二代,我们如果确认目前需要攻击的时B密钥,那么就需要更换攻击参数
|
||||
if (type == 0x61) {
|
||||
dist = 161;
|
||||
}
|
||||
else if (type == 0x60) {
|
||||
dist = 160;
|
||||
}
|
||||
else {
|
||||
// can't to here!!!
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// can't to here!!!
|
||||
goto error;
|
||||
}
|
||||
check_st_level_at_sirst_run = true;
|
||||
}
|
||||
|
||||
nttest = prng_successor(nt1, dist);
|
||||
ks1 = nt2 ^ nttest;
|
||||
++j;
|
||||
dist += 160;
|
||||
|
||||
void* tmp = realloc(pNK, sizeof(NtpKs1) * j);
|
||||
if (tmp == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
pNK = tmp;
|
||||
pNK[j - 1].ntp = nttest;
|
||||
pNK[j - 1].ks1 = ks1;
|
||||
}
|
||||
uint32_t keyCount = 0;
|
||||
uint64_t *keys = nested(pNK, j, authuid, &keyCount);
|
||||
|
||||
if (keyCount > 0) {
|
||||
for (i = 0; i < keyCount; i++) {
|
||||
printf("Key %d... %" PRIx64 " \r\n", i + 1, keys[i]);
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
fflush(stdout);
|
||||
free(keys);
|
||||
exit(EXIT_SUCCESS);
|
||||
error:
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
Reference in New Issue
Block a user