clean up binary_formats

This commit is contained in:
swrup 2025-10-17 20:05:48 +02:00
parent c992e1d402
commit 207cf58446
3 changed files with 223 additions and 260 deletions

View file

@ -1,14 +1,14 @@
(* https://docs.taler.net/core/api-common.html#binary-formats (* https://docs.taler.net/core/api-common.html#binary-formats
- numeric values are in network byte order (big endian) *) numeric values are in network byte order (big endian) *)
(* structs that are packed and do not contain pointers and are (* structs that are 'packed' and do not contain pointers and are
thus suitable for hashing or similar operations are distinguished thus suitable for hashing or similar operations are distinguished
by adding a P at the end of the name. (NEW) Note that this convention by adding a 'P' at the end of the name.
does not hold for the GNUnet-structs (yet). (NEW) Note that this convention does not hold for the GNUnet-structs (yet).
structs that are used with a purpose for signatures, structs that are used with a purpose for signatures,
additionally get an S at the end of the name. additionally get an 'S' at the end of the name.
(from https://docs.taler.net/taler-developer-manual.html) *) (from https://docs.taler.net/taler-developer-manual.html) *)
@ -18,19 +18,171 @@
- we don't need to worry about struct having "P" suffix - we don't need to worry about struct having "P" suffix
remove them remove them
- test them - test them
- union: not sure what to do of them
not needed or relevant i think
- some purpose (`TALER_SIGNATURE_XXX`) are missing - some purpose (`TALER_SIGNATURE_XXX`) are missing
exchange and gana master branch are not in sync - exchange and gana master branch are not in sync
and we should use a specific git tag instead and we should use a specific git tag instead
- outdated doc(?)
- some missing struct documentation *)
- clean up functor mess module UTIL = struct
don't have not hash thing be HashCode like let int32_size = 4
- have a make function for wrapped HashCode structs let int64_size = 8
make it take the relevant type *)
(* microseconds since the UNIX Epoch
UINT64_MAX represents "never" *)
module MK_TIME () = struct
type t = { v: int64 }
(* not BE (?) *)
let bin =
let open Bin in
record (fun v -> { v }) |+ field neint64 (fun t -> t.v) |> sealr
end
module MK_TIME_NBO () = struct
type t = { v: int64 }
let bin =
let open Bin in
record (fun v -> { v }) |+ field beint64 (fun t -> t.v) |> sealr
end
(* MK_XX functor for structs like:
struct Foo { uint8t thing[XX]; }
MK_HASH_XX for hashed value
taler doc:
- Taler uses 512-bit hash codes (64 bytes).
- usually SHA-512 *)
module MK_32 () = struct
type t = { v: string }
let bin =
let open Bin in
record (fun v -> { v }) |+ field (bytes 32) (fun t -> t.v) |> sealr
end
module MK_64 () = struct
type t = { v: string }
let bin =
let open Bin in
record (fun v -> { v }) |+ field (bytes 64) (fun t -> t.v) |> sealr
end
module MK_HASH_32 () = struct
type t = { hash: string }
let bin =
let open Bin in
record (fun hash -> { hash })
|+ field (bytes 32) (fun t -> t.hash)
|> sealr
end
module MK_HASH_64 () = struct
type t = { hash: string }
let bin =
let open Bin in
record (fun hash -> { hash })
|+ field (bytes 64) (fun t -> t.hash)
|> sealr
end
end
open UTIL
module Taler_signatures = Include.Taler_signatures module Taler_signatures = Include.Taler_signatures
(* -- Crypto keys -- *) (* -- Time -- *)
module TimeAbsolute = MK_TIME ()
module TimeAbsoluteNBO = MK_TIME_NBO ()
module TimeRelative = MK_TIME ()
module TimeRelativeNBO = MK_TIME_NBO ()
module Timestamp = MK_TIME ()
module TimestampNBO = MK_TIME_NBO ()
(* -- Cryptographic primitives -- *)
(* --- Hashes --- *)
module ShortHashCode = MK_HASH_32 ()
module HashCode = MK_HASH_64 ()
(* Hash over a full payto://-URI, including receiver-name
(and possibly BIC and other optional fields). *)
module FullPaytoHash = MK_HASH_32 ()
(* Hash over a normalized payto://-URI, including all optional
fields and also with account-part canonicalized (so no BIC). *)
module NormalizedPaytoHash = MK_HASH_32 ()
module DenominationHash = MK_HASH_64 ()
module PrivateContractHash = MK_HASH_64 ()
module ExtensionsPolicyHash = MK_HASH_64 ()
module MerchantWireHash = MK_HASH_64 ()
(* TODO missing doc *)
module AgeCommitmentHash = MK_HASH_64 ()
(* Hash over:
a) the hash of the denomination's public key,
b) an enum value identifying the cipher, and
c) cipher-dependant blinded information.
See implementation of `TALER_CoinEvHash`
in libtalerexchange for details. *)
module BlindedCoinHash = MK_HASH_64 ()
module CoinPubHash = MK_HASH_64 ()
module OutputCommitmentHash = MK_HASH_64 ()
(* This is the running SHA512-hash over all
`TALER_BlindedCoinHashP` values of an array of coins.
Note that each `TALER_BlindedCoinHashP` itself
captures the hash of the corresponding denomination's
public key. *)
module HashPlanchetsP = MK_HASH_64 ()
(* --- Keys --- *)
module PursePublicKey = MK_32 () (* missing doc *)
module AuditorPublicKeyP = MK_32 () (* missing doc *)
module BlindingMasterSeed = MK_32 ()
module BlindingMasterSecret = MK_32 ()
module ReservePublicKeyP = MK_32 ()
module ReservePrivateKeyP = MK_32 ()
module MerchantPublicKeyP = MK_32 ()
module MerchantPrivateKeyP = MK_32 ()
module TransferPublicKeyP = MK_32 ()
module TransferPrivateKeyP = MK_32 ()
module AmlOfficerPublicKeyP = MK_32 ()
module AmlOfficerPrivateKeyP = MK_32 ()
module ExchangePublicKeyP = MK_32 ()
module ExchangePrivateKeyP = MK_32 ()
module MasterPublicKeyP = MK_32 ()
module MasterPrivateKeyP = MK_32 ()
module WireTransferIdentifierRawP = MK_32 ()
module CoinSpendPublicKeyP = MK_32 () (* union *)
module CoinSpendPrivateKeyP = MK_32 () (* union *)
module TokenPublicKeyP = MK_32 () (* union *)
module PublicRefreshCoinNonceP = MK_64 () (* missing doc *)
module ReserveSignatureP = MK_64 ()
module ExchangeSignatureP = MK_64 ()
module MasterSignatureP = MK_64 ()
module CoinSpendSignatureP = MK_64 ()
module TransferSecretP = MK_64 ()
module LinkSecretP = MK_64 ()
module EncryptedLinkSecretP = MK_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 = MK_32 ()
(* GNUNET_CRYPTO format *)
module RsaPublicKey = struct module RsaPublicKey = struct
(* libgnuutil format: (* libgnuutil format:
https://docs.gnunet.org/doxygen/d9/dbe/structGNUNET__CRYPTO__RsaPublicKeyHeaderP.html https://docs.gnunet.org/doxygen/d9/dbe/structGNUNET__CRYPTO__RsaPublicKeyHeaderP.html
@ -70,6 +222,7 @@ module RsaPublicKey = struct
|+ field beint16 (fun t -> t.e_len) |+ field beint16 (fun t -> t.e_len)
|> sealr |> sealr
(* note: this one has a dynamic sizeof *)
let bin = let bin =
let open Bin in let open Bin in
record (fun header n e -> record (fun header n e ->
@ -84,152 +237,9 @@ module RsaPublicKey = struct
|> sealr |> sealr
end end
let int32_size = 4 (* --- Various --- *)
let int64_size = 8
(* -- Time -- *) module RefreshCommitmentP = MK_64 ()
(* microseconds since the UNIX Epoch. UINT64_MAX represents "never" *)
module MK_TIME () = struct
type t = { v: int64 }
(* not BE (?) *)
let bin =
let open Bin in
record (fun v -> { v }) |+ field neint64 (fun t -> t.v) |> sealr
end
module MK_TIME_NBO () = struct
type t = { v: int64 }
let bin =
let open Bin in
record (fun v -> { v }) |+ field beint64 (fun t -> t.v) |> sealr
end
module TimeAbsolute = MK_TIME ()
module TimeAbsoluteNBO = MK_TIME_NBO ()
module TimeRelative = MK_TIME ()
module TimeRelativeNBO = MK_TIME_NBO ()
module Timestamp = MK_TIME ()
module TimestampNBO = MK_TIME_NBO ()
(* -- Cryptographic primitives -- *)
(* MK_BASIC_XX functor for structs like:
struct Foo { uint8t bar[XX]; } *)
module MKMK_BASIC (S : sig
val v : int
end) =
struct
type t = { v: string }
let bin =
let open Bin in
record (fun v -> { v }) |+ field (bytes S.v) (fun t -> t.v) |> sealr
end
module SIZE_32 = struct
let v = 32
end
module SIZE_64 = struct
let v = 64
end
module MK_BASIC_32 () = MKMK_BASIC (SIZE_32)
module MK_BASIC_64 () = MKMK_BASIC (SIZE_64)
(* MK_XX functor for structs like:
struct FooWrap { struct Foo { uint8t bar[XX]; } } *)
module MKMK (S : sig
val v : int
end) =
struct
module H = MKMK_BASIC (S)
type t = { v: H.t }
let bin =
let open Bin in
record (fun v -> { v }) |+ field H.bin (fun t -> t.v) |> sealr
end
module MK_32 () = MKMK (SIZE_32)
module MK_64 () = MKMK (SIZE_64)
(* - *)
module ShortHashCode = MK_BASIC_32 ()
module HashCode = MK_BASIC_64 ()
module DenominationHash = MK_64 ()
module PrivateContractHash = MK_64 ()
module ExtensionsPolicyHash = MK_64 ()
module MerchantWireHash = MK_64 ()
(* Hash over a full payto://-URI, including receiver-name
(and possibly BIC and other optional fields). *)
module FullPaytoHash = MK_32 ()
(* Hash over a normalized payto://-URI, including all optional
fields and also with account-part canonicalized (so no BIC). *)
module NormalizedPaytoHash = MK_32 ()
(* TODO Taler doc: missing *)
module AgeCommitmentHash = MK_64 ()
(* TODO Taler doc: missing *)
module PublicRefreshCoinNonceP = MK_64 ()
(* TODO Taler doc: missing *)
module PursePublicKey = MK_32 ()
(* TODO Taler doc: missing *)
module AuditorPublicKeyP = MK_32 ()
(* TODO
// Secret for blinding/unblinding.
// An RSA blinding secret, which is basically
// a 256-bit nonce, converted to Crockford `Base32`.
type DenominationBlindingKeyP = string;
*)
module DenominationBlindingKeyP = MK_64 ()
(* Hash over:
a) the hash of the denomination's public key,
b) an enum value identifying the cipher, and
c) cipher-dependant blinded information.
See implementation of `TALER_CoinEvHash`
in libtalerexchange for details. *)
module BlindedCoinHash = MK_64 ()
(* -- *)
module CoinPubHash = MK_64 ()
module OutputCommitmentHash = MK_64 ()
module ReservePublicKeyP = MK_32 ()
module ReservePrivateKeyP = MK_32 ()
module ReserveSignatureP = MK_64 ()
module MerchantPublicKeyP = MK_32 ()
module MerchantPrivateKeyP = MK_32 ()
(*module MerchantSignatureP = MK_64 () *)
module TransferPublicKeyP = MK_32 ()
module TransferPrivateKeyP = MK_32 ()
(*
enum TALER_AmlDecisionState {
NORMAL, PENDING, FROZEN
};
*)
module AmlOfficerPublicKeyP = MK_32 ()
module AmlOfficerPrivateKeyP = MK_32 ()
module ExchangePublicKeyP = MK_32 ()
module ExchangePrivateKeyP = MK_32 ()
module ExchangeSignatureP = MK_64 ()
module MasterPublicKeyP = MK_32 ()
module MasterPrivateKeyP = MK_32 ()
module MasterSignatureP = MK_64 ()
module WireTransferIdentifierRawP = MK_BASIC_32 ()
module UUID = struct module UUID = struct
(* uint32t value[4]; *) (* uint32t value[4]; *)
@ -255,90 +265,6 @@ module WadId = struct
record (fun raw -> { raw }) |+ field (bytes size) (fun t -> t.raw) |> sealr record (fun raw -> { raw }) |+ field (bytes size) (fun t -> t.raw) |> sealr
end end
(* TODO not sure what to do of union, probably not needed *)
(*
union TALER_CoinSpendPublicKeyP {
uint8t eddsaPub[32];
uint8t ecdhePub[32];
};
union TALER_CoinSpendPrivateKeyP {
uint8t eddsaPriv[32];
uint8t ecdhePriv[32];
};
*)
module CoinSpendPublicKeyP = MK_32 ()
module CoinSpendPrivateKeyP = MK_32 ()
module CoinSpendSignatureP = MK_64 ()
(* TODO padding: sizeof used here (assume no padding for now) *)
(*
struct TALER_TransferSecretP {
uint8t key[sizeof (struct HashCode)];
};
uint8t key[sizeof (struct HashCode)];
};
struct TALER_EncryptedLinkSecretP {
uint8t enc[sizeof (struct TALER_LinkSecretP)];
};
*)
module TransferSecretP = MK_64 ()
module LinkSecretP = MK_64 ()
module EncryptedLinkSecretP = MK_64 ()
(*
union TALER_TokenPublicKeyP {
uint8t eddsaPub[32];
uint8t ecdhePub[32];
};
*)
module TokenPublicKeyP = MK_32 ()
(* -- Signatures -- *)
(* EccSignaturePurpose *)
module Purpose = struct
type t = {
(* This field equals the number of bytes being signed,
namely 'sizeof (struct Data)'. *)
size: int32;
(* This field is used to express the context in
which the signature is made, ensuring that a
signature cannot be lifted from one part of the protocol
to another. *)
purpose: int32;
}
let bin =
let open Bin in
record (fun size purpose -> { size; purpose })
|+ field beint32 (fun t -> t.size)
|+ field beint32 (fun t -> t.purpose)
|> sealr
let make ~size purpose = { size= Int32.of_int size; purpose }
let dummy = make ~size:0 0_l
(* helper function to make ['signature Bin.t] *)
let make_bin =
let get_size f =
let open Bin in
match Size.of_value (Size.size_of (f dummy)) with
| Dynamic _ | Unknown ->
Fmt.failwith "size_of failure: size is not Static"
| Static n -> n
in
fun code f -> make ~size:(get_size f) code |> f
let field purpose = Bin.field bin (fun _t -> purpose)
end
(* This is the running SHA512-hash over all
`TALER_BlindedCoinHashP` values of an array of coins.
Note that each `TALER_BlindedCoinHashP` itself
captures the hash of the corresponding denomination's
public key. *)
module HashPlanchetsP = MK_64 ()
module AgeMask = struct module AgeMask = struct
type t = { mask: int32 } type t = { mask: int32 }
@ -348,17 +274,27 @@ module AgeMask = struct
end end
(* TODO (* TODO
- why is the non-NBO version only used in TALER_WithdrawRequestPS? *) - why is the non-NBO version only used in TALER_WithdrawRequestPS?
- correctly do the padding and 0-termination
- handle "invalid" values *)
(* documentation: *)
(* Number of characters (plus 1 for 0-termination) for currency names. (* Number of characters (plus 1 for 0-termination) for currency names.
typically an ISO 4217 currency code when an alphanumeric 3-digit code is used. typically an ISO 4217 currency code when an alphanumeric 3-digit code is used.
For regional currencies, the first character should be a "*" followed For regional currencies, the first character should be a "*" followed
by a region-specific name (i.e. "*BRETAGNEFR"). by a region-specific name (i.e. "*BRETAGNEFR").
Currency codes are compared case-insensitively.
Currency string, left adjusted and padded with zeros. All zeros Currency string, left adjusted and padded with zeros.
for "invalid" values. *) All zeros for "invalid" values.
Name of the currency, using either a three-character ISO 4217 currency
code, or a regional currency identifier between 4 and 11 characters,
consisting of ASCII alphabetic characters ("a-zA-Z").
Should be padded to 12 bytes with 0-characters.
Currency codes are compared case-insensitively. *)
let currency_len = 12 let currency_len = 12
(* TODO Taler doc: missing (* TODO missing doc
found in src/include/taler/taler_amount_lib.h *) found in src/include/taler/taler_amount_lib.h *)
module Amount = struct module Amount = struct
type t = { type t = {
@ -393,7 +329,40 @@ module AmountNBO = struct
|> sealr |> sealr
end end
module BlindingMasterSeed = MK_BASIC_32 () (* -- Signatures -- *)
(* PS: Packed Signature *)
(* EccSignaturePurpose *)
module Purpose = struct
type t = {
size: int32;
purpose: int32;
}
let bin =
let open Bin in
record (fun size purpose -> { size; purpose })
|+ field beint32 (fun t -> t.size)
|+ field beint32 (fun t -> t.purpose)
|> sealr
let make ~size purpose = { size= Int32.of_int size; purpose }
let dummy = make ~size:0 0_l
(* helper function to make ['signature Bin.t]
to compute [t.size], we first build a bin with a dummy purpose *)
let make_bin =
let get_size f =
let open Bin in
match Size.of_value (Size.size_of (f dummy)) with
| Dynamic _ | Unknown ->
Fmt.failwith "size_of failure: size is not Static"
| Static n -> n
in
fun code f -> make ~size:(get_size f) code |> f
let field purpose = Bin.field bin (fun _t -> purpose)
end
module WithdrawRequestPS = struct module WithdrawRequestPS = struct
(* Purpose is #TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW *) (* Purpose is #TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW *)
@ -401,9 +370,7 @@ module WithdrawRequestPS = struct
amount: Amount.t; amount: Amount.t;
fee: Amount.t; fee: Amount.t;
h_planchets: HashPlanchetsP.t; h_planchets: HashPlanchetsP.t;
(* TODO TALER doc blinding_seed: BlindingMasterSecret.t;
`TALER_BlindingMasterSecretP` in doc, but probably TALER_BlindingMasterSeed *)
blinding_seed: BlindingMasterSeed.t;
max_age_group: int32; max_age_group: int32;
mask: AgeMask.t; mask: AgeMask.t;
} }
@ -418,7 +385,7 @@ module WithdrawRequestPS = struct
|+ field Amount.bin (fun t -> t.amount) |+ field Amount.bin (fun t -> t.amount)
|+ field Amount.bin (fun t -> t.fee) |+ field Amount.bin (fun t -> t.fee)
|+ field HashPlanchetsP.bin (fun t -> t.h_planchets) |+ field HashPlanchetsP.bin (fun t -> t.h_planchets)
|+ field BlindingMasterSeed.bin (fun t -> t.blinding_seed) |+ field BlindingMasterSecret.bin (fun t -> t.blinding_seed)
|+ field beint32 (fun t -> t.max_age_group) |+ field beint32 (fun t -> t.max_age_group)
|+ field AgeMask.bin (fun t -> t.mask) |+ field AgeMask.bin (fun t -> t.mask)
|> sealr |> sealr
@ -533,8 +500,6 @@ module DepositConfirmationPS = struct
} }
end end
module RefreshCommitmentP = MK_64 ()
module RefreshMeltCoinAffirmationPS = struct module RefreshMeltCoinAffirmationPS = struct
(* purpose.purpose = TALER_SIGNATURE_WALLET_COIN_MELT *) (* purpose.purpose = TALER_SIGNATURE_WALLET_COIN_MELT *)
type t = { type t = {

View file

@ -30,13 +30,13 @@ let mk_future_denom denom_secmod_sign_f
let open Binary_formats in let open Binary_formats in
let h_denom_pub = let h_denom_pub =
(* TODO hash *) (* TODO hash *)
let v = pub |> Types.RsaPublicKey.to_b32 in let hash = pub |> Types.RsaPublicKey.to_b32 in
DenominationHash.{ v= { v } } DenominationHash.{ hash }
in in
let h_section_name = let h_section_name =
(* TODO hash *) (* TODO hash *)
let v = section_name in let hash = section_name in
HashCode.{ v } HashCode.{ hash }
in in
let anchor_time = let anchor_time =
TimeAbsoluteNBO.{ v= Util.ptime_to_int64_us stamp_start } TimeAbsoluteNBO.{ v= Util.ptime_to_int64_us stamp_start }
@ -75,9 +75,7 @@ let mk_future_sign_key signkey_secmod_sign_f
({ pub; sign= _; stamp_start; stamp_expire; stamp_end } : Secmod_keys.t) = ({ pub; sign= _; stamp_start; stamp_expire; stamp_end } : Secmod_keys.t) =
let signkey_secmod_sig = let signkey_secmod_sig =
let open Binary_formats in let open Binary_formats in
let exchange_pub = let exchange_pub = ExchangePublicKeyP.{ v= EddsaPublicKey.to_octets pub } in
ExchangePublicKeyP.{ v= { v= EddsaPublicKey.to_octets pub } }
in
let anchor_time = let anchor_time =
TimeAbsoluteNBO.{ v= Util.ptime_to_int64_us stamp_start } TimeAbsoluteNBO.{ v= Util.ptime_to_int64_us stamp_start }
in in

View file

@ -78,7 +78,7 @@ let () =
let () = let () =
let open Binary_formats.WithdrawConfirmationPS in let open Binary_formats.WithdrawConfirmationPS in
let str64 = String.init 64 (fun i -> Char.unsafe_chr (i + 1)) in let str64 = String.init 64 (fun i -> Char.unsafe_chr (i + 1)) in
let dummy_t = { h_planchets= { v= { v= str64 } }; noreveal_index= 0_l } in let dummy_t = { h_planchets= { hash= str64 }; noreveal_index= 0_l } in
let size = Bin.size_of_value bin dummy_t |> Option.get in let size = Bin.size_of_value bin dummy_t |> Option.get in
assert (size = 76); assert (size = 76);