Using an external oscilloscope with CW Lite UFO boards


I’m trying to converge to using an external oscilloscope to capture traces for SCA (it’ll enable me to do more advanced attacks and also become more independent). As a first step I’m capturing traces parallel with the CW Lite, and using the UFO board with the STMF303 shield. My problem is that for the traces captured with the CW Lite, I can extract all 16 subkeys of the AES-128 using CPA. But when I run the CPA code on the traces captured by the oscilloscope, nothing converges to the correct subkey.

The way I’ve hooked things up is by using the T-connection on the UFO board, where one output goes to the CW Lite, and the other to my LeCroy WaveRunner 8208 oscilloscope. The connection to the scope goes through a 30 dB PA303 amplifier from Langer. The CW clock is HS2/OUT, and the CW Lite and scope are not using the same clock reference (would this really be necesssary?). Furthermore, I’ve connected CH2 of my scope to the trigger pin on the UFO board, so the triggering should be synchronized.

My main routine for collecting traces in Python is this:

time_stamp1 = time.time()

for i in trange(num_of_traces, desc='Capturing traces'):
    key, text =
    key = bytearray([0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF])
    # key = bytearray([0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c])
    # text = bytearray([0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a]) 
    # start LeCroy trigger
    if (start_trigger()==False):
        print("Triggering Error!")
    trace = cw.capture_trace(scope, target, text, key)
    if trace is None:
    trc = get_channel(num_of_samples, 'C1') # Capturing trace from LeCroy oscilloscope

    traces_LeCroy[i,:] = trc
    traces[i,:] = trace.wave
    keys[i,:] = trace.key
    pt[i,:] = trace.textin
    ct[i,:] = trace.textout

time_stamp2 = time.time()-time_stamp1
print("\n\nTime elapsed: {:.2f} seconds".format(time_stamp2))
print("Rate of trace collection: {:.2f} tr/s".format(num_of_traces/time_stamp2))

keys = np.uint8(keys)
pt = np.uint8(pt)
ct = np.uint8(ct)
for i in range(1):
    print("\nTrace {}:".format(i))
    print("\tKey: \t\t\t{}".format(bytearray(keys[i,:]).hex(' ')))
    print("\tPlaintext: \t\t{}".format(bytearray(pt[i,:]).hex(' ')))
    print("\tCiphertext: \t{}".format(bytearray(ct[i,:]).hex(' ')))

Here is a screenshot from the scope for one of the trace captures:

The purple signal (CH2) is the trigger signal from the UFO board, and the mustard coloured signal is the trace. At least to me it looks like the AES-128 rounds are quite visible. However, as mentioned in the beginning, I am not able to extract any of the subkeys from the LeCroy traces using CPA, whereas the CW Lite traces converges to the correct subkeys after 30-40 traces.

This is my first time trying to do this on anything else other than the CW Lite, so I’d appreaciate any feedback as to what might be wrong. My LeCroy scope has a very accurate 10 MHz input/ouput reference clock, in case I need to synchronize something, but is that really necessary? The scope samples at 100 MS/s and collects 200 kS of data with a 12-bit resolution.


Use your scope’s highest sampling rate, and use more traces.
With 100MS/s asynchronous sampling, you can expect to require a lot more traces:

Thank you. I will read the paper provided and try what you say about maximizing the sampling frequency and use a lot more traces in the CPA. Just a newbie question: would it help if I used the 10 MHz output timebase reference from the scope as external clock on the UFO board? According to the spec sheet the output is “10 MHz, 5 dBm +/- 2.5 dBm, sinewave synchronized to reference being used (internal or external reference)”. Could this even be used on the UFO board?

You can try and see – but that would defeat the purpose of learning to attack targets that can’t be sampled synchronously.
If you do have the ability to drive the target’s clock, using CW to supply that clock is probably your best bet, but I don’t have any experience with that particular oscilloscope.

Thank you. If I try to drive the target with the 10 MHz reference signal from the scope, what settings do I need to set in the Python code?

