Commit 997c5110 authored by Luc Libralesso's avatar Luc Libralesso
Browse files

add AES keyschedule + tests keyschedule

parent a6320b1c
# AES Key Schedule operator. Takes as a parameter a key and expands it
require_relative "../cryptodag.rb"
require_relative "../operators/elementary.rb"
class AESKeyScheduleNode < CryptoDagNode
##
# @param name [String]: name of the node
# @param input [DTable<Variable> of size 4x4] input variables
# @param subtable [Vec<u8> of size |u8|] byte substitution table
# @param rcon [u8]: round constant
def initialize(name:, input:, subtable:, rcon:)
raise "AESKeyScheduleNode: incorrect input size (#{input.dimensions} should be [4,4])" unless input.dimensions == [4,4]
output = DTable.new("#{name}", input.type, input.dimensions)
operators = []
@subtable = subtable
# apply rotation (first byte becomes last, second first, ...)
rotated_bytes = [
Variable.new("#{name}_rot_0", 0..255),
Variable.new("#{name}_rot_1", 0..255),
Variable.new("#{name}_rot_2", 0..255),
Variable.new("#{name}_rot_3", 0..255),
]
4.times do |i|
operators.push(EqualityOperator.new(
input[(i+1) % 4][3], # last column of the input
rotated_bytes[i],
))
end
# apply substitution
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),
Variable.new("#{name}_sub_rot_3", 0..255),
]
4.times do |i|
operators.push(SOperator.new(
rotated_bytes[i],
sub_rotated_bytes[i],
@subtable
))
end
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),
Variable.new("rcon_#{name}_0", 0..256, value=0)
]
# 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],
input[i][0],
rcon_col[i],
output[i][0]
))
end
3.times do |i|
index = i+1 # index in [1..3]
4.times do |j| # for each row of the column
operators.push(XorOperator.new(
input[j][index],
output[j][index-1],
output[j][index]
))
end
end
super(inputs: [input], outputs: [output], operators: operators, name: name)
end
end
......@@ -132,7 +132,6 @@ class MixColumnsOperator < CryptoOperator
@inputs.length.times do |i|
tmp = 0
@inputs.length.times do |j|
# tmp = tmp ^ compute_galois_multiply(values[j], @m[i][j])
tmp = tmp ^ g_mul(values[j], @m[i][j])
end
outputs.push(tmp)
......@@ -140,34 +139,23 @@ class MixColumnsOperator < CryptoOperator
check_outputs(outputs)
return outputs
end
end
def g_mul(a, b)
p = 0
8.times do |counter|
if (b&1) != 0
p ^= a;
end
hi_bit_set = (a&0x80) != 0
a <<= 1
if hi_bit_set
a ^= 0x1B
end
b >>= 1
## Galois Field multiplication (a.b)
# @return a.b
def g_mul(a, b)
p = 0
8.times do |counter|
if (b&1) != 0
p ^= a;
end
return p % 256
hi_bit_set = (a&0x80) != 0
a <<= 1
if hi_bit_set
a ^= 0x1B
end
b >>= 1
end
# def compute_galois_multiply(e, m)
# if m == 0
# return 0
# elsif m == 1
# return e
# elsif m == 2
# return (e*2) % 256
# elsif m == 3
# return ((e*2)^e) % 256
# else
# raise "compute_galois_multiply: m=#{m} not supported yet (for now, it should be <= 3)"
# end
# end
end
return p % 256
end
\ No newline at end of file
......@@ -28,15 +28,24 @@ def compute_set_of_operators(input_values, input_variables, output_variables, op
end
end
dg = RGL::DirectedAdjacencyGraph[*adj_list]
sorted_vertices = dg.topsort_iterator.to_a
sorted_vertices = sorted_vertices.filter{|v| v.class <= CryptoOperator}
# feed a variable map with initial values
value_map = {} # associates variables to their value
input_values.length.times do |i|
value_map[input_variables[i]] = input_values[i]
end
# register variables where a value is already registered
for e in adj_list
if e.class <= Variable
if e.value != nil
value_map[e] = e.value
end
end
end
dg = RGL::DirectedAdjacencyGraph[*adj_list]
sorted_vertices = dg.topsort_iterator.to_a
sorted_vertices = sorted_vertices.filter{|v| v.class <= CryptoOperator}
# execute operators
sorted_vertices.each do |op|
raise "compute_set_of_operators: should be a CryptoOperator (current #{op.class})" unless op.class <= CryptoOperator
......@@ -45,7 +54,7 @@ def compute_set_of_operators(input_values, input_variables, output_variables, op
if value_map.include?(e)
values.push(value_map[e])
else
raise "compute_set_of_operators: bad topological sort"
raise "compute_set_of_operators: bad topological sort (variable #{e} should be computed)"
end
end
outputs = op.compute(values)
......
#!/usr/bin/ruby
## test AESKeyScheduleNode
require_relative "../../nodes/input.rb"
require_relative "../../nodes/aeskeyschedule.rb"
require_relative "../../simulate_cryptodag.rb"
require "minitest/autorun"
require "pry"
class TestExecAesKeySchedule < Minitest::Unit::TestCase
def setup()
@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
]
end
def test_a()
k = InputNode.new(name:"K0",dimensions:[4,4])
ks = AESKeyScheduleNode.new(name:"KS", input:k.outputs[0], subtable:@sbox, rcon:1)
computed_outputs = compute_set_of_operators(
[0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0],
[
k.outputs[0][0][0],k.outputs[0][1][0],k.outputs[0][2][0],k.outputs[0][3][0],
k.outputs[0][0][1],k.outputs[0][1][1],k.outputs[0][2][1],k.outputs[0][3][1],
k.outputs[0][0][2],k.outputs[0][1][2],k.outputs[0][2][2],k.outputs[0][3][2],
k.outputs[0][0][3],k.outputs[0][1][3],k.outputs[0][2][3],k.outputs[0][3][3],
],
[
ks.outputs[0][0][0],ks.outputs[0][1][0],ks.outputs[0][2][0],ks.outputs[0][3][0],
ks.outputs[0][0][1],ks.outputs[0][1][1],ks.outputs[0][2][1],ks.outputs[0][3][1],
ks.outputs[0][0][2],ks.outputs[0][1][2],ks.outputs[0][2][2],ks.outputs[0][3][2],
ks.outputs[0][0][3],ks.outputs[0][1][3],ks.outputs[0][2][3],ks.outputs[0][3][3],
],
ks.operators
)
assert_equal(computed_outputs, [
0x62, 0x63, 0x63, 0x63, 0x62, 0x63, 0x63, 0x63, 0x62, 0x63, 0x63, 0x63, 0x62, 0x63, 0x63, 0x63
])
end
def test_b()
k = InputNode.new(name:"K0",dimensions:[4,4])
ks = AESKeyScheduleNode.new(name:"KS", input:k.outputs[0], subtable:@sbox, rcon:2)
computed_outputs = compute_set_of_operators(
[0x62, 0x63, 0x63, 0x63, 0x62, 0x63, 0x63, 0x63, 0x62, 0x63, 0x63, 0x63, 0x62, 0x63, 0x63, 0x63],
[
k.outputs[0][0][0],k.outputs[0][1][0],k.outputs[0][2][0],k.outputs[0][3][0],
k.outputs[0][0][1],k.outputs[0][1][1],k.outputs[0][2][1],k.outputs[0][3][1],
k.outputs[0][0][2],k.outputs[0][1][2],k.outputs[0][2][2],k.outputs[0][3][2],
k.outputs[0][0][3],k.outputs[0][1][3],k.outputs[0][2][3],k.outputs[0][3][3],
],
[
ks.outputs[0][0][0],ks.outputs[0][1][0],ks.outputs[0][2][0],ks.outputs[0][3][0],
ks.outputs[0][0][1],ks.outputs[0][1][1],ks.outputs[0][2][1],ks.outputs[0][3][1],
ks.outputs[0][0][2],ks.outputs[0][1][2],ks.outputs[0][2][2],ks.outputs[0][3][2],
ks.outputs[0][0][3],ks.outputs[0][1][3],ks.outputs[0][2][3],ks.outputs[0][3][3],
],
ks.operators
)
assert_equal(computed_outputs, [
0x9b, 0x98, 0x98, 0xc9, 0xf9, 0xfb, 0xfb, 0xaa, 0x9b, 0x98, 0x98, 0xc9, 0xf9, 0xfb, 0xfb, 0xaa
])
end
end
\ No newline at end of file
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