mte/src/bin_type.ml
2025-12-11 07:37:41 +01:00

230 lines
5.6 KiB
OCaml

(* 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?
*)
(* TODO hash and C(ancer)-terminated strings
- "A JSON object is canonicalized by converting it to an ASCII byte array
with the algorithm specified in RFC 8785. The resulting bytes are
terminated with a single 0-byte and then hashed with SHA512."
- from the code it looks like its the same for all stringy-strings
! not strings that are raw-bytes-data-like
? only for "HashCode" and "ShortHashCode"
*)
module Taler_signatures = Include.Taler_signatures
let int32_size = 4
let int64_size = 8
module Bytes_32 = struct
type t = string
let bin = Bin.bytes 32
end
module Bytes_64 = struct
type t = string
let bin = Bin.bytes 64
end
(* -- Time -- *)
module TimeAbsolute = struct
type t = Timestamp.t
let bin = Timestamp.bin
end
module TimeAbsoluteNBO = struct
type t = Timestamp.t
let bin = Timestamp.bin_nbo
end
module TimeRelative = struct
type t = Timestamp.Span.t
let bin = Timestamp.Span.bin
end
module TimeRelativeNBO = struct
type t = Timestamp.Span.t
let bin = Timestamp.Span.bin_nbo
end
(* -- Cryptographic primitives -- *)
(* Hashes *)
module Hash_32 = struct
type t = Digestif.SHA256.t
let hash s = Digestif.SHA256.(digest_string s)
let of_octets s =
match String.length s = 32 with
| false -> Fmt.failwith "Hash.of_octets failure: data is not 32 bytes"
| true -> Digestif.SHA256.of_raw_string s
let to_octets = Digestif.SHA256.to_raw_string
let bin =
let open Bin in
map (bytes 32) of_octets to_octets
let caqti : t Caqti_type.t =
let open Caqti_type in
custom
~encode:(fun v -> Ok (to_octets v))
~decode:(fun v -> Ok (of_octets v))
octets
end
module Hash_64 = struct
type t = Digestif.SHA512.t
let hash s = Digestif.SHA512.(digest_string s)
let of_octets s =
match String.length s = 64 with
| false -> Fmt.failwith "Hash.of_octets failure: data is not 64 bytes"
| true -> Digestif.SHA512.of_raw_string s
let to_octets v =
let s = Digestif.SHA512.to_raw_string v in
match String.length s = 64 with
| false -> Fmt.failwith "Hash.to_octets failure: data is not 64 bytes"
| true -> s
let bin =
let open Bin in
map (bytes 64) of_octets to_octets
let caqti : t Caqti_type.t =
let open Caqti_type in
custom
~encode:(fun v -> Ok (to_octets v))
~decode:(fun v -> Ok (of_octets v))
octets
end
(* Hash over string + '\0' *)
module Hash_32_cstr = struct
include Hash_32
let hash s =
let s = s ^ "\x00" in
Digestif.SHA256.(digest_string s)
end
module Hash_64_cstr = struct
include Hash_64
let hash s =
let s = s ^ "\x00" in
Digestif.SHA512.(digest_string s)
end
module type Hash_S = sig
type t
val bin : t Bin.t
val caqti : t Caqti_type.t
val hash : string -> t
val of_octets : string -> t
val to_octets : t -> string
end
module FullPaytoHash : Hash_S = Hash_32
module NormalizedPaytoHash : Hash_S = Hash_32
module DenominationHash : Hash_S = Hash_64
module PrivateContractHash : Hash_S = Hash_64
module ExtensionsPolicyHash : Hash_S = Hash_64
module MerchantWireHash : Hash_S = Hash_64
module AgeCommitmentHash : Hash_S = Hash_64
module BlindedCoinHash : Hash_S = Hash_64
module CoinPubHash : Hash_S = Hash_64
module OutputCommitmentHash : Hash_S = Hash_64
module HashPlanchetsP : Hash_S = Hash_64
(* --- Various --- *)
module TransferSecretP = Bytes_64
module LinkSecretP = Bytes_64
module EncryptedLinkSecretP = Bytes_64
module BlindingMasterSeed = Bytes_32
module BlindingMasterSecret = Bytes_32
module WireTransferIdentifierRawP = Bytes_32
module PublicRefreshCoinNonceP = Bytes_64
(* 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 = Bytes_32
module RefreshCommitmentP = Bytes_64
(* -- 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