Skip to content

Commit 8ff7576

Browse files
add --nfc flag to use NFC apdu media
1 parent d04cdf2 commit 8ff7576

File tree

3 files changed

+97
-3
lines changed

3 files changed

+97
-3
lines changed

speculos/main.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ def main(prog=None) -> int:
269269
parser.add_argument('-t', '--trace', action='store_true', help='Trace syscalls')
270270
parser.add_argument('-u', '--usb', default='hid', help='Configure the USB transport protocol, '
271271
'either HID (default) or U2F')
272+
parser.add_argument('--nfc', action='store_true', help='Use NFC transport instead of USB')
272273

273274
group = parser.add_argument_group('network arguments')
274275
group.add_argument('--apdu-port', default=9999, type=int, help='ApduServer TCP port')
@@ -473,7 +474,7 @@ def main(prog=None) -> int:
473474
use_bagl=use_bagl,
474475
automation=automation_path,
475476
automation_server=automation_server,
476-
transport=args.usb)
477+
transport='nfc' if args.nfc else args.usb)
477478

478479
button = None
479480
if args.button_port:

speculos/mcu/nfc.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
"""
2+
Forward NFC packets between the MCU and the SE
3+
"""
4+
5+
from abc import ABC, abstractmethod
6+
from construct import Int8ub, Int16ub, Int16ul, Struct
7+
import binascii
8+
import enum
9+
import logging
10+
11+
12+
class SephNfcTag(enum.IntEnum):
13+
NFC_APDU_EVENT = 0x1C
14+
NFC_EVENT = 0x1E
15+
16+
17+
class NFC:
18+
def __init__(self, _queue_event_packet):
19+
self._queue_event_packet = _queue_event_packet
20+
self.packets_to_send = []
21+
self.MTU=140
22+
self.rx_sequence = 0
23+
self.rx_size = 0
24+
self.rx_data = []
25+
26+
self.logger = logging.getLogger("nfc")
27+
28+
29+
def handle_rapdu_chunk(self, data):
30+
"""concatenate apdu chunks into full apdu"""
31+
32+
# example of data
33+
# 0000050000002b3330000409312e302e302d72633104e600000008362e312e302d646508352e312e302d6465010001009000
34+
35+
# only APDU packets are suported
36+
if data[2] != 0x05:
37+
return None
38+
39+
sequence = int.from_bytes(data[3:5], 'big')
40+
if self.rx_sequence != sequence:
41+
print(f"Unexpected sequence number:{sequence}")
42+
return None
43+
44+
if sequence == 0:
45+
self.rx_size = int.from_bytes(data[5:7], "big")
46+
self.rx_data = data[7:]
47+
else:
48+
self.rx_data.append(data[5:])
49+
50+
if len(self.rx_data) == self.rx_size:
51+
#prepare for next call
52+
self.rx_sequence = 0
53+
return self.rx_data
54+
55+
return None
56+
57+
58+
def apdu(self, data):
59+
chunks: List[bytes] = []
60+
data_len = len(data)
61+
62+
while len(data) > 0:
63+
size = self.MTU-5
64+
chunks.append(data[:size])
65+
data = data[size:]
66+
67+
for i, chunk in enumerate(chunks):
68+
# ledger protocol header
69+
header = bytes([0x00, 0x00, 0x05]) # APDU
70+
header += i.to_bytes(2, "big")
71+
# first packet contains the size of full buffer
72+
if i == 0:
73+
header += data_len.to_bytes(2, "big")
74+
75+
self._queue_event_packet(SephNfcTag.NFC_APDU_EVENT, header+chunk)

speculos/mcu/seproxyhal.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from speculos.observer import BroadcastInterface, TextEvent
1111
from . import usb
12+
from . import nfc
1213
from .automation import Automation
1314
from .display import DisplayNotifier, IODevice
1415
from .nbgl import NBGL
@@ -30,6 +31,9 @@ class SephTag(IntEnum):
3031
USB_CONFIG = 0x4f
3132
USB_EP_PREPARE = 0x50
3233

34+
NFC_RAPDU = 0x4A
35+
NFC_POWER = 0x34
36+
3337
REQUEST_STATUS = 0x52
3438
RAPDU = 0x53
3539
PLAY_TUNE = 0x56
@@ -261,6 +265,7 @@ def __init__(self,
261265
self.automation_server = automation_server
262266
self.events: List[TextEvent] = []
263267
self.refreshed = False
268+
self.transport = transport
264269

265270
self.status_event = threading.Event()
266271
self.socket_helper = SocketHelper(self._socket, self.status_event)
@@ -270,7 +275,10 @@ def __init__(self,
270275
self.socket_helper.wait_until_tick_is_processed)
271276
self.time_ticker_thread.start()
272277

273-
self.usb = usb.USB(self.socket_helper.queue_packet, transport=transport)
278+
usb_transport = transport if transport in ['hid', 'u2f'] else 'hid'
279+
self.usb = usb.USB(self.socket_helper.queue_packet, transport=usb_transport)
280+
281+
self.nfc = nfc.NFC(self.socket_helper.queue_packet)
274282

275283
self.ocr = OCR(model, use_bagl)
276284

@@ -449,6 +457,13 @@ def can_read(self, screen: DisplayNotifier):
449457
assert isinstance(screen.display.gl, NBGL)
450458
screen.display.gl.hal_draw_image_file(data)
451459

460+
elif tag == SephTag.NFC_RAPDU:
461+
data = self.nfc.handle_rapdu_chunk(data)
462+
if data:
463+
for c in self.apdu_callbacks:
464+
c(data)
465+
screen.display.forward_to_apdu_client(data)
466+
452467
else:
453468
self.logger.error(f"unknown tag: {tag:#x}")
454469
sys.exit(0)
@@ -506,7 +521,10 @@ def to_app(self, packet: bytes):
506521
tag, packet = packet[4], packet[5:]
507522
self.socket_helper.queue_packet(SephTag(tag), packet)
508523
else:
509-
self.usb.xfer(packet)
524+
if self.transport == 'nfc':
525+
self.nfc.apdu(packet)
526+
else:
527+
self.usb.xfer(packet)
510528

511529
def get_tick_count(self):
512530
return self.socket_helper.get_tick_count()

0 commit comments

Comments
 (0)