Assuming your target is running SimpleSerial at 7.37 MHz, you’ll have to scale its baud rate (i.e. increase target.baud by a factor of 10/7.37); and disable the HS2 output clock ( = 'disabled')

And if I decide you try the external crystal oscillator that came with the UFO board, I just have to switch jumper J3 to CRYSTAL, and no changes in the Python code?

Correct – if you’re not using CW to collect power traces.
If you are, then you’ll also need to jumper HS1/in on J3, so that the clock is also routed to CW, and set the CW adc clock source accordingly (scope.clock.adc_src = 'extclk_<x1|x4|dir>').

I’m doing something wrong here. I get the following error message when I try to capture traces:

The way I set things up in Python was this:

ktp = cw.ktp.Basic()
ktp.fixed_key = True 
ktp.fixed_text = False 
scope.adc.samples = 5000
scope.clock.adc_src = 'extclk_x1' 
scope.adc.offset = 0

On the UFO board I have (of course) inserted the crystal oscillator that came with the board, and have jumpers on J3 in positions CRYSTAL (routed to CLKIN), and HS1/IN (routed to CLKFB.


From the error you can see that somehow adc_freq is zero. What does scope.clock say?

It returns this:


This is telling you that CW isn’t getting a clock. You need to provide it on the HS1 pin.
Also, set scope.clock.extclk_freq to the external clock frequency.
Finally, if after fixing that you still have scope.clock.adc_locked = False, then do scope.clock.reset_adc().

Thank you, I will try that once I’m back in the office on Monday.

What’s the frequency of the external oscillator that came with the UFO board? I can’t seem to find the info anywhere. I Googled what’s typed on the oscillator, but I get two hits, and nothing useful.

It should be 7.37 MHz.


Thank you, and thank you for the advice of jacking up the sampling rate and number of traces. So I did CPA on the first 3 sub-bytes of AES-128 and this was the result:

This was conducted using the external crystal on the UFO board, but asynchronously with the oscilloscope. I sampled with 2.5 GS/s. Now this is not the maximum sampling rate of the oscilloscope, which is 10 GS/s, and a memory depth of 5 Gpt. The reason is that the amount of data becomes very large for my setup. For this experiment the data size became almost 30 GB, and since I’m not very good at programming, it was all stored in one .h5 file, and so when I load it into Python as one trace variable it becomes rather large compared to my poor laptop’s RAM of 32 GB. So I should look into how to perform CPA on chunks of data at a time, in order to facilitate much large data sizes. Do you happen to have any code examples that does this, or articles which describe how this type of calculations are done? Can you even do CPA on, say, 10 GB of data at a time? (as mentioned, I’m not the best of programmers).

Another question: I haven’t experimented much with this, but if I have to juggle with the sampling rate and the number of traces to get below a certain data size, which one is most important for better performance?


Hi again,

I also have another question related to this. I want to be able to do synchronous sampling, where the reference clock source is from my oscilloscope (10 MHz). First of all, I’m not sure if I have connected things correctly on J3. The clock is connected to pin 2, and then I have a jumper on HS1 (pins 3 and 4). And here’s my relevant code:

scope.clock.adc_src = 'extclk_x1' 
scope.clock.extclk_freq = 10000000 = 'disabled' 
target.baud = target.baud*(10/7.37)

The output of scope.clock is


But the ouput when trying to capture traces is


First of all, I’m not sure if my way of changing target.baud is a good way of doing it (suggestion for how to do this properly is received with great thanks). But any idea what in my connection or clock setup might be wrong?


Our own ChipWhisperer analyzer code does CPA progressively, processing a small number of traces at a time. However it’s not optimized for performance, and there are faster analysis options out there like LASCAR and SCAred. If you look through our set of courses you’ll see examples using these.

I don’t think there is a one-size-fits-all answer to that, unfortunately.

That part is fine.

As I mentioned before, I don’t know that the target can be clocked from your scope’s 5 dBm sine wave. Maybe it can’t.

Thank you! I’ll have a look at the CW courses that are using LASCAR and SCARED.