(* Lennart Beringer, June 2015*)
(*This is a formalization in Gallina of Dan Bernstein's document "Salsa20 specification" (version 2005.04.27), Sections 1 - 9.*)
Definition map4 {A B} (f:A -> B) a :=
match a with (a0, a1, a2, a3) => (f a0, f a1, f a2, f a3) end.
Definition bytelist2Z (l:list byte) : Z :=
match l with
[a; b; c; d] => Byte.unsigned d + Byte.unsigned c * 2^8 + Byte.unsigned b * 2^16 + Byte.unsigned a * 2^24
| _ => 0 end.
Definition hexstring_to_Z s := bytelist2Z (hexstring_to_bytelist s).
Goal Zmod (hexstring_to_Z "c0a8787e"%string +
hexstring_to_Z "9fd1161d"%string) (2^32)  =
hexstring_to_Z "60798e9b"%string.
reflexivity. Qed.
(*Explicit definition of sum following Bernstein's "Salsa20 specification" paper:*)
Definition sum (a b:int): int :=
Int.repr (Zmod (Int.unsigned a + Int.unsigned b) (2^32)).
unfold sum. rewrite Int.unsigned_zero.
rewrite Z.add_0_r. rewrite <- Int.unsigned_repr_eq.
rewrite Int.repr_unsigned.
apply Int.repr_unsigned.
Qed.
unfold sum. rewrite Z.add_comm. trivial. Qed.
rewrite sum_sym. apply sum_zero_r. Qed.
(*We're using sum in the following because it appears to compute more nicely.*)
(*hexstring_to_int*)
Definition x2i s : int := Int.repr (hexstring_to_Z s).
Goal sum (x2i "c0a8787e"%string)
(x2i "9fd1161d"%string) =
x2i "60798e9b"%string.
reflexivity. Qed.
Goal Int.xor (x2i "c0a8787e"%string)
(x2i "9fd1161d"%string) =
x2i "5f796e63"%string.
reflexivity. Qed.
Definition QuadWord:Type := (int * int * int * int)%type.
Definition quarterround (y:QuadWord): QuadWord :=
match y with (y0, y1, y2, y3) =>
let z1 := Int.xor y1 (Int.rol (sum y0 y3) (Int.repr 7)) in
let z2 := Int.xor y2 (Int.rol (sum y0 z1) (Int.repr 9)) in
let z3 := Int.xor y3 (Int.rol (sum z1 z2) (Int.repr 13)) in
let z0 := Int.xor y0 (Int.rol (sum z2 z3) (Int.repr 18))
in (z0, z1, z2, z3)
end.
Definition quarterround_old (y:QuadWord): QuadWord :=
match y with (y0, y1, y2, y3) =>
let z1 := Int.xor y1 (Int.rol (sum y0 y3) (Int.repr 7)) in
let z2 := Int.xor y2 (Int.rol (sum z1 y0) (Int.repr 9)) in
let z3 := Int.xor y3 (Int.rol (sum z2 z1) (Int.repr 13)) in
let z0 := Int.xor y0 (Int.rol (sum z3 z2) (Int.repr 18))
in (z0, z1, z2, z3)
end.
destruct y as [[[y0 y1] y2] y3]. simpl. repeat rewrite sum_add.
repeat rewrite (Int.add_commut y0).
f_equal. f_equal. f_equal. f_equal. f_equal.
rewrite Int.add_commut.  f_equal.  f_equal. rewrite Int.add_commut. trivial.
f_equal. rewrite Int.add_commut. trivial.
Qed.
Definition zeroHex := x2i "00000000".
Goal zeroHex = Int.zero. reflexivity. Qed.
Goal quarterround (Int.zero, Int.zero, Int.zero, Int.zero) =
(Int.zero, Int.zero, Int.zero, Int.zero).
reflexivity. Qed.
Goal quarterround (map4 x2i ("d3917c5b", "55f1c407", "52a58a7a", "8f887a3b")%string) =
map4 x2i ("3e2f308c", "d90a8f36", "6ab2a923", "2883524c")%string. reflexivity. Qed.
(*MEMORYEXHAUSTION. USE VM_COMPUTE INSTEAD! Eval compute in (quarterround (Int.zero, Int.zero, Int.zero, Int.zero)).*)
Definition SixteenWord:Type := (QuadWord * QuadWord * QuadWord * QuadWord)%type.
Definition rowround(y: SixteenWord) : SixteenWord :=
match y with
((y0, y1, y2, y3), (y4, y5, y6, y7), (y8, y9, y10, y11), (y12, y13, y14, y15)) =>
match quarterround(y0, y1, y2, y3) with (z0, z1, z2, z3) =>
match quarterround(y5, y6, y7, y4) with (z5, z6, z7, z4) =>
match quarterround(y10, y11, y8, y9) with (z10, z11, z8, z9) =>
match quarterround(y15, y12, y13, y14) with (z15, z12, z13, z14) =>
((z0, z1, z2, z3), (z4, z5, z6, z7), (z8, z9, z10, z11), (z12, z13, z14, z15))
end end end end end.
Goal rowround (map4 x2i ("08521bd6", "1fe88837", "bb2aa576", "3aa26365"),
map4 x2i ("c54c6a5b", "2fc74c2f", "6dd39cc3", "da0a64f6"),
map4 x2i ("90a2f23d", "067f95a6", "06b35f61", "41e4732e"),
map4 x2i ("e859c100", "ea4d84b7", "0f619bff", "bc6e965a"))%string =
(map4 x2i ("a890d39d", "65d71596", "e9487daa", "c8ca6a86"),
map4 x2i ("949d2192", "764b7754", "e408d9b9", "7a41b4d1"),
map4 x2i ("3402e183", "3c3af432", "50669f96", "d89ef0a8"),
map4 x2i ("0040ede5", "b545fbce", "d257ed4f", "1818882d"))%string.
reflexivity. Qed.
Definition columnround(x: SixteenWord) : SixteenWord :=
match x with
((x0, x1, x2, x3), (x4, x5, x6, x7), (x8, x9, x10, x11), (x12, x13, x14, x15)) =>
match quarterround(x0, x4, x8, x12) with (y0, y4, y8, y12) =>
match quarterround(x5, x9, x13, x1) with (y5, y9, y13, y1) =>
match quarterround(x10, x14, x2, x6) with (y10, y14, y2, y6) =>
match quarterround(x15, x3, x7, x11) with (y15, y3, y7, y11) =>
((y0, y1, y2, y3), (y4, y5, y6, y7), (y8, y9, y10, y11), (y12, y13, y14, y15))
end end end end end.

Lemma columnround_rowround: forall x,
match columnround x with
((y0, y1, y2, y3), (y4, y5, y6, y7), (y8, y9, y10, y11), (y12, y13, y14, y15)) =>
match x with
((x0, x1, x2, x3), (x4, x5, x6, x7), (x8, x9, x10, x11), (x12, x13, x14, x15)) =>
((y0, y4, y8, y12), (y1, y5, y9, y13), (y2, y6, y10, y14), (y3, y7, y11, y15))
=
rowround ((x0, x4, x8, x12), (x1, x5, x9, x13), (x2, x6, x10, x14), (x3, x7, x11, x15))
end end.