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

add computation operators + tests for eq, xor, S, mixcolumns

parent 0cd65368
......@@ -14,19 +14,6 @@ require_relative "constraints/constraints"
require_relative "constraints/variables"
require_relative "constraints/model"
# ## encodes an atomic part of the memory in a cryptosystem
# class CryptoAtom
# attr_accessor :variable
# def initialize(variable)
# raise "CryptoAtom: variable should be of type Variable" unless variable.class == Variable
# @variable = variable
# end
# def register_to_model(model)
# raise "CryptoAtom: model should be of class Model" unless model.kind_of?(Model)
# model.add_variables(@variable)
# end
# end
## represents an elementary operator (Equality, S-box, )
# # examples
......@@ -38,12 +25,14 @@ class CryptoOperator
attr_accessor :inputs # vector of input variables
attr_accessor :outputs # vector of output variables
attr_accessor :constraints # constraints implementing the operator
attr_accessor :global_constraints # global constraints that should be registered while creating the CP model
##
# @param inputs [Vec<Variable>]
# @param outputs [Vec<Variable>]
# @param constraints [Vec<Constraint>]
protected def initialize(inputs:, outputs:, constraints:)
# @param global_constraints [Vec<String>]
protected def initialize(inputs:, outputs:, constraints:, global_constraints:)
inputs.each do |e|
raise "CryptoOperator: inputs should be of type <= Variable (current class: #{e.class})" unless e.class <= Variable
end
......@@ -53,16 +42,39 @@ class CryptoOperator
constraints.each do |e|
raise "CryptoOperator: constraint should be of type Constraint" unless e.class < Constraint
end
global_constraints.each do |e|
raise "CryptoOperator: global_constraints should be of type String" unless e.class == String
end
@inputs = inputs
@outputs = outputs
@constraints = constraints
@global_constraints = global_constraints
end
def register_to_model(model)
raise "CryptoOperator: model should be of class Model" unless model.kind_of?(Model)
model.add_constraints(*@constraints)
end
def check_inputs(values)
raise "CryptoOperator.check_inputs: values do not match (#{@values.length} should be #{@inputs.length})" unless values.length == @inputs.length
# TODO(all) check domains
end
def check_outputs(values)
raise "CryptoOperator.check_outputs: values do not match (#{@values.length} should be #{@outputs.length})" unless values.length == @outputs.length
# TODO(all) check domains
end
##
# @param values that should be the same size of the inputs (and same domains)
# @return output values
#
def compute(values)
raise "CryptoOperator: compute not implemented in child class"
end
end
## represents a DAG
......
require_relative '../cryptodag.rb'
## defines an constant node (it differs from the InputNode because the attacker cannot use it as an input)
# simply returns the value it inputs
class ConstantNode < CryptoDagNode
def initialize(x,y,name)
super(x:x, y:y, inputs:[], name:name)
end
end
\ No newline at end of file
# MixColumn operator.
require_relative '../cryptodag.rb'
require_relative "../cryptodag.rb"
require_relative "../operators/elementary.rb"
class MixColumnNode < CryptoDagNode
def initialize(input,matrix,name)
super(x:input.x, y:input.y, inputs:[input], name:name)
@matrix = matrix
@is_computed = false
end
def compute_call()
input = @inputs[0].compute()
res = Array.new(input.length()) { |i| Array.new(input[0].length()) { |j| 0 } }
@x.times do |i|
@y.times do |j|
m0 = @matrix[i][0]
m1 = @matrix[i][1]
m2 = @matrix[i][2]
m3 = @matrix[i][3]
s0 = input[0][j]
s1 = input[1][j]
s2 = input[2][j]
s3 = input[3][j]
res[i][j] = (m0*s0) ^ (m1*s1) ^ (m2*s2) ^ (m3*s3)
end
end
return res
end
def initialize(name:, input:, m:)
output = DTable.new("#{name}", input.type, input.dimensions)
@m = m
operators = [
MixColumnsOperator.new(input, output, m),
]
super(inputs: [input], outputs: [output], operators: operators, name: name)
end
end
......@@ -17,15 +17,4 @@ class ShiftRowsNode < CryptoDagNode
end
super(inputs: [input], outputs: [output], operators: operators, name: name)
end
# def compute_call()
# input = @inputs[0].compute()
# res = Array.new(input.length()) { |i| Array.new(input[0].length()) { |j| 0 } }
# @x.times() do |i| # shift row i by i positions
# @y.times() do |j|
# res[i][j] = input[i][(i + j) % 4]
# end
# end
# return res
# end
end
......@@ -6,7 +6,7 @@ require_relative "../constraints/expression"
## equality operator
class EqualityOperator < CryptoOperator
## Build an Equality ( "a + b = b" )
## Build an Equality ( "a = b" )
# @param input [ Expression ]
# @param output [ Variable ]
def initialize(input, output)
......@@ -14,8 +14,16 @@ class EqualityOperator < CryptoOperator
inputs: [input],
outputs: [output],
constraints: [Equality.new(input, output)],
global_constraints: [],
)
end
def compute(values)
check_inputs(values)
outputs = [values[0]]
check_outputs(values)
return outputs
end
end
## xor operator
......@@ -31,8 +39,16 @@ class XorOperator < CryptoOperator
Xor.new(*inputs),
output
)],
global_constraints: [],
)
end
def compute(values)
check_inputs(values)
outputs = [values.reduce(0, :^)]
check_outputs(outputs)
return outputs
end
end
## S operator
......@@ -49,8 +65,16 @@ class SOperator < CryptoOperator
[input, output],
s_array.length.times.map { |i| [i, s_array[i]] }
)],
global_constraints: ["table"],
)
end
def compute(values)
check_inputs(values)
outputs = [@s_array[values[0]]]
check_outputs(outputs)
return outputs
end
end
## MixColumns operator
......@@ -77,6 +101,7 @@ class MixColumnsOperator < CryptoOperator
inputs: inputs,
outputs: outputs,
constraints: constraints,
global_constraints: [],
)
end
......@@ -100,4 +125,49 @@ class MixColumnsOperator < CryptoOperator
raise "galois_field_multiply: m=#{m} not supported yet (for now, it should be <= 3)"
end
end
def compute(values)
check_inputs(values)
outputs = []
@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)
end
check_outputs(outputs)
return outputs
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
end
return p % 256
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
#!/usr/bin/ruby
## test equality operator
require_relative "../../operators/elementary.rb"
require "minitest/autorun"
require "pry"
class TestExecEq < Minitest::Unit::TestCase
def test_a()
a = Variable.new("a", 0..255)
b = Variable.new("b", 0..255)
op = EqualityOperator.new(a,b)
assert_equal(op.compute([0]), [0])
assert_equal(op.compute([1]), [1])
assert_equal(op.compute([2]), [2])
end
end
\ No newline at end of file
#!/usr/bin/ruby
## test MixColumns operator
require_relative "../../operators/elementary.rb"
require "minitest/autorun"
require "pry"
class TestExecS < Minitest::Unit::TestCase
def test_aes()
inputs = [
Variable.new("a", 0.255),
Variable.new("b", 0.255),
Variable.new("c", 0.255),
Variable.new("d", 0.255),
]
outputs = [
Variable.new("e", 0.255),
Variable.new("f", 0.255),
Variable.new("g", 0.255),
Variable.new("h", 0.255),
]
m = [
[2,3,1,1],
[1,2,3,1],
[1,1,2,3],
[3,1,1,2],
]
op = MixColumnsOperator.new(inputs, outputs, m)
assert_equal(op.compute([0,0,0,0]), [0,0,0,0])
assert_equal(op.compute([1,1,1,1]), [1,1,1,1])
assert_equal(op.compute([219,19,83,69]), [142,77,161,188])
assert_equal(op.compute([242,10,34,92]), [159,220,88,157])
assert_equal(op.compute([198,198,198,198]), [198,198,198,198])
assert_equal(op.compute([212,212,212,213]), [213,213,215,214])
assert_equal(op.compute([45,38,49,76]), [77,126,189,248])
end
def test_midori()
inputs = [
Variable.new("a", 0.255),
Variable.new("b", 0.255),
Variable.new("c", 0.255),
Variable.new("d", 0.255),
]
outputs = [
Variable.new("e", 0.255),
Variable.new("f", 0.255),
Variable.new("g", 0.255),
Variable.new("h", 0.255),
]
m = [
[0,1,1,1],
[1,0,1,1],
[1,1,0,1],
[1,1,1,0],
]
op = MixColumnsOperator.new(inputs, outputs, m)
assert_equal(op.compute([0,0,0,0]), [0,0,0,0])
assert_equal(op.compute([1,1,1,1]), [1,1,1,1])
assert_equal(op.compute([
0xa5,
0x08,
0x10,
0x88,
]),[
0x90,
0x3d,
0x25,
0xbd,
])
assert_equal(op.compute([
0x56,
0x2c,
0x77,
0xa3,
]),[
0xf8,
0x82,
0xd9,
0x0d,
])
assert_equal(op.compute([
0xb8,
0xb7,
0xac,
0x84,
]),[
0x9f,
0x90,
0x8b,
0xa3,
])
assert_equal(op.compute([
0x3f,
0x9f,
0xc2,
0x2b,
]),[
0x76,
0xd6,
0x8b,
0x62,
])
end
end
\ No newline at end of file
#!/usr/bin/ruby
## test S operator
require_relative "../../operators/elementary.rb"
require "minitest/autorun"
require "pry"
class TestExecS < Minitest::Unit::TestCase
def test_a()
a = Variable.new("a", 0..255)
b = Variable.new("b", 0..255)
op = SOperator.new(a, b, (0..255).map{|i| i*2 % 256}.to_a)
assert_equal(op.compute([0]), [0])
assert_equal(op.compute([1]), [2])
assert_equal(op.compute([2]), [4])
assert_equal(op.compute([128]), [0])
end
end
\ No newline at end of file
#!/usr/bin/ruby
## test Xor operator
require_relative "../../operators/elementary.rb"
require "minitest/autorun"
require "pry"
class TestExecXor < Minitest::Unit::TestCase
def test_2input()
a = Variable.new("a", 0..255)
b = Variable.new("b", 0..255)
c = Variable.new("c", 0..255)
op = XorOperator.new(a,b, c)
assert_equal(op.compute([0,0]), [0])
assert_equal(op.compute([0,1]), [1])
assert_equal(op.compute([1,1]), [0])
assert_equal(op.compute([3,2]), [1])
end
def test_3input()
a = Variable.new("a", 0..255)
b = Variable.new("b", 0..255)
c = Variable.new("c", 0..255)
d = Variable.new("d", 0..255)
op = XorOperator.new(a,b,c, d)
assert_equal(op.compute([0,0,0]), [0])
assert_equal(op.compute([0,0,1]), [1])
assert_equal(op.compute([0,1,1]), [0])
assert_equal(op.compute([1,1,1]), [1])
assert_equal(op.compute([1,2,3]), [0])
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