mte/unikernel/duniverse/eqaf/lib/eqaf.ml

390 lines
12 KiB
OCaml
Raw Normal View History

2025-11-11 02:07:51 +01:00
let[@inline always] char_chr ch =
(* Char.chr contains a branch on [ch] and a plt indirection, this
* implementation ensures well-formedness by construction and avoids that: *)
Char.unsafe_chr (ch land 0xff)
let[@inline] get x i = String.unsafe_get x i |> Char.code
(* XXX(dinosaure): we use [unsafe_get] to avoid jump to exception:
sarq $1, %rbx
movzbq (%rax,%rbx), %rax
leaq 1(%rax,%rax), %rax
ret
*)
external unsafe_get_int16 : string -> int -> int = "%caml_string_get16u"
let[@inline] get16 x i = unsafe_get_int16 x i
(* XXX(dinosaure): same as [unsafe_get] but for [int16]:
sarq $1, %rbx
movzwq (%rax,%rbx), %rax
leaq 1(%rax,%rax), %rax
ret
*)
let equal ~ln a b =
let l1 = ln asr 1 in
(*
sarq $1, %rcx
orq $1, %rcx
*)
let r = ref 0 in
(*
movq $1, %rdx
*)
for i = 0 to pred l1 do r := !r lor (get16 a (i * 2) lxor get16 b (i * 2)) done ;
(*
movq $1, %rsi
addq $-2, %rcx
cmpq %rcx, %rsi
jg .L104
.L105:
leaq -1(%rsi,%rsi), %r8
sarq $1, %r8
movzwq (%rdi,%r8), %r9
leaq 1(%r9,%r9), %r9
movzwq (%rbx,%r8), %r8
leaq 1(%r8,%r8), %r8
// [unsafe_get_int16 a i] and [unsafe_get_int6 b i]
xorq %r9, %r8
orq $1, %r8
orq %r8, %rdx
movq %rsi, %r8
addq $2, %rsi
cmpq %rcx, %r8
jne .L105
.L104:
*)
for _ = 1 to ln land 1 do r := !r lor (get a (ln - 1) lxor get b (ln - 1)) done ;
(*
movq $3, %rsi
movq %rax, %rcx
andq $3, %rcx
cmpq %rcx, %rsi
jg .L102
.L103:
movq %rax, %r8
addq $-2, %r8
sarq $1, %r8
movzbq (%rdi,%r8), %r9
leaq 1(%r9,%r9), %r9
movzbq (%rbx,%r8), %r8
leaq 1(%r8,%r8), %r8
// [unsafe_get a i] and [unsafe_get b i]
xorq %r9, %r8
orq $1, %r8
orq %r8, %rdx
movq %rsi, %r8
addq $2, %rsi
cmpq %rcx, %r8
jne .L103
.L102:
*)
!r = 0
(*
cmpq $1, %rdx
sete %al
movzbq %al, %rax
leaq 1(%rax,%rax), %rax
ret
*)
let equal a b =
let al = String.length a in
let bl = String.length b in
if al <> bl
then false
else equal ~ln:al a b
let[@inline always] compare (a:int) b = a - b
let[@inline always] sixteen_if_minus_one_or_less n = (n asr Sys.int_size) land 16
let[@inline always] eight_if_one_or_more n = ((-n) asr Sys.int_size) land 8
let compare_le ~ln a b =
let r = ref 0 in
let i = ref (pred ln) in
while !i >= 0 do
let xa = get a !i and xb = get b !i in
let c = compare xa xb in
r := !r lor ((sixteen_if_minus_one_or_less c + eight_if_one_or_more c) lsr !r) ;
decr i ;
done ;
(!r land 8) - (!r land 16)
let compare_le_with_len ~len:ln a b =
let al = String.length a in
let bl = String.length b in
if ln = 0 then 0
else if (al lxor ln) lor (bl lxor ln) <> 0
then invalid_arg "compare_le_with_len"
else compare_le ~ln a b
let compare_le a b =
let al = String.length a in
let bl = String.length b in
if al < bl
then 1
else if al > bl
then (-1)
else compare_le ~ln:al (* = bl *) a b
let compare_be ~ln a b =
let r = ref 0 in
let i = ref 0 in
while !i < ln do
let xa = get a !i and xb = get b !i in
let c = compare xa xb in
r := !r lor ((sixteen_if_minus_one_or_less c + eight_if_one_or_more c) lsr !r) ;
incr i ;
done ;
(!r land 8) - (!r land 16)
let compare_be_with_len ~len:ln a b =
let al = String.length a in
let bl = String.length b in
if ln = 0 then 0
else if (al lxor ln) lor (bl lxor ln) <> 0
then invalid_arg "compare_be_with_len"
else compare_be ~ln a b
let compare_be a b =
let al = String.length a in
let bl = String.length b in
if al < bl then 1
else if al > bl then (-1)
else compare_be ~ln:al (* = bl *) a b
let[@inline always] minus_one_or_less n =
n lsr (Sys.int_size - 1)
let[@inline always] one_if_not_zero n =
minus_one_or_less ((- n) lor n)
let[@inline always] zero_if_not_zero n =
(one_if_not_zero n) - 1
let[@inline always] select_int choose_b a b =
let mask = ((- choose_b) lor choose_b) asr Sys.int_size in
(a land (lnot mask)) lor (b land mask)
external int_of_bool : bool -> int = "%identity"
external unsafe_bool_of_int : int -> bool = "%identity"
let[@inline] bool_of_int n =
unsafe_bool_of_int (one_if_not_zero n)
let[@inline always] find_uint8 ~off ~len ~f str =
let i = ref (len - 1) in
let a = ref (lnot 0) in
while !i >= off do
let byte = get str !i in
let pred = int_of_bool (f byte) in
(* XXX(dinosaure): a composition of [f] with [bool_of_int] such as
[let f = bool_of_int <.> f in] implies an allocation (of a closure).
To be GC-free, we must store result of [f] into a register, and apply
[bool_of_int] then (introspection was done on OCaml 4.08.1). *)
a := select_int (((!i - off) land min_int) lor pred) !a !i ;
decr i ;
done ; !a
let find_uint8 ?(off= 0) ~f str =
(* XXX(dinosaure): with this overload, OCaml is able to produce 2 [find_uint8].
One with [off= 0] and one other where [off] is an argument. I think it's about
cross-module optimization where a call to [find_uint8 ~f v] will directly call
the first one and a call to [find_uint8 ~off:x ~f v] will call the second one. *)
let len = String.length str in
find_uint8 ~off ~len ~f str
let exists_uint8 ?off ~f str =
let v = find_uint8 ?off ~f str in
let r = select_int (v + 1) 0 1 in
unsafe_bool_of_int r
let divmod ~(x:int32) ~(m:int32) : int32 * int32 =
(* Division and remainder being constant-time with respect to [x]
* ( NOT [m] !). The OCaml variant would be:
* [(x / m , x mod m)] where [x] is a secret and [m] is not secret.
* Adapted from the NTRU Prime team's algorithm from
* supercop/crypto_kem/sntrup761/ref/uint32.c
* cite the round-2 ntru prime submission to nistpqc (march 2019)
* Note that in practice this works for at least some much larger [x] and [m],
* but it's unclear to me how to evaluate *which*, so leaving the original
* restrictions in.
*)
let ( - ) , ( + ), ( * ) = Int32.(sub, add, mul) in
let ( >> ) = Int32.shift_right_logical in
if (m <= 0l) then raise (Invalid_argument "m <= 0") ;
if (m >= 16348l) then raise (Invalid_argument "m >= 16348 not supported") ;
let of_uint32 uint =
(* apparently Int64.of_int32 sign-extends ... great... avoid that: *)
let b = Bytes.make 8 '\x00' in
Unsafe.set_int32_le b 0 uint ;
Unsafe.get_int64_le b 0
in
let x_0 = x in
let x_2, q_1 =
let int32_div_unsigned n d =
(* can be replaced by Int32.unsigned_div
* from OCaml >= 4.10 *)
let sub,min_int = Int32.(sub,min_int)in
let int32_unsigned_compare n m =
Int32.compare (sub n min_int) (sub m min_int)
in
if d < 0_l then
if int32_unsigned_compare n d < 0 then 0_l else 1_l
else
let q =
let open Int32 in
shift_left (Int32.div (Int32.shift_right_logical n 1) d) 1 in
let r = sub n (Int32.mul q d) in
if int32_unsigned_compare r d >= 0 then Int32.succ q else q
in
let v = int32_div_unsigned Int32.min_int m |> of_uint32 in
(*let v = 0x80_00_00_00 / m in*) (* floored div *)
let x_1, q_0 =
let qpart_0 =
let open Int64 in
shift_right_logical (mul (of_uint32 x_0) v) 31
|> to_int32
in
x_0 - (qpart_0 * m), qpart_0
in
let qpart_1 =
let open Int64 in
shift_right_logical (mul (of_uint32 x_1) v) 31
|> to_int32 in
x_1 - (qpart_1 * m),
(q_0 + qpart_1 + 1l) in
let x_3 = x_2 - m in
let mask = 0l - (x_3 >> 31) in
q_1 + mask, x_3 + (Int32.logand mask m)
let ascii_of_int32 ~digits (n:int32) : string =
(* Recursively calls [divmod n 10]; the remainder is turned into ASCII
and the quotient is used for the next division.*)
if digits < 0 then raise (Invalid_argument "digits < 0");
let out = Bytes.make digits '0' in
let rec loop x = function
| -1 -> Bytes.unsafe_to_string out
| idx ->
let next, this = divmod ~x ~m:10l in
Bytes.set out idx @@ char_chr (0x30 lor (Int32.to_int this)) ;
loop next (pred idx)
in loop n (pred digits)
let[@inline always] to_hex_nibble f : char =
let a = 86 + f in
let c = 1 + ((a - 71 * ((a land 0x10) lsr 4)) lor 0x20) in
char_chr c
let hex_of_string rawbytes =
String.init (2 * String.length rawbytes)
(fun idx ->
let byt = String.get rawbytes (idx lsr 1) |> Char.code in
(* select which 4 bits to use, this can probably be done faster:*)
let nib = 0xf land (byt lsr (((lnot idx) land 1) lsl 2)) in
to_hex_nibble nib)
let hex_of_bytes rawbytes = hex_of_string (Bytes.unsafe_to_string rawbytes)
let[@inline always] select_a_if_in_range ~low ~high ~n a b =
(* select [a] if [low <= n <= high] and [b] if [n] is out of range.*)
(* NB: ONLY WORKS FOR [0 <= low <= high <= max_int]*)
(* The idea being that:
1.a) if low <= n : (n - low) is positive +
1.b) if low > n : (n - low) is negative -
2.a) if n <= high: (high - n) is positive +
2.b) if n > high: (high - n) is negative -
We OR the numbers together; we only really care about the sign bit
which is set when negative.
Thus both numbers are positive iff (low <= n && n <= high).
We then select the sign bit with (land min_int) and use that to choose:
*)
let out_of_range = (* choose b if out of range *)
((n - low) lor (high - n)
land min_int)
in
select_int out_of_range a b
let lowercase_ascii src =
(* ct version of String.lowercase_ascii *)
String.map
( fun ch -> let n = Char.code ch in
(* 0x41 is 'A'; 0x5a is 'Z'; 0x20 controls case for ASCII letters *)
select_a_if_in_range ~low:0x41 ~high:0x5a ~n (n lor 0x20) (n)
|> char_chr
) src
let uppercase_ascii src =
(* ct version of String.uppercase_ascii *)
String.map
( fun ch -> let n = Char.code ch in
(* 0x61 is 'a'; 0x7a is 'z'; 0x20 controls case for ASCII letters *)
select_a_if_in_range ~low:0x61 ~high:0x7a ~n (n lxor 0x20) (n)
|> char_chr
) src
let bytes_of_hex rawhex =
(* hex length must be multiple of 2: *)
let error_bitmap = ref ((String.length rawhex land 1) lsl 4) in
let decoded =
Bytes.init (String.length rawhex lsr 1)
(fun idx ->
let idx = idx lsl 1 in
let nib idx =
String.get rawhex idx
|> Char.code
|> fun n -> (* uppercase -> lowercase: *)
select_a_if_in_range ~low:0x41 ~high:0x5a
~n
(n lor 0x20) (* set case bit *)
n (* leave as-is *)
|> fun n -> (* now either invalid; lowercase; numeric*)
(select_a_if_in_range ~low:0x30 ~high:0x39
~n
(n - 0x30) (* numeric: subtract '0' to get [0..9] *)
(select_a_if_in_range ~low:0x61 ~high:0x66
~n
(* a-f: subtract 'a' and add 10 to get [10..15]: *)
(n - 0x61 + 10)
(0xff) (* invalid, ensure we set upper bits of error_bitmap *)
)
)
in
let nibf0 = nib idx
and nib0f = nib (succ idx) in
error_bitmap := !error_bitmap lor nibf0 lor nib0f ;
char_chr ((nibf0 lsl 4) lor nib0f)
)
in
(* if any non-nibble bits were set in !error_bitmap, decoding failed: *)
decoded, !error_bitmap land (lnot 0xf)
let string_of_hex rawhex =
let byt, error = bytes_of_hex rawhex in
Bytes.unsafe_to_string byt, error