As of August 2020 the site you are on (wiki.newae.com) is deprecated, and content is now at rtfm.newae.com.

Changes

Jump to: navigation, search

Making Scripts

9,648 bytes added, 21:48, 2 March 2018
Added the example scripts and a description of each
== Scripting with ChipWhisperer as a python module ==
When used without the GUI, the 4.0 API removes much of the high level abstractions so you can have more control over the capture process. It also answers questions like--when I capture a trace in what order are things happening? The following example scripts will give you a starting point when scripting with the ChipWhisperer tool. All these examples on this page can be found in their home <code>chipwhisperer/software/scripting-examples</code>. 
=== Perform Some Traces during AES encryption and get the results as Numpy array ===
This script is an example of using the <code>chipwhisperer</code> module for capturing traces during AES encryption. <b>Make sure to have the correct firmware loaded on the target.</b>These traces are then saved and loaded later for analysis. 
<syntaxhighlight lang=python>
from __future__ import division, print_function
 
import time
import os
import numpy as np
import matplotlib.pyplot as plt
from datetime import datetime
from tqdm import tqdm
 
import chipwhisperer as cw
from chipwhisperer.capture.acq_patterns.basic import AcqKeyTextPattern_Basic
from chipwhisperer.tests.tools_for_tests import matplotlibFIRMWARE_DIRfrom chipwhisperer.pyplot as pltcapture.api.programmers import XMEGAProgrammer
scope = cw.scope()
scope.io.tio2 = "serial_tx"
scope.io.hs2 = "clkgen"
 
# program the target
xmega = XMEGAProgrammer()
xmega.setUSBInterface(scope.scopetype.dev.xmega)
xmega._logging = None
xmega.find()
xmega.erase()
glitch_simple_firmware_dir = os.path.join(FIRMWARE_DIR, 'simpleserial-aes')
glitch_simple_hex = os.path.join(glitch_simple_firmware_dir, r"simpleserial-aes-CW303.hex")
xmega.program(glitch_simple_hex, memtype="flash", verify=True)
xmega.close()
ktp = AcqKeyTextPattern_Basic(target=target)
traces = []
textin = []keys = []N = 50 # Number of tracesfor i in tqdm(range(N), desc='Capturing traces'): # run aux stuff that should come before trace here  key, text = ktp.newPair() # manual creation of a key, text pair can be substituted here textin.append(text) keys.append(key)
target.reinit()
target.setModeEncrypt() # only does something for targets that support it
target.loadEncryptionKey(key)
target.loadInput(text)
target.go()
timeout=50
# wait for target to finish
while target.isDone() is False and timeout:
traces.append(scope.getLastTrace())
trace_array = np.asarray(traces) # if you prefer to work with numpy array for number crunching
textin_array = np.asarray(textin)
known_keys = np.asarray(keys) # for fixed key, these keys are all the same
trace_array now = npdatetime.asarraynow()fmt_string = '{:02}{:02}_{}.npy'trace_file_path = fmt_string.format(now.hour, now.minute, "traces") textin_file_path = fmt_string.format(now.hour, now.minute, "textins")keys_file_path = fmt_string.format(now.hour, now.minute, "keys") print('Saving results to {},{} and {}...'.format(trace_file_path, textin_file_path, keys_file_path), end='')# if you prefer save to work with numpy array a files for number crunchinglater processingnp.save(trace_file_path, trace_array)np.save(textin_file_path, textin_array)np.save(keys_file_path, known_keys)print('Done')
# show an example trace
scope.dis()
target.dis()
</syntaxhighlight>
 
=== Manually breaking AES encryption with your recorded traces (As much as scripting is manual) ===
Using the saved traces of the AES encryption you can now break the sub-keys of the encryption key. This script is covered in more detail in [[Tutorial_B6_Breaking_AES_(Manual_CPA_Attack) | Tutorial B6]].
 
<syntaxhighlight lang=python>
from __future__ import division, print_function
 
import numpy as np
from tqdm import tqdm
 
HW = [bin(n).count("1") for n in range(0, 256)]
 
sbox = (
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16)
 
 
def intermediate(pt, keyguess):
return sbox[pt ^ keyguess]
 
# put the actual file names in here
keys = np.load('1147_keys.npy')
textins = np.load('1147_textins.npy')
traces = np.load('1147_traces.npy')
 
knownkey = keys[0] # for fixed key they are all the same
pt = textins
numtraces = np.shape(traces)[0]
numpoint = np.shape(traces)[1]
 
bestguess = [0] * 16
pge = [256] * 16
for bnum in tqdm(range(0, 16), desc='Attacking subkeys'):
cpaoutput = [0] * 256
maxcpa = [0] * 256
for kguess in range(0, 256):
 
# Initialize arrays &amp; variables to zero
sumnum = np.zeros(numpoint)
sumden1 = np.zeros(numpoint)
sumden2 = np.zeros(numpoint)
 
hyp = np.zeros(numtraces)
for tnum in range(0, numtraces):
hyp[tnum] = HW[intermediate(pt[tnum][bnum], kguess)]
 
# Mean of hypothesis
meanh = np.mean(hyp, dtype=np.float64)
 
