Voltage Glitch attack using volatile and non volatile variables

hello

Hardware Used: CWLITEARM + CW308_STM32F303

I have been trying to reset the nonce_val in between the two cbd function calls. I had initially defined nonce_val using volatile which gave a successful result (probably due to the longer attack duration given by ldr adds and str ). Now that I remove the volatile definition, this doesn’t seem to work at all. I am not sure if the width and offset setting have changed or not but the ext_offset has definitely changed as seen the assembly.
If i am doing anything wrong please do correct me. i am still learning to use this platform and want to extend it beyond the tutorials. the files i am sending now are the updated files (without volatile)

you can see the jupyter notebook i have used here: reference to my notebook

Thanks

Rohit

The glitch setting which worked for me then (when I defined nonce_val with volatile) were:

scope.glitch.output = "glitch_only"   
scope.glitch.trigger_src = "ext_single"
scope.io.hs2 = "clkgen" 
// trig_count= 53492 {as found through scope.adc.trig_count}
gc.set_range("width", 1.4, 2.2)
gc.set_step("width", 0.1)            # fine resolution(it may have worked without the fine setting)
gc.set_range("offset", -5, -3)    
gc.set_step("offset", 0.4)
gc.set_range("ext_offset", trig_count//8 - 500, trig_count//8 + 300)
gc.set_step("ext_offset", 1)

ss-glitch.c


#include "hal.h"
#include <stdint.h>
#include <stdlib.h>

#include "simpleserial.h"
#include "cbd_dummy.h" //to mimic the polycbd kind of call from indcpa.h // required to prevent loop unrolling and simplification

//uint8_t infinite_loop(uint8_t* in);
//uint8_t glitch_loop(uint8_t* in);
//uint8_t password(uint8_t* pw);

// Make sure no optimization happens for demo glitch logic.
// #pragma GCC push_options
// #pragma GCC optimize ("O0")


#if SS_VER == SS_VER_2_1
uint8_t glitch_loop(uint8_t cmd, uint8_t scmd, uint8_t len, uint8_t* in)
#else
uint8_t glitch_loop(uint8_t* in, uint8_t len)
#endif
{
    uint16_t i;
    uint32_t nonce_val = 0;
    trigger_high();

    //k=2 for mlkem512
    for(i=0; i<2; i++){
        //mimicking the cbd operation
        cbd_dummy(nonce_val) ;
        nonce_val++;
    }
    for(i=0; i<2; i++){
        //mimicking the cbd operation
        cbd_dummy(nonce_val) ;
        nonce_val++;
    }
    trigger_low();
    simpleserial_put('r', 4, (uint8_t*)&nonce_val);
#if SS_VER == SS_VER_2_1
    return (nonce_val == 2) ? 0x10 : (nonce_val ==4)? 0x00 : (nonce_val>2)? 0xe0:0xf0; //==2 means glitch has occurred in between the for loops and nonce_val is  reset to 0
#else
    return (nonce_val != 4);
#endif
}

#if SS_VER == SS_VER_2_1
uint8_t glitch_comparison(uint8_t cmd, uint8_t scmd, uint8_t len, uint8_t* in)
#else
uint8_t glitch_comparison(uint8_t* in, uint8_t len)
#endif
{
    uint8_t ok = 5;
    trigger_high();
    if (*in == 0xA2){
        ok = 1;
    } else {
        ok = 0;
    }
    trigger_low();
    simpleserial_put('r', 1, (uint8_t*)&ok);
    return 0x00;
}

// #pragma GCC pop_options

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

    /* Device reset detected */
    putch('r');
    putch('R');
    putch('E');
    putch('S');
    putch('E');
    putch('T');
    putch(' ');
    putch(' ');
    putch(' ');
    putch('\n');

    simpleserial_init();
    simpleserial_addcmd('g', 0, glitch_loop);
    simpleserial_addcmd('c', 1, glitch_comparison);
    while(1)
        simpleserial_get();
}

cbd_dummy.c

#include <stdint.h>

// defined in separate TU so compiler cannot inline or optimize away
void __attribute__((noinline)) cbd_dummy(uint32_t nonce) {
    volatile uint16_t s_val;
    for(s_val = 0; s_val < 256; s_val++) continue;
}

makefile


TARGET = ss-glitch

# List C source files here.
# Header files (.h) are automatically pulled in.
SRC += ss-glitch.c
SRC += cbd_dummy.c

# -----------------------------------------------------------------------------

CRYPTO_TARGET=NONE

#Add simpleserial project to build
include ../simpleserial/Makefile.simpleserial

FIRMWAREPATH = ../.
include $(FIRMWAREPATH)/Makefile.inc


the assembly code concerning the glitch loop:

