Skip to content

Commit

Permalink
add --nfc flag to use NFC apdu media
Browse files Browse the repository at this point in the history
  • Loading branch information
yrichard-ledger committed Sep 27, 2024
1 parent d04cdf2 commit 8ff7576
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 3 deletions.
3 changes: 2 additions & 1 deletion speculos/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ def main(prog=None) -> int:
parser.add_argument('-t', '--trace', action='store_true', help='Trace syscalls')
parser.add_argument('-u', '--usb', default='hid', help='Configure the USB transport protocol, '
'either HID (default) or U2F')
parser.add_argument('--nfc', action='store_true', help='Use NFC transport instead of USB')

group = parser.add_argument_group('network arguments')
group.add_argument('--apdu-port', default=9999, type=int, help='ApduServer TCP port')
Expand Down Expand Up @@ -473,7 +474,7 @@ def main(prog=None) -> int:
use_bagl=use_bagl,
automation=automation_path,
automation_server=automation_server,
transport=args.usb)
transport='nfc' if args.nfc else args.usb)

button = None
if args.button_port:
Expand Down
75 changes: 75 additions & 0 deletions speculos/mcu/nfc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"""
Forward NFC packets between the MCU and the SE
"""

from abc import ABC, abstractmethod
from construct import Int8ub, Int16ub, Int16ul, Struct
import binascii
import enum
import logging


class SephNfcTag(enum.IntEnum):
NFC_APDU_EVENT = 0x1C
NFC_EVENT = 0x1E


class NFC:
def __init__(self, _queue_event_packet):
self._queue_event_packet = _queue_event_packet
self.packets_to_send = []
self.MTU=140
self.rx_sequence = 0
self.rx_size = 0
self.rx_data = []

self.logger = logging.getLogger("nfc")


def handle_rapdu_chunk(self, data):
"""concatenate apdu chunks into full apdu"""

# example of data
# 0000050000002b3330000409312e302e302d72633104e600000008362e312e302d646508352e312e302d6465010001009000

# only APDU packets are suported

Check failure on line 35 in speculos/mcu/nfc.py

View workflow job for this annotation

GitHub Actions / Check misspellings

suported ==> supported
if data[2] != 0x05:
return None

sequence = int.from_bytes(data[3:5], 'big')
if self.rx_sequence != sequence:
print(f"Unexpected sequence number:{sequence}")
return None

if sequence == 0:
self.rx_size = int.from_bytes(data[5:7], "big")
self.rx_data = data[7:]
else:
self.rx_data.append(data[5:])

if len(self.rx_data) == self.rx_size:
#prepare for next call
self.rx_sequence = 0
return self.rx_data

return None


def apdu(self, data):
chunks: List[bytes] = []
data_len = len(data)

while len(data) > 0:
size = self.MTU-5
chunks.append(data[:size])
data = data[size:]

for i, chunk in enumerate(chunks):
# ledger protocol header
header = bytes([0x00, 0x00, 0x05]) # APDU
header += i.to_bytes(2, "big")
# first packet contains the size of full buffer
if i == 0:
header += data_len.to_bytes(2, "big")

self._queue_event_packet(SephNfcTag.NFC_APDU_EVENT, header+chunk)
22 changes: 20 additions & 2 deletions speculos/mcu/seproxyhal.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from speculos.observer import BroadcastInterface, TextEvent
from . import usb
from . import nfc
from .automation import Automation
from .display import DisplayNotifier, IODevice
from .nbgl import NBGL
Expand All @@ -30,6 +31,9 @@ class SephTag(IntEnum):
USB_CONFIG = 0x4f
USB_EP_PREPARE = 0x50

NFC_RAPDU = 0x4A
NFC_POWER = 0x34

REQUEST_STATUS = 0x52
RAPDU = 0x53
PLAY_TUNE = 0x56
Expand Down Expand Up @@ -261,6 +265,7 @@ def __init__(self,
self.automation_server = automation_server
self.events: List[TextEvent] = []
self.refreshed = False
self.transport = transport

self.status_event = threading.Event()
self.socket_helper = SocketHelper(self._socket, self.status_event)
Expand All @@ -270,7 +275,10 @@ def __init__(self,
self.socket_helper.wait_until_tick_is_processed)
self.time_ticker_thread.start()

self.usb = usb.USB(self.socket_helper.queue_packet, transport=transport)
usb_transport = transport if transport in ['hid', 'u2f'] else 'hid'
self.usb = usb.USB(self.socket_helper.queue_packet, transport=usb_transport)

self.nfc = nfc.NFC(self.socket_helper.queue_packet)

self.ocr = OCR(model, use_bagl)

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

elif tag == SephTag.NFC_RAPDU:
data = self.nfc.handle_rapdu_chunk(data)
if data:
for c in self.apdu_callbacks:
c(data)
screen.display.forward_to_apdu_client(data)

else:
self.logger.error(f"unknown tag: {tag:#x}")
sys.exit(0)
Expand Down Expand Up @@ -506,7 +521,10 @@ def to_app(self, packet: bytes):
tag, packet = packet[4], packet[5:]
self.socket_helper.queue_packet(SephTag(tag), packet)
else:
self.usb.xfer(packet)
if self.transport == 'nfc':
self.nfc.apdu(packet)
else:
self.usb.xfer(packet)

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

0 comments on commit 8ff7576

Please sign in to comment.