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

Hey @jpthibault

i tried a complete sweep and was able to get system resets or normal counts only. nothing in between. the width setting of about 30.2 to 30.6 and repeats of 12,13,14 seemed to be the best section where i had about 45% of resets and the rest being normal. i am not sure if i am overlooking anything else.

Thanks

Rohit