08000260 <glitch_loop>:
#if SS_VER == SS_VER_2_1
uint8_t glitch_loop(uint8_t cmd, uint8_t scmd, uint8_t len, uint8_t* in)
#else
uint8_t glitch_loop(uint8_t* in, uint8_t len)
#endif
{
 8000260:	b513      	push	{r0, r1, r4, lr}
    uint16_t i;
    uint32_t nonce_val = 0;
 8000262:	2400      	movs	r4, #0
 8000264:	9401      	str	r4, [sp, #4]
    trigger_high();
 8000266:	f000 fa57 	bl	8000718 <trigger_high>

    //k=2 for mlkem512
    for(i=0; i<2; i++){
        //mimicking the cbd operation
        cbd_dummy(nonce_val) ;
 800026a:	9801      	ldr	r0, [sp, #4]
 800026c:	f000 f87a 	bl	8000364 <cbd_dummy>
        nonce_val++;
 8000270:	9801      	ldr	r0, [sp, #4]
 8000272:	3001      	adds	r0, #1
 8000274:	9001      	str	r0, [sp, #4]
        cbd_dummy(nonce_val) ;
 8000276:	f000 f875 	bl	8000364 <cbd_dummy>
        nonce_val++;
 800027a:	9801      	ldr	r0, [sp, #4]
 800027c:	3001      	adds	r0, #1
 800027e:	9001      	str	r0, [sp, #4]
    }
    for(i=0; i<2; i++){
        //mimicking the cbd operation
        cbd_dummy(nonce_val) ;
 8000280:	f000 f870 	bl	8000364 <cbd_dummy>
        nonce_val++;
 8000284:	9801      	ldr	r0, [sp, #4]
 8000286:	3001      	adds	r0, #1
 8000288:	9001      	str	r0, [sp, #4]
        cbd_dummy(nonce_val) ;
 800028a:	f000 f86b 	bl	8000364 <cbd_dummy>
        nonce_val++;
 800028e:	9b01      	ldr	r3, [sp, #4]
 8000290:	3301      	adds	r3, #1
 8000292:	9301      	str	r3, [sp, #4]
    }
    trigger_low();
 8000294:	f000 fa47 	bl	8000726 <trigger_low>
    simpleserial_put('r', 4, (uint8_t*)&nonce_val);
 8000298:	aa01      	add	r2, sp, #4
 800029a:	2104      	movs	r1, #4
 800029c:	2072      	movs	r0, #114	@ 0x72
 800029e:	f000 f8d3 	bl	8000448 <simpleserial_put>
#if SS_VER == SS_VER_2_1
    return (nonce_val == 2) ? 0x10 : (nonce_val ==4)? 0x00 : (nonce_val>2)? 0xe0:0xf0; //==2 means glitch has occurred in between the for loops and nonce_val is  reset to 0
 80002a2:	9b01      	ldr	r3, [sp, #4]
 80002a4:	2b02      	cmp	r3, #2
 80002a6:	d007      	beq.n	80002b8 <glitch_loop+0x58>
 80002a8:	2b04      	cmp	r3, #4
 80002aa:	d007      	beq.n	80002bc <glitch_loop+0x5c>
 80002ac:	2b03      	cmp	r3, #3
 80002ae:	bf34      	ite	cc
 80002b0:	20f0      	movcc	r0, #240	@ 0xf0
 80002b2:	20e0      	movcs	r0, #224	@ 0xe0
#else
    return (nonce_val != 4);
#endif
}
 80002b4:	b002      	add	sp, #8
 80002b6:	bd10      	pop	{r4, pc}
    return (nonce_val == 2) ? 0x10 : (nonce_val ==4)? 0x00 : (nonce_val>2)? 0xe0:0xf0; //==2 means glitch has occurred in between the for loops and nonce_val is  reset to 0
 80002b8:	2010      	movs	r0, #16
 80002ba:	e7fb      	b.n	80002b4 <glitch_loop+0x54>
 80002bc:	4620      	mov	r0, r4
 80002be:	e7f9      	b.n	80002b4 <glitch_loop+0x54>

volatile will change the assembly generated by the compiler; you can compare the assembly between the two cases to see what changes in your case.

Typically, successful scope.glitch.width and scope.glitch.offset settings will carry over; however the vulnerable instruction that you successfully glitched in the volatile case may be have moved to a different clock cycle and so you will need to adjust scope.glitch.ext_offset accordingly.

Hey @jpthibault .

I did check the instructions in assembly. when using volatile, there was loop unrolling which ensured there were 4 nonce_value memory load, add 1 and store back.

When i removed the volatile keyword, it seems like some optimisation is done which leads to the above assembly code being generated.

even i was convinced of the same and used a similar glitch setting and tried finding the the right scope.glitch.ext_offset since like you mentioned the updation of the internal variable would have changed its time of execution.

i initially tried running a complete sweep through all the adc sample points collected (57676). logically speaking the second nonce_value updation must occur at around half of the the points collected. i tried updating the other parameter like repeat and tries as well but didn’t get anything different.

also i just want to be sure about these settings:

print("baud rate:", target.baud)
print("clkgen_freq:", scope.clock.clkgen_freq)
print("adc_freq:", scope.clock.adc_freq)
print("adc.samples:", scope.adc.samples)
print("adc.trig_count:", scope.adc.trig_count)

----------
OUTPUT
----------
baud rate: 230400
clkgen_freq: 7384615.384615385
adc_freq: 29538459
adc.samples: 24000
adc.trig_count: 57676

I am unable to think what is possibly going wrong.

Thanks and best regards,

Rohit

I don’t have an answer for you; I would encourage you to compare the assembly for both cases and try to understand why glitching without volatile may be harder.

I would also note that if you are not getting any glitch effects whatsoever (not even a target reset), then you need to expand your search of glitch settings.

I have no way of knowing whether these are right for your target.

Thank you for your help.

I will expand the glitch settings and check out.

P.S. i think i started getting target resets now so it may just work now.

Thanks

Rohit