#! /usr/bin/env python2
import tr_pcc
import sys
import argparse
import des
import traces
# The P permutation table, as in the standard. The first entry (16) is the
# position of the first (leftmost) bit of the result in the input 32 bits word.
# Used to convert target bit index into SBox index (just for printed summary
# after attack completion).
p_table = [16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25]
def hamming_weight (v):
v = v - ((v>>1) & 0x5555555555555555)
v = (v & 0x3333333333333333) + ((v>>2) & 0x3333333333333333)
return (((v + (v>>4) & 0xF0F0F0F0F0F0F0F) * 0x101010101010101) >> 56) & 0xFF
def hamming_distance(n1,n2):
return hamming_weight(n1^n2)
def main ():
# ************************************************************************
# * Before doing anything else, check the correctness of the DES library *
# ************************************************************************
if not des.check ():
sys.exit ("DES functional test failed")
# *************************************
# * Check arguments and read datafile *
# *************************************
argparser = argparse.ArgumentParser(description="Apply P. Kocher's DPA algorithm based on decision function")
argparser.add_argument("datafile", metavar='FILE',
help='name of the traces file in HWSec format. (e.g. pa.dat)')
argparser.add_argument("n", metavar='N', type=int,
help='number of acquisitions to use')
argparser.add_argument("target_bit", metavar='[B]', type=int, nargs='?', default=1,
help='index of target bit in L15 (1 to 32, as in DES standard, default: 1)')
args = argparser.parse_args()
if args.n < 1: # If invalid number of acquisitions.
sys.exit ("Invalid number of acquisitions: %d (shall be greater than 1)" % args.n)
if args.target_bit < 1 or args.target_bit > 32: # If invalid target bit index.
sys.exit ("Invalid target bit index: %d (shall be between 1 and 32 included)" % args.target_bit)
# Compute index of corresponding SBox
target_sbox = (p_table[args.target_bit - 1] - 1) / 4 + 1
# Read power traces and ciphertexts. n is the number of acquisitions to use.
ctx = read_datafile (args.datafile, args.n)
print >> sys.stderr, 'Hey'
print >> sys.stderr, 'Hope you have had a nice day ! :)'
# ***************************************************************
dpa_attack (ctx)
#print >> sys.stderr, "target bit %d" % args.target_bit
#dpa_attack(ctx, 1)
# *******************************************************************************
# * Print the 64 DPA traces in a data file named dpa.dat. Print corresponding *
# * gnuplot commands in a command file named dpa.cmd. All DPA traces are *
# * plotted in blue but the one corresponding to the best guess which is *
# * plotted in red with the title "Trace X (0xY)" where X and Y are the decimal *
# * and heaxdecimal forms of the 6 bits best guess. *
# *******************************************************************************
# Plot DPA traces in dpa.dat, gnuplot commands in dpa.cmd
# *****************
# * Print summary *
# *****************
#print >> sys.stderr, "Target bit: %d" % args.target_bit
#print >> sys.stderr, "Target SBox: %d" % target_sbox
#print >> sys.stderr, "Best guess: %d (0x%02x)" % (best_guess, best_guess)
#print >> sys.stderr, "Maximum of DPA trace: %e" % best_max
#print >> sys.stderr, "Index of maximum in DPA trace: %d" % best_idx
#print >> sys.stderr, "DPA traces stored in file 'dpa.dat'. In order to plot them, type:"
#print >> sys.stderr, "$ gnuplot -persist dpa.cmd"
# ************************
# * Print last round key *
# ************************
# A function to allocate cipher texts and power traces, read the
# datafile and store its content in allocated context.
def read_datafile (name, n):
ctx = traces.trContext (name, n)
if ctx.n != n:
sys.exit ("Could not read %d acquisitions from traces file. Traces file contains %d acquisitions." % (n, ctx.n));
return ctx
# Apply P. Kocher's DPA algorithm based on decision function. Computes 64 DPA
# traces dpa[0..63], best_guess (6-bits subkey corresponding to highest DPA
# peak), best_idx (index of sample with maximum value in best DPA trace) and
# best_max (value of sample with maximum value in best DPA trace).
def dpa_attack (ctx):
pcc = [0]*64
sbomask = 0xf0000000
key=[]
for sbox in range(8):
print >> sys.stderr, 'Calculating subkey for Sbox number: ' + str(sbox)
ctx_pcc = tr_pcc.pccContext(ctx.l,64) # pccContext object
for i in range(ctx.n):
ct = ctx.c[i]
r16l16 = des.ip(ct)
l16 = des.right_half(r16l16)
r15 = l16
r16 = des.left_half(r16l16)
er15 = des.e(l16)
ctx_pcc.insert_x(ctx.t[i][:])
rk= 0x0
for j in range(64):
l15 = r16^des.p(des.sboxes(er15^rk))
r14 = l15
ctx_pcc.insert_y(j, int(hamming_distance(r14 & des.p(sbomask), r15 & des.p(sbomask))))
rk+= 0x041041041041
ctx_pcc.consolidate()
dpa = list([ctx_pcc.get_pcc(g) for g in range(64)])
maxs = [max(l) for l in dpa]
idx = [l.index(m) for l,m in zip (dpa, maxs)]
# Get best maximum
best_max = max (maxs)
best_guess = maxs.index (best_max)
best_idx = idx[best_guess]
sbomask = sbomask >> 4
key.append(best_guess)
print >> sys.stderr, 'Subkey for Sbox nr. ' + str(sbox) +'are ' + str(hex(best_guess))
#traces.plot('sboxnr' + str(sbox), -1, dpa) # Plot the traces for each sbox
# Format output
res = ''
for i in range(len(key)):
res += bin(key[i])[2:].zfill(6)
print hex(int(res,2))
def shit_i_dont_like (ctx, target_bit):
t0 = [[0.0] * ctx.l] * 64
n0 = [0] * 64
t1 = [[0.0] * ctx.l] * 64
n1 = [0] * 64
for ct, t in zip (ctx.c, ctx.t): # For all acquisitions
d = decision (ct, target_bit) # Compute the 64 decisions
# For all guesses (64)
for g in xrange (64):
if d[g] == 0: # If decision on target bit is zero
t0[g] = [a+b for a,b in zip (t0[g], t)] # Accumulate power trace in zero-set
n0[g] += 1 # Increment traces count for zero-set
else: # If decision on target bit is one
t1[g] = [a+b for a,b in zip (t1[g], t)] # Accumulate power trace in one-set
n1[g] += 1 # Increment traces count for one-set
#DPA=[]
#for g in range(64):
#DPA[g] = sum(t1)/float(len(t1))
# Compute normalized one-set minus zero-set
dpa = [[t1[g][i]/n1[g] - t0[g][i]/n0[g] for i in xrange (ctx.l)] for g in xrange (64)]
# Get max and argmax for each guess
maxs = [max(l) for l in dpa]
idx = [l.index(m) for l,m in zip (dpa, maxs)]
# Get best maximum
best_max = max (maxs)
best_guess = maxs.index (best_max)
best_idx = idx[best_guess]
return dpa, best_guess, best_max, best_idx
if __name__ == "__main__":
main ()
Comments