From 589626ec0cb5eddbe69bae1f19ab04e826e4c395 Mon Sep 17 00:00:00 2001 From: swrup Date: Thu, 5 Feb 2026 18:24:43 +0100 Subject: [PATCH] ~ Bin_rsa --- src/bin_sig.ml | 53 ++++++++++++++++++++-- src/bin_type.ml | 102 ------------------------------------------ src/crypto.ml | 116 ++++++++++++++++++++++++++++++++++++++++++------ src/util.ml | 111 --------------------------------------------- 4 files changed, 153 insertions(+), 229 deletions(-) delete mode 100644 src/bin_type.ml diff --git a/src/bin_sig.ml b/src/bin_sig.ml index 6cda3669..97152fff 100644 --- a/src/bin_sig.ml +++ b/src/bin_sig.ml @@ -1,7 +1,5 @@ -(* Packed Signature *) - -open Bin_type open Hash +module Taler_signatures = Include.Taler_signatures module Aliases = struct module Timestamp = struct @@ -64,6 +62,53 @@ end open Aliases +let int32_size = 4 +let int64_size = 8 + +module Bytes32 = struct + type t = string + + let bin = Bin.bytes 32 +end + +module Bytes64 = struct + type t = string + + let bin = Bin.bytes 64 +end + +module TransferSecretP = Bytes64 +module LinkSecretP = Bytes64 +module EncryptedLinkSecretP = Bytes64 +module BlindingMasterSeed = Bytes32 +module BlindingMasterSecret = Bytes32 +module WireTransferIdentifierRawP = Bytes32 +module PublicRefreshCoinNonceP = Bytes64 +module DenominationBlindingKeyP = Bytes32 +module RefreshCommitmentP = Bytes64 + +module UUID = struct + type t = string + + let size = 4 * int32_size + let bin = Bin.bytes size +end + +module WadId = struct + type t = string + + let size = 6 * int32_size + let bin = Bin.bytes size +end + +module AgeMask = struct + type t = int32 + + let bin = Bin.beint32 +end + +(* --- *) + (* EccSignaturePurpose *) module Purpose = struct type t = { @@ -96,6 +141,8 @@ module Purpose = struct let field purpose = Bin.field bin (fun _t -> purpose) end +(* --- Packed Signatures --- *) + module MK (R : sig type r diff --git a/src/bin_type.ml b/src/bin_type.ml deleted file mode 100644 index 9d3fd4a7..00000000 --- a/src/bin_type.ml +++ /dev/null @@ -1,102 +0,0 @@ -(* https://docs.taler.net/core/api-common.html#binary-formats - - numeric values are in network byte order (big endian) *) - -(* structs that are 'packed' and do not contain pointers and are - thus suitable for hashing or similar operations are distinguished - by adding a 'P' at the end of the name. - (NEW) Note that this convention does not hold for the GNUnet-structs (yet). - - structs that are used with a purpose for signatures, - additionally get an 'S' at the end of the name. - - (from https://docs.taler.net/taler-developer-manual.html) *) - -(* TODO - - correctly handle endianness - - check that our struct are well packed - - it looks like Bin only define packed structs - - we don't need to worry about struct having "P" suffix - remove them - - test them - - union: not sure what to do of them - not needed or relevant i think - - some purpose (`TALER_SIGNATURE_XXX`) are missing - - exchange and gana master branch are not in sync - and we should use a specific git tag instead - - outdated doc(?) - - some missing struct documentation - - - better way to have module aliases? - *) - -module Taler_signatures = Include.Taler_signatures - -let int32_size = 4 -let int64_size = 8 - -module Bytes32 = struct - type t = string - - let bin = Bin.bytes 32 -end - -module Bytes64 = struct - type t = string - - let bin = Bin.bytes 64 -end - -(* -- Cryptographic primitives -- *) - -(* --- Various --- *) - -module TransferSecretP = Bytes64 -module LinkSecretP = Bytes64 -module EncryptedLinkSecretP = Bytes64 -module BlindingMasterSeed = Bytes32 -module BlindingMasterSecret = Bytes32 -module WireTransferIdentifierRawP = Bytes32 -module PublicRefreshCoinNonceP = Bytes64 - -(* TODO ? need to use/save a specific nonce for cryptographic blinding *) -(* Secret for blinding/unblinding. - An RSA blinding secret, which is basically - a 256-bit nonce, converted to Crockford `Base32`. - - type DenominationBlindingKeyP = string; *) -module DenominationBlindingKeyP = Bytes32 -module RefreshCommitmentP = Bytes64 - -(* -- TODO better: -- *) -module UUID = struct - (* uint32t value[4]; *) - type t = { value: string } - - let size = 4 * int32_size - - let bin = - let open Bin in - record (fun value -> { value }) - |+ field (bytes size) (fun t -> t.value) - |> sealr -end - -module WadId = struct - (* uint32t value[6]; *) - type t = { raw: string } - - let size = 6 * int32_size - - let bin = - let open Bin in - record (fun raw -> { raw }) |+ field (bytes size) (fun t -> t.raw) |> sealr -end - -module AgeMask = struct - type t = { mask: int32 } - - let bin = - let open Bin in - record (fun mask -> { mask }) |+ field beint32 (fun t -> t.mask) |> sealr -end diff --git a/src/crypto.ml b/src/crypto.ml index 3849c761..bf437fce 100644 --- a/src/crypto.ml +++ b/src/crypto.ml @@ -1,3 +1,100 @@ +open Syntax + +module Binary_format_rsa = struct + (* TODO tests: + - need to strip leading zeros? + - endianess ok? *) + (* 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 *) + + let rev_string len s = String.init len (fun i -> s.[len - 1 - i]) + + (* we 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 z_array_to_octets (arr : Z.t array) = + let nb = Array.length arr in + let bits_arr = Array.map z_to_bits_be arr in + let len_arr = Array.map String.length bits_arr in + let len = (2 * nb) + Array.fold_left ( + ) 0 len_arr in + let b = Bytes.make len '\x00' in + let pos = ref 0 in + Array.iter + (fun len -> + Bytes.set_uint16_be b !pos len; + pos := !pos + 2) + len_arr; + Array.iteri + (fun i bits -> + let len = len_arr.(i) in + Bytes.blit_string bits 0 b !pos len; + pos := !pos + len) + bits_arr; + Bytes.unsafe_to_string b + + let check = function + | false -> Error "rsa of_octets error, invalid data" + | true -> Ok () + + 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 len = (2 * nb) + Array.fold_left ( + ) 0 len_arr in + check (s_len = 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 + + let pub_to_octets ({ n; e } : Mirage_crypto_pk.Rsa.pub) = + z_array_to_octets [| n; e |] + + 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 |> unwrap_err_msg in + pub + | _ -> assert false + + 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* 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' |> unwrap_err_msg + in + priv + | _ -> assert false +end + (* TODO key format - check what is the exact format in GNUNET - endianess issue? *) @@ -17,13 +114,10 @@ module EddsaPublicKey = struct | Ok v -> Ok v let bin = - let of_octets o = - match of_octets o with Error e -> raise (Util.Bin_error e) | Ok v -> v - in - Bin.map (Bin.bytes 32) of_octets to_octets + let of_octets_exn t = of_octets t |> Result.get_ok in + Bin.map (Bin.bytes 32) of_octets_exn to_octets let of_b32 s = - let open Syntax in let* octets = B32.decode s in let* pub = of_octets octets in Ok pub @@ -62,7 +156,6 @@ module EddsaPrivateKey = struct let jsont = let of_b32 s = - let open Syntax in let* octets = B32.decode s in of_octets octets in @@ -117,7 +210,6 @@ end = struct let jsont = let of_b32 s = - let open Syntax in let* t = B32.decode s in let+ () = check_size t in t @@ -137,12 +229,11 @@ module RsaPublicKey = struct type t = Rsa.pub - let to_octets = Util.Bin_rsa.pub_to_octets - let of_octets = Util.Bin_rsa.pub_of_octets + let to_octets = Binary_format_rsa.pub_to_octets + let of_octets = Binary_format_rsa.pub_of_octets let jsont = let of_b32 s = - let open Syntax in let* s = B32.decode s in let+ v = of_octets s in v @@ -168,12 +259,11 @@ module RsaPrivateKey = struct (priv, pub) let pub_of_priv = pub_of_priv - let of_octets = Util.Bin_rsa.priv_of_octets - let to_octets = Util.Bin_rsa.priv_to_octets + let of_octets = Binary_format_rsa.priv_of_octets + let to_octets = Binary_format_rsa.priv_to_octets let jsont = let of_b32 s = - let open Syntax in let* s = B32.decode s in let+ v = of_octets s in v diff --git a/src/util.ml b/src/util.ml index 5d6bac8e..c690a009 100644 --- a/src/util.ml +++ b/src/util.ml @@ -1,114 +1,3 @@ -exception Bin_error of string - -(* TODO bin - - no [Bin.of_string] ? *) -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 z_array_to_octets (arr : Z.t array) = - let nb = Array.length arr in - let bits_arr = Array.map z_to_bits_be arr in - let len_arr = Array.map String.length bits_arr in - let len = (2 * nb) + Array.fold_left ( + ) 0 len_arr in - let b = Bytes.make len '\x00' in - let pos = ref 0 in - Array.iter - (fun len -> - Bytes.set_uint16_be b !pos len; - pos := !pos + 2) - len_arr; - Array.iteri - (fun i bits -> - let len = len_arr.(i) in - Bytes.blit_string bits 0 b !pos len; - pos := !pos + len) - bits_arr; - Bytes.unsafe_to_string b - - let check = function - | false -> Error "rsa of_octets error, invalid data" - | true -> Ok () - - 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 len = (2 * nb) + Array.fold_left ( + ) 0 len_arr in - check (s_len = 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 - - let pub_to_octets ({ n; e } : Mirage_crypto_pk.Rsa.pub) = - z_array_to_octets [| n; e |] - - 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 |> unwrap_err_msg in - pub - | _ -> assert false - - 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* 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' |> unwrap_err_msg - in - priv - | _ -> assert false -end - module Log_reporter = struct let detail_tag : string Logs.Tag.def = Logs.Tag.def "Detail tag" ~doc:"" Fmt.string