add test/validate_response.ml
This commit is contained in:
parent
b984190d6d
commit
cbf54c16df
6 changed files with 277 additions and 12 deletions
|
|
@ -915,6 +915,10 @@ module SignKey = struct
|
||||||
Signkey.{ pub; stamp_start; stamp_expire; stamp_end; master_sig } =
|
Signkey.{ pub; stamp_start; stamp_expire; stamp_end; master_sig } =
|
||||||
{ key= pub; stamp_start; stamp_expire; stamp_end; master_sig }
|
{ key= pub; stamp_start; stamp_expire; stamp_end; master_sig }
|
||||||
|
|
||||||
|
let to_signkey { key= pub; stamp_start; stamp_expire; stamp_end; master_sig }
|
||||||
|
=
|
||||||
|
Signkey.{ pub; stamp_start; stamp_expire; stamp_end; master_sig }
|
||||||
|
|
||||||
let jsont =
|
let jsont =
|
||||||
let make key stamp_start stamp_expire stamp_end master_sig =
|
let make key stamp_start stamp_expire stamp_end master_sig =
|
||||||
{ key; stamp_start; stamp_expire; stamp_end; master_sig }
|
{ key; stamp_start; stamp_expire; stamp_end; master_sig }
|
||||||
|
|
@ -1261,7 +1265,7 @@ module ExchangeKeysResponse = struct
|
||||||
stefan_lin: Float.t;
|
stefan_lin: Float.t;
|
||||||
asset_type: string;
|
asset_type: string;
|
||||||
accounts: ExchangeWireAccount.t list;
|
accounts: ExchangeWireAccount.t list;
|
||||||
wire_fees: AggregateTransferFee.t list Stdlib.Map.Make(Stdlib.String).t;
|
wire_fees: AggregateTransferFee.t list String_map.t;
|
||||||
wads: ExchangePartnerListEntry.t list;
|
wads: ExchangePartnerListEntry.t list;
|
||||||
kyc_enabled: bool;
|
kyc_enabled: bool;
|
||||||
disable_direct_deposit: bool;
|
disable_direct_deposit: bool;
|
||||||
|
|
@ -1278,7 +1282,7 @@ module ExchangeKeysResponse = struct
|
||||||
list_issue_date: Timestamp.t;
|
list_issue_date: Timestamp.t;
|
||||||
auditors: AuditorKeys.t list;
|
auditors: AuditorKeys.t list;
|
||||||
signkeys: SignKey.t list;
|
signkeys: SignKey.t list;
|
||||||
extensions: ExtensionManifest.t Stdlib.Map.Make(Stdlib.String).t option;
|
extensions: ExtensionManifest.t String_map.t option;
|
||||||
(* Signature by the exchange master key of the SHA-256 hash of the
|
(* Signature by the exchange master key of the SHA-256 hash of the
|
||||||
normalized JSON-object of field extensions, if it was set.
|
normalized JSON-object of field extensions, if it was set.
|
||||||
The signature has purpose TALER_SIGNATURE_MASTER_EXTENSIONS. *)
|
The signature has purpose TALER_SIGNATURE_MASTER_EXTENSIONS. *)
|
||||||
|
|
|
||||||
|
|
@ -110,3 +110,60 @@ let make_denom_group_sorted denominations =
|
||||||
|> List.sort cmp_group
|
|> List.sort cmp_group
|
||||||
in
|
in
|
||||||
l
|
l
|
||||||
|
|
||||||
|
let denoms_of_denomgroups l =
|
||||||
|
let open Api in
|
||||||
|
l
|
||||||
|
|> List.concat_map
|
||||||
|
(fun
|
||||||
|
(DenomGroup.Rsa
|
||||||
|
RsaDenomGroup.
|
||||||
|
{
|
||||||
|
denoms;
|
||||||
|
value;
|
||||||
|
fee_withdraw;
|
||||||
|
fee_deposit;
|
||||||
|
fee_refresh;
|
||||||
|
fee_refund;
|
||||||
|
})
|
||||||
|
->
|
||||||
|
let open RsaDenom in
|
||||||
|
denoms
|
||||||
|
|> List.map
|
||||||
|
(fun
|
||||||
|
{
|
||||||
|
rsa_pub;
|
||||||
|
master_sig;
|
||||||
|
stamp_start;
|
||||||
|
stamp_expire_withdraw;
|
||||||
|
stamp_expire_deposit;
|
||||||
|
stamp_expire_legal;
|
||||||
|
lost= _;
|
||||||
|
}
|
||||||
|
->
|
||||||
|
let h_pub =
|
||||||
|
DenominationHash.hash (Crypto.RsaPublicKey.to_octets rsa_pub)
|
||||||
|
in
|
||||||
|
{
|
||||||
|
pub= rsa_pub;
|
||||||
|
value;
|
||||||
|
stamp_start;
|
||||||
|
stamp_expire_withdraw;
|
||||||
|
stamp_expire_deposit;
|
||||||
|
stamp_expire_legal;
|
||||||
|
fee_withdraw;
|
||||||
|
fee_deposit;
|
||||||
|
fee_refresh;
|
||||||
|
fee_refund;
|
||||||
|
age_mask= 0;
|
||||||
|
h_pub;
|
||||||
|
master_sig;
|
||||||
|
}))
|
||||||
|
|
||||||
|
let hash_over_master_sigs denominations =
|
||||||
|
denominations
|
||||||
|
|> List.map (fun (Api.DenomGroup.Rsa g) -> g.Api.RsaDenomGroup.denoms)
|
||||||
|
|> List.concat_map (List.map (fun dn -> dn.Api.RsaDenom.master_sig))
|
||||||
|
|> List.map Signatures.DenominationKeyValidity.to_octets
|
||||||
|
|> String.concat ""
|
||||||
|
|> Hash.H64.hash
|
||||||
|
|
|
||||||
|
|
@ -104,16 +104,13 @@ let mk_keys ~db_conn (module Keys : Keys.S) ~last_issue_date =
|
||||||
|
|
||||||
(* ! depends on denominations order *)
|
(* ! depends on denominations order *)
|
||||||
let exchange_sig =
|
let exchange_sig =
|
||||||
let hc =
|
|
||||||
denominations
|
|
||||||
|> List.map (fun (DenomGroup.Rsa g) -> g.RsaDenomGroup.denoms)
|
|
||||||
|> List.concat_map (List.map (fun dn -> dn.RsaDenom.master_sig))
|
|
||||||
|> List.map Signatures.DenominationKeyValidity.to_octets
|
|
||||||
|> String.concat ""
|
|
||||||
|> Hash.H64.hash
|
|
||||||
in
|
|
||||||
let open Signatures.ExchangeKeySet in
|
let open Signatures.ExchangeKeySet in
|
||||||
signf (Keys.sign exchange_pub) R.{ list_issue_date; hc }
|
signf (Keys.sign exchange_pub)
|
||||||
|
R.
|
||||||
|
{
|
||||||
|
list_issue_date;
|
||||||
|
hc= Denomination.hash_over_master_sigs denominations;
|
||||||
|
}
|
||||||
in
|
in
|
||||||
|
|
||||||
let recoup = (* /recoup *) [] in
|
let recoup = (* /recoup *) [] in
|
||||||
|
|
|
||||||
|
|
@ -7,3 +7,8 @@
|
||||||
(name test_crypto)
|
(name test_crypto)
|
||||||
(modules test_crypto)
|
(modules test_crypto)
|
||||||
(libraries mte fmt))
|
(libraries mte fmt))
|
||||||
|
|
||||||
|
(executable
|
||||||
|
(name validate_response)
|
||||||
|
(modules validate_response)
|
||||||
|
(libraries mte fmt bos))
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ echo "[OK] /management/auditors/\$AUDITOR_PUB/disable"
|
||||||
offline_tool wire-fee \
|
offline_tool wire-fee \
|
||||||
--master_key $master_key \
|
--master_key $master_key \
|
||||||
--output $b \
|
--output $b \
|
||||||
--wire_method "xxx" \
|
--wire_method "x-taler-bank" \
|
||||||
--fee_start 0 \
|
--fee_start 0 \
|
||||||
--fee_end 99999999 \
|
--fee_end 99999999 \
|
||||||
--closing_fee $zero_kudos \
|
--closing_fee $zero_kudos \
|
||||||
|
|
|
||||||
202
test/validate_response.ml
Normal file
202
test/validate_response.ml
Normal file
|
|
@ -0,0 +1,202 @@
|
||||||
|
open Syntax
|
||||||
|
|
||||||
|
let keys content =
|
||||||
|
let open Api in
|
||||||
|
let open ExchangeKeysResponse in
|
||||||
|
let* v = Api.decode jsont content in
|
||||||
|
let* () =
|
||||||
|
if
|
||||||
|
Libtool_version.is_compatible
|
||||||
|
~implementation:Libtool_version.mte_protocol_version v.version
|
||||||
|
then Ok ()
|
||||||
|
else Fmt.error "version incompatible"
|
||||||
|
in
|
||||||
|
|
||||||
|
(* TODO need hash over json..
|
||||||
|
validate ExchangeWireAccount *)
|
||||||
|
let* () =
|
||||||
|
let open AggregateTransferFee in
|
||||||
|
v.wire_fees
|
||||||
|
|> String_map.bindings
|
||||||
|
|> List.concat_map (fun (wire_method, l) ->
|
||||||
|
List.map (fun item -> (wire_method, item)) l)
|
||||||
|
|> list_iter
|
||||||
|
(fun
|
||||||
|
(wire_method, { wire_fee; closing_fee; start_date; end_date; sig_ })
|
||||||
|
->
|
||||||
|
let open Signatures.MasterWireFee in
|
||||||
|
verify v.master_public_key sig_
|
||||||
|
{
|
||||||
|
h_wire_method= Hash.Cstring.H64.hash wire_method;
|
||||||
|
start_date;
|
||||||
|
end_date;
|
||||||
|
wire_fee;
|
||||||
|
closing_fee;
|
||||||
|
})
|
||||||
|
in
|
||||||
|
|
||||||
|
let* () =
|
||||||
|
let open SignKey in
|
||||||
|
v.signkeys
|
||||||
|
|> list_iter
|
||||||
|
(fun { key; stamp_start; stamp_expire; stamp_end; master_sig } ->
|
||||||
|
let open Signatures.ExchangeSigningKeyValidity in
|
||||||
|
verify v.master_public_key master_sig
|
||||||
|
{
|
||||||
|
R.start= stamp_start;
|
||||||
|
expire= stamp_expire;
|
||||||
|
end_= stamp_end;
|
||||||
|
signkey_pub= key;
|
||||||
|
})
|
||||||
|
in
|
||||||
|
|
||||||
|
let* () =
|
||||||
|
let opt =
|
||||||
|
List.find_opt (fun sk -> sk.SignKey.key = v.exchange_pub) v.signkeys
|
||||||
|
in
|
||||||
|
match opt with
|
||||||
|
| None -> Fmt.error "exchange_pub is not in signkeys list"
|
||||||
|
| Some sk ->
|
||||||
|
let sk = SignKey.to_signkey sk in
|
||||||
|
(* TODO will need to fake time to validate expired data *)
|
||||||
|
let now = Timestamp.of_ptime (Ptime_clock.now ()) in
|
||||||
|
if Signkey.is_valid_at ~timestamp:now sk then Ok ()
|
||||||
|
else Fmt.error "exchange_pub is not valid at the current time"
|
||||||
|
in
|
||||||
|
let* () =
|
||||||
|
let open Signatures.ExchangeKeySet in
|
||||||
|
verify v.exchange_pub v.exchange_sig
|
||||||
|
R.
|
||||||
|
{
|
||||||
|
list_issue_date= v.list_issue_date;
|
||||||
|
hc= Denomination.hash_over_master_sigs v.denominations;
|
||||||
|
}
|
||||||
|
in
|
||||||
|
|
||||||
|
let denom_l = v.denominations |> Denomination.denoms_of_denomgroups in
|
||||||
|
let* () =
|
||||||
|
let open Denomination in
|
||||||
|
denom_l
|
||||||
|
|> list_iter (fun dn ->
|
||||||
|
let open Signatures.DenominationKeyValidity in
|
||||||
|
verify v.master_public_key dn.master_sig
|
||||||
|
{
|
||||||
|
R.master= v.master_public_key;
|
||||||
|
start= dn.stamp_start;
|
||||||
|
expire_withdraw= dn.stamp_expire_withdraw;
|
||||||
|
expire_spend= dn.stamp_expire_deposit;
|
||||||
|
expire_legal= dn.stamp_expire_legal;
|
||||||
|
value= dn.value;
|
||||||
|
fee_withdraw= dn.fee_withdraw;
|
||||||
|
fee_deposit= dn.fee_deposit;
|
||||||
|
fee_refresh= dn.fee_refresh;
|
||||||
|
fee_refund= dn.fee_refund;
|
||||||
|
denom_hash= dn.h_pub;
|
||||||
|
})
|
||||||
|
in
|
||||||
|
let* () =
|
||||||
|
let last_issue_date =
|
||||||
|
denom_l
|
||||||
|
|> List.map (fun dn -> dn.Denomination.stamp_start)
|
||||||
|
|> List.fold_left Timestamp.max Timestamp.zero
|
||||||
|
in
|
||||||
|
if last_issue_date = v.list_issue_date then Ok ()
|
||||||
|
else Fmt.error "list_issue_date is wrong"
|
||||||
|
in
|
||||||
|
|
||||||
|
let* () =
|
||||||
|
v.global_fees
|
||||||
|
|> list_iter
|
||||||
|
(fun
|
||||||
|
GlobalFees.
|
||||||
|
{
|
||||||
|
start_date;
|
||||||
|
end_date;
|
||||||
|
history_fee;
|
||||||
|
account_fee;
|
||||||
|
purse_fee;
|
||||||
|
history_expiration;
|
||||||
|
purse_account_limit;
|
||||||
|
purse_timeout;
|
||||||
|
master_sig;
|
||||||
|
}
|
||||||
|
->
|
||||||
|
let open Signatures.GlobalFees in
|
||||||
|
verify v.master_public_key master_sig
|
||||||
|
{
|
||||||
|
start_date;
|
||||||
|
end_date;
|
||||||
|
purse_timeout;
|
||||||
|
history_expiration;
|
||||||
|
history_fee;
|
||||||
|
account_fee;
|
||||||
|
purse_fee;
|
||||||
|
purse_account_limit;
|
||||||
|
})
|
||||||
|
in
|
||||||
|
|
||||||
|
let* () =
|
||||||
|
(* TODO O(n^2) *)
|
||||||
|
v.auditors
|
||||||
|
|> list_iter
|
||||||
|
(fun
|
||||||
|
AuditorKeys.
|
||||||
|
{ auditor_pub; auditor_url; auditor_name= _; denomination_keys }
|
||||||
|
->
|
||||||
|
let auditor_url_hash = Hash.H64.hash auditor_url in
|
||||||
|
denomination_keys
|
||||||
|
|> list_iter
|
||||||
|
(fun AuditorDenominationKey.{ denom_pub_h; auditor_sig } ->
|
||||||
|
let open Denomination in
|
||||||
|
let opt =
|
||||||
|
denom_l |> List.find_opt (fun dn -> dn.h_pub = denom_pub_h)
|
||||||
|
in
|
||||||
|
match opt with
|
||||||
|
| None -> Fmt.error "auditor denomination key not found"
|
||||||
|
| Some dn ->
|
||||||
|
let open Signatures.ExchangeKeyValidity in
|
||||||
|
verify auditor_pub auditor_sig
|
||||||
|
{
|
||||||
|
auditor_url_hash;
|
||||||
|
master= auditor_pub;
|
||||||
|
start= dn.stamp_start;
|
||||||
|
expire_withdraw= dn.stamp_expire_withdraw;
|
||||||
|
expire_spend= dn.stamp_expire_deposit;
|
||||||
|
expire_legal= dn.stamp_expire_legal;
|
||||||
|
value= dn.value;
|
||||||
|
fee_withdraw= dn.fee_withdraw;
|
||||||
|
fee_deposit= dn.fee_deposit;
|
||||||
|
fee_refresh= dn.fee_refresh;
|
||||||
|
denom_hash= denom_pub_h;
|
||||||
|
}))
|
||||||
|
in
|
||||||
|
|
||||||
|
Ok ()
|
||||||
|
|
||||||
|
(* --- *)
|
||||||
|
open Cmdliner
|
||||||
|
open Cmdliner.Term.Syntax
|
||||||
|
|
||||||
|
let keys_cmd =
|
||||||
|
let input =
|
||||||
|
let doc = "input file" in
|
||||||
|
Arg.(required & opt (some filepath) None & info [ "i"; "input" ] ~doc)
|
||||||
|
in
|
||||||
|
let doc = "validate a /keys JSON response" in
|
||||||
|
Cmd.make (Cmd.info "keys" ~doc)
|
||||||
|
@@
|
||||||
|
let+ input = input in
|
||||||
|
let* content =
|
||||||
|
Result.bind (Fpath.of_string input) Bos.OS.File.read |> unwrap_err_msg
|
||||||
|
in
|
||||||
|
keys content
|
||||||
|
|
||||||
|
let cli =
|
||||||
|
let info =
|
||||||
|
let doc = "Testing tool to validate exchange responses" in
|
||||||
|
Cmd.info "validate" ~doc
|
||||||
|
in
|
||||||
|
Cmd.group info [ keys_cmd ]
|
||||||
|
|
||||||
|
let main () = Cmd.eval_result cli
|
||||||
|
let () = if !Sys.interactive then () else exit (main ())
|
||||||
Loading…
Add table
Add a link
Reference in a new issue