Problem while measuring traces on Ascon cipher

Hi everyone,
I’m trying to perform CPA and DPA on the Ascon cipher (I modified the reference implementation for it to fit on the chipwhisperer lite).
I didn’t get any good result so I decided to do some SPA to spot some of the operations.

For this I tried reducing the number of rounds of the Ascon SPN (12, 6, 3 and 1).
When I output the tag, I get a different one for each of them, probably showing that the modification are working. However, the power traces are always the same. I think I’m not measuring them the right away.

Here is the Python code I use:

f=open("traces_P12.dat", "w")
    
scope.adc.samples = 24000
N =100
for i in tnrange(N, desc='Capturing traces'):
    
    scope.arm()
    target.simpleserial_write('k', bytearray(key_bytearray))
    target.simpleserial_wait_ack()
    target.simpleserial_write('a', bytearray(nonce_bytearray[i]))
    ret = scope.capture()
    if ret:
        print("Target timed out!")
        continue
    
    response = target.simpleserial_read('r', 16) 
   
    trace=scope.get_last_trace()
    trace_array.append(trace)
    for value in trace:
        f.write(str(value))
        f.write(",")
    f.write("\n")
    
trace_array = np.array(trace_array)
f.close()

And here are the important functions of the C code I use:

uint8_t ascon_encrypt_own(uint8_t* n, uint8_t len)
{
    // Load nonce here
    for (int i=0;i<nonce_array_size;i++){
        nonce[i]=n[i];
    }
 
    trigger_high();
    crypto_aead_encrypt(tag, &tag_array_size,
                        NULL, 0,
                        NULL, 0,
                        NULL, nonce,
                        key);
    trigger_low();
    simpleserial_put('r', 16, tag);
    return 0;

}

int main(void)
{
    platform_init();
	init_uart();
	trigger_setup();

	simpleserial_init();
#if SS_VER != SS_VER_2_0
	simpleserial_addcmd('k', 16, get_key);
    simpleserial_addcmd('a', 16, ascon_encrypt_own);
    simpleserial_addcmd('x', 0, reset);
   
#endif
	while(1)
		simpleserial_get();
}


Is there somthing wrong in the way I measure the traces ?
Thanks in advance

I can’t tell what’s going on from this. Can you show us the traces?
One possibility is that you’re not capturing enough samples, so you’re never capturing the full operation. What are your scope.adc settings, and what does scope.adc.trig_count show after you run the target operation for each of the number of rounds settings?
Jean-Pierre

Sure, here are the plots for each of them:

I haven’t specified any scope.adc settings, so I think it might take the last one I used during one of the labs. Which ones should I specify ?

I unfortunately don’t know how to use the scope.adc.trig_count method. Is it in the Python code or in the C firmware code that I should put that line ?
Python is giving me the error : ‘int’ object is not callable.

Just run scope.adc.trig_count in Jupyter, after running your target operation.
This will report how many cycles the trigger line was high for.
Also, show me the output of running scope.adc (again in Jupyter).

thank you very much.

For 12 rounds:
state = False
basic_mode = rising_edge
timeout = 2
offset = 0
presamples = 0
samples = 24000
decimate = 1
trig_count = 266968
fifo_fill_mode = normal

For 6:
state = False
basic_mode = rising_edge
timeout = 2
offset = 0
presamples = 0
samples = 24000
decimate = 1
trig_count = 145624
fifo_fill_mode = normal

For 3:
state = False
basic_mode = rising_edge
timeout = 2
offset = 0
presamples = 0
samples = 24000
decimate = 1
trig_count = 84952
fifo_fill_mode = normal

And for 1 I get:
state = False
basic_mode = rising_edge
timeout = 2
offset = 0
presamples = 0
samples = 24000
decimate = 1
trig_count = 44504
fifo_fill_mode = normal

So there’s your problem: as I suspected, your target operation is much longer than what you can capture (assuming you have a CW-lite). You’re only capturing part of the 1st round, so that’s why the power trace looks the same independently of how many rounds were run.

Your options are:
1- get a CW-pro (or Husky when it becomes available), which can capture more samples (this is the easiest solution but obviously more costly)
2- use scope.adc.decimate = x to capture one out of every x samples (this will almost certainly prevent side-channel attacks from succeeding, but at least you’ll be able to see the full waveform)
3- You can capture the full waveform in segments: capture the first 24000 samples, then set scope.adc.offset = 24000 and capture again to get the next 24000 samples, etc… until you’ve assembled the full waveform. This will only work if your target does exactly the same thing every time when given the same inputs. If you’re not sure whether that’s the case, you can use #2 to capture the complete operation and get a pretty good idea.
4- If you’re sampling at 4x, change to scope.clock.adc_src = clkgen_x1 to get an immediate 4x increase in how much of the target operation you can capture in one go. (Most attacks don’t need x4 sampling to succeed.)

It may also be that you attack doesn’t need the full waveform. However it can be hard to determine that without having the full waveform! So unless you have access to a Pro, I would start with #3. Once you develop a working attack, you may find that you don’t need the full waveform.

Hope this helps,
Jean-Pierre

Thanks you so. much for all these information.

I will follow your advices and start with #3 by making sure to give the target the same inputs everytime.
You may be right about the window once I know where to target.

Thanks again for everything and have a great evening

Best wishes
Nathan