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
- 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
by adding a P at the end of the name. (NEW) Note that this convention
does not hold for the GNUnet-structs (yet).
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.
additionally get an 'S' at the end of the name.
(from https://docs.taler.net/taler-developer-manual.html) *)
@ -18,19 +18,171 @@
- 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
- exchange and gana master branch are not in sync
and we should use a specific git tag instead
- outdated doc(?)
- some missing struct documentation *)
- clean up functor mess
don't have not hash thing be HashCode like
- have a make function for wrapped HashCode structs
make it take the relevant type *)
module UTIL = struct
let int32_size = 4
let int64_size = 8
(* 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
(* -- 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
(* libgnuutil format:
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)
|> sealr
(* note: this one has a dynamic sizeof *)
let bin =
let open Bin in
record (fun header n e ->
@ -84,152 +237,9 @@ module RsaPublicKey = struct
|> sealr
end
let int32_size = 4
let int64_size = 8
(* --- Various --- *)
(* -- Time -- *)
(* 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 RefreshCommitmentP = MK_64 ()
module UUID = struct
(* uint32t value[4]; *)
@ -255,90 +265,6 @@ module WadId = struct
record (fun raw -> { raw }) |+ field (bytes size) (fun t -> t.raw) |> sealr
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
type t = { mask: int32 }
@ -348,17 +274,27 @@ module AgeMask = struct
end
(* 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.
typically an ISO 4217 currency code when an alphanumeric 3-digit code is used.
For regional currencies, the first character should be a "*" followed
by a region-specific name (i.e. "*BRETAGNEFR").
Currency codes are compared case-insensitively.
Currency string, left adjusted and padded with zeros. All zeros
for "invalid" values. *)
Currency string, left adjusted and padded with zeros.
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
(* TODO Taler doc: missing
(* TODO missing doc
found in src/include/taler/taler_amount_lib.h *)
module Amount = struct
type t = {
@ -393,7 +329,40 @@ module AmountNBO = struct
|> sealr
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
(* Purpose is #TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW *)
@ -401,9 +370,7 @@ module WithdrawRequestPS = struct
amount: Amount.t;
fee: Amount.t;
h_planchets: HashPlanchetsP.t;
(* TODO TALER doc
`TALER_BlindingMasterSecretP` in doc, but probably TALER_BlindingMasterSeed *)
blinding_seed: BlindingMasterSeed.t;
blinding_seed: BlindingMasterSecret.t;
max_age_group: int32;
mask: AgeMask.t;
}
@ -418,7 +385,7 @@ module WithdrawRequestPS = struct
|+ field Amount.bin (fun t -> t.amount)
|+ field Amount.bin (fun t -> t.fee)
|+ 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 AgeMask.bin (fun t -> t.mask)
|> sealr
@ -533,8 +500,6 @@ module DepositConfirmationPS = struct
}
end
module RefreshCommitmentP = MK_64 ()
module RefreshMeltCoinAffirmationPS = struct
(* purpose.purpose = TALER_SIGNATURE_WALLET_COIN_MELT *)
type t = {