From 7942050c302f64258b7f453973dafc7d4b90e64d Mon Sep 17 00:00:00 2001 From: swrup Date: Sat, 29 Nov 2025 17:34:24 +0100 Subject: [PATCH] --- src/crypto.ml | 69 +++--------------------- src/util.ml | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+), 61 deletions(-) diff --git a/src/crypto.ml b/src/crypto.ml index f5031c98..4c835b1b 100644 --- a/src/crypto.ml +++ b/src/crypto.ml @@ -97,67 +97,17 @@ end = struct end module RsaPublicKey = struct - module Binary = struct - (* https://www.gnupg.org/documentation/manuals/gcrypt/MPI-formats.html - format: { uint16_be: n size; uint16_be: e size; n; e} - - integer in big-endian format (MSB first) - leading zeroes are stripped unless they are required to keep a value positive - no 0-termination *) - - let rev_string len s = String.init len (fun i -> s.[len - 1 - i]) - - (* todo: need to strip leading zeros or something? *) - (* reverse bytes because Z.of_bits reads bytes in little endian *) - let z_of_bits_be src pos len = - String.sub src pos len |> rev_string len |> Z.of_bits - - let to_octets ({ n; e } : Mirage_crypto_pk.Rsa.pub) = - let n = Z.to_bits n in - let e = Z.to_bits e in - let n_len = String.length n in - let e_len = String.length e in - let len = 4 + n_len + e_len in - let b = Bytes.make len '\x00' in - Bytes.set_uint16_be b 0 n_len; - Bytes.set_uint16_be b 2 e_len; - let n = rev_string n_len n in - let e = rev_string e_len e in - Bytes.blit_string n 0 b 4 n_len; - Bytes.blit_string e 0 b (4 + n_len) e_len; - Bytes.to_string b - - let of_octets = - let check = function - | false -> Error "RsaPublicKey.of_octets: invalid data" - | true -> Ok () - in - fun s -> - let open Syntax in - let len = String.length s in - let* () = check (len >= 4) in - let n_len = String.get_uint16_be s 0 in - let e_len = String.get_uint16_be s 2 in - let* () = check (len = n_len + e_len + 4) in - let n = z_of_bits_be s 4 n_len in - let e = z_of_bits_be s (4 + n_len) e_len in - Mirage_crypto_pk.Rsa.pub ~n ~e |> unwrap_err_msg - - let of_octets s = - match of_octets s with - | Error e -> Fmt.failwith "RsaPublicKey.of_octets failure: %s@." e - | Ok v -> v - end - - include Binary open Mirage_crypto_pk type t = Rsa.pub + let of_octets = Util.Bin_rsa.pub_of_octets + let to_octets = Util.Bin_rsa.pub_to_octets + let of_b32 s = let open Syntax in let* s = B32.decode s in - let v = of_octets s in + let v = Util.Bin_rsa.pub_of_octets s in let+ v = Rsa.pub ~n:v.n ~e:v.e |> unwrap_err_msg in v @@ -171,16 +121,13 @@ module RsaPrivateKey = struct type t = priv let pub_of_priv = pub_of_priv - - (* TODO rsa *) - let to_octets _t : string = assert false - let of_octets _t : t = assert false - (*let bin = Bin.map (Bin.bytes 32) of_octets to_octets*) + let of_octets = Util.Bin_rsa.priv_of_octets + let to_octets = Util.Bin_rsa.priv_to_octets let of_b32 s = let open Syntax in - let* octets = B32.decode s in - Ok (of_octets octets) + let* s = B32.decode s in + Ok (of_octets s) let to_b32 t = B32.encode (to_octets t) let jsont = Jsont.of_of_string ~kind:"EddsaPrivateKey" of_b32 ~enc:to_b32 diff --git a/src/util.ml b/src/util.ml index 59ab2c3c..c8fb197e 100644 --- a/src/util.ml +++ b/src/util.ml @@ -3,3 +3,148 @@ let bin_of_string bin s = let v = Bin.decode bin s (ref 0) in Ok v + +module Bin_rsa = struct + (* TODO + - need to strip leading zeros? + - endianess ok? + - tests *) + (* RSA public key binary format + https://www.gnupg.org/documentation/manuals/gcrypt/MPI-formats.html + := { uint16_be: n size; uint16_be: e size; n; e} + + integer in big-endian format (MSB first) + leading zeroes are stripped unless they are required to keep a value positive + no 0-termination *) + (* RSA private key custom format is inspired by the public rsa key format + used by secmod to save private key to file *) + open Syntax + + module Internal = struct + let rev_string len s = String.init len (fun i -> s.[len - 1 - i]) + + (* we need reverse bytes because Z.of_bits reads bytes in little endian *) + let z_of_bits_be src pos len = + String.sub src pos len |> rev_string len |> Z.of_bits + + let z_to_bits_be z = + let bits = Z.to_bits z in + rev_string (String.length bits) bits + + let check = function false -> Error (`Msg "invalid data") | true -> Ok () + + let z_array_to_octets (arr : Z.t array) = + let arr = + Array.map + (fun z -> + let bits = z_to_bits_be z in + (String.length bits, bits)) + arr + in + let len = Array.fold_left (fun acc (n, _) -> acc + n) 0 arr in + let b = Bytes.make len '\x00' in + let bits_start_pos = + Array.fold_left + (fun pos (len, _bits) -> + Bytes.set_uint16_be b pos len; + pos + 2) + 0 arr + in + let _end : int = + Array.fold_left + (fun pos (len, bits) -> + Bytes.blit_string bits 0 b pos len; + pos + len) + bits_start_pos arr + in + Bytes.unsafe_to_string b + + let z_array_of_octets ~nb s = + let s_len = String.length s in + let* () = check (s_len <= 2 * nb) in + let pos = ref 0 in + let len_arr = + Array.init nb (fun _i -> + let len = String.get_uint16_be s !pos in + pos := !pos + 2; + len) + in + let* () = + let total_len = (2 * nb) + Array.fold_left ( + ) 0 len_arr in + check (s_len = total_len) + in + let z_arr = + Array.init nb (fun i -> + let len = len_arr.(i) in + let z = z_of_bits_be s !pos len in + pos := !pos + len; + z) + in + Ok z_arr + end + + open Internal + + module Implementation_1 = struct + let pub_to_octets ({ n; e } : Mirage_crypto_pk.Rsa.pub) = + let n = z_to_bits_be n in + let e = z_to_bits_be e in + let n_len = String.length n in + let e_len = String.length e in + let len = 4 + n_len + e_len in + let b = Bytes.make len '\x00' in + Bytes.set_uint16_be b 0 n_len; + Bytes.set_uint16_be b 2 e_len; + Bytes.blit_string n 0 b 4 n_len; + Bytes.blit_string e 0 b (4 + n_len) e_len; + Bytes.unsafe_to_string b + + let pub_of_octets s = + let pub_of_octets s = + let len = String.length s in + let* () = check (len >= 4) in + let n_len = String.get_uint16_be s 0 in + let e_len = String.get_uint16_be s 2 in + let* () = check (len = n_len + e_len + 4) in + let n = z_of_bits_be s 4 n_len in + let e = z_of_bits_be s (4 + n_len) e_len in + Mirage_crypto_pk.Rsa.pub ~n ~e + in + match pub_of_octets s with + | Error (`Msg e) -> Fmt.failwith "rsa pub_of_octets failure: %s@." e + | Ok v -> v + end + + let pub_to_octets ({ n; e } : Mirage_crypto_pk.Rsa.pub) = + z_array_to_octets [| n; e |] + + let pub_of_octets s = + let pub_of_octets s = + let* arr = z_array_of_octets ~nb:2 s in + match arr with + | [| n; e |] -> + let* pub = Mirage_crypto_pk.Rsa.pub ~n ~e in + Ok pub + | _ -> Fmt.error_msg "rsa pub_of_octets invalid data" + in + match pub_of_octets s with + | Error (`Msg e) -> Fmt.failwith "rsa pub_of_octets failure: %s@." e + | Ok v -> v + + let priv_to_octets ({ e; d; n; p; q; dp; dq; q' } : Mirage_crypto_pk.Rsa.priv) + = + z_array_to_octets [| e; d; n; p; q; dp; dq; q' |] + + let priv_of_octets s = + let priv_of_octets s = + let* arr = z_array_of_octets ~nb:8 s in + match arr with + | [| e; d; n; p; q; dp; dq; q' |] -> + let* priv = Mirage_crypto_pk.Rsa.priv ~e ~d ~n ~p ~q ~dp ~dq ~q' in + Ok priv + | _ -> Fmt.error_msg "rsa priv_of_octets invalid data" + in + match priv_of_octets s with + | Error (`Msg e) -> Fmt.failwith "rsa priv_of_octets failure: %s@." e + | Ok v -> v +end