Commit 90747200 authored by Luc Libralesso's avatar Luc Libralesso
Browse files

add unit tests for AES128

parent c66a9146
......@@ -124,6 +124,12 @@ class Equals < Operation
end
end
class EqualsExpr < Priority
def initialize(a,b)
super(Equals.new(a,b))
end
end
class DiffOp < Operation
def initialize(*parameters)
super(Operator.new("!="), *parameters)
......
......@@ -17,7 +17,7 @@ class AESKeyScheduleNode < CryptoDagNode
operators = []
@subtable = subtable
# apply rotation (first byte becomes last, second first, ...)
rotated_bytes = [
@rotated_bytes = [
Variable.new("#{name}_rot_0", 0..255),
Variable.new("#{name}_rot_1", 0..255),
Variable.new("#{name}_rot_2", 0..255),
......@@ -26,11 +26,11 @@ class AESKeyScheduleNode < CryptoDagNode
4.times do |i|
operators.push(EqualityOperator.new(
input[(i+1) % 4][3], # last column of the input
rotated_bytes[i],
@rotated_bytes[i],
))
end
# apply substitution
sub_rotated_bytes = [
@sub_rotated_bytes = [
Variable.new("#{name}_sub_rot_0", 0..255),
Variable.new("#{name}_sub_rot_1", 0..255),
Variable.new("#{name}_sub_rot_2", 0..255),
......@@ -38,12 +38,12 @@ class AESKeyScheduleNode < CryptoDagNode
]
4.times do |i|
operators.push(SOperator.new(
rotated_bytes[i],
sub_rotated_bytes[i],
@rotated_bytes[i],
@sub_rotated_bytes[i],
@subtable
))
end
rcon_col = [
@rcon_col = [
Variable.new("rcon_#{name}_0", 0..256, value=rcon),
Variable.new("rcon_#{name}_0", 0..256, value=0),
Variable.new("rcon_#{name}_0", 0..256, value=0),
......@@ -52,16 +52,17 @@ class AESKeyScheduleNode < CryptoDagNode
# construct first column of the output (sub_rotated_bytes XOR [first input col] XOR [Rcon,0,0,0])
4.times do |i|
operators.push(XorOperator.new(
sub_rotated_bytes[i],
@sub_rotated_bytes[i],
input[i][0],
rcon_col[i],
@rcon_col[i],
output[i][0]
))
end
@sub_vars = []
(nb_cols-1).times do |i|
index = i+1 # index in [1..3]
if nb_cols > 6 && index % nb_cols == 4 # if AES256 apply additional S-box
sub_vars = [
@sub_vars = [
Variable.new("#{name}_sub_intermediate_0", 0..255),
Variable.new("#{name}_sub_intermediate_1", 0..255),
Variable.new("#{name}_sub_intermediate_2", 0..255),
......@@ -91,4 +92,8 @@ class AESKeyScheduleNode < CryptoDagNode
end
super(inputs: [input], outputs: [output], operators: operators, name: name)
end
def all_atoms()
return flatten_output(0)+@sub_vars+@rcon_col+@sub_rotated_bytes+@rotated_bytes
end
end
#!/usr/bin/ruby
require 'minitest/autorun'
require_relative '../../cryptosystems/midori128/constants.rb'
require_relative "../../writers/graphviz_atomic.rb"
require_relative '../../cryptodag.rb'
require_relative '../../simulate_cryptodag.rb'
require_relative '../../nodes/input.rb'
require_relative '../../nodes/indexedsubbytes.rb'
require_relative '../../nodes/subbytes.rb'
require_relative '../../nodes/shiftrows.rb'
require_relative '../../nodes/mixcolumn.rb'
require_relative '../../nodes/xor.rb'
require_relative '../../nodes/aeskeyschedule.rb'
class TestAes128StepByStep < Minitest::Unit::TestCase
def setup()
# message values
@x_val = [
0x32, 0x88, 0x31, 0xe0,
0x43, 0x5a, 0x31, 0x37,
0xf6, 0x30, 0x98, 0x7,
0xa8, 0x8d, 0xa2, 0x34
]
# key values
@k_val = [
0x2b, 0x28, 0xab, 0x9,
0x7e, 0xae, 0xf7, 0xcf,
0x15, 0xd2, 0x15, 0x4f,
0x16, 0xa6, 0x88, 0x3c
]
# create DAG
@x = InputNode.new(name:"X", dimensions:[4,4])
@k = InputNode.new(name:"K", dimensions:[4,4])
# Sbox for AES128
@sbox = [
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
]
# mix columns matrix m
@mc = [
[2,3,1,1],
[1,2,3,1],
[1,1,2,3],
[3,1,1,2],
]
# @csts = []
# for i in 0..@nb_rounds-2 do
# cst = InputNode.new(name:"Cst_#{i}", dimensions:[4,4], values:cst_tabs(i))
# @csts.append(cst)
# end
end
def test_add_key()
#define internal nodes
nodes = []
addkey = XorNode.new(name:"addKey", inputs:[@x.outputs[0],@k.outputs[0]])
nodes.push(addkey)
computed_outputs = compute_set_of_operators(
@x_val.flatten()+@k_val.flatten(), # input values
@x.flatten_output(0)+@k.flatten_output(0), # input variables
addkey.flatten_output(0), # output variables
nodes.map{|n| n.operators}.reduce([], :+) # all operators of the cryptosystem
)
expected_output = [
0x19, 0xa0, 0x9a, 0xe9,
0x3d, 0xf4, 0xc6, 0xf8,
0xe3, 0xe2, 0x8d, 0x48,
0xbe, 0x2b, 0x2a, 0x8
]
assert_equal(expected_output, computed_outputs)
end
def test_ks1()
#define internal nodes
nodes = []
# first key schedule
k1 = AESKeyScheduleNode.new(name:"K1", input:@k.outputs[0], subtable:@sbox, rcon:1)
nodes.push(k1)
writefile_graphviz(
@k.flatten_output(0)+k1.all_atoms,
nodes.map{|n| n.operators}.reduce([], :+),
"test_ks1.dot"
)
computed_outputs = compute_set_of_operators(
@x_val.flatten()+@k_val.flatten(), # input values
@x.flatten_output(0)+@k.flatten_output(0), # input variables
k1.flatten_output(0), # output variables
nodes.map{|n| n.operators}.reduce([], :+) # all operators of the cryptosystem
)
expected_output = [
0xa0, 0x88, 0x23, 0x2a,
0xfa, 0x54, 0xa3, 0x6c,
0xfe, 0x2c, 0x39, 0x76,
0x17, 0xb1, 0x39, 0x5
]
assert_equal(expected_output, computed_outputs)
end
def test_first_s()
#define internal nodes
nodes = []
# first ark
addkey = XorNode.new(name:"addKey", inputs:[@x.outputs[0],@k.outputs[0]])
nodes.push(addkey)
# first key schedule
k1 = AESKeyScheduleNode.new(name:"K1", input:@k.outputs[0], subtable:@sbox, rcon:1)
nodes.push(k1)
s1 = SubBytesNode.new(name:"SB_#{1}", input:addkey.outputs[0], subtable:@sbox)
nodes.push(s1)
computed_outputs = compute_set_of_operators(
@x_val.flatten()+@k_val.flatten(), # input values
@x.flatten_output(0)+@k.flatten_output(0), # input variables
s1.flatten_output(0), # output variables
nodes.map{|n| n.operators}.reduce([], :+) # all operators of the cryptosystem
)
expected_output = [
0xd4, 0xe0, 0xb8, 0x1e,
0x27, 0xbf, 0xb4, 0x41,
0x11, 0x98, 0x5d, 0x52,
0xae, 0xf1, 0xe5, 0x30
]
assert_equal(expected_output, computed_outputs)
end
def test_first_shift()
#define internal nodes
nodes = []
# first ark
addkey = XorNode.new(name:"addKey", inputs:[@x.outputs[0],@k.outputs[0]])
nodes.push(addkey)
# first key schedule
k1 = AESKeyScheduleNode.new(name:"K1", input:@k.outputs[0], subtable:@sbox, rcon:1)
nodes.push(k1)
s1 = SubBytesNode.new(name:"SB_#{1}", input:addkey.outputs[0], subtable:@sbox)
nodes.push(s1)
shift1 = ShiftRowsNode.new(name:"SR_#{1}", input:s1.outputs[0])
nodes.push(shift1)
computed_outputs = compute_set_of_operators(
@x_val.flatten()+@k_val.flatten(), # input values
@x.flatten_output(0)+@k.flatten_output(0), # input variables
shift1.flatten_output(0), # output variables
nodes.map{|n| n.operators}.reduce([], :+) # all operators of the cryptosystem
)
expected_output = [
0xd4, 0xe0, 0xb8, 0x1e,
0xbf, 0xb4, 0x41, 0x27,
0x5d, 0x52, 0x11, 0x98,
0x30, 0xae, 0xf1, 0xe5
]
assert_equal(expected_output, computed_outputs)
end
def test_first_mc()
#define internal nodes
nodes = []
# first ark
addkey = XorNode.new(name:"addKey", inputs:[@x.outputs[0],@k.outputs[0]])
nodes.push(addkey)
# first key schedule
k1 = AESKeyScheduleNode.new(name:"K1", input:@k.outputs[0], subtable:@sbox, rcon:1)
nodes.push(k1)
s1 = SubBytesNode.new(name:"SB_#{1}", input:addkey.outputs[0], subtable:@sbox)
nodes.push(s1)
shift1 = ShiftRowsNode.new(name:"SR_#{1}", input:s1.outputs[0])
nodes.push(shift1)
mc1 = MixColumnNode.new(name:"MC_#{1}", input:shift1.outputs[0], m:@mc)
nodes.push(mc1)
computed_outputs = compute_set_of_operators(
@x_val.flatten()+@k_val.flatten(), # input values
@x.flatten_output(0)+@k.flatten_output(0), # input variables
mc1.flatten_output(0), # output variables
nodes.map{|n| n.operators}.reduce([], :+) # all operators of the cryptosystem
)
expected_output = [
0x4, 0xe0, 0x48, 0x28,
0x66, 0xcb, 0xf8, 0x6,
0x81, 0x19, 0xd3, 0x26,
0xe5, 0x9a, 0x7a, 0x4c,
]
assert_equal(expected_output, computed_outputs)
end
def test_k2()
#define internal nodes
nodes = []
# first ark
addkey = XorNode.new(name:"addKey", inputs:[@x.outputs[0],@k.outputs[0]])
nodes.push(addkey)
# first key schedule
k1 = AESKeyScheduleNode.new(name:"K1", input:@k.outputs[0], subtable:@sbox, rcon:1)
nodes.push(k1)
s1 = SubBytesNode.new(name:"SB_#{1}", input:addkey.outputs[0], subtable:@sbox)
nodes.push(s1)
shift1 = ShiftRowsNode.new(name:"SR_#{1}", input:s1.outputs[0])
nodes.push(shift1)
mc1 = MixColumnNode.new(name:"MC_#{1}", input:shift1.outputs[0], m:@mc)
nodes.push(mc1)
k2 = AESKeyScheduleNode.new(name:"K2", input:k1.outputs[0], subtable:@sbox, rcon:2)
nodes.push(k2)
computed_outputs = compute_set_of_operators(
@x_val.flatten()+@k_val.flatten(), # input values
@x.flatten_output(0)+@k.flatten_output(0), # input variables
k2.flatten_output(0), # output variables
nodes.map{|n| n.operators}.reduce([], :+) # all operators of the cryptosystem
)
expected_output = [
0xf2, 0x7a, 0x59, 0x73,
0xc2, 0x96, 0x35, 0x59,
0x95, 0xb9, 0x80, 0xf6,
0xf2, 0x43, 0x7a, 0x7f,
]
assert_equal(expected_output, computed_outputs)
end
def test_ark2()
#define internal nodes
nodes = []
# first ark
addkey = XorNode.new(name:"addKey", inputs:[@x.outputs[0],@k.outputs[0]])
nodes.push(addkey)
# first key schedule
k1 = AESKeyScheduleNode.new(name:"K1", input:@k.outputs[0], subtable:@sbox, rcon:1)
nodes.push(k1)
s1 = SubBytesNode.new(name:"SB_#{1}", input:addkey.outputs[0], subtable:@sbox)
nodes.push(s1)
shift1 = ShiftRowsNode.new(name:"SR_#{1}", input:s1.outputs[0])
nodes.push(shift1)
mc1 = MixColumnNode.new(name:"MC_#{1}", input:shift1.outputs[0], m:@mc)
nodes.push(mc1)
k2 = AESKeyScheduleNode.new(name:"K2", input:k1.outputs[0], subtable:@sbox, rcon:2)
nodes.push(k2)
ark2 = XorNode.new(name:"ARK2", inputs:[mc1.outputs[0],k1.outputs[0]])
nodes.push(ark2)
computed_outputs = compute_set_of_operators(
@x_val.flatten()+@k_val.flatten(), # input values
@x.flatten_output(0)+@k.flatten_output(0), # input variables
ark2.flatten_output(0), # output variables
nodes.map{|n| n.operators}.reduce([], :+) # all operators of the cryptosystem
)
expected_output = [
0xa4, 0x68, 0x6b, 0x2,
0x9c, 0x9f, 0x5b, 0x6a,
0x7f, 0x35, 0xea, 0x50,
0xf2, 0x2b, 0x43, 0x49,
]
assert_equal(expected_output, computed_outputs)
end
end
\ No newline at end of file
......@@ -4,6 +4,7 @@ require_relative "../../cryptodag.rb"
require_relative "../../cryptosystems/midori128/midori128.rb"
require_relative "../../writers/abstract_constraints_atomic.rb"
require_relative "../../backends/minizinc"
require_relative "../../writers/graphviz_atomic.rb"
require "minitest/autorun"
# require "minitest/color"
......@@ -17,6 +18,7 @@ class TestWriterGraphviz < Minitest::Unit::TestCase
dag = Midori128_Dag.new(nb_rounds = nb_rounds)
# shave dag
atoms,operators = *shave_dag(dag.atoms, dag.operators)
writefile_graphviz(atoms, operators, "test#{nb_rounds}.dot")
# create initial model
model,variable_dict,xor_clauses = *create_abstract_model(
atoms,
......@@ -29,10 +31,10 @@ class TestWriterGraphviz < Minitest::Unit::TestCase
# add midori128 specific constraints
mc_operators = operators.filter{|op| op.class <= MixColumnsOperator}
mc_operators.each do |op|
model.add_constraints(Diff1.new(variable_dict[op.outputs[0]], variable_dict[op.inputs[1]], variable_dict[op.inputs[2]], variable_dict[op.inputs[3]]))
model.add_constraints(Diff1.new(variable_dict[op.outputs[1]], variable_dict[op.inputs[0]], variable_dict[op.inputs[2]], variable_dict[op.inputs[3]]))
model.add_constraints(Diff1.new(variable_dict[op.outputs[2]], variable_dict[op.inputs[0]], variable_dict[op.inputs[1]], variable_dict[op.inputs[3]]))
model.add_constraints(Diff1.new(variable_dict[op.outputs[3]], variable_dict[op.inputs[0]], variable_dict[op.inputs[1]], variable_dict[op.inputs[2]]))
# model.add_constraints(Diff1.new(variable_dict[op.outputs[0]], variable_dict[op.inputs[1]], variable_dict[op.inputs[2]], variable_dict[op.inputs[3]]))
# model.add_constraints(Diff1.new(variable_dict[op.outputs[1]], variable_dict[op.inputs[0]], variable_dict[op.inputs[2]], variable_dict[op.inputs[3]]))
# model.add_constraints(Diff1.new(variable_dict[op.outputs[2]], variable_dict[op.inputs[0]], variable_dict[op.inputs[1]], variable_dict[op.inputs[3]]))
# model.add_constraints(Diff1.new(variable_dict[op.outputs[3]], variable_dict[op.inputs[0]], variable_dict[op.inputs[1]], variable_dict[op.inputs[2]]))
xor_clauses.push(Set.new([variable_dict[op.outputs[0]], variable_dict[op.inputs[1]], variable_dict[op.inputs[2]], variable_dict[op.inputs[3]]]))
xor_clauses.push(Set.new([variable_dict[op.outputs[1]], variable_dict[op.inputs[0]], variable_dict[op.inputs[2]], variable_dict[op.inputs[3]]]))
xor_clauses.push(Set.new([variable_dict[op.outputs[2]], variable_dict[op.inputs[0]], variable_dict[op.inputs[1]], variable_dict[op.inputs[3]]]))
......
......@@ -264,10 +264,10 @@ def create_abstract_model(atoms, operators, solve_instructions)
operators.each do |op|
case op.class.name
when "XorOperator"
model.add_constraints(Diff1.new(
*op.inputs.map {|atom| variable_dict[atom]},
*op.outputs.map {|atom| variable_dict[atom]}
))
# model.add_constraints(Diff1.new(
# *op.inputs.map {|atom| variable_dict[atom]},
# *op.outputs.map {|atom| variable_dict[atom]}
# ))
xor_clauses.push(Set.new(
op.inputs.map {|atom| variable_dict[atom]}+
op.outputs.map {|atom| variable_dict[atom]}
......@@ -354,22 +354,22 @@ def add_diff_variables_mixcolumn_lines(model, atoms, operators, atom_to_variable
diffs.each do |d|
o1,o2,row,t = d[0],d[1],d[2],d[3]
if t == "I"
model.add_constraints(Diff1.new(*[
atom_to_variable[ids_to_op[o1].inputs[row]],
atom_to_variable[ids_to_op[o2].inputs[row]],
diffs_to_var[d]
]))
# model.add_constraints(Diff1.new(*[
# atom_to_variable[ids_to_op[o1].inputs[row]],
# atom_to_variable[ids_to_op[o2].inputs[row]],
# diffs_to_var[d]
# ]))
res_xors.push(Set.new([
ids_to_op[o1].inputs[row],
ids_to_op[o2].inputs[row],
diffs_to_var[d]
]))
else
model.add_constraints(Diff1.new(*[
atom_to_variable[ids_to_op[o1].outputs[row]],
atom_to_variable[ids_to_op[o2].outputs[row]],
diffs_to_var[d]
]))
# model.add_constraints(Diff1.new(*[
# atom_to_variable[ids_to_op[o1].outputs[row]],
# atom_to_variable[ids_to_op[o2].outputs[row]],
# diffs_to_var[d]
# ]))
res_xors.push(Set.new([
ids_to_op[o1].outputs[row],
ids_to_op[o2].outputs[row],
......@@ -425,7 +425,6 @@ def add_diff_variables_mixcolumn_lines(model, atoms, operators, atom_to_variable
Equals.new(Sum.new(*4.times.map{|row| diffs_to_var[[id1,id2,row,"I"]]}), 0),
Equals.new(Sum.new(*4.times.map{|row| diffs_to_var[[id1,id2,row,"O"]]}), 0)
))
model.add_constraints()
end
end
end
......
......@@ -347,7 +347,7 @@ def add_diff_variables_mixcolumn_lines(model, atoms, operators, atom_to_variable
mc_operators.each do |o1|
mc_operators.each do |o2|
if op_to_ids[o1] < op_to_ids[o2]
exprs_in = 4.times.map{|i| DiffExpr.new(atom_to_variable[o1.inputs[i]], atom_to_variable[o2.inputs[i]])}
exprs_in = 4.times.map{|i| DiffExpr.new(atom_to_variable[o1.inputs[i]], atom_to_variable[o2.inputs[i]])}
exprs_out = 4.times.map{|i| DiffExpr.new(atom_to_variable[o1.outputs[i]], atom_to_variable[o2.outputs[i]])}
model.add_constraints(IncludedIn.new(
Sum.new(
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment