Skip to main content
Version: 0.19.0

Trace Acquisition

This section demonstrates the basic data acquisition process using the nut_stm32_f103c8 as the Nut (which comes preloaded with Golden firmware for demo/testing purposes). We'll use Jupyter as the development environment (install via pip install cracknuts[jupyter]).

If you're already familiar with Jupyter, feel free to skip the basics. To learn more, visit the Project Jupyter Documentation.

Create a Jupyter Notebook​

Launch Jupyter in the CrackNuts environment using either cracknuts lab or jupyter lab:

image-20241129093834443

tip

Jupyter defaults to English. If you want a Chinese interface, run pip install jupyterlab-language-pack-zh-CN and switch language in Settings -> Language.

Double-click Notebook -> Python3(ipykernel) on the Jupyter start page to create a new notebook:

image-20241129093914741

Connect to Device​

In the new notebook, insert and run the following code to create a CrackerS1 device object and connect:

# Import dependency
import cracknuts as cn

# Create cracker
cracker = cn.new_cracker('192.168.0.10')

image-20241205102039622

tip

192.168.0.10 is the default IP address for CrackerS1. Adjust it to match your actual configuration.

cn.new_cracker('192.168.0.10') defaults to S1 device type. To specify another model, use: cn.new_cracker('192.168.0.10', module = CrackerG1) or module = 'G1'.

Make sure the device is network-accessible. Then connect by running:

cracker.connect()

image-20241205102519618

If the connection fails, you’ll see an error like this:

image-20241205102638087

tip

If the Cracker is connected directly via Ethernet, set your PC’s IP to 192.168.0.11/24 and use ping to verify connection.

Get Device Info​

To retrieve the device ID:

cracker.get_id()

alt text

Create the Control Flow​

A control flow defines a repeatable acquisition process. Acquisition is the CrackNuts class that manages this.

Below is a basic setup for acquiring AES traces from a CrackerS1 with STM32:

import random
import time
from cracknuts.cracker import serial

cmd_set_aes_enc_key = "01 00 00 00 00 00 00 10"
cmd_aes_enc = "01 02 00 00 00 00 00 10"

aes_key = "11 22 33 44 55 66 77 88 99 00 aa bb cc dd ee ff"
aes_data_len = 16
sample_length = 1024 * 20

def init(c):
cracker.nut_voltage_enable()
cracker.nut_voltage(3.4)
cracker.nut_clock_enable()
cracker.nut_clock_freq('8M')
cracker.uart_enable()
cracker.osc_sample_clock('48m')
cracker.osc_sample_length(sample_length)
cracker.osc_trigger_source('N')
cracker.osc_analog_gain('B', 10)
cracker.osc_trigger_level(0)
cracker.osc_trigger_mode('E')
cracker.osc_trigger_edge('U')
cracker.uart_config(
baudrate=serial.Baudrate.BAUDRATE_115200,
bytesize=serial.Bytesize.EIGHTBITS,
parity=serial.Parity.PARITY_NONE,
stopbits=serial.Stopbits.STOPBITS_ONE
)

time.sleep(2)
cmd = cmd_set_aes_enc_key + aes_key
status, ret = cracker.uart_transmit_receive(cmd, timeout=1000, rx_count=6)

def do(c):
plaintext_data = random.randbytes(aes_data_len)
tx_data = bytes.fromhex(cmd_aes_enc.replace(' ', '')) + plaintext_data
status, ret = cracker.uart_transmit_receive(tx_data, rx_count=6 + aes_data_len, is_trigger=True)

return {
"plaintext": plaintext_data,
"ciphertext": ret[-aes_data_len:],
"key": bytes.fromhex(aes_key)
}

acq = cn.new_acquisition(cracker, do=do, init=init)

This defines init (setup) and do (acquire) methods that operate with the cracker object.

Cracker Acquisition Control Panel​

Display the Cracker acquisition control panel by running:

cn.panel(acq)

Result:

alt text

Capture Traces​

In the panel, after connecting, device info will be displayed. Use the "Test" button to preview waveform acquisition (ensure monitoring is enabled):

alt text

Once parameters are set (Golden firmware defaults should work), stop the test and click “Run” to start acquisition and save the trace data:

alt text

Congratulations! You’ve successfully captured AES power traces using CrackNuts.

tip

The default Nut (stm32_f103c8) includes pre-flashed HSI.elf. Additional firmwares can be downloaded from:
https://pan.baidu.com/s/1PXyKqeTfemepZ-wD9gDwYQ?pwd=2cda (See NutGolden folder).

Using the Trace Data​

Captured traces are stored using the scarr format, based on zarr with CrackNuts metadata.

Format structure:

  • traces:
    • directory.zarr/X/Y/traces
  • metadata:
    • directory.zarr/X/Y/ciphertext
    • directory.zarr/X/Y/plaintext
    • (optional) directory.zarr/X/Y/key
    • (optional) directory.zarr/X/Y/extended

Saved trace files are timestamped and located under a dataset folder in the notebook's root. Load them using:

Option 1: CrackNuts Interface​

from cracknuts.trace import ScarrTraceDataset
ds = ScarrTraceDataset.load(r'dataset\20250512163354.zarr')

Visualize using:

import cracknuts as cn
pt = cn.panel_trace()
pt.set_trace_dataset(ds)
pt

alt text

Control displayed index and range:

pt.show_trace[0, 10:20]  # Channel 0, trace 10–19
pt.change_range(400, 1000) # Samples 400–999

Option 2: Directly with zarr​

import zarr

zd = zarr.open(r'D:\project\cracknuts\demo\jupyter\dataset\20250515165503.zarr')

zd.tree()

This reveals the data structure:

alt text