def DESDpaCC(self, sboxNum, plot = False): """ Returns DES DPA Confusion coefficents Matrix of size [number of keys x number of keys] #inputs: sboxNum - the sbox number we are targeting (values 1 to 8) plot - if True, will plot the matrix as a histogram """ numPT = 2 ** 6 # number of possible PT/CT numKey = 2 ** 6 # number of possible keys cc = np.zeros((numKey,numKey), np.float) # Confusion Coefficient matrix histogram = [] for ki in range(numKey): for kj in range(numKey): numNotEqual = 0 for ptBlock in range(numPT): sboxOuti = des_block.sbox(sboxNum, ptBlock ^ ki) sboxOutj = des_block.sbox(sboxNum, ptBlock ^ kj) if (self.msb(sboxOuti) != self.msb(sboxOutj)): numNotEqual += 1.0 coefficient = numNotEqual / numPT cc[ki][kj] = coefficient if (ki != kj and ki<kj): histogram.append(coefficient) if (plot): # Plot a histogram of the coefficients weights = np.ones_like(histogram)/len(histogram) fig = plt.hist(histogram, 1000, weights=weights) plt.ylabel('Frequency') plt.xlabel('Confusion coefficient') plt.show(fig) return cc
def DESDpaCC(self, sboxNum, plot=False): """ Returns DES DPA Confusion coefficents Matrix of size [number of keys x number of keys] #inputs: sboxNum - the sbox number we are targeting (values 1 to 8) plot - if True, will plot the matrix as a histogram """ numPT = 2**6 # number of possible PT/CT numKey = 2**6 # number of possible keys cc = np.zeros((numKey, numKey), np.float) # Confusion Coefficient matrix histogram = [] for ki in range(numKey): for kj in range(numKey): numNotEqual = 0 for ptBlock in range(numPT): sboxOuti = des_block.sbox(sboxNum, ptBlock ^ ki) sboxOutj = des_block.sbox(sboxNum, ptBlock ^ kj) if (self.msb(sboxOuti) != self.msb(sboxOutj)): numNotEqual += 1.0 coefficient = numNotEqual / numPT cc[ki][kj] = coefficient if (ki != kj and ki < kj): histogram.append(coefficient) if (plot): # Plot a histogram of the coefficients weights = np.ones_like(histogram) / len(histogram) fig = plt.hist(histogram, 1000, weights=weights) plt.ylabel('Frequency') plt.xlabel('Confusion coefficient') plt.show(fig) return cc
def cpaPoint2(self, sk, sboxNum, samplePoint, startTrace=0, endTrace=0, leakage='HW'): """Same as cpaPoint but uses the traditional `means' method instead of the covariance method. """ if endTrace == 0: endTrace = self.acq.numTraces HammW = [] for i in range(5): HammW.append([]) numTraces = endTrace - startTrace #covMethod = np.zeros((2,numTraces)) for i in range(numTraces): ptStr = self.ptNumpyArray2String(self.acq.inputtext[i + startTrace]) ptBlock = des_block.ptblock2( ptStr, sboxNum) # 6-bit block of plaintext which is fed into sboxNum sboxOut = des_block.sbox(sboxNum, ptBlock ^ sk) if leakage == 'HW': hyp = HW( sboxOut ) # or use HD(sboxOut, sboxIn) for hardware implementations elif leakage == 'HDRound': hyp = HD( sboxOut, des_block.ptRoundIn(ptStr, sboxNum) ) # or use HD(sboxOut, sboxIn) for hardware implementations elif leakage == 'HD': hyp = HD(sboxOut, sboxIn) else: print "undefined leakage model" return 0 HammW[hyp].append(self.acq.traces[i + startTrace][samplePoint]) noiseCPA = [] signalCPA = [] signalCPAdiff = [] for j in range(5): noise = np.std(HammW[j]) if (np.isnan(noise)): print "noise isnan, rejected" pass else: noiseCPA.append(noise) signalCPA.append(np.mean(HammW[j])) noiseCPA = np.mean(noiseCPA) for j in range(4): signalCPAdiff.append(np.abs(signalCPA[j + 1] - signalCPA[j])) signalCPA = np.mean(signalCPAdiff) return signalCPA * self.acq.yscale, noiseCPA * self.acq.yscale
def cpaPoint(self, sk, sboxNum, samplePoint, startTrace = 0, endTrace=0, leakage='HW'): """The cpaPoint function calculates the CPA signal to noise ratio for a single sample of a given traceset. This saves a lot of time compared to cpaTrace. The recommended usage scenario will be to determine the sample point of interest using cpaTrace (with fewer traces), and then to use cpaPoint (with many traces) Inputs: subkey - the correct subkey (which is 0 to 63 for DES) sboxNum - the sbox number we are targeting (values 1 to 8) samplePoint - the single samplePoint to calculate the SNR for startTrace, endTrace - SNR is calculated from Trace startTrace to endTrace. This value is optional. If left out, it will calculate SNR for entire trace set leakage - The leakage model. This takes values 'HW', 'HD', 'HDRound' returns: signal, noise """ if endTrace == 0: endTrace = self.acq.numTraces HammW = [] for i in range(5): HammW.append([]) numTraces = endTrace - startTrace covMethod = np.zeros((2,numTraces)) for i in range(numTraces): ptStr = self.ptNumpyArray2String(self.acq.inputtext[i + startTrace]) ptBlock = des_block.ptblock2(ptStr, sboxNum) # 6-bit block of plaintext which is fed into sboxNum sboxOut = des_block.sbox(sboxNum, ptBlock ^ sk) if leakage =='HW': hyp = HW(sboxOut) # or use HD(sboxOut, sboxIn) for hardware implementations elif leakage =='HDRound': hyp = HD(sboxOut,des_block.ptRoundIn(ptStr,sboxNum)) # or use HD(sboxOut, sboxIn) for hardware implementations elif leakage =='HD': hyp = HD(sboxOut, sboxIn) else: print "undefined leakage model" return 0 HammW[hyp].append(self.acq.traces[i+startTrace][samplePoint]) covMethod[0][i] = self.acq.traces[i+startTrace][samplePoint] covMethod[1][i] = hyp noiseCPA = [] signalCPA = [] signalCPAdiff = [] for j in range(5): noise = np.std(HammW[j]) if (np.isnan(noise)): pass else: noiseCPA.append(noise) noiseCPA = np.mean(noiseCPA) signalCov = np.abs(((np.cov(covMethod))[1][0])) return signalCov*self.acq.yscale, noiseCPA *self.acq.yscale
def corrCoefficient(self, subkey, sboxNum, startTrace = 0, endTrace = 0, leakage='HW'): """ Calculate CPA correlation co-efficient: a. Calculate sbox output for the given subkey b. Calculate hypothesis (which is hamming weight or hamming distance depending on power model) c. Calculate pearson's correlation co-efficient of given subkey Input: subkey (6bit key which is input to sbox) sboxNum (1...8) Return the correlation co-efficient of the given subkey """ # Based on ChipWhisperer CPASimpleLoop.py sumnum = np.zeros(self.acq.numSamples) sumden1 = np.zeros(self.acq.numSamples) sumden2 = np.zeros(self.acq.numSamples) if (endTrace == 0): endTrace = self.acq.numTraces # Generate hypotheticals hyp = np.zeros(endTrace - startTrace) for i in range(endTrace - startTrace): ptStr = self.ptNumpyArray2String(self.acq.inputtext[i + startTrace]) ptBlock = des_block.ptblock2(ptStr, sboxNum) # 6-bit block of plaintext which is fed into sboxNum sboxIn = ptBlock ^ subkey sboxOut = des_block.sbox(sboxNum, sboxIn) if leakage =='HW': hyp[i] = HW(sboxOut) elif leakage =='HDRound': hyp[i] = HD(sboxOut,des_block.ptRoundIn(ptStr,sboxNum)) elif leakage =='HD': hyp[i] = HD(sboxOut, sboxIn) else: print "undefined leakage model" return 0 meanh = np.mean(hyp, dtype = np.float) meant = np.mean(self.acq.traces[startTrace:endTrace], axis=0, dtype=np.float) for tnum in range(endTrace - startTrace): hdiff = (hyp[tnum] - meanh) tdiff = self.acq.traces[tnum+startTrace,:] - meant sumnum += hdiff * tdiff sumden1 += hdiff * hdiff sumden2 += tdiff * tdiff #print sumden1 #print sumden2 #print np.sqrt(sumden1 * sumden2) corr = sumnum / np.sqrt (sumden1 * sumden2) return corr
def DESCpaCC(self, sboxNum, plot=False, leakage='HW'): """ Returns DES CPA 2-way Confusion coefficents Matrix of size [number of keys x number of keys] #inputs: sboxNum - the sbox number we are targeting (values 1 to 8) plot - if True, will plot the matrix as a histogram leakage - The leakage model. This takes values 'HW', 'HD', 'HDRound' """ numPT = 2**6 # number of possible PT/CT numKey = 2**6 # number of possible keys cc = np.zeros((numKey, numKey), np.float) # Confusion Coefficient matrix histogram = [] for ki in range(numKey): for kj in range(numKey): numNotEqual = 0.0 k = [] for ptBlock in range(numPT): sboxIni = ptBlock ^ ki sboxInj = ptBlock ^ kj sboxOuti = des_block.sbox(sboxNum, sboxIni) sboxOutj = des_block.sbox(sboxNum, sboxInj) if leakage == 'HW': k.append((self.hw(sboxOuti) - self.hw(sboxOutj))**2) if leakage == 'HD': k.append( (HD(sboxOuti, sboxIni) - HD(sboxOutj, sboxInj))**2) cc[ki][kj] = np.mean(k) if (ki != kj and ki < kj): histogram.append(cc[ki][kj]) if (plot): weights = np.ones_like(histogram) / len(histogram) fig = plt.hist(histogram, 1000, weights=weights) plt.ylabel('Frequency') plt.xlabel('Confusion coefficient') plt.show(fig) return cc
def DESCpaCC(self, sboxNum, plot = False, leakage ='HW'): """ Returns DES CPA 2-way Confusion coefficents Matrix of size [number of keys x number of keys] #inputs: sboxNum - the sbox number we are targeting (values 1 to 8) plot - if True, will plot the matrix as a histogram leakage - The leakage model. This takes values 'HW', 'HD', 'HDRound' """ numPT = 2 ** 6 # number of possible PT/CT numKey = 2 ** 6 # number of possible keys cc = np.zeros((numKey,numKey), np.float) # Confusion Coefficient matrix histogram = [] for ki in range(numKey): for kj in range(numKey): numNotEqual = 0.0 k = [] for ptBlock in range(numPT): sboxIni = ptBlock ^ ki sboxInj = ptBlock ^ kj sboxOuti = des_block.sbox(sboxNum, sboxIni) sboxOutj = des_block.sbox(sboxNum, sboxInj) if leakage =='HW': k.append((self.hw(sboxOuti) - self.hw(sboxOutj)) ** 2) if leakage =='HD': k.append((HD(sboxOuti, sboxIni) - HD(sboxOutj,sboxInj)) ** 2) cc[ki][kj] = np.mean(k) if (ki != kj and ki<kj): histogram.append(cc[ki][kj]) if (plot): weights = np.ones_like(histogram)/len(histogram) fig = plt.hist(histogram, 1000, weights=weights) plt.ylabel('Frequency') plt.xlabel('Confusion coefficient') plt.show(fig) return cc
def corrCoefficientSinglePointRivian(self, subkey, sboxNum, samplePoint, startTrace=0, endTrace=0, leakage='HW'): """ Calculate CPA correlation co-efficient: a. Calculate sbox out given subkey b. Calculate hypothesis (which is hamming weight or hamming distance depending on power model) c. Calculate pearson's correlation co-efficient of given subkey Input: subkey (6bit key which is input to sbox) sboxNum (1...8) Return the correlation co-efficient of the given subkey """ # Based on ChipWhisperer CPASimpleLoop.py if (endTrace == 0): endTrace = self.acq.numTraces coeff = [] # Generate hypotheticals hyp = np.zeros(endTrace - startTrace) for i in range(endTrace - startTrace): ptStr = self.ptNumpyArray2String(self.acq.inputtext[i + startTrace]) ptBlock = des_block.ptblock2( ptStr, sboxNum) # 6-bit block of plaintext which is fed into sboxNum sboxIn = ptBlock ^ subkey sboxOut = des_block.sbox(sboxNum, sboxIn) if leakage == 'HW': hyp[i] = HW( sboxOut ) # or use HD(sboxOut, sboxIn) for hardware implementations elif leakage == 'HDRound': hyp[i] = HD( sboxOut, des_block.ptRoundIn(ptStr, sboxNum) ) # or use HD(sboxOut, sboxIn) for hardware implementations elif leakage == 'HD': hyp[i] = HD(sboxOut, sboxIn) else: print "undefined leakage model" return 0 coeff.append(hyp[i] * self.acq.traces[i + startTrace]) corr = np.mean(coeff) return corr
def buildTemplate( self, subkey, sboxNum, samplePoint, startTrace=0, endTrace=0, excludeStartTrace=None, excludeEndTrace=None ): """ Calculate template for each sbox input b. Calculate variance of the noise # Inputs: subkey - the correct subkey sboxNum - the sbox number we are targeting (values 1 to 8) samplePoint - the single sample point to calculate the PI for startTrace, endTrace - SNR is calculated from Trace startTrace to endTrace. This value is optional. If left out, it will calculate SNR for entire trace set excludestartTrace, excludedEndTrace - Exclude the traces between excludeStartTrace to excludeEndTrace. This interval should be between startTrace and endTrace. It is used for cross validation. """ self.noise = [] self.samplePoint = samplePoint self.num = endTrace - startTrace for i in range(DES_PI.NK): self.noise.append([]) if excludeStartTrace == None and excludeEndTrace == None: exclude = False else: exclude = True if endTrace == 0: endTrace = self.acq.numTraces for i in range(endTrace - startTrace): if exclude == True and i >= excludeStartTrace and i <= excludeEndTrace: pass else: ptStr = self.ptNumpyArray2String(self.acq.inputtext[i + startTrace]) ptBlock = des_block.ptblock2(ptStr, sboxNum) # 6-bit block of plaintext which is fed into sboxNum sboxIn = ptBlock ^ subkey sboxOut = des_block.sbox(sboxNum, sboxIn) hyp = HW(sboxOut) self.noise[hyp].append(self.acq.traces[i + startTrace][samplePoint]) for i in range(DES_PI.NK): self.mean[i] = np.mean(self.noise[i]) self.sd[i] = np.std(self.noise[i]) # print "Num Measurements for sbox input %d is %d" % (i, len(noise[i])) if len(self.noise[i]) == 0: print "Not enough measurements for sbox input %d ! Please rebuild the template with more measurements" % i print self.mean print self.sd
def buildTemplate(self, subkey, sboxNum, samplePoint, startTrace = 0, endTrace = 0, excludeStartTrace =None, excludeEndTrace=None): """ Calculate template for each sbox input b. Calculate variance of the noise # Inputs: subkey - the correct subkey sboxNum - the sbox number we are targeting (values 1 to 8) samplePoint - the single sample point to calculate the PI for startTrace, endTrace - SNR is calculated from Trace startTrace to endTrace. This value is optional. If left out, it will calculate SNR for entire trace set excludestartTrace, excludedEndTrace - Exclude the traces between excludeStartTrace to excludeEndTrace. This interval should be between startTrace and endTrace. It is used for cross validation. """ self.noise = [] self.samplePoint = samplePoint self.num = endTrace - startTrace for i in range(DES_PI.NK): self.noise.append([]) if (excludeStartTrace ==None and excludeEndTrace == None): exclude = False else: exclude = True if (endTrace == 0): endTrace = self.acq.numTraces for i in range(endTrace - startTrace): if (exclude == True and i>= excludeStartTrace and i<=excludeEndTrace): pass else: ptStr = self.ptNumpyArray2String(self.acq.inputtext[i + startTrace]) ptBlock = des_block.ptblock2(ptStr, sboxNum) # 6-bit block of plaintext which is fed into sboxNum sboxIn = ptBlock ^ subkey sboxOut = des_block.sbox(sboxNum, sboxIn) hyp = HD(sboxOut,des_block.ptRoundIn(ptStr,sboxNum)) self.noise[hyp].append(self.acq.traces[i + startTrace][samplePoint]) for i in range(DES_PI.NK): self.mean[i] = np.mean(self.noise[i]) self.sd[i] = np.std(self.noise[i]) #print "Num Measurements for sbox input %d is %d" % (i, len(noise[i])) if len(self.noise[i]) == 0: print "Not enough measurements for sbox input %d ! Please rebuild the template with more measurements" % i print self.mean print self.sd
def dpaPoint(self, sk, sboxNum, samplePoint, startTrace=0, endTrace=0, bit=1): """The dpaPoint function calculates the DPA signal to noise ratio for a single sample of a given traceset. Inputs: subkey - the correct subkey (which is 0 to 63 for DES) sboxNum - the sbox number we are targeting (values 1 to 8) samplePoint - the single samplePoint to calculate the SNR for startTrace, endTrace - SNR is calculated from Trace startTrace to endTrace. This value is optional. If left out, it will calculate SNR for entire trace set leakage - The leakage model. This takes values 'HW', 'HD', 'HDRound' if bit is provided, returns the SNR of that specific sbox output bit. This takes values of 0,1,2,3 """ if endTrace == 0: endTrace = self.acq.numTraces LSB = [] for i in range(2): LSB.append([]) numTraces = endTrace - startTrace for i in range(numTraces): ptStr = self.ptNumpyArray2String(self.acq.inputtext[i + startTrace]) ptBlock = des_block.ptblock2( ptStr, sboxNum) # 6-bit block of plaintext which is fed into sboxNum sboxOut = des_block.sbox(sboxNum, ptBlock ^ sk) LSB[(sboxOut & (2**bit)) >> bit].append( self.acq.traces[i + startTrace][samplePoint]) signalDPA = np.abs(np.mean(LSB[0]) - np.mean(LSB[1])) noiseDPA = [] # Traditional method for calculating SNR for DPA for j in range(2): noiseDPA.append(np.std(LSB[j])) noiseDPA = np.mean(noiseDPA) return signalDPA * self.acq.yscale, noiseDPA * self.acq.yscale
def dom(self, subkey, sboxNum, startTrace=0, endTrace=0): """ Calculate DPA DOM: a. Calculate LSB of sbox out given subkey b. average all the traces with LSB= one. Repeat for LSB= zero c. Calculate the difference of means Input: subkey (6bit key which is input to sbox) sboxNum (1...8) Return a trace of the difference of means """ traceLSB0 = np.zeros(self.acq.numSamples, dtype = np.float64) traceLSB1 = np.zeros(self.acq.numSamples, dtype = np.float64) diff = np.zeros(self.acq.numSamples, dtype = np.float64) numLSB0 = 0 numLSB1 = 0 if (endTrace == 0): endTrace = self.acq.numTraces for i in range(startTrace, endTrace): ptStr = self.ptNumpyArray2String(self.acq.inputtext[i]) #print ("ptStr is " + ptStr) ptBlock = des_block.ptblock2(ptStr, sboxNum) # 6-bit block of plaintext which is fed into sboxNum sboxOut = des_block.sbox(sboxNum, ptBlock ^ subkey) if ((sboxOut & 1) == 1): # Sum traces (traceLSB1) with LSB = 1 numLSB1 += 1 for j in range(self.acq.numSamples): traceLSB1[j] += self.acq.traces[i][j] else: # Sum traces (traceLSB0) with LSB = 0 numLSB0 += 1 for j in range(self.acq.numSamples): traceLSB0[j] += self.acq.traces[i][j] for j in range(self.acq.numSamples): traceLSB0[j] = traceLSB0[j] / numLSB0 traceLSB1[j] = traceLSB1[j] / numLSB1 diff[j] = traceLSB1[j] - traceLSB0[j] #print "numLSB0 = %d, numLSB1 = %d" % (numLSB0, numLSB1) return diff
def cpaPoint2(self, sk, sboxNum, samplePoint, startTrace = 0, endTrace=0, leakage='HW'): """Same as cpaPoint but uses the traditional `means' method instead of the covariance method. """ if endTrace == 0: endTrace = self.acq.numTraces HammW = [] for i in range(5): HammW.append([]) numTraces = endTrace - startTrace #covMethod = np.zeros((2,numTraces)) for i in range(numTraces): ptStr = self.ptNumpyArray2String(self.acq.inputtext[i + startTrace]) ptBlock = des_block.ptblock2(ptStr, sboxNum) # 6-bit block of plaintext which is fed into sboxNum sboxOut = des_block.sbox(sboxNum, ptBlock ^ sk) if leakage =='HW': hyp = HW(sboxOut) # or use HD(sboxOut, sboxIn) for hardware implementations elif leakage =='HDRound': hyp = HD(sboxOut,des_block.ptRoundIn(ptStr,sboxNum)) # or use HD(sboxOut, sboxIn) for hardware implementations elif leakage =='HD': hyp = HD(sboxOut, sboxIn) else: print "undefined leakage model" return 0 HammW[hyp].append(self.acq.traces[i+startTrace][samplePoint]) noiseCPA = [] signalCPA = [] signalCPAdiff = [] for j in range(5): noise = np.std(HammW[j]) if (np.isnan(noise)): print "noise isnan, rejected" pass else: noiseCPA.append(noise) signalCPA.append(np.mean(HammW[j])) noiseCPA = np.mean(noiseCPA) for j in range(4): signalCPAdiff.append(np.abs(signalCPA[j+1] - signalCPA[j])) signalCPA = np.mean(signalCPAdiff) return signalCPA*self.acq.yscale, noiseCPA*self.acq.yscale
def domSinglePoint(self, subkey, sboxNum, samplePoint, startTrace=0, endTrace=0): """ Calculate DPA DOM for a single point: a. Calculate LSB of sbox out given subkey b. average all the traces with LSB= one. Repeat for LSB= zero c. Calculate the difference of means Input: subkey (6bit key which is input to sbox) sboxNum (1...8) Return a trace of the difference of means """ traceLSB0 = 0.0 traceLSB1 = 0.0 diff = 0.0 numLSB0 = 0 numLSB1 = 0 if (endTrace == 0): endTrace = self.acq.numTraces for i in range(startTrace, endTrace): ptStr = self.ptNumpyArray2String(self.acq.inputtext[i]) #print ("ptStr is " + ptStr) ptBlock = des_block.ptblock2(ptStr, sboxNum) # 6-bit block of plaintext which is fed into sboxNum sboxOut = des_block.sbox(sboxNum, ptBlock ^ subkey) if ((sboxOut & 1) == 1): # Sum traces (traceLSB1) with LSB = 1 numLSB1 += 1.0 traceLSB1 += self.acq.traces[i][samplePoint] else: # Sum traces (traceLSB0) with LSB = 0 numLSB0 += 1.0 traceLSB0 += self.acq.traces[i][samplePoint] traceLSB0 = traceLSB0 / numLSB0 traceLSB1 = traceLSB1 / numLSB1 diff = traceLSB1 - traceLSB0 #print "traceLSB0 = %f, traceLSB1 = %f" %(traceLSB0, traceLSB1) #print "numLSB0 = %f, numLSB1 = %f" % (numLSB0, numLSB1) return diff
def dpaPoint(self, sk, sboxNum, samplePoint, startTrace = 0, endTrace=0, bit=1): """The dpaPoint function calculates the DPA signal to noise ratio for a single sample of a given traceset. Inputs: subkey - the correct subkey (which is 0 to 63 for DES) sboxNum - the sbox number we are targeting (values 1 to 8) samplePoint - the single samplePoint to calculate the SNR for startTrace, endTrace - SNR is calculated from Trace startTrace to endTrace. This value is optional. If left out, it will calculate SNR for entire trace set leakage - The leakage model. This takes values 'HW', 'HD', 'HDRound' if bit is provided, returns the SNR of that specific sbox output bit. This takes values of 0,1,2,3 """ if endTrace == 0: endTrace = self.acq.numTraces LSB = [] for i in range(2): LSB.append([]) numTraces = endTrace - startTrace for i in range(numTraces): ptStr = self.ptNumpyArray2String(self.acq.inputtext[i + startTrace]) ptBlock = des_block.ptblock2(ptStr, sboxNum) # 6-bit block of plaintext which is fed into sboxNum sboxOut = des_block.sbox(sboxNum, ptBlock ^ sk) LSB[(sboxOut & (2**bit)) >> bit].append(self.acq.traces[i+startTrace][samplePoint]) signalDPA = np.abs(np.mean(LSB[0]) - np.mean(LSB[1])) noiseDPA = [] # Traditional method for calculating SNR for DPA for j in range(2): noiseDPA.append(np.std(LSB[j])) noiseDPA = np.mean(noiseDPA) return signalDPA * self.acq.yscale, noiseDPA * self.acq.yscale
def corrCoefficientSinglePointRivian(self, subkey, sboxNum, samplePoint, startTrace = 0, endTrace = 0, leakage ='HW'): """ Calculate CPA correlation co-efficient: a. Calculate sbox out given subkey b. Calculate hypothesis (which is hamming weight or hamming distance depending on power model) c. Calculate pearson's correlation co-efficient of given subkey Input: subkey (6bit key which is input to sbox) sboxNum (1...8) Return the correlation co-efficient of the given subkey """ # Based on ChipWhisperer CPASimpleLoop.py if (endTrace == 0): endTrace = self.acq.numTraces coeff = [] # Generate hypotheticals hyp = np.zeros(endTrace - startTrace) for i in range(endTrace - startTrace): ptStr = self.ptNumpyArray2String(self.acq.inputtext[i + startTrace]) ptBlock = des_block.ptblock2(ptStr, sboxNum) # 6-bit block of plaintext which is fed into sboxNum sboxIn = ptBlock ^ subkey sboxOut = des_block.sbox(sboxNum, sboxIn) if leakage =='HW': hyp[i] = HW(sboxOut) # or use HD(sboxOut, sboxIn) for hardware implementations elif leakage =='HDRound': hyp[i] = HD(sboxOut,des_block.ptRoundIn(ptStr,sboxNum)) # or use HD(sboxOut, sboxIn) for hardware implementations elif leakage =='HD': hyp[i] = HD(sboxOut, sboxIn) else: print "undefined leakage model" return 0 coeff.append(hyp[i] * self.acq.traces[i + startTrace]) corr = np.mean(coeff) return corr
def CPA_K(self, correctKey=0, leakage='HW'): """Returns the 3-way CPA confusion matrix K, K* and K** (refer to "A Statistics-based Fundamental Model for Side-channel Attack Analysis.") #inputs: correctKey - takes values (0...63) leakage - the leakage model. Takes values 'HW' and 'HD'. """ Nk = self.NkDES numPT = 2 ** 6 sboxNum = self.sboxNum cv = self.CPA_CV(correctKey = correctKey) # Diagonal of the confusion matrix K = np.zeros((Nk, Nk)) Ks = np.zeros((Nk, Nk)) # K* Kss = np.zeros((Nk, Nk)) # K** keys = np.arange(64) # List of wrong keys keys = np.delete(keys, correctKey) evkc = [] # E [V | kc] for ptBlock in range(numPT): sboxOutc = des_block.sbox(sboxNum, ptBlock ^ correctKey) evkc.append(self.hw(sboxOutc)) evkc = np.mean(evkc) #print evkc for i in keys: for j in keys: # Calculate kcij = E[(V|kc - V|ki) * (V|kc - V|ki)] # Calculate kcijss = E[4 * (V|kc - E[V|kc])^2 * (V|kc - V|ki) * (V|kc - V | kj)] kcij = [] kcijs = [] kcijss = [] for ptBlock in range(numPT): sboxOutc = des_block.sbox(sboxNum, ptBlock ^ correctKey) sboxOuti = des_block.sbox(sboxNum, ptBlock ^ i) sboxOutj = des_block.sbox(sboxNum, ptBlock ^ j) if self.leakage =='HW': vkc = self.hw(sboxOutc) # V | kc vki = self.hw(sboxOuti) # V | ki vkj = self.hw(sboxOutj) # V | kj elif self.leakage =='HD': vkc = HD(sboxOutc, correctKey^ptBlock) # V | kc vki = HD(sboxOuti, i^ptBlock) # V | ki vkj = HD(sboxOutj, j^ptBlock) # V | kj kcij.append((vkc - vki) * (vkc -vkj)) kcijs.append(((vkc-vki)**2) * ((vkc-vkj)**2)) kcijss.append( 4 * ((vkc - evkc)**2) * (vkc - vki) * (vkc -vkj)) kcij = np.mean(kcij) kcijss = np.mean(kcijss) kcijs = np.mean(kcijs) K[i][j] = kcij Ks[i][j] = kcijs Kss[i][j] = kcijs K = np.delete(K, correctKey,0) K = np.delete(K, correctKey,1) Ks = np.delete(Ks, correctKey,0) Ks = np.delete(Ks, correctKey,1) Kss = np.delete(Kss, correctKey,0) Kss = np.delete(Kss, correctKey,1) return K, Ks, Kss
def cpaTrace(self, subkey, sboxNum, startTrace=0, endTrace=0, leakage='HW'): """The cpaTrace function calculates the CPA signal to noise ratio over all the samples of a traceset Inputs: subkey - the correct subkey (which is 0 to 63 for DES) sboxNum - the sbox number we are targeting (values 1 to 8) startTrace, endTrace - SNR is calculated from Trace startTrace to endTrace. This value is optional. If left out, it will calculate SNR for entire trace set leakage - The leakage model. This takes values 'HW', 'HD', 'HDRound' Returns: Signal matrix, noise matrix """ sumnum = np.zeros(self.acq.numSamples) if (endTrace == 0): endTrace = self.acq.numTraces # Group traces according to the hamming weight of the leakage traceHW = [] noise = [] for i in range(5): traceHW.append(None) noise.append(None) # Generate hypotheticals hyp = np.zeros(endTrace - startTrace, dtype=int) for i in range(endTrace - startTrace): ptStr = self.ptNumpyArray2String(self.acq.inputtext[i + startTrace]) ptBlock = des_block.ptblock2( ptStr, sboxNum) # 6-bit block of plaintext which is fed into sboxNum sboxIn = ptBlock ^ subkey sboxOut = des_block.sbox(sboxNum, sboxIn) if leakage == 'HW': hyp[i] = HW( sboxOut ) # or use HD(sboxOut, sboxIn) for hardware implementations elif leakage == 'HDRound': hyp[i] = HD( sboxOut, des_block.ptRoundIn(ptStr, sboxNum) ) # or use HD(sboxOut, sboxIn) for hardware implementations elif leakage == 'HD': hyp[i] = HD(sboxOut, sboxIn) else: print "undefined leakage model" return 0 if traceHW[hyp[i]] == None: traceHW[hyp[i]] = self.acq.traces[i] else: traceHW[hyp[i]] = np.vstack( (traceHW[hyp[i]], self.acq.traces[i])) meanh = np.mean(hyp, dtype=np.float) meant = np.mean(self.acq.traces[startTrace:endTrace], axis=0, dtype=np.float) noise = np.std(traceHW[0], axis=0) for i in range(5): noise = np.vstack((noise, np.std(traceHW[i], axis=0))) noise = np.mean(noise, axis=0) for tnum in range(endTrace - startTrace): hdiff = (hyp[tnum] - meanh) tdiff = self.acq.traces[tnum + startTrace, :] - meant sumnum += hdiff * tdiff signal = np.abs(sumnum / (endTrace - startTrace)) return signal, noise
def cpaPoint(self, sk, sboxNum, samplePoint, startTrace=0, endTrace=0, leakage='HW'): """The cpaPoint function calculates the CPA signal to noise ratio for a single sample of a given traceset. This saves a lot of time compared to cpaTrace. The recommended usage scenario will be to determine the sample point of interest using cpaTrace (with fewer traces), and then to use cpaPoint (with many traces) Inputs: subkey - the correct subkey (which is 0 to 63 for DES) sboxNum - the sbox number we are targeting (values 1 to 8) samplePoint - the single samplePoint to calculate the SNR for startTrace, endTrace - SNR is calculated from Trace startTrace to endTrace. This value is optional. If left out, it will calculate SNR for entire trace set leakage - The leakage model. This takes values 'HW', 'HD', 'HDRound' returns: signal, noise """ if endTrace == 0: endTrace = self.acq.numTraces HammW = [] for i in range(5): HammW.append([]) numTraces = endTrace - startTrace covMethod = np.zeros((2, numTraces)) for i in range(numTraces): ptStr = self.ptNumpyArray2String(self.acq.inputtext[i + startTrace]) ptBlock = des_block.ptblock2( ptStr, sboxNum) # 6-bit block of plaintext which is fed into sboxNum sboxOut = des_block.sbox(sboxNum, ptBlock ^ sk) if leakage == 'HW': hyp = HW( sboxOut ) # or use HD(sboxOut, sboxIn) for hardware implementations elif leakage == 'HDRound': hyp = HD( sboxOut, des_block.ptRoundIn(ptStr, sboxNum) ) # or use HD(sboxOut, sboxIn) for hardware implementations elif leakage == 'HD': hyp = HD(sboxOut, sboxIn) else: print "undefined leakage model" return 0 HammW[hyp].append(self.acq.traces[i + startTrace][samplePoint]) covMethod[0][i] = self.acq.traces[i + startTrace][samplePoint] covMethod[1][i] = hyp noiseCPA = [] signalCPA = [] signalCPAdiff = [] for j in range(5): noise = np.std(HammW[j]) if (np.isnan(noise)): pass else: noiseCPA.append(noise) noiseCPA = np.mean(noiseCPA) signalCov = np.abs(((np.cov(covMethod))[1][0])) return signalCov * self.acq.yscale, noiseCPA * self.acq.yscale
def cpaTrace(self, subkey, sboxNum, startTrace =0, endTrace = 0, leakage='HW'): """The cpaTrace function calculates the CPA signal to noise ratio over all the samples of a traceset Inputs: subkey - the correct subkey (which is 0 to 63 for DES) sboxNum - the sbox number we are targeting (values 1 to 8) startTrace, endTrace - SNR is calculated from Trace startTrace to endTrace. This value is optional. If left out, it will calculate SNR for entire trace set leakage - The leakage model. This takes values 'HW', 'HD', 'HDRound' Returns: Signal matrix, noise matrix """ sumnum = np.zeros(self.acq.numSamples) if (endTrace == 0): endTrace = self.acq.numTraces # Group traces according to the hamming weight of the leakage traceHW = [] noise = [] for i in range(5): traceHW.append(None) noise.append(None) # Generate hypotheticals hyp = np.zeros(endTrace - startTrace, dtype=int) for i in range(endTrace - startTrace): ptStr = self.ptNumpyArray2String(self.acq.inputtext[i + startTrace]) ptBlock = des_block.ptblock2(ptStr, sboxNum) # 6-bit block of plaintext which is fed into sboxNum sboxIn = ptBlock ^ subkey sboxOut = des_block.sbox(sboxNum, sboxIn) if leakage =='HW': hyp[i] = HW(sboxOut) # or use HD(sboxOut, sboxIn) for hardware implementations elif leakage =='HDRound': hyp[i] = HD(sboxOut,des_block.ptRoundIn(ptStr,sboxNum)) # or use HD(sboxOut, sboxIn) for hardware implementations elif leakage =='HD': hyp[i] = HD(sboxOut, sboxIn) else: print "undefined leakage model" return 0 if traceHW[hyp[i]] == None: traceHW[hyp[i]] = self.acq.traces[i] else: traceHW[hyp[i]] = np.vstack((traceHW[hyp[i]] ,self.acq.traces[i])) meanh = np.mean(hyp, dtype = np.float) meant = np.mean(self.acq.traces[startTrace:endTrace], axis=0, dtype=np.float) noise = np.std(traceHW[0], axis=0) for i in range(5): noise = np.vstack((noise, np.std(traceHW[i], axis=0))) noise = np.mean(noise, axis = 0) for tnum in range(endTrace - startTrace): hdiff = (hyp[tnum] - meanh) tdiff = self.acq.traces[tnum+startTrace,:] - meant sumnum += hdiff * tdiff signal = np.abs(sumnum / (endTrace - startTrace)) return signal, noise
def corrCoefficient(self, subkey, sboxNum, startTrace=0, endTrace=0, leakage='HW'): """ Calculate CPA correlation co-efficient: a. Calculate sbox output for the given subkey b. Calculate hypothesis (which is hamming weight or hamming distance depending on power model) c. Calculate pearson's correlation co-efficient of given subkey Input: subkey (6bit key which is input to sbox) sboxNum (1...8) Return the correlation co-efficient of the given subkey """ # Based on ChipWhisperer CPASimpleLoop.py sumnum = np.zeros(self.acq.numSamples) sumden1 = np.zeros(self.acq.numSamples) sumden2 = np.zeros(self.acq.numSamples) if (endTrace == 0): endTrace = self.acq.numTraces # Generate hypotheticals hyp = np.zeros(endTrace - startTrace) for i in range(endTrace - startTrace): ptStr = self.ptNumpyArray2String(self.acq.inputtext[i + startTrace]) ptBlock = des_block.ptblock2( ptStr, sboxNum) # 6-bit block of plaintext which is fed into sboxNum sboxIn = ptBlock ^ subkey sboxOut = des_block.sbox(sboxNum, sboxIn) if leakage == 'HW': hyp[i] = HW(sboxOut) elif leakage == 'HDRound': hyp[i] = HD(sboxOut, des_block.ptRoundIn(ptStr, sboxNum)) elif leakage == 'HD': hyp[i] = HD(sboxOut, sboxIn) else: print "undefined leakage model" return 0 meanh = np.mean(hyp, dtype=np.float) meant = np.mean(self.acq.traces[startTrace:endTrace], axis=0, dtype=np.float) for tnum in range(endTrace - startTrace): hdiff = (hyp[tnum] - meanh) tdiff = self.acq.traces[tnum + startTrace, :] - meant sumnum += hdiff * tdiff sumden1 += hdiff * hdiff sumden2 += tdiff * tdiff #print sumden1 #print sumden2 #print np.sqrt(sumden1 * sumden2) corr = sumnum / np.sqrt(sumden1 * sumden2) return corr
def CPA_K(self, correctKey=0, leakage='HW'): """Returns the 3-way CPA confusion matrix K, K* and K** (refer to "A Statistics-based Fundamental Model for Side-channel Attack Analysis.") #inputs: correctKey - takes values (0...63) leakage - the leakage model. Takes values 'HW' and 'HD'. """ Nk = self.NkDES numPT = 2**6 sboxNum = self.sboxNum cv = self.CPA_CV( correctKey=correctKey) # Diagonal of the confusion matrix K = np.zeros((Nk, Nk)) Ks = np.zeros((Nk, Nk)) # K* Kss = np.zeros((Nk, Nk)) # K** keys = np.arange(64) # List of wrong keys keys = np.delete(keys, correctKey) evkc = [] # E [V | kc] for ptBlock in range(numPT): sboxOutc = des_block.sbox(sboxNum, ptBlock ^ correctKey) evkc.append(self.hw(sboxOutc)) evkc = np.mean(evkc) #print evkc for i in keys: for j in keys: # Calculate kcij = E[(V|kc - V|ki) * (V|kc - V|ki)] # Calculate kcijss = E[4 * (V|kc - E[V|kc])^2 * (V|kc - V|ki) * (V|kc - V | kj)] kcij = [] kcijs = [] kcijss = [] for ptBlock in range(numPT): sboxOutc = des_block.sbox(sboxNum, ptBlock ^ correctKey) sboxOuti = des_block.sbox(sboxNum, ptBlock ^ i) sboxOutj = des_block.sbox(sboxNum, ptBlock ^ j) if self.leakage == 'HW': vkc = self.hw(sboxOutc) # V | kc vki = self.hw(sboxOuti) # V | ki vkj = self.hw(sboxOutj) # V | kj elif self.leakage == 'HD': vkc = HD(sboxOutc, correctKey ^ ptBlock) # V | kc vki = HD(sboxOuti, i ^ ptBlock) # V | ki vkj = HD(sboxOutj, j ^ ptBlock) # V | kj kcij.append((vkc - vki) * (vkc - vkj)) kcijs.append(((vkc - vki)**2) * ((vkc - vkj)**2)) kcijss.append(4 * ((vkc - evkc)**2) * (vkc - vki) * (vkc - vkj)) kcij = np.mean(kcij) kcijss = np.mean(kcijss) kcijs = np.mean(kcijs) K[i][j] = kcij Ks[i][j] = kcijs Kss[i][j] = kcijs K = np.delete(K, correctKey, 0) K = np.delete(K, correctKey, 1) Ks = np.delete(Ks, correctKey, 0) Ks = np.delete(Ks, correctKey, 1) Kss = np.delete(Kss, correctKey, 0) Kss = np.delete(Kss, correctKey, 1) return K, Ks, Kss