|
@@ -162,9 +162,15 @@ PicopassListenerCommand
|
|
|
uint8_t block_num = bit_buffer_get_byte(buf, 1);
|
|
uint8_t block_num = bit_buffer_get_byte(buf, 1);
|
|
|
if(block_num > PICOPASS_MAX_APP_LIMIT) break;
|
|
if(block_num > PICOPASS_MAX_APP_LIMIT) break;
|
|
|
|
|
|
|
|
|
|
+ bool secured = (instance->data->AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[7] &
|
|
|
|
|
+ PICOPASS_FUSE_CRYPT10) != PICOPASS_FUSE_CRYPT0;
|
|
|
|
|
+
|
|
|
|
|
+ // TODO: Check CRC?
|
|
|
|
|
+ // TODO: Check auth?
|
|
|
|
|
+
|
|
|
bit_buffer_reset(instance->tx_buffer);
|
|
bit_buffer_reset(instance->tx_buffer);
|
|
|
- if((block_num == PICOPASS_SECURE_KD_BLOCK_INDEX) ||
|
|
|
|
|
- (block_num == PICOPASS_SECURE_KC_BLOCK_INDEX)) {
|
|
|
|
|
|
|
+ if(secured && ((block_num == PICOPASS_SECURE_KD_BLOCK_INDEX) ||
|
|
|
|
|
+ (block_num == PICOPASS_SECURE_KC_BLOCK_INDEX))) {
|
|
|
for(size_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
|
|
for(size_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
|
|
|
bit_buffer_append_byte(instance->tx_buffer, 0xff);
|
|
bit_buffer_append_byte(instance->tx_buffer, 0xff);
|
|
|
}
|
|
}
|
|
@@ -193,6 +199,8 @@ static PicopassListenerCommand
|
|
|
uint8_t block_num = bit_buffer_get_byte(buf, 1);
|
|
uint8_t block_num = bit_buffer_get_byte(buf, 1);
|
|
|
if(block_num != PICOPASS_SECURE_EPURSE_BLOCK_INDEX) break;
|
|
if(block_num != PICOPASS_SECURE_EPURSE_BLOCK_INDEX) break;
|
|
|
|
|
|
|
|
|
|
+ // note that even non-secure chips seem to reply to READCHECK still
|
|
|
|
|
+
|
|
|
// loclass mode doesn't do any card side crypto, just logs the readers crypto, so no-op in this mode
|
|
// loclass mode doesn't do any card side crypto, just logs the readers crypto, so no-op in this mode
|
|
|
// we can also no-op if the key block is the same, CHECK re-inits if it failed already
|
|
// we can also no-op if the key block is the same, CHECK re-inits if it failed already
|
|
|
if((instance->key_block_num != key_block_num) &&
|
|
if((instance->key_block_num != key_block_num) &&
|
|
@@ -238,8 +246,7 @@ PicopassListenerCommand
|
|
|
|
|
|
|
|
PicopassBlock key = instance->data->AA1[PICOPASS_SECURE_KD_BLOCK_INDEX];
|
|
PicopassBlock key = instance->data->AA1[PICOPASS_SECURE_KD_BLOCK_INDEX];
|
|
|
uint8_t rmac[4];
|
|
uint8_t rmac[4];
|
|
|
- uint8_t rx_data[9] = {};
|
|
|
|
|
- bit_buffer_write_bytes(buf, rx_data, sizeof(rx_data));
|
|
|
|
|
|
|
+ const uint8_t* rx_data = bit_buffer_get_data(buf);
|
|
|
loclass_opt_doReaderMAC_2(instance->cipher_state, &rx_data[1], rmac, key.data);
|
|
loclass_opt_doReaderMAC_2(instance->cipher_state, &rx_data[1], rmac, key.data);
|
|
|
|
|
|
|
|
if(!memcmp(&rx_data[5], rmac, 4)) {
|
|
if(!memcmp(&rx_data[5], rmac, 4)) {
|
|
@@ -300,7 +307,8 @@ PicopassListenerCommand
|
|
|
return command;
|
|
return command;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-PicopassListenerCommand picopass_listener_save_mac(PicopassListener* instance, uint8_t* rx_data) {
|
|
|
|
|
|
|
+PicopassListenerCommand
|
|
|
|
|
+ picopass_listener_save_mac(PicopassListener* instance, const uint8_t* rx_data) {
|
|
|
PicopassListenerCommand command = PicopassListenerCommandSilent;
|
|
PicopassListenerCommand command = PicopassListenerCommandSilent;
|
|
|
Picopass* picopass = instance->context;
|
|
Picopass* picopass = instance->context;
|
|
|
|
|
|
|
@@ -360,13 +368,15 @@ PicopassListenerCommand
|
|
|
PicopassListenerCommand command = PicopassListenerCommandSilent;
|
|
PicopassListenerCommand command = PicopassListenerCommandSilent;
|
|
|
|
|
|
|
|
do {
|
|
do {
|
|
|
|
|
+ bool secured = (instance->data->AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[7] &
|
|
|
|
|
+ PICOPASS_FUSE_CRYPT10) != PICOPASS_FUSE_CRYPT0;
|
|
|
|
|
+ if(!secured) break;
|
|
|
|
|
+
|
|
|
uint8_t rmac[4] = {};
|
|
uint8_t rmac[4] = {};
|
|
|
uint8_t tmac[4] = {};
|
|
uint8_t tmac[4] = {};
|
|
|
const uint8_t* key = instance->data->AA1[instance->key_block_num].data;
|
|
const uint8_t* key = instance->data->AA1[instance->key_block_num].data;
|
|
|
- // Since nr isn't const in loclass_opt_doBothMAC_2() copy buffer
|
|
|
|
|
- uint8_t rx_data[9] = {};
|
|
|
|
|
- bit_buffer_write_bytes(buf, rx_data, sizeof(rx_data));
|
|
|
|
|
bool no_key = picopass_is_memset(key, 0x00, PICOPASS_BLOCK_LEN);
|
|
bool no_key = picopass_is_memset(key, 0x00, PICOPASS_BLOCK_LEN);
|
|
|
|
|
+ const uint8_t* rx_data = bit_buffer_get_data(buf);
|
|
|
|
|
|
|
|
if(no_key) {
|
|
if(no_key) {
|
|
|
// We're emulating a partial dump of an iClass SE card and should capture the NR and MAC
|
|
// We're emulating a partial dump of an iClass SE card and should capture the NR and MAC
|
|
@@ -424,27 +434,31 @@ PicopassListenerCommand
|
|
|
|
|
|
|
|
PicopassBlock config_block = instance->data->AA1[PICOPASS_CONFIG_BLOCK_INDEX];
|
|
PicopassBlock config_block = instance->data->AA1[PICOPASS_CONFIG_BLOCK_INDEX];
|
|
|
bool pers_mode = PICOPASS_LISTENER_HAS_MASK(config_block.data[7], PICOPASS_FUSE_PERS);
|
|
bool pers_mode = PICOPASS_LISTENER_HAS_MASK(config_block.data[7], PICOPASS_FUSE_PERS);
|
|
|
|
|
+ bool secured = (instance->data->AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[7] &
|
|
|
|
|
+ PICOPASS_FUSE_CRYPT10) != PICOPASS_FUSE_CRYPT0;
|
|
|
|
|
|
|
|
const uint8_t* rx_data = bit_buffer_get_data(buf);
|
|
const uint8_t* rx_data = bit_buffer_get_data(buf);
|
|
|
uint8_t block_num = rx_data[1];
|
|
uint8_t block_num = rx_data[1];
|
|
|
if(block_num == PICOPASS_CSN_BLOCK_INDEX) break; // CSN is always read only
|
|
if(block_num == PICOPASS_CSN_BLOCK_INDEX) break; // CSN is always read only
|
|
|
if(!pers_mode && PICOPASS_LISTENER_HAS_MASK(config_block.data[3], 0x80))
|
|
if(!pers_mode && PICOPASS_LISTENER_HAS_MASK(config_block.data[3], 0x80))
|
|
|
break; // Chip is in RO mode, no updated possible (even ePurse)
|
|
break; // Chip is in RO mode, no updated possible (even ePurse)
|
|
|
- if(!pers_mode && (block_num == PICOPASS_SECURE_AIA_BLOCK_INDEX))
|
|
|
|
|
|
|
+ if(!pers_mode && ((secured && block_num == PICOPASS_SECURE_AIA_BLOCK_INDEX) ||
|
|
|
|
|
+ (!secured && block_num == PICOPASS_NONSECURE_AIA_BLOCK_INDEX)))
|
|
|
break; // AIA can only be set in personalisation mode
|
|
break; // AIA can only be set in personalisation mode
|
|
|
- if(!pers_mode &&
|
|
|
|
|
|
|
+ if(!pers_mode && secured &&
|
|
|
((block_num == PICOPASS_SECURE_KD_BLOCK_INDEX ||
|
|
((block_num == PICOPASS_SECURE_KD_BLOCK_INDEX ||
|
|
|
block_num == PICOPASS_SECURE_KC_BLOCK_INDEX) &&
|
|
block_num == PICOPASS_SECURE_KC_BLOCK_INDEX) &&
|
|
|
(!PICOPASS_LISTENER_HAS_MASK(config_block.data[7], PICOPASS_FUSE_CRYPT10))))
|
|
(!PICOPASS_LISTENER_HAS_MASK(config_block.data[7], PICOPASS_FUSE_CRYPT10))))
|
|
|
- break;
|
|
|
|
|
|
|
+ break; // TODO: Is this the right response?
|
|
|
|
|
|
|
|
if(block_num >= 6 && block_num <= 12) {
|
|
if(block_num >= 6 && block_num <= 12) {
|
|
|
// bit0 is block6, up to bit6 being block12
|
|
// bit0 is block6, up to bit6 being block12
|
|
|
if(!PICOPASS_LISTENER_HAS_MASK(config_block.data[3], (1 << (block_num - 6)))) {
|
|
if(!PICOPASS_LISTENER_HAS_MASK(config_block.data[3], (1 << (block_num - 6)))) {
|
|
|
// Block is marked as read-only, deny writing
|
|
// Block is marked as read-only, deny writing
|
|
|
- break;
|
|
|
|
|
|
|
+ break; // TODO: Is this the right response?
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
// TODO: Check CRC/SIGN depending on if in secure mode
|
|
// TODO: Check CRC/SIGN depending on if in secure mode
|
|
|
// Check correct key
|
|
// Check correct key
|
|
|
// -> Kd only allows decrementing e-Purse
|
|
// -> Kd only allows decrementing e-Purse
|
|
@@ -477,15 +491,17 @@ PicopassListenerCommand
|
|
|
break;
|
|
break;
|
|
|
|
|
|
|
|
case PICOPASS_SECURE_EPURSE_BLOCK_INDEX:
|
|
case PICOPASS_SECURE_EPURSE_BLOCK_INDEX:
|
|
|
- // ePurse updates swap first and second half of the block each update
|
|
|
|
|
- memcpy(&new_block.data[4], &rx_data[2], 4);
|
|
|
|
|
- memcpy(&new_block.data[0], &rx_data[6], 4);
|
|
|
|
|
|
|
+ if(secured) {
|
|
|
|
|
+ // ePurse updates swap first and second half of the block each update on secure cards
|
|
|
|
|
+ memcpy(&new_block.data[4], &rx_data[2], 4);
|
|
|
|
|
+ memcpy(&new_block.data[0], &rx_data[6], 4);
|
|
|
|
|
+ }
|
|
|
break;
|
|
break;
|
|
|
|
|
|
|
|
case PICOPASS_SECURE_KD_BLOCK_INDEX:
|
|
case PICOPASS_SECURE_KD_BLOCK_INDEX:
|
|
|
// fallthrough
|
|
// fallthrough
|
|
|
case PICOPASS_SECURE_KC_BLOCK_INDEX:
|
|
case PICOPASS_SECURE_KC_BLOCK_INDEX:
|
|
|
- if(!pers_mode) {
|
|
|
|
|
|
|
+ if(!pers_mode && secured) {
|
|
|
new_block = instance->data->AA1[block_num];
|
|
new_block = instance->data->AA1[block_num];
|
|
|
for(size_t i = 0; i < sizeof(PicopassBlock); i++) {
|
|
for(size_t i = 0; i < sizeof(PicopassBlock); i++) {
|
|
|
new_block.data[i] ^= rx_data[i + 2];
|
|
new_block.data[i] ^= rx_data[i + 2];
|
|
@@ -500,14 +516,14 @@ PicopassListenerCommand
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
instance->data->AA1[block_num] = new_block;
|
|
instance->data->AA1[block_num] = new_block;
|
|
|
- if((block_num == instance->key_block_num) ||
|
|
|
|
|
- (block_num == PICOPASS_SECURE_EPURSE_BLOCK_INDEX)) {
|
|
|
|
|
|
|
+ if(secured && ((block_num == instance->key_block_num) ||
|
|
|
|
|
+ (block_num == PICOPASS_SECURE_EPURSE_BLOCK_INDEX))) {
|
|
|
picopass_listener_init_cipher_state(instance);
|
|
picopass_listener_init_cipher_state(instance);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
bit_buffer_reset(instance->tx_buffer);
|
|
bit_buffer_reset(instance->tx_buffer);
|
|
|
- if((block_num == PICOPASS_SECURE_KD_BLOCK_INDEX) ||
|
|
|
|
|
- (block_num == PICOPASS_SECURE_KC_BLOCK_INDEX)) {
|
|
|
|
|
|
|
+ if(secured && ((block_num == PICOPASS_SECURE_KD_BLOCK_INDEX) ||
|
|
|
|
|
+ (block_num == PICOPASS_SECURE_KC_BLOCK_INDEX))) {
|
|
|
// Key updates always return FF's
|
|
// Key updates always return FF's
|
|
|
for(size_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
|
|
for(size_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
|
|
|
bit_buffer_append_byte(instance->tx_buffer, 0xff);
|
|
bit_buffer_append_byte(instance->tx_buffer, 0xff);
|
|
@@ -539,12 +555,16 @@ PicopassListenerCommand
|
|
|
uint8_t block_start = bit_buffer_get_byte(buf, 1);
|
|
uint8_t block_start = bit_buffer_get_byte(buf, 1);
|
|
|
if(block_start + 4 >= PICOPASS_MAX_APP_LIMIT) break;
|
|
if(block_start + 4 >= PICOPASS_MAX_APP_LIMIT) break;
|
|
|
|
|
|
|
|
|
|
+ bool secured = (instance->data->AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[7] &
|
|
|
|
|
+ PICOPASS_FUSE_CRYPT10) != PICOPASS_FUSE_CRYPT0;
|
|
|
|
|
+
|
|
|
// TODO: Check CRC?
|
|
// TODO: Check CRC?
|
|
|
// TODO: Check auth?
|
|
// TODO: Check auth?
|
|
|
|
|
|
|
|
bit_buffer_reset(instance->tx_buffer);
|
|
bit_buffer_reset(instance->tx_buffer);
|
|
|
for(uint8_t i = block_start; i < block_start + 4; i++) {
|
|
for(uint8_t i = block_start; i < block_start + 4; i++) {
|
|
|
- if((i == PICOPASS_SECURE_KD_BLOCK_INDEX) || (i == PICOPASS_SECURE_KC_BLOCK_INDEX)) {
|
|
|
|
|
|
|
+ if(secured &&
|
|
|
|
|
+ ((i == PICOPASS_SECURE_KD_BLOCK_INDEX) || (i == PICOPASS_SECURE_KC_BLOCK_INDEX))) {
|
|
|
for(size_t j = 0; j < sizeof(PicopassBlock); j++) {
|
|
for(size_t j = 0; j < sizeof(PicopassBlock); j++) {
|
|
|
bit_buffer_append_byte(instance->tx_buffer, 0xff);
|
|
bit_buffer_append_byte(instance->tx_buffer, 0xff);
|
|
|
}
|
|
}
|
|
@@ -622,6 +642,7 @@ static const PicopassListenerCmd picopass_listener_cmd_handlers[] = {
|
|
|
.cmd_len_bits = 8 * 4,
|
|
.cmd_len_bits = 8 * 4,
|
|
|
.handler = picopass_listener_read4_handler,
|
|
.handler = picopass_listener_read4_handler,
|
|
|
},
|
|
},
|
|
|
|
|
+ // TODO: RFAL_PICOPASS_CMD_DETECT
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
PicopassListener* picopass_listener_alloc(Nfc* nfc, const PicopassDeviceData* data) {
|
|
PicopassListener* picopass_listener_alloc(Nfc* nfc, const PicopassDeviceData* data) {
|