CrackNuts 0.0.1.dev2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cracknuts/__init__.py +5 -0
- cracknuts/__main__.py +33 -0
- cracknuts/acquisition/__init__.py +3 -0
- cracknuts/acquisition/acquisition.py +554 -0
- cracknuts/cracker/__init__.py +0 -0
- cracknuts/cracker/basic_cracker.py +334 -0
- cracknuts/cracker/cracker.py +548 -0
- cracknuts/cracker/mock_cracker.py +163 -0
- cracknuts/cracker/protocol.py +83 -0
- cracknuts/cracker/stateful_cracker.py +214 -0
- cracknuts/logger.py +88 -0
- cracknuts/solver/__init__.py +0 -0
- cracknuts/solver/trace.py +524 -0
- cracknuts/utils/__init__.py +0 -0
- cracknuts/utils/hex_util.py +38 -0
- cracknuts-0.0.1.dev2.dist-info/LICENSE +21 -0
- cracknuts-0.0.1.dev2.dist-info/METADATA +63 -0
- cracknuts-0.0.1.dev2.dist-info/RECORD +21 -0
- cracknuts-0.0.1.dev2.dist-info/WHEEL +5 -0
- cracknuts-0.0.1.dev2.dist-info/entry_points.txt +2 -0
- cracknuts-0.0.1.dev2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import socket
|
|
3
|
+
import struct
|
|
4
|
+
import threading
|
|
5
|
+
import typing
|
|
6
|
+
from abc import ABC
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
import cracknuts.utils.hex_util as hex_util
|
|
12
|
+
from cracknuts import logger
|
|
13
|
+
from cracknuts.cracker import protocol
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Cracker(typing.Protocol):
|
|
17
|
+
def get_default_config(self) -> typing.Optional["Config"]: ...
|
|
18
|
+
|
|
19
|
+
def set_addr(self, ip, port) -> None: ...
|
|
20
|
+
|
|
21
|
+
def set_uri(self, uri: str) -> None: ...
|
|
22
|
+
|
|
23
|
+
def get_uri(self): ...
|
|
24
|
+
|
|
25
|
+
def connect(self): ...
|
|
26
|
+
|
|
27
|
+
def disconnect(self): ...
|
|
28
|
+
|
|
29
|
+
def reconnect(self): ...
|
|
30
|
+
|
|
31
|
+
def get_connection_status(self) -> bool: ...
|
|
32
|
+
|
|
33
|
+
def send_and_receive(self, message) -> tuple[int, bytes]: ...
|
|
34
|
+
|
|
35
|
+
def send_with_command(self, command: int, rfu: int = 0, payload: str | bytes = None) -> tuple[int, bytes]: ...
|
|
36
|
+
|
|
37
|
+
def echo(self, payload: str) -> str: ...
|
|
38
|
+
|
|
39
|
+
def echo_hex(self, payload: str) -> str: ...
|
|
40
|
+
|
|
41
|
+
def get_id(self) -> str | None: ...
|
|
42
|
+
|
|
43
|
+
def get_name(self) -> str | None: ...
|
|
44
|
+
|
|
45
|
+
def get_version(self) -> str | None: ...
|
|
46
|
+
|
|
47
|
+
def osc_set_analog_channel_enable(self, enable: dict[int, bool]): ...
|
|
48
|
+
|
|
49
|
+
def osc_set_analog_coupling(self, coupling: dict[int, int]): ...
|
|
50
|
+
|
|
51
|
+
def osc_set_analog_voltage(self, channel: int, voltage: int): ...
|
|
52
|
+
|
|
53
|
+
def osc_set_analog_bias_voltage(self, channel: int, voltage: int): ...
|
|
54
|
+
|
|
55
|
+
def osc_set_digital_channel_enable(self, enable: dict[int, bool]): ...
|
|
56
|
+
|
|
57
|
+
def osc_set_digital_voltage(self, voltage: int): ...
|
|
58
|
+
|
|
59
|
+
def osc_set_trigger_mode(self, source: int, stop: int): ...
|
|
60
|
+
|
|
61
|
+
def osc_set_analog_trigger_source(self, channel: int): ...
|
|
62
|
+
|
|
63
|
+
def osc_set_digital_trigger_source(self, channel: int): ...
|
|
64
|
+
|
|
65
|
+
def osc_set_analog_trigger_voltage(self, voltage: int): ...
|
|
66
|
+
|
|
67
|
+
def osc_set_sample_delay(self, delay: int): ...
|
|
68
|
+
|
|
69
|
+
def osc_set_sample_len(self, length: int): ...
|
|
70
|
+
|
|
71
|
+
def osc_single(self): ...
|
|
72
|
+
|
|
73
|
+
def osc_force(self): ...
|
|
74
|
+
|
|
75
|
+
def osc_is_triggered(self): ...
|
|
76
|
+
|
|
77
|
+
def osc_get_analog_wave(self, channel: int, offset: int, sample_count: int) -> tuple[int, np.ndarray]: ...
|
|
78
|
+
|
|
79
|
+
def osc_get_digital_wave(self, channel: int, offset: int, sample_count: int): ...
|
|
80
|
+
|
|
81
|
+
def osc_set_analog_gain(self, channel: int, gain: int): ...
|
|
82
|
+
|
|
83
|
+
def osc_set_analog_gain_raw(self, channel: int, gain: int): ...
|
|
84
|
+
|
|
85
|
+
def osc_set_sample_clock(self, clock: int): ...
|
|
86
|
+
|
|
87
|
+
def osc_set_sample_phase(self, phase: int): ...
|
|
88
|
+
|
|
89
|
+
def nut_enable(self, enable: int): ...
|
|
90
|
+
|
|
91
|
+
def nut_voltage(self, voltage: int): ...
|
|
92
|
+
|
|
93
|
+
def nut_voltage_raw(self, voltage: int): ...
|
|
94
|
+
|
|
95
|
+
def nut_clock(self, clock: int): ...
|
|
96
|
+
|
|
97
|
+
def nut_interface(self, interface: dict[int, bool]): ...
|
|
98
|
+
|
|
99
|
+
def nut_timeout(self, timeout: int): ...
|
|
100
|
+
|
|
101
|
+
def cracker_serial_baud(self, baud: int): ...
|
|
102
|
+
|
|
103
|
+
def cracker_serial_width(self, width: int): ...
|
|
104
|
+
|
|
105
|
+
def cracker_serial_stop(self, stop: int): ...
|
|
106
|
+
|
|
107
|
+
def cracker_serial_odd_eve(self, odd_eve: int): ...
|
|
108
|
+
|
|
109
|
+
def cracker_serial_data(self, expect_len: int, data: bytes): ...
|
|
110
|
+
|
|
111
|
+
def cracker_spi_cpol(self, cpol: int): ...
|
|
112
|
+
|
|
113
|
+
def cracker_spi_cpha(self, cpha: int): ...
|
|
114
|
+
|
|
115
|
+
def cracker_spi_data_len(self, cpha: int): ...
|
|
116
|
+
|
|
117
|
+
def cracker_spi_freq(self, freq: int): ...
|
|
118
|
+
|
|
119
|
+
def cracker_spi_timeout(self, timeout: int): ...
|
|
120
|
+
|
|
121
|
+
def cracker_spi_data(self, expect_len: int, data: bytes): ...
|
|
122
|
+
|
|
123
|
+
def cracker_i2c_freq(self, freq: int): ...
|
|
124
|
+
|
|
125
|
+
def cracker_i2c_timeout(self, timeout: int): ...
|
|
126
|
+
|
|
127
|
+
def cracker_i2c_data(self, expect_len: int, data: bytes): ...
|
|
128
|
+
|
|
129
|
+
def cracker_can_freq(self, freq: int): ...
|
|
130
|
+
|
|
131
|
+
def cracker_can_timeout(self, timeout: int): ...
|
|
132
|
+
|
|
133
|
+
def cracker_can_data(self, expect_len: int, data: bytes): ...
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class Commands:
|
|
137
|
+
"""
|
|
138
|
+
Protocol commands.
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
GET_ID = 0x0001
|
|
142
|
+
GET_NAME = 0x0002
|
|
143
|
+
GET_VERSION = 0x0003
|
|
144
|
+
|
|
145
|
+
OSC_ANALOG_CHANNEL_ENABLE = 0x0100
|
|
146
|
+
OSC_ANALOG_COUPLING = 0x0101
|
|
147
|
+
OSC_ANALOG_VOLTAGE = 0x0102
|
|
148
|
+
OSC_ANALOG_BIAS_VOLTAGE = 0x0103
|
|
149
|
+
OSC_ANALOG_GAIN = 0x0104
|
|
150
|
+
OSC_ANALOG_GAIN_RAW = 0x0107
|
|
151
|
+
|
|
152
|
+
OSC_SAMPLE_CLOCK = 0x0105
|
|
153
|
+
OSC_SAMPLE_PHASE = 0x0106
|
|
154
|
+
|
|
155
|
+
OSC_DIGITAL_CHANNEL_ENABLE = 0x0110
|
|
156
|
+
OSC_DIGITAL_VOLTAGE = 0x0111
|
|
157
|
+
|
|
158
|
+
OSC_TRIGGER_MODE = 0x0120
|
|
159
|
+
|
|
160
|
+
OSC_ANALOG_TRIGGER_SOURCE = 0x0121
|
|
161
|
+
OSC_DIGITAL_TRIGGER_SOURCE = 0x0122
|
|
162
|
+
|
|
163
|
+
OSC_ANALOG_TRIGGER_VOLTAGE = 0x0123
|
|
164
|
+
|
|
165
|
+
OSC_SAMPLE_DELAY = 0x0124
|
|
166
|
+
|
|
167
|
+
OSC_SAMPLE_LENGTH = 0x0125
|
|
168
|
+
OSC_SAMPLE_RATE = 0x0128
|
|
169
|
+
|
|
170
|
+
OSC_SINGLE = 0x0126
|
|
171
|
+
|
|
172
|
+
OSC_IS_TRIGGERED = 0x0127
|
|
173
|
+
OSC_FORCE = 0x0129
|
|
174
|
+
|
|
175
|
+
OSC_GET_ANALOG_WAVES = 0x0130
|
|
176
|
+
OSC_GET_DIGITAL_WAVES = 0x0130
|
|
177
|
+
|
|
178
|
+
NUT_ENABLE = 0x0200
|
|
179
|
+
NUT_VOLTAGE = 0x0201
|
|
180
|
+
NUT_VOLTAGE_RAW = 0x0203
|
|
181
|
+
NUT_CLOCK = 0x0202
|
|
182
|
+
NUT_INTERFACE = 0x0210
|
|
183
|
+
NUT_TIMEOUT = 0x0224
|
|
184
|
+
|
|
185
|
+
CRACKER_SERIAL_BAUD = 0x0220
|
|
186
|
+
CRACKER_SERIAL_WIDTH = 0x0221
|
|
187
|
+
CRACKER_SERIAL_STOP = 0x0222
|
|
188
|
+
CRACKER_SERIAL_ODD_EVE = 0x0223
|
|
189
|
+
CRACKER_SERIAL_DATA = 0x022A
|
|
190
|
+
|
|
191
|
+
CRACKER_SPI_CPOL = 0x0230
|
|
192
|
+
CRACKER_SPI_CPHA = 0x0231
|
|
193
|
+
CRACKER_SPI_DATA_LEN = 0x0232
|
|
194
|
+
CRACKER_SPI_FREQ = 0x0233
|
|
195
|
+
CRACKER_SPI_TIMEOUT = 0x0234
|
|
196
|
+
CRACKER_SPI_DATA = 0x023A
|
|
197
|
+
|
|
198
|
+
CRACKER_I2C_FREQ = 0x0240
|
|
199
|
+
CRACKER_I2C_TIMEOUT = 0x0244
|
|
200
|
+
CRACKER_I2C_DATA = 0x024A
|
|
201
|
+
|
|
202
|
+
CRACKER_CAN_FREQ = 0x0250
|
|
203
|
+
CRACKER_CAN_TIMEOUT = 0x0254
|
|
204
|
+
CRACKER_CA_DATA = 0x025A
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
@dataclass
|
|
208
|
+
class Config:
|
|
209
|
+
def __init__(
|
|
210
|
+
self,
|
|
211
|
+
nut_enable: bool = None,
|
|
212
|
+
nut_voltage: int = None,
|
|
213
|
+
nut_clock: int = None,
|
|
214
|
+
osc_analog_channel_enable: dict[int, bool] = None,
|
|
215
|
+
osc_sample_len: int = None,
|
|
216
|
+
osc_sample_delay: int = None,
|
|
217
|
+
osc_sample_phase: int = None,
|
|
218
|
+
osc_sample_clock: int = None,
|
|
219
|
+
):
|
|
220
|
+
self._binder: dict[str, callable] = {}
|
|
221
|
+
self.nut_enable: bool = nut_enable
|
|
222
|
+
self.nut_voltage: int = nut_voltage
|
|
223
|
+
self.nut_clock: int = nut_clock
|
|
224
|
+
self.osc_sample_len = osc_sample_len
|
|
225
|
+
self.osc_sample_delay = osc_sample_delay
|
|
226
|
+
self.osc_sample_phase = osc_sample_phase
|
|
227
|
+
self.osc_sample_clock = osc_sample_clock
|
|
228
|
+
self.osc_analog_channel_enable: dict[int, bool] = osc_analog_channel_enable
|
|
229
|
+
|
|
230
|
+
def __setattr__(self, key, value):
|
|
231
|
+
self.__dict__[key] = value
|
|
232
|
+
if "_binder" in self.__dict__ and (binder := self._binder.get(key)) is not None:
|
|
233
|
+
binder(value)
|
|
234
|
+
|
|
235
|
+
def bind(self, key: str, callback: callable):
|
|
236
|
+
"""
|
|
237
|
+
Bind a callback which will be call when the key field is updated.
|
|
238
|
+
:param key: a filed name of class `Config`
|
|
239
|
+
:param callback:
|
|
240
|
+
:return:
|
|
241
|
+
"""
|
|
242
|
+
self._binder[key] = callback
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
class AbsCnpCracker(ABC, Cracker):
|
|
246
|
+
"""Cracker
|
|
247
|
+
Cracker
|
|
248
|
+
"""
|
|
249
|
+
|
|
250
|
+
def __init__(self, server_address=None):
|
|
251
|
+
"""
|
|
252
|
+
:param server_address: Cracker device address (ip, port)
|
|
253
|
+
"""
|
|
254
|
+
self._command_lock = threading.Lock()
|
|
255
|
+
self._logger = logger.get_logger(self)
|
|
256
|
+
self._server_address = server_address
|
|
257
|
+
self._socket: socket.socket | None = None
|
|
258
|
+
self._connection_status = False
|
|
259
|
+
self._channel_enable: dict = {
|
|
260
|
+
0: False,
|
|
261
|
+
1: False,
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
@abc.abstractmethod
|
|
265
|
+
def get_default_config(self) -> Config | None: ...
|
|
266
|
+
|
|
267
|
+
def set_addr(self, ip, port) -> None:
|
|
268
|
+
self._server_address = ip, port
|
|
269
|
+
|
|
270
|
+
def set_uri(self, uri: str) -> None:
|
|
271
|
+
if not uri.startswith("cnp://") and uri.count(":") < 2:
|
|
272
|
+
uri = "cnp://" + uri
|
|
273
|
+
|
|
274
|
+
uri = uri.replace("cnp://", "", 1)
|
|
275
|
+
if ":" in uri:
|
|
276
|
+
host, port = uri.split(":")
|
|
277
|
+
else:
|
|
278
|
+
host, port = uri, protocol.DEFAULT_PORT
|
|
279
|
+
|
|
280
|
+
self._server_address = host, int(port)
|
|
281
|
+
|
|
282
|
+
def get_uri(self):
|
|
283
|
+
if self._server_address is None:
|
|
284
|
+
return None
|
|
285
|
+
else:
|
|
286
|
+
return f"cnp://{self._server_address[0]}:{self._server_address[1]}"
|
|
287
|
+
|
|
288
|
+
def connect(self):
|
|
289
|
+
"""
|
|
290
|
+
Connect to Cracker device.
|
|
291
|
+
:return: Cracker self.
|
|
292
|
+
"""
|
|
293
|
+
try:
|
|
294
|
+
if not self._socket:
|
|
295
|
+
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
296
|
+
self._socket.settimeout(5)
|
|
297
|
+
self._socket.connect(self._server_address)
|
|
298
|
+
self._connection_status = True
|
|
299
|
+
self._logger.info(f"Connected to cracker: {self._server_address}")
|
|
300
|
+
except OSError as e:
|
|
301
|
+
self._logger.error("Connection failed: %s", e)
|
|
302
|
+
self._connection_status = False
|
|
303
|
+
|
|
304
|
+
def disconnect(self):
|
|
305
|
+
"""
|
|
306
|
+
Disconnect Cracker device.
|
|
307
|
+
:return: Cracker self.
|
|
308
|
+
"""
|
|
309
|
+
try:
|
|
310
|
+
if self._socket:
|
|
311
|
+
self._socket.close()
|
|
312
|
+
self._socket = None
|
|
313
|
+
self._logger.info(f"Disconnect from {self._server_address}")
|
|
314
|
+
except OSError as e:
|
|
315
|
+
self._logger.error("Disconnection failed: %s", e)
|
|
316
|
+
finally:
|
|
317
|
+
self._connection_status = False
|
|
318
|
+
|
|
319
|
+
def reconnect(self):
|
|
320
|
+
"""
|
|
321
|
+
Reconnect to Cracker device.
|
|
322
|
+
:return: Cracker self.
|
|
323
|
+
"""
|
|
324
|
+
self.disconnect()
|
|
325
|
+
self.connect()
|
|
326
|
+
|
|
327
|
+
def get_connection_status(self) -> bool:
|
|
328
|
+
"""
|
|
329
|
+
Get connection status.
|
|
330
|
+
:return: True or False
|
|
331
|
+
"""
|
|
332
|
+
return self._connection_status
|
|
333
|
+
|
|
334
|
+
def send_and_receive(self, message) -> tuple[int, bytes | None]:
|
|
335
|
+
"""
|
|
336
|
+
Send message to socket
|
|
337
|
+
:param message:
|
|
338
|
+
:return:
|
|
339
|
+
"""
|
|
340
|
+
try:
|
|
341
|
+
self._command_lock.acquire()
|
|
342
|
+
if not self.get_connection_status():
|
|
343
|
+
self._logger.error("Cracker is not connected.")
|
|
344
|
+
return protocol.STATUS_ERROR, None
|
|
345
|
+
self._logger.debug(f"Send message to {self._server_address}: \n{hex_util.get_bytes_matrix(message)}")
|
|
346
|
+
self._socket.sendall(message)
|
|
347
|
+
resp_header = self._socket.recv(protocol.RES_HEADER_SIZE)
|
|
348
|
+
self._logger.debug(
|
|
349
|
+
"Get response header from %s: \n%s",
|
|
350
|
+
self._server_address,
|
|
351
|
+
hex_util.get_bytes_matrix(resp_header),
|
|
352
|
+
)
|
|
353
|
+
magic, version, direction, status, length = struct.unpack(protocol.RES_HEADER_FORMAT, resp_header)
|
|
354
|
+
self._logger.debug(
|
|
355
|
+
f"Receive header from {self._server_address}: {magic}, {version}, {direction}, {status:02X}, {length}"
|
|
356
|
+
)
|
|
357
|
+
if status >= protocol.STATUS_ERROR:
|
|
358
|
+
self._logger.error(f"Receive status error: {status:02X}")
|
|
359
|
+
if length == 0:
|
|
360
|
+
return status, None
|
|
361
|
+
resp_payload = self._recv(length)
|
|
362
|
+
if status >= protocol.STATUS_ERROR:
|
|
363
|
+
self._logger.error(
|
|
364
|
+
"Receive payload from {self._server_address}: \n{hex_util.get_bytes_matrix(resp_payload)}"
|
|
365
|
+
)
|
|
366
|
+
else:
|
|
367
|
+
self._logger.debug(
|
|
368
|
+
"Receive payload from {self._server_address}: \n{hex_util.get_bytes_matrix(resp_payload)}"
|
|
369
|
+
)
|
|
370
|
+
return status, resp_payload
|
|
371
|
+
except OSError as e:
|
|
372
|
+
self._logger.error("Send message failed: %s, and msg: %s", e, message)
|
|
373
|
+
return protocol.STATUS_ERROR, None
|
|
374
|
+
finally:
|
|
375
|
+
self._command_lock.release()
|
|
376
|
+
|
|
377
|
+
def _recv(self, length):
|
|
378
|
+
resp_payload = b""
|
|
379
|
+
while (received_len := len(resp_payload)) < length:
|
|
380
|
+
for_receive_len = length - received_len
|
|
381
|
+
resp_payload += self._socket.recv(for_receive_len)
|
|
382
|
+
|
|
383
|
+
return resp_payload
|
|
384
|
+
|
|
385
|
+
def send_with_command(self, command: int, rfu: int = 0, payload: str | bytes = None) -> tuple[int, bytes]:
|
|
386
|
+
if isinstance(payload, str):
|
|
387
|
+
payload = bytes.fromhex(payload)
|
|
388
|
+
return self.send_and_receive(protocol.build_send_message(command, rfu, payload))
|
|
389
|
+
|
|
390
|
+
def echo(self, payload: str) -> str:
|
|
391
|
+
"""
|
|
392
|
+
length <= 1024
|
|
393
|
+
"""
|
|
394
|
+
self._socket.sendall(payload.encode("ascii"))
|
|
395
|
+
res = self._socket.recv(1024).decode("ascii")
|
|
396
|
+
self._logger.debug(f"Get response: {res}")
|
|
397
|
+
return res
|
|
398
|
+
|
|
399
|
+
def echo_hex(self, payload: str) -> str:
|
|
400
|
+
"""
|
|
401
|
+
length <= 1024
|
|
402
|
+
"""
|
|
403
|
+
payload = bytes.fromhex(payload)
|
|
404
|
+
self._socket.sendall(payload)
|
|
405
|
+
res = self._socket.recv(1024).hex()
|
|
406
|
+
self._logger.debug(f"Get response: {res}")
|
|
407
|
+
return res
|
|
408
|
+
|
|
409
|
+
@abc.abstractmethod
|
|
410
|
+
def get_id(self) -> str | None: ...
|
|
411
|
+
|
|
412
|
+
@abc.abstractmethod
|
|
413
|
+
def get_name(self) -> str | None: ...
|
|
414
|
+
|
|
415
|
+
def get_version(self) -> str | None:
|
|
416
|
+
return super().get_version()
|
|
417
|
+
|
|
418
|
+
@abc.abstractmethod
|
|
419
|
+
def osc_set_analog_channel_enable(self, enable: dict[int, bool]): ...
|
|
420
|
+
|
|
421
|
+
@abc.abstractmethod
|
|
422
|
+
def osc_set_analog_coupling(self, coupling: dict[int, int]): ...
|
|
423
|
+
|
|
424
|
+
@abc.abstractmethod
|
|
425
|
+
def osc_set_analog_voltage(self, channel: int, voltage: int): ...
|
|
426
|
+
|
|
427
|
+
@abc.abstractmethod
|
|
428
|
+
def osc_set_analog_bias_voltage(self, channel: int, voltage: int): ...
|
|
429
|
+
|
|
430
|
+
@abc.abstractmethod
|
|
431
|
+
def osc_set_digital_channel_enable(self, enable: dict[int, bool]): ...
|
|
432
|
+
|
|
433
|
+
@abc.abstractmethod
|
|
434
|
+
def osc_set_digital_voltage(self, voltage: int): ...
|
|
435
|
+
|
|
436
|
+
@abc.abstractmethod
|
|
437
|
+
def osc_set_trigger_mode(self, source: int, stop: int): ...
|
|
438
|
+
|
|
439
|
+
@abc.abstractmethod
|
|
440
|
+
def osc_set_analog_trigger_source(self, channel: int): ...
|
|
441
|
+
|
|
442
|
+
@abc.abstractmethod
|
|
443
|
+
def osc_set_digital_trigger_source(self, channel: int): ...
|
|
444
|
+
|
|
445
|
+
@abc.abstractmethod
|
|
446
|
+
def osc_set_analog_trigger_voltage(self, voltage: int): ...
|
|
447
|
+
|
|
448
|
+
@abc.abstractmethod
|
|
449
|
+
def osc_set_sample_delay(self, delay: int): ...
|
|
450
|
+
|
|
451
|
+
@abc.abstractmethod
|
|
452
|
+
def osc_set_sample_len(self, length: int): ...
|
|
453
|
+
|
|
454
|
+
@abc.abstractmethod
|
|
455
|
+
def osc_single(self): ...
|
|
456
|
+
|
|
457
|
+
@abc.abstractmethod
|
|
458
|
+
def osc_force(self): ...
|
|
459
|
+
|
|
460
|
+
@abc.abstractmethod
|
|
461
|
+
def osc_is_triggered(self): ...
|
|
462
|
+
|
|
463
|
+
@abc.abstractmethod
|
|
464
|
+
def osc_get_analog_wave(self, channel: int, offset: int, sample_count: int) -> tuple[int, np.ndarray]: ...
|
|
465
|
+
|
|
466
|
+
@abc.abstractmethod
|
|
467
|
+
def osc_get_digital_wave(self, channel: int, offset: int, sample_count: int): ...
|
|
468
|
+
|
|
469
|
+
@abc.abstractmethod
|
|
470
|
+
def osc_set_analog_gain(self, channel: int, gain: int): ...
|
|
471
|
+
|
|
472
|
+
@abc.abstractmethod
|
|
473
|
+
def osc_set_analog_gain_raw(self, channel: int, gain: int): ...
|
|
474
|
+
|
|
475
|
+
@abc.abstractmethod
|
|
476
|
+
def osc_set_sample_clock(self, clock: int): ...
|
|
477
|
+
|
|
478
|
+
@abc.abstractmethod
|
|
479
|
+
def osc_set_sample_phase(self, phase: int): ...
|
|
480
|
+
|
|
481
|
+
@abc.abstractmethod
|
|
482
|
+
def nut_enable(self, enable: int): ...
|
|
483
|
+
|
|
484
|
+
@abc.abstractmethod
|
|
485
|
+
def nut_voltage(self, voltage: int): ...
|
|
486
|
+
|
|
487
|
+
@abc.abstractmethod
|
|
488
|
+
def nut_voltage_raw(self, voltage: int): ...
|
|
489
|
+
|
|
490
|
+
@abc.abstractmethod
|
|
491
|
+
def nut_clock(self, clock: int): ...
|
|
492
|
+
|
|
493
|
+
@abc.abstractmethod
|
|
494
|
+
def nut_interface(self, interface: dict[int, bool]): ...
|
|
495
|
+
|
|
496
|
+
@abc.abstractmethod
|
|
497
|
+
def nut_timeout(self, timeout: int): ...
|
|
498
|
+
|
|
499
|
+
@abc.abstractmethod
|
|
500
|
+
def cracker_serial_baud(self, baud: int): ...
|
|
501
|
+
|
|
502
|
+
@abc.abstractmethod
|
|
503
|
+
def cracker_serial_width(self, width: int): ...
|
|
504
|
+
|
|
505
|
+
@abc.abstractmethod
|
|
506
|
+
def cracker_serial_stop(self, stop: int): ...
|
|
507
|
+
|
|
508
|
+
@abc.abstractmethod
|
|
509
|
+
def cracker_serial_odd_eve(self, odd_eve: int): ...
|
|
510
|
+
|
|
511
|
+
@abc.abstractmethod
|
|
512
|
+
def cracker_serial_data(self, expect_len: int, data: bytes): ...
|
|
513
|
+
|
|
514
|
+
@abc.abstractmethod
|
|
515
|
+
def cracker_spi_cpol(self, cpol: int): ...
|
|
516
|
+
|
|
517
|
+
@abc.abstractmethod
|
|
518
|
+
def cracker_spi_cpha(self, cpha: int): ...
|
|
519
|
+
|
|
520
|
+
@abc.abstractmethod
|
|
521
|
+
def cracker_spi_data_len(self, cpha: int): ...
|
|
522
|
+
|
|
523
|
+
@abc.abstractmethod
|
|
524
|
+
def cracker_spi_freq(self, freq: int): ...
|
|
525
|
+
|
|
526
|
+
@abc.abstractmethod
|
|
527
|
+
def cracker_spi_timeout(self, timeout: int): ...
|
|
528
|
+
|
|
529
|
+
@abc.abstractmethod
|
|
530
|
+
def cracker_spi_data(self, expect_len: int, data: bytes): ...
|
|
531
|
+
|
|
532
|
+
@abc.abstractmethod
|
|
533
|
+
def cracker_i2c_freq(self, freq: int): ...
|
|
534
|
+
|
|
535
|
+
@abc.abstractmethod
|
|
536
|
+
def cracker_i2c_timeout(self, timeout: int): ...
|
|
537
|
+
|
|
538
|
+
@abc.abstractmethod
|
|
539
|
+
def cracker_i2c_data(self, expect_len: int, data: bytes): ...
|
|
540
|
+
|
|
541
|
+
@abc.abstractmethod
|
|
542
|
+
def cracker_can_freq(self, freq: int): ...
|
|
543
|
+
|
|
544
|
+
@abc.abstractmethod
|
|
545
|
+
def cracker_can_timeout(self, timeout: int): ...
|
|
546
|
+
|
|
547
|
+
@abc.abstractmethod
|
|
548
|
+
def cracker_can_data(self, expect_len: int, data: bytes): ...
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import socket
|
|
3
|
+
import struct
|
|
4
|
+
import sys
|
|
5
|
+
import threading
|
|
6
|
+
import time
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
from cracknuts import logger
|
|
11
|
+
from cracknuts.cracker import protocol, cracker
|
|
12
|
+
from cracknuts.utils import hex_util
|
|
13
|
+
|
|
14
|
+
_handler_dict = {}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _handler(command: int, has_payload: bool = True):
|
|
18
|
+
def decorator(func):
|
|
19
|
+
def wrapper(*args, **kwargs):
|
|
20
|
+
if not has_payload:
|
|
21
|
+
del kwargs["payload"]
|
|
22
|
+
return func(*args, **kwargs)
|
|
23
|
+
|
|
24
|
+
_handler_dict[command] = wrapper
|
|
25
|
+
return wrapper
|
|
26
|
+
|
|
27
|
+
return decorator
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class MockCracker:
|
|
31
|
+
def __init__(self, host: str = "127.0.0.1", port: int = protocol.DEFAULT_PORT, logging_level=logging.INFO):
|
|
32
|
+
self._logger = logger.get_logger(MockCracker, logging_level)
|
|
33
|
+
self._server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
34
|
+
self._server_socket.settimeout(1)
|
|
35
|
+
self._server_socket.bind((host, port))
|
|
36
|
+
self._server_socket.listen()
|
|
37
|
+
self._logger.debug("MockCracker initialized")
|
|
38
|
+
|
|
39
|
+
def start(self):
|
|
40
|
+
try:
|
|
41
|
+
print(f"MockCracker listen on {self._server_socket.getsockname()}")
|
|
42
|
+
self._start_accept()
|
|
43
|
+
except KeyboardInterrupt:
|
|
44
|
+
sys.exit(0)
|
|
45
|
+
|
|
46
|
+
def _start_accept(self):
|
|
47
|
+
while True:
|
|
48
|
+
try:
|
|
49
|
+
conn, addr = self._server_socket.accept()
|
|
50
|
+
conn.settimeout(5)
|
|
51
|
+
self._logger.debug(f"Get client request from {addr}.")
|
|
52
|
+
threading.Thread(target=self._handle, args=(conn,), daemon=True).start()
|
|
53
|
+
# self._handle(conn)
|
|
54
|
+
except TimeoutError:
|
|
55
|
+
continue
|
|
56
|
+
except KeyboardInterrupt:
|
|
57
|
+
self._logger.debug("exist by keyboard interrupt.")
|
|
58
|
+
sys.exit(0)
|
|
59
|
+
|
|
60
|
+
def _handle(self, conn):
|
|
61
|
+
while True:
|
|
62
|
+
try:
|
|
63
|
+
header_data = conn.recv(protocol.REQ_HEADER_SIZE)
|
|
64
|
+
if not header_data:
|
|
65
|
+
self._logger.debug("disconnected...")
|
|
66
|
+
break
|
|
67
|
+
|
|
68
|
+
self._logger.debug(f"Received header:\n{hex_util.get_bytes_matrix(header_data)}")
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
magic, version, direction, command, rfu, length = struct.unpack(
|
|
72
|
+
protocol.REQ_HEADER_FORMAT, header_data
|
|
73
|
+
)
|
|
74
|
+
except struct.error as e:
|
|
75
|
+
err_msg = f"Message format error: {e.args[0]}\n"
|
|
76
|
+
self._logger.error(err_msg)
|
|
77
|
+
err_res = self._error(f"Header format error: {hex_util.get_hex(header_data)}")
|
|
78
|
+
self._logger.debug(f"Send error:\n{hex_util.get_bytes_matrix(err_res)}")
|
|
79
|
+
conn.sendall(err_res)
|
|
80
|
+
continue
|
|
81
|
+
|
|
82
|
+
self._logger.debug(
|
|
83
|
+
f"Received header: Magic={magic}, Version={version}, Direction={direction}, "
|
|
84
|
+
f"Command={command}, Length={length}"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
payload_data = conn.recv(length)
|
|
88
|
+
if len(payload_data) > 0:
|
|
89
|
+
self._logger.debug(f"Received payload:\n{hex_util.get_bytes_matrix(payload_data)}")
|
|
90
|
+
if command not in _handler_dict:
|
|
91
|
+
self._logger.warning(f'Get command not supported: 0x{format(command, '04x')}')
|
|
92
|
+
unsupported_res = self._unsupported(command)
|
|
93
|
+
self._logger.debug(f"Send unsupported:\n{hex_util.get_bytes_matrix(unsupported_res)}")
|
|
94
|
+
conn.sendall(unsupported_res)
|
|
95
|
+
continue
|
|
96
|
+
func_res = _handler_dict.get(command, None)(self, payload=payload_data)
|
|
97
|
+
if type(func_res) is tuple:
|
|
98
|
+
status, res_payload = func_res
|
|
99
|
+
else:
|
|
100
|
+
status, res_payload = protocol.STATUS_OK, func_res
|
|
101
|
+
res = protocol.build_response_message(status, res_payload)
|
|
102
|
+
self._logger.debug(f"Sending:\n{hex_util.get_bytes_matrix(res)}")
|
|
103
|
+
conn.sendall(res)
|
|
104
|
+
except TimeoutError:
|
|
105
|
+
continue
|
|
106
|
+
except ConnectionResetError:
|
|
107
|
+
break
|
|
108
|
+
except Exception as e:
|
|
109
|
+
self._logger.error(e)
|
|
110
|
+
raise e
|
|
111
|
+
|
|
112
|
+
@staticmethod
|
|
113
|
+
def _error(error: str) -> bytes:
|
|
114
|
+
return protocol.build_response_message(protocol.STATUS_ERROR, error.encode())
|
|
115
|
+
|
|
116
|
+
@staticmethod
|
|
117
|
+
def _unsupported(command):
|
|
118
|
+
return protocol.build_response_message(
|
|
119
|
+
protocol.STATUS_UNSUPPORTED, f'Command [0x{format(command, '04x')}] not supported'.encode()
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
@_handler(cracker.Commands.GET_ID, has_payload=False)
|
|
123
|
+
def get_id(self) -> bytes:
|
|
124
|
+
return b"mock_001"
|
|
125
|
+
|
|
126
|
+
@_handler(cracker.Commands.GET_NAME, has_payload=False)
|
|
127
|
+
def get_name(self) -> bytes:
|
|
128
|
+
return b"MOCK 001"
|
|
129
|
+
|
|
130
|
+
@_handler(cracker.Commands.GET_VERSION, has_payload=False)
|
|
131
|
+
def get_version(self) -> bytes:
|
|
132
|
+
return b"0.1.0(adc:0.1.0,hardware:0.1.0)"
|
|
133
|
+
|
|
134
|
+
@_handler(cracker.Commands.OSC_GET_ANALOG_WAVES)
|
|
135
|
+
def osc_get_analog_wave(self, payload: bytes) -> bytes:
|
|
136
|
+
channel, offset, sample_count = struct.unpack(">BII", payload)
|
|
137
|
+
return struct.pack(f">{sample_count}h", *np.random.randint(-100, 100, size=sample_count).tolist())
|
|
138
|
+
|
|
139
|
+
@_handler(cracker.Commands.OSC_IS_TRIGGERED)
|
|
140
|
+
def osc_is_trigger(self, payload: bytes) -> tuple[int, bytes | None]:
|
|
141
|
+
time.sleep(0.05) # Simulate device I/O operations.
|
|
142
|
+
return protocol.STATUS_OK, None
|
|
143
|
+
|
|
144
|
+
@_handler(cracker.Commands.OSC_FORCE, has_payload=False)
|
|
145
|
+
def osc_force(self) -> bytes: ...
|
|
146
|
+
|
|
147
|
+
@_handler(cracker.Commands.OSC_SINGLE, has_payload=False)
|
|
148
|
+
def osc_arm(self) -> bytes: ...
|
|
149
|
+
|
|
150
|
+
@_handler(cracker.Commands.NUT_VOLTAGE, has_payload=False)
|
|
151
|
+
def nut_voltage(self) -> bytes: ...
|
|
152
|
+
|
|
153
|
+
@_handler(cracker.Commands.OSC_ANALOG_CHANNEL_ENABLE, has_payload=False)
|
|
154
|
+
def osc_analog_channel_enable(self) -> bytes: ...
|
|
155
|
+
|
|
156
|
+
@_handler(cracker.Commands.NUT_CLOCK, has_payload=False)
|
|
157
|
+
def nut_clock(self) -> bytes: ...
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
if __name__ == "__main__":
|
|
161
|
+
mock_cracker = MockCracker()
|
|
162
|
+
logger.set_level(logging.DEBUG, mock_cracker)
|
|
163
|
+
mock_cracker.start()
|