penguin/homekey-relay

homekey-relay

tests/pn532_test.c

raw ยท 5416 bytes

#include <stdio.h>
#include "../esp32/main/pn532.c"
static uint8_t stream[1024];
static size_t total, pos, chunk = 1;
static int64_t now;
static unsigned writes;
static uint8_t last_tx[300];
static size_t last_tx_len;
static uint8_t tx_history[16][300];
bool relay_ecp_group(uint8_t out[8]) { (void)out; return false; }
int64_t esp_timer_get_time(void) { return now; }
void vTaskDelay(TickType_t t) { now += t * 1000; }
int uart_read_bytes(int u, void *dst, size_t len, TickType_t t)
{
	(void)u;
	now += 100;
	if (pos == total) {
		now += t * 1000;
		return 0;
	}
	size_t n = total - pos;
	if (n > len)
		n = len;
	if (n > chunk)
		n = chunk;
	memcpy(dst, stream + pos, n);
	pos += n;
	return (int)n;
}
int uart_write_bytes(int u, const void *d, size_t n)
{
	(void)u;
	assert(n <= sizeof(last_tx));
	memcpy(last_tx, d, n);
	last_tx_len = n;
	assert(writes < 16);
	memcpy(tx_history[writes], d, n);
	++writes;
	return (int)n;
}
int uart_wait_tx_done(int u, TickType_t t)
{
	(void)u;
	(void)t;
	return 0;
}
int uart_flush_input(int u)
{
	(void)u;
	pos = total;
	return 0;
}
int uart_driver_install(int a, int b, int c, int d, void *e, int f)
{
	(void)a;
	(void)b;
	(void)c;
	(void)d;
	(void)e;
	(void)f;
	return 0;
}
int uart_param_config(int u, const uart_config_t *c)
{
	(void)u;
	(void)c;
	return 0;
}
int uart_set_pin(int a, int b, int c, int d, int e)
{
	(void)a;
	(void)b;
	(void)c;
	(void)d;
	(void)e;
	return 0;
}
static void reset(void)
{
	total = pos = 0;
	now = 0;
	writes = 0;
}
static void append(const uint8_t *d, size_t n)
{
	memcpy(stream + total, d, n);
	total += n;
}
static void response(const uint8_t *d, uint8_t n)
{
	uint8_t h[] = {0, 0, 255, n, (uint8_t)(0 - n)};
	append(ack, sizeof(ack));
	append(h, 5);
	append(d, n);
	uint8_t sum = 0;
	for (unsigned i = 0; i < n; i++)
		sum += d[i];
	uint8_t tail[] = {(uint8_t)(0 - sum), 0};
	append(tail, 2);
}
int main(void)
{
	uint8_t out[128];
	const uint8_t fw[] = {0xd5, 3, 0x32, 1, 6, 7}, cmd[] = {2};
	for (chunk = 1; chunk <= 64; chunk *= 2) {
		reset();
		response(fw, 6);
		assert(command(cmd, 1, out) == 6);
		assert(memcmp(out, fw, 6) == 0);
		assert(writes == 1);
		assert(last_tx_len == 14 && memcmp(last_tx, "\x55\x55\0\0\0", 5) == 0);
	}
	reset();
	response(fw, 6);
	stream[total - 2] ^= 1;
	assert(command(cmd, 1, out) == -1);
	assert(writes == 2);
	reset();
	response(fw, 6);
	stream[sizeof(ack) + 4] ^= 1;
	assert(command(cmd, 1, out) == -1);
	reset();
	response(fw, 6);
	--total;
	assert(command(cmd, 1, out) == -1);
	reset();
	append(ack, sizeof(ack));
	assert(command(cmd, 1, out) == -1);
	const uint8_t rf[] = {0xd5, 0x33};
	const unsigned lens[] = {4, 7, 10};
	for (unsigned k = 0; k < 3; k++) {
		uint8_t target[18] = {0xd5, 0x4b, 1, 1, 0, 4, 0};
		target[7] = lens[k];
		for (unsigned j = 0; j < lens[k]; j++)
			target[8 + j] = j + 1;
		reset();
		response(target, 8 + lens[k]);
		response(rf, 2);
		nfc_card_t card = {0};
		assert(poll_card(&card) == 1);
		assert(card.uid_len == lens[k]);
		assert(memcmp(card.uid, target + 8, lens[k]) == 0);
	}
	uint8_t absent[] = {0xd5, 0x4b, 0};
	reset();
	response(absent, 3);
	response(rf, 2);
	nfc_card_t card = {0};
	assert(poll_card(&card) == 0);
	uint8_t short_uid[] = {0xd5, 0x4b, 1, 1, 0, 4, 0, 10, 1};
	reset();
	response(short_uid, sizeof(short_uid));
	response(rf, 2);
	assert(poll_card(&card) == -1);
	reset();
	response(absent, 3);
	stream[total - 2] ^= 1;
	assert(poll_card(&card) == -1);
	uint8_t apdu[114] = {0x80, 0x80, 0, 1}, reply[] = {0xd5, 0x41, 0, 0x90, 0};
	size_t out_length = 0;
	reset();
	response(reply, sizeof(reply));
	assert(pn532_exchange(1, apdu, sizeof(apdu), out, sizeof(out), &out_length) == 0);
	assert(out_length == 2 && out[0] == 0x90);
	assert(last_tx_len == sizeof(apdu) + 10);
	assert(last_tx[5] == 0xd4 && last_tx[6] == 0x40 && last_tx[7] == 1);
	assert(memcmp(last_tx + 8, apdu, sizeof(apdu)) == 0);
	reset();
	reply[2] = 1;
	response(reply, sizeof(reply));
	assert(pn532_exchange(1, apdu, sizeof(apdu), out, sizeof(out), &out_length) == 3);
	reset();
	reply[2] = 0x40;
	response(reply, sizeof(reply));
	assert(pn532_exchange(1, apdu, sizeof(apdu), out, sizeof(out), &out_length) == 3);
	assert(pn532_exchange(1, apdu, RELAY_APDU_MAX + 1, out, sizeof(out), &out_length) == 1);
	const uint8_t gid[] = {0x94, 0x62, 0x5f, 0xd2, 0xc3, 0x2c, 0xf3, 0xc2};
	uint8_t ecp[18];
	build_ecp(ecp, gid);
	assert(memcmp(ecp, "\x6a\x02\xcb\x02\x06\x02\x11\x00", 8) == 0);
	assert(memcmp(ecp + 8, gid, 8) == 0);
	/* Independent CRC-A vector for the Home Key subtype 06. */
	assert(ecp[16] == 0x66 && ecp[17] == 0x3a);
	const uint8_t reg_ok[] = {0xd5, 0x09}, timeout[] = {0xd5, 0x43, 1};
	reset();
	response(reg_ok, 2);
	response(rf, 2);
	response(rf, 2);
	response(timeout, 3);
	response(rf, 2);
	assert(broadcast_ecp(gid));
	assert(writes == 5); /* Expected RF timeout never causes an abort ACK. */
	assert(tx_history[0][6] == 0x08 && tx_history[0][9] == 0);
	assert(tx_history[3][6] == 0x42);
	assert(memcmp(tx_history[3] + 7, ecp, 18) == 0);
	reset();
	response(reg_ok, 2);
	response(rf, 2);
	response(rf, 2);
	const uint8_t bad_status[] = {0xd5, 0x43, 2};
	response(bad_status, 3);
	response(rf, 2);
	assert(!broadcast_ecp(gid));
	assert(writes == 5);
	puts("PN532: ECP frame/CRC-A, raw transmit and expected timeout; startup prefix preserved; APDU >64 bytes; status/chaining rejection; fragmented "
		 "frames, checksums, timeout and UID parsing passed");
}