# Mean of all points in trace
meant = np.mean(traces, axis=0, dtype=np.float64)
 
# For each trace, do the following
for tnum in range(0, numtraces):
hdiff = (hyp[tnum] - meanh)
tdiff = traces[tnum, :] - meant
 
sumnum = sumnum + (hdiff * tdiff)
sumden1 = sumden1 + hdiff * hdiff
sumden2 = sumden2 + tdiff * tdiff
 
cpaoutput[kguess] = sumnum / np.sqrt(sumden1 * sumden2)
maxcpa[kguess] = max(abs(cpaoutput[kguess]))
 
bestguess[bnum] = np.argmax(maxcpa)
 
cparefs = np.argsort(maxcpa)[::-1]
 
# Find PGE
pge[bnum] = list(cparefs).index(knownkey[bnum])
 
print("Best Key Guess: ", end="")
for b in bestguess: print("%02x " % b, end="")
 
print("")
print("PGE: ", end="")
for b in pge: print("%02d " % b, end="")
</syntaxhighlight>
 
=== Exploring glitches ===
This script shows an example of using the ChipWhisperer tool for performing glitch attacks on a target executing code. This script has similar functionality of the glitch explorer in the GUI but exposes more the insides of the ChipWhisperer tool. This script varies the offset and the width percentage of the glitch applied to the target during code execution. More details about glitching can be found in [[Tutorial_A2_Introduction_to_Glitch_Attacks_(including_Glitch_Explorer) | Tutorial A2]]
 
<syntaxhighlight lang=python>
from __future__ import print_function, division
 
import time
import logging
import os
from collections import namedtuple
 
import numpy as np
 
import chipwhisperer as cw
from chipwhisperer.tests.tools_for_tests import FIRMWARE_DIR
from chipwhisperer.capture.api.programmers import XMEGAProgrammer
from scripting_utils import GlitchResultsDisplay
 
logging.basicConfig(level=logging.WARN)
scope = cw.scope()
target = cw.target(scope)
 
# program the XMEGA with the built hex file
xmega = XMEGAProgrammer()
xmega.setUSBInterface(scope.scopetype.dev.xmega)
xmega._logging = None
xmega.find()
xmega.erase()
glitch_simple_firmware_dir = os.path.join(FIRMWARE_DIR, 'glitch-simple')
glitch_simple_hex = os.path.join(glitch_simple_firmware_dir, r"glitchsimple-CW303.hex")
xmega.program(glitch_simple_hex, memtype="flash", verify=True)
xmega.close()
 
# setup parameters needed for glitch the XMEGA
scope.glitch.clk_src = 'clkgen'
 
scope.gain.gain = 45
scope.adc.samples = 3000
scope.adc.offset = 0
scope.adc.basic_mode = "rising_edge"
scope.clock.clkgen_freq = 7370000
scope.clock.adc_src = "clkgen_x4"
scope.trigger.triggers = "tio4"
scope.io.tio1 = "serial_rx"
scope.io.tio2 = "serial_tx"
scope.io.hs2 = "glitch"
 
target.go_cmd = ""
target.key_cmd = ""
 
# format output table
headers = ['target output', 'width', 'offset', 'success']
glitch_display = GlitchResultsDisplay(headers)
 
# set glitch parameters
# trigger glitches with external trigger
scope.glitch.trigger_src = 'ext_single'
scope.glitch.repeat = 105
 
traces = []
outputs = []
widths = []
offsets = []
 
# named tuples to make it easier to change the scope of the test
Range = namedtuple('Range', ['min', 'max', 'step'])
width_range = Range(-10, 10, 4)
offset_range = Range(-10, 10, 4)
 
# glitch cycle
scope.glitch.width = width_range.min
while scope.glitch.width < width_range.max:
scope.glitch.offset = offset_range.min
while scope.glitch.offset < offset_range.max:
# call before trace things here
 
# resets the target for the next glitch cycle
# similar to Check Signature button in GUI
xmega.find()
xmega.close()
 
target.reinit()
# call target functions here, setModeEncrypt...
 
# run aux stuff that should run before the scope arms here
 
scope.arm()
 
# run aux stuff that should run after the scope arms here
 
target.go()
timeout = 50
# wait for target to finish
while target.isDone() is False and timeout:
timeout -= 1
time.sleep(0.01)
 
try:
ret = scope.capture()
if ret:
logging.warning('Timeout happened during acquisition')
except IOError as e:
logging.error('IOError: %s' % str(e))
 
# get the results from the scope
trace = scope.getLastTrace()
# read from the targets buffer
output = target.ser.read(target.output_len * 2, timeout=1000)
traces.append(trace)
outputs.append(output)
widths.append(scope.glitch.width)
offsets.append(scope.glitch.width)
 
# for table display purposes
success = '1234' in repr(output) # check for glitch success (depends on targets active firmware)
glitch_display.add_data([repr(output), scope.glitch.width, scope.glitch.offset, success])
 
# run aux stuff that should happen after trace here
scope.glitch.offset += offset_range.step
scope.glitch.width += width_range.step
 
traces = np.asarray(traces)
# the rest of the data is available with the outputs, widths, and offsets lists
glitch_display.display()
print('Done')
</syntaxhighlight>

Navigation menu