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

Tutorial B11 Breaking RSA

8,598 bytes removed, 13:26, 29 July 2019
no edit summary
== RSA Attack Theory ==We won't go into what RSA is, see the [[wikipedia:RSA_(cryptosystem){{Warningbox|RSA Wikipedia]] article This tutorial has been updated for a quick backgroundChipWhisperer 5 release. What we really care about is If you are using 4.x.x or 3.x.x see the following pieces of sudocode from that article used "V4" or "V3" link in decrypting the sidebar.}}{{Warningbox|This tutorial works differently than in V4, now utilizing a message:fault attack instead of a power analysis attack.}}
<syntaxhighlight lang{{Infobox tutorial|name ="C">B11: Breaking RSA/**|image = * Decrypt|caption = *|software versions = * @param {c} int / bigInt: the 'message' to be decoded (encoded with RSA.encrypt())|capture hardware = * @param {d} int / bigInt: d value returned from RSA.generate() aka private key|Target Device = * @param {n} int / bigInt: n value returned from RSA.generate() aka public key (part I)|Target Architecture = Arm * @returns {bigInt} decrypted message|Hardware Crypto = No */RSA.decrypt |Purchase Hardware = function(c, d, n){ return bigInt(c).modPow(d, n); };}</syntaxhighlight!-- To edit this, edit Template:Tutorial_boilerplate -->{{Tutorial boilerplate}}
The most critical piece of information is that value * Jupyter file: ''d''Fault_5-RSA_Fault_Attack. It contains the private key, which if leaked would mean a compromise of the entire system. So letipynb''s assume we can monitor a target device while it decrypts any message (we don't even care what the message is). Our objective is to recover d.
Let's consider our actual target code, which will be the RSA implementation in avr-crypto-lib. This has been copied over to be part of the ChipWhisperer repository, and you can see the implementation [https://github.com/newaetech/chipwhisperer/blob/master/hardware/victims/firmware/crypto/avrcryptolib/rsa/rsa_basic.c#L163|in rsa_basic.c of rsa_dec()]. The function in question looks like this:
<syntaxhighlight lang="c">uint8_t rsa_dec(bigint_t* data, const rsa_privatekey_t* key){ if(key->n =XMEGA Target = 1){ bigint_expmod_u(data, data, &(key->components[0]), &key->modulus); return 0; } if(key->n == 5){ if (rsa_dec_crt_mono(data, key)){ return 3; } return 0; } if(key->n<8 || (key->n-5)%3 != 0){ return 1; } //rsa_dec_crt_multi(data, key, (key->n-5)/3); return 2;}</syntaxhighlight>
We'll consider the case where ''key->n == 5'', so we have the ''rsa_dec_crt_mono()'' to attackThis tutorial is not available for XMEGA targets. You can see that function [https://github.com/newaetech/chipwhisperer/blob/master/hardware/victims/firmware/crypto/avrcryptolib/rsa/rsa_basic.c#L53|at Line 53 of that same file]. I've removed all the debug code in the following so you can better see the program flow:
<syntaxhighlight lang="c">uint8_t rsa_dec_crt_mono(bigint_t* data, const rsa_privatekey_t* key){ bigint_t m1, m2; m1.wordv = malloc((keyChipWhisperer->components[0].length_B Lite ARM /* + 1 */) * sizeof(bigint_word_t)); m2.wordv STM32F3 Target == malloc((key->components[1].length_B /* + 1 */) * sizeof(bigint_word_t)); if(!m1.wordv || !m2.wordv){ //Out of memory error free(m1.wordv); free(m2.wordv); return 1; } bigint_expmod_u(&m1, data, &(key->components[2]), &(key->components[0])); bigint_expmod_u(&m2, data, &(key->components[3]), &(key->components[1])); bigint_sub_s(&m1, &m1, &m2); while(BIGINT_NEG_MASK & m1.info){ bigint_add_s(&m1, &m1, &(key->components[0])); }
bigint_reduceSee the following for using:* ChipWhisperer-Lite 32-bit (&m1, &(key->components[0])STM32F3 Target); bigint_mul_u(data, &m1, &(key* ChipWhisperer->components[4])); bigint_reduce(data, &Lite Capture + STM32F3 Target on UFO Board (keyincluding NAE->components[0]SCAPACK-L1/L2 users)); bigint_mul_u(data, data, &(key* ChipWhisperer->components[1])); bigint_add_u(data, data, &m2); free(m2.wordv); free(m1.wordv); return 0;}</syntaxhighlight>Pro + STM32F3 Target on UFO Board
https://chipwhisperer.readthedocs.io/en/latest/tutorials/fault_5-openadc-cwlitearm.html#tutorial-fault-5-openadc-cwlitearm
Note all the calls to <code>bigint_expmod_u()</code> with the private key material. If we could attack that function, all would be lost. These functions are elsewhere - it's in the [https://github.com/newaetech/chipwhisperer/blob/master/hardware/victims/firmware/crypto/avrcryptolib/bigint/bigint.c#L812 bigint.c file at Line 812]. Again we can see the source code here:== ChipWhisperer Nano Target ==
<syntaxhighlight lang="c">oid bigint_expmod_u(bigint_t* dest, const bigint_t* a, const bigint_t* exp, const bigint_t* r){ if(a->length_B==0 || r->length_B==0){ return; }  bigint_t res, base; bigint_word_t t, base_b[MAX(a->length_B,r->length_B)], res_b[r->length_B*2]; uint16_t i; uint8_t j; res.wordv = res_b; base.wordv = base_b; bigint_copy(&base, a); bigint_reduce(&base, r); res.wordv[0]=1; res.length_B=1; res.info = 0; bigint_adjust(&res); if(exp->length_B == 0){ bigint_copy(dest, &res); return; } uint8_t flag = 0; t=exp->wordv[exp->length_B - 1]; for(i=exp->length_B; i > 0; --i){ t = exp->wordv[i - 1]; for(j=BIGINT_WORD_SIZE; j > 0; --j){ if(!flag){ if(t & (1<<(BIGINT_WORD_SIZE-1))){ flag = 1; } } if(flag){ bigint_square(&res, &res); bigint_reduce(&res, r); if(t & (1<<(BIGINT_WORD_SIZE-1))){ bigint_mul_u(&res, &res, &base); bigint_reduce(&res, r); } } t<<=1; } }  SET_POS(&res); bigint_copy(dest, &res);}</syntaxhighlight> Within that file, the part is the loop at the end. This is actually going through and doing the required ''a**exp % r '' function. If you look closely into that loop, you can see there is a variable <code>t</code>, which is set to the value <code>t = exp->wordv[i - 1];</code>. After each run through the loop it is shifted left one. That <code>t</code> variable contains the private key, and the reason it is shifted left is the following piece of code is checking if the MSB is '0' or '1': <syntaxhighlight lang="c">bigint_square(&res, &res);bigint_reduce(&res, r);if(t & (1<<(BIGINT_WORD_SIZE-1))){ bigint_mul_u(&res, &res, &base); bigint_reduce(&res, r);}</syntaxhighlight> What does this mean? While there is data-dependent code execution! If we could determine the program flow, we could simply '''read the private key off one bit at a time'''. This will be our attack on RSA that we perform in this tutorial. == Hardware Setup == == Building Example == == Finding SPA Leakage == We'll now get into experimenting with the SPA leakage. To do so we'll use the "SPA Setup" script, then make a few modifications. Run the SPA setup script. Change the CLKGEN to be CLKGEN x1 via DCM [[File:B11_clkgenx1.png|400px]] Change the length of the trigger to be 24000 samples: [[File:B11_settriglen.png|400px]] If you are using Capture V3.5.2 or later you will have support for the length of the trigger output being high reported back to you. If you run capture-1 for example you'll see the trigger was high for XX cycles: This is way too long! You won't be able to capture the entire trace in your 24000 length sample buffer. Instead we'll make the demo even shorter - in our case looking at the source code you can see there is a "flag" which is set high only AFTER the first 1 is received. Thus using a fixed plaintext, change the input plaintext to be   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 We'll only be able to change the LAST TWO bytes, everything else will be too slow. So change the input plaintext to   00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 And you can see the power trace change drastically, as below: [[File:B11_RSA_example8000.png|400px]] Finally, let's flip another bit. Change the input plaintext as follows, such that bit #4 in the final bit is set HIGH. We can plot the two power traces on top of each other, and you see that they are differing at a specific point in time: [[File:B11_RSA_bit4diff.png|400px]] Walking back from the right, you can see this almost directly matches bit numbering for those last two bytes: [[File:B11_RSA_example8010_annotated.png|400px]]  [[File:B11_plaintext_setting.png|400px]] [[File:B11_acqsetting.png|400px]]  == Automating Attack == The final step is to automate this attack. There are a few ways to do this - we'll demonstrate a basic method, that you can extend to do a complete attack. In this example we're going to use a repeatable sequence, and look not available for the delay between this sequence. If we see a larger delay we know a square-multiply has occurred, otherwise it was only a square. We'll simply define a "reference" sequence, and look for this sequence in the rest of the power trace. The following will be done in regular old Python, so start up your favorite Python editor to finish off the tutorial! Our objectives are to do the following: # Load the trace file. # Find a suitable reference pattern. # Using the reference pattern, find the timing information and break the RSA power trace. Loading the trace can be done with the ChipWhisperer software. Let's first do a few steps to load the data, as follows: <syntaxhighlight lang="python">from chipwhisperer.common.api.CWCoreAPI import CWCoreAPIfrom matplotlib.pylab import *import numpy as np cwapi = CWCoreAPI()cwapi.openProject(r'c:\examplelocation\rsa_test.cwp') tm = cwapi.project().traceManager()ntraces = tm.numTraces() #Reference tracetrace_ref = tm.getTrace(0) plot(trace_ref)</syntaxhighlight> This should plot the example trace which might look something like this: [[File:B11_plotreftrace.png|400px]] So what's a good reference location? This is a little arbitrary, we will just define it as a suitable-sounding piece of information. You could get a reference pattern with something like the following: <syntaxhighlight lang="python">start = 3600rsa_ref_pattern = trace_ref[start:(start+500)]</syntaxhighlight>  Finally, let's compare the reference plot.  <syntaxhighlight lang="python">from chipwhisperer.common.api.CWCoreAPI import CWCoreAPIfrom matplotlib.pylab import *import numpy as np cwapi = CWCoreAPI()cwapi.openProject(r'c:\users\colin\chipwhisperer_projects\tmp\rsa_test_16byte_80.cwp') tm = cwapi.project().traceManager()ntraces = tm.numTraces() #Reference tracetrace_ref = tm.getTrace(0) #plot(trace_ref) start = 3600rsa_one = trace_ref[start:(start+500)] diffs = [] for i in range(0, 22999): diff = tm.getTrace(1)[i:(i+len(rsa_one))] - rsa_one diffs.append(np.sum(abs(diff))) plot(diffs)</syntaxhighlight> The result should be a difference plot. Note the "difference" falls close to zero at a number of times. Depending on which trace you compare this with the exact pattern might vary, but you should see something roughly like the following: [[File:B11_diff_plotNano.png|400px]]
Approved_users, administrator
366
edits

Navigation menu