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

Merge branch 'master' into 'optimize_ctree'

# Conflicts:
#   main.rb
parents 50bab7dc d862b6f9
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "bit-set"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de"
dependencies = [
"bit-vec",
]
[[package]]
name = "bit-vec"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0dc55f2d8a1a85650ac47858bb001b4c0dd73d79e3c455a842925e68d29cd3"
[[package]]
name = "computemds"
version = "0.1.0"
dependencies = [
"bit-set",
]
......@@ -7,3 +7,4 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bit-set = "0.5.2"
\ No newline at end of file
use std::collections::{HashSet};
use bit_set::BitSet;
pub type Variable = usize;
pub type XorClause = BitSet;
pub fn clause_from_vec(v:Vec<usize>) -> XorClause {
let mut res = XorClause::new();
for e in v {
res.insert(e);
}
return res;
}
pub fn generate_clauses(clauses:&Vec<XorClause>, maxsize:usize) -> HashSet<XorClause> {
let mut res:HashSet<XorClause> = HashSet::new();
let mut new_clauses = clauses.clone();
while !new_clauses.is_empty() {
// add new_clauses to res
while !new_clauses.is_empty() {
res.insert(new_clauses.pop().unwrap());
}
// iterate over all couples of active clauses
for c1 in res.iter() {
for c2 in res.iter() {
let mut inter = c1.clone();
inter.intersect_with(c2);
if !inter.is_empty() {
let mut union = c1.clone();
union.union_with(c2);
union.symmetric_difference_with(&inter);
if union.len() <= maxsize && !res.contains(&union) {
new_clauses.push(union);
}
}
}
}
}
return res;
}
\ No newline at end of file
mod generatexors;
use generatexors::{XorClause, Variable, generate_clauses, clause_from_vec};
pub struct BoolVec {
pub a:bool,
pub b:bool,
......@@ -121,31 +126,69 @@ pub fn aesmixcolumns(input:&ByteVec) -> ByteVec {
fn main() {
println!("### start compute mds");
// test vectors
// println!("test vector: {:?}", aesmixcolumns(&ByteVec::new(219,19,83,69)));
println!("test vector: {:?}", aesmixcolumns(&ByteVec::new(242,10,34,92)));
// println!("test vector: {:?}", aesmixcolumns(&ByteVec::new(1,1,1,1)));
// run tests
let res = computemds(aesmixcolumns);
// display result table
println!("result occurences");
for (i,&e) in res.iter().enumerate() {
if e > 0 {
println!("{:08b}:\t{}",i as u8,e);
// let res = generate_clauses(vec![
// clause_from_vec(vec![0,1,2]),
// clause_from_vec(vec![2,3,4]),
// ], 4);
let inputclauses:Vec<XorClause> = (vec![
vec![1,2,3],
vec![3,31,5],
vec![5,32,7],
vec![7,33,9],
vec![9,34,1],
vec![19,34,11],
vec![17,36,19],
vec![15,32,17],
vec![13,35,15],
vec![11,12,13],
vec![21,22,23],
vec![23,35,25],
vec![25,33,27],
vec![27,36,29],
vec![29,31,21],
]).iter().map(|e| clause_from_vec(e.to_vec())).collect();
let res_4 = generate_clauses(&inputclauses, 4);
println!("res_4:\n{:?}",res_4);
println!("restest\n{:?}", generate_clauses(&inputclauses,3));
let res_more = generate_clauses(&inputclauses, 6);
// println!("\nres_more:\n{:?}",res_more);
let mut filtered_res_more:Vec<XorClause> = vec![];
for e in res_more {
// if e.contains(2) && e.contains(12) && e.contains(22) {
// println!("{:?}",e);
// }
if e.len() <= 4 {
filtered_res_more.push(e);
}
}
// count nb occurences
let mut nbuniquevalues:usize = 0;
let mut nbtotal:usize = 0;
for &e in res.iter() {
nbtotal += e;
if e > 0 {
nbuniquevalues += 1;
}
}
if nbtotal != 256*256*256*256 {
panic!("error in total elements ({})", nbtotal);
}
println!("nb unique values: {}", nbuniquevalues);
println!("\nfiltered_res_more:\n{:?}",filtered_res_more);
println!("size4: {}\t sizen: {}", res_4.len(), filtered_res_more.len());
// println!("### start compute mds");
// // test vectors
// // println!("test vector: {:?}", aesmixcolumns(&ByteVec::new(219,19,83,69)));
// println!("test vector: {:?}", aesmixcolumns(&ByteVec::new(242,10,34,92)));
// // println!("test vector: {:?}", aesmixcolumns(&ByteVec::new(1,1,1,1)));
// // run tests
// let res = computemds(aesmixcolumns);
// // display result table
// println!("result occurences");
// for (i,&e) in res.iter().enumerate() {
// if e > 0 {
// println!("{:08b}:\t{}",i as u8,e);
// }
// }
// // count nb occurences
// let mut nbuniquevalues:usize = 0;
// let mut nbtotal:usize = 0;
// for &e in res.iter() {
// nbtotal += e;
// if e > 0 {
// nbuniquevalues += 1;
// }
// }
// if nbtotal != 256*256*256*256 {
// panic!("error in total elements ({})", nbtotal);
// }
// println!("nb unique values: {}", nbuniquevalues);
}
......@@ -14,8 +14,8 @@ class MixColumnNode < CryptoDagNode
def compute_call()
input = @inputs[0].compute()
res = Array.new(input.length()) { |i| Array.new(input[0].length()) { |j| 0 } }
@y.times do |i|
@x.times do |j|
@x.times do |i|
@y.times do |j|
m0 = @matrix[i][0]
m1 = @matrix[i][1]
m2 = @matrix[i][2]
......
......@@ -12,8 +12,8 @@ class ShiftRowsNode < CryptoDagNode
def compute_call()
input = @inputs[0].compute()
res = Array.new(input.length()) { |i| Array.new(input[0].length()) { |j| 0 } }
@y.times() do |i| # shift row i by i positions
@x.times() do |j|
@x.times() do |i| # shift row i by i positions
@y.times() do |j|
res[i][j] = input[i][(i+j) % 4]
end
end
......
......@@ -13,8 +13,8 @@ class ShuffleCellNode < CryptoDagNode
def compute_call()
input = @inputs[0].compute()
res = Array.new(input.length()) { |i| Array.new(input[0].length()) { |j| 0 } }
@y.times() do |i|
@x.times() do |j|
@x.times() do |i|
@y.times() do |j|
row, col = permuted_indices(i, j)
res[j][i] = input[row][col]
end
......
......@@ -12,8 +12,8 @@ class SubCellNode < CryptoDagNode
def compute_call()
res = @inputs[0].compute()
@y.times do |i|
@x.times do |j|
@x.times do |i|
@y.times do |j|
res[i][j] = @sub_fn.call(i, j, res[i][j])
end
end
......
......@@ -18,11 +18,11 @@ class XorNode < CryptoDagNode
def compute_call()
sx = @x
sy = @y
res = Array.new(sy) { |i| Array.new(sx) { |j| 0 } }
res = Array.new(sx) { |i| Array.new(sy) { |j| 0 } }
@inputs.each() do |e|
computed_input = e.compute()
sy.times do |i|
sx.times do |j|
sx.times do |i|
sy.times do |j|
res[i][j] ^= computed_input[i][j]
end
end
......
# XOR generation
## conjecture 1:
Given a set of xor clauses, all generated clauses of size <= 4 can be found by a "path" that does not contain any clause of size > 4. If this conjecture is true, we can benefit from a strong abstraction with a ``little'' generation cost.
## counter-example for conjecture 1:
input clauses:
```
{{21, 29, 31}, {13, 15, 35}, {21, 22, 23}, {15, 17, 32}, {27, 29, 36}, {3, 5, 31}, {5, 7, 32}, {1, 2, 3}, {11, 12, 13}, {7, 9, 33}, {1, 9, 34}, {11, 19, 34}, {17, 19, 36}, {23, 25, 35}, {25, 27, 33}}```
```
set of clauses of size <= 4 we obtain if we do not consider clauses of size >= 5 (37 clauses):
```
{{2, 3, 9, 34}, {1, 7, 33, 34}, {15, 19, 32, 36}, {7, 9, 33}, {1, 2, 5, 31}, {1, 9, 11, 19}, {3, 5, 21, 29}, {1, 2, 3}, {11, 12, 13}, {21, 22, 23}, {15, 17, 32}, {12, 13, 19, 34}, {25, 29, 33, 36}, {5, 9, 32, 33}, {22, 23, 29, 31}, {}, {23, 27, 33, 35}, {21, 29, 31}, {23, 25, 35}, {7, 9, 25, 27}, {3, 5, 31}, {1, 9, 34}, {5, 7, 32}, {27, 29, 36}, {17, 19, 36}, {13, 17, 32, 35}, {17, 19, 27, 29}, {11, 17, 34, 36}, {3, 7, 31, 32}, {11, 12, 15, 35}, {25, 27, 33}, {13, 15, 35}, {13, 15, 23, 25}, {21, 22, 25, 35}, {21, 27, 31, 36}, {11, 19, 34}, {5, 7, 15, 17}}
```
set of clauses <= 4 we obtain by also considering clauses of size >= 5 (41 clauses):
```
{{12, 13, 19, 34}, {2, 3, 9, 34}, {13, 15, 23, 25}, {21, 22, 23}, {22, 23, 29, 31}, {11, 17, 34, 36}, {}, {3, 7, 31, 32}, {13, 15, 35}, {15, 17, 32}, {25, 27, 33}, {1, 9, 34}, {1, 2, 5, 31}, {1, 3, 12, 22}, {11, 19, 34}, {5, 9, 32, 33}, {21, 29, 31}, {1, 9, 11, 19}, {21, 22, 25, 35}, {3, 5, 21, 29}, {11, 12, 15, 35}, {2, 12, 21, 23}, {1, 7, 33, 34}, {17, 19, 36}, {7, 9, 25, 27}, {15, 19, 32, 36}, {23, 25, 35}, {13, 17, 32, 35}, {17, 19, 27, 29}, {25, 29, 33, 36}, {7, 9, 33}, {3, 5, 31}, {21, 27, 31, 36}, {2, 12, 22}, {11, 12, 13}, {5, 7, 15, 17}, {27, 29, 36}, {1, 2, 3}, {23, 27, 33, 35}, {5, 7, 32}, {2, 11, 13, 22}}
```
For instance, in this case, the limitation does not allow to infer the clause `{2, 12, 22}`, but this clause can be obtained by xor-ing all the input clauses.
\ No newline at end of file
#!/usr/bin/ruby
require_relative '../cryptosystems/midori128/midori128.rb'
require_relative "../writers/step1diff"
require_relative "../backends/minizinc"
## generates minizinc models for midori
#
def main
for i in 3..20
dag = Midori128_Dag.new(nb_rounds=i)
file = File.open("tmp/diff_midori_#{i}.mzn", "w")
model = Step1Diff.new.generate_extended_model(dag, nb_rounds)
file.puts Minizinc.new.generate_code(model)
file.close
end
end
main
\ No newline at end of file
......@@ -9,6 +9,7 @@ require_relative "./cryptosystems/midori128/midori128"
require_relative "writers/graphviz"
require_relative "writers/step1opt"
require_relative "writers/constraintwriter"
require_relative "writers/step1diff"
require_relative "backends/minizinc"
......
......@@ -229,4 +229,31 @@ class TestMultiMatrix < MiniTest::Unit::TestCase
end
end
end
def test_submatrix
i = 0
@m.each_index do |*index|
@m[*index] = i
i += 1
end
ident = @m.sub_matrix(0..2,0..3,0..4)
@m.each_index do |i|
assert_equal( @m.at(i), ident.at(i) )
end
m2 = MultiMatrix.build(:int, [5, 5])
i = 0
m2.each_index do |*index|
m2[*index] = i
i += 1
end
small = m2.sub_matrix(1..2,1..2)
assert_equal(6, small[0,0])
assert_equal(7, small[0,1])
assert_equal(11, small[1,0])
assert_equal(12, small[1,1])
end
end
......@@ -262,6 +262,27 @@ class MultiMatrix
@data.clone.freeze
end
def sub_matrix(*sub_range)
cdr = sub_range.map{ |r| r.to_a }
car = cdr.shift
shift = sub_range.map(&:first)
size = sub_range.map(&:size)
destination = Array.new(sub_range.size)
matrix = MultiMatrix.build(@type, size)
car.product(*cdr) do |coordinates|
check_all_dimensions(coordinates)
coordinates.each_index do |i|
destination[i] = coordinates[i] - shift[i]
end
matrix[*destination] = self[*coordinates]
end
matrix
end
class DimensionError < Exception
end
end
require 'set'
require_relative "abstractwriter"
require_relative "../constraints/all"
class DiffHalfInfo
attr_accessor :t, :i, :j, :k, :v
def initialize(t:,i:,j:,k:,v:)
@t=t
@i=i
@j=j
@k=k
@v=v
end
def v_id()
return "#{@t}_#{@i}_#{@k}"
end
end
## Diff model implementation
class Step1Diff < AbstractWriter
def generate_extended_model(dag, rounds)
generate_variables(dag)
generate_constraints(dag)
add_diff_variables(dag, rounds)
generate_objective(dag)
end
def add_node_variables(node)
name = node.name
case node
when ConstantNode
matrix = ConstantTable.new(name, "int", [node.y, node.x], Array.new(node.x * node.y, 0))
@model.add_variables(matrix)
@variable_dict[node] = matrix
else
table = Table.new(name, "bool", [node.y, node.x])
@model.add_variables(table)
@variable_dict[node] = table
end
end
## adds diff variables (for each couple of mixcolumn states columns, diff in the column position)
def add_diff_variables(dag, rounds)
# compute diff variables
var_diff_by_name = {}
(rounds-1).times do |i1|
(rounds-1).times do |i2|
4.times do |j|
4.times do |k|
yname = "diff_y_ia_#{i1}_j_#{j}_k_#{k}_ib_#{i2}"
vy = Variable.new(yname, "bool")
var_diff_by_name[yname] = vy
@model.add_variables(vy)
zname = "diff_z_ia_#{i1}_j_#{j}_k_#{k}_ib_#{i2}"
vz = Variable.new(zname, "bool")
var_diff_by_name[zname] = vz
@model.add_variables(vz)
# kname = "diff_k_ia_#{i1}_j_#{j}_k_#{k}_ib_#{i2}"
# vk = Variable.new(kname, "bool")
# var_diff_by_name[kname] = vk
# @model.add_variables(vk)
end
end
end
end
# diff symmetry: diff_b1_b2 = diff_b2_b1 (C'7)
(rounds-1).times do |i1|
i1.times do |i2|
4.times do |j|
4.times do |k|
@model.add_constraints(Equality.new(
var_diff_by_name["diff_y_ia_#{i1}_j_#{j}_k_#{k}_ib_#{i2}"],
var_diff_by_name["diff_y_ia_#{i2}_j_#{j}_k_#{k}_ib_#{i1}"]
))
@model.add_constraints(Equality.new(
var_diff_by_name["diff_z_ia_#{i1}_j_#{j}_k_#{k}_ib_#{i2}"],
var_diff_by_name["diff_z_ia_#{i2}_j_#{j}_k_#{k}_ib_#{i1}"]
))
end
end
end
end
# diff transitivity: diff_b1_b2 + diff_b2_b3 + diff_b1_b3 != 1 (C'8)
(rounds-1).times do |i1|
(rounds-1).times do |i2|
(rounds-1).times do |i3|
if i3 > i2
4.times do |j|
4.times do |k|
@model.add_constraints(Diff1.new(
var_diff_by_name["diff_y_ia_#{i1}_j_#{j}_k_#{k}_ib_#{i3}"],
var_diff_by_name["diff_y_ia_#{i1}_j_#{j}_k_#{k}_ib_#{i2}"],
var_diff_by_name["diff_y_ia_#{i2}_j_#{j}_k_#{k}_ib_#{i3}"]
))
@model.add_constraints(Diff1.new(
var_diff_by_name["diff_z_ia_#{i1}_j_#{j}_k_#{k}_ib_#{i3}"],
var_diff_by_name["diff_z_ia_#{i1}_j_#{j}_k_#{k}_ib_#{i2}"],
var_diff_by_name["diff_z_ia_#{i2}_j_#{j}_k_#{k}_ib_#{i3}"]
))
end
end
end
end
end
end
# linking between diff and abstract byte (C'9)
yvars = (rounds-1).times.collect{|i| nil}
zvars = (rounds-1).times.collect{|i| nil}
for n in dag.find_all_nodes().filter{|n| n.class == MixColumnNode }
i = n.name.split("_")[1].to_i
in_var = n.inputs.map { |inputnode| @variable_dict[inputnode] }[0]
out_var = @variable_dict[n]
yvars[i] = in_var
zvars[i] = out_var
end
(rounds-1).times do |i1|
(rounds-1).times do |i2|
4.times do |j|
4.times do |k|
@model.add_constraints(Diff1.new(
yvars[i1][j][k],
yvars[i2][j][k],
var_diff_by_name["diff_y_ia_#{i1}_j_#{j}_k_#{k}_ib_#{i2}"]
))
@model.add_constraints(Diff1.new(
zvars[i1][j][k],
zvars[i2][j][k],
var_diff_by_name["diff_z_ia_#{i1}_j_#{j}_k_#{k}_ib_#{i2}"]
))
end
end
end
end
# create xvars variables
xvars = (rounds).times.collect{|i| nil}
xvars[0] = @variable_dict[dag.find_all_nodes().filter{|n| n.name == "addKey" }[0]]
for n in dag.find_all_nodes().filter{|n| n.name.split("_")[0] == "endRound" }
i = n.name.split("_")[1].to_i
xvars[i+1] = @variable_dict[n]
end
# link diffz and xi and x_{i+1}
# XOR(DIFFZ[i1, j, k, i2], X[i1 + 1, j, k], X[i2 + 1, j, k])
(rounds-1).times do |i1|
(rounds-1).times do |i2|
4.times do |j|
4.times do |k|
@model.add_constraints(Diff1.new(
var_diff_by_name["diff_z_ia_#{i1}_j_#{j}_k_#{k}_ib_#{i2}"],
xvars[i1+1][j][k],
xvars[i2+1][j][k]
))
end
end
end
end
# Quasi-MDS property over differents rounds
(rounds-1).times do |i1|
i1.times do |i2|
4.times do |k|
model.add_constraints(IncludedIn.new(
Sum.new(
var_diff_by_name["diff_y_ia_#{i1}_j_#{0}_k_#{k}_ib_#{i2}"],
var_diff_by_name["diff_y_ia_#{i1}_j_#{1}_k_#{k}_ib_#{i2}"],
var_diff_by_name["diff_y_ia_#{i1}_j_#{2}_k_#{k}_ib_#{i2}"],
var_diff_by_name["diff_y_ia_#{i1}_j_#{3}_k_#{k}_ib_#{i2}"],
var_diff_by_name["diff_z_ia_#{i1}_j_#{0}_k_#{k}_ib_#{i2}"],
var_diff_by_name["diff_z_ia_#{i1}_j_#{1}_k_#{k}_ib_#{i2}"],
var_diff_by_name["diff_z_ia_#{i1}_j_#{2}_k_#{k}_ib_#{i2}"],
var_diff_by_name["diff_z_ia_#{i1}_j_#{3}_k_#{k}_ib_#{i2}"],
),
Set[0,4,5,6,7,8]
))
end
end
end
@var_diff_by_name = var_diff_by_name
@model
end
def add_node_constraints(node)
name = node.name
modelinputvars = node.inputs.map { |inputnode| @variable_dict[inputnode] }
outputvar = @variable_dict[node]
case node
when ConstantNode
# nothing to do here
when InputNode
# nothing to do here
when OutputNode
# input[*] = output[*]
outputvar.each_index do |*index|
@model.add_constraints(
Equality.new(modelinputvars[0][*index], outputvar[*index])
)
end
when KeySeparationNode
# input[*] = output[*]
outputvar.each_index do |*index|
@model.add_constraints(
Equality.new(modelinputvars[0][*index], outputvar[*index])
)
end
when XorNode
outputvar.each_index do |*index|
@model.add_constraints(Diff1.new(
modelinputvars[0][*index],
modelinputvars[1][*index],
outputvar[*index]
))
end
when SubCellNode
# input[*] = output[*]
outputvar.each_index do |*index|
@model.add_constraints(
Equality.new(modelinputvars[0][*index], outputvar[*index])
)
end
when ShuffleCellNode
# apply permutation input[indices] = output[shuffle(indices)]
outputvar.each_index do |*index|
y,x = *index
@model.add_constraints(Equality.new(
outputvar[x,y],
modelinputvars[0][*node.permuted_indices(*index)],
))
end
when MixColumnNode
coeff_tab = [
[[1, 2, 3], 0],
[[0, 2, 3], 1],
[[0, 1, 3], 2],
[[0, 1, 2], 3], </