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 last_issue_date = ref Timestamp.zero in let* () = (* TODO parse CS *) v.denominations |> list_iter (fun (DenomGroup.Rsa RsaDenomGroup. { denoms; value; fee_withdraw; fee_deposit; fee_refresh; fee_refund; }) -> let open RsaDenom in denoms |> list_iter (fun { rsa_pub; master_sig; stamp_start; stamp_expire_withdraw; stamp_expire_deposit; stamp_expire_legal; lost= _; } -> last_issue_date := Timestamp.max !last_issue_date stamp_start; let denom_hash = DenominationHash.hash (Crypto.RsaPublicKey.to_octets rsa_pub) in let open Signatures.DenominationKeyValidity in verify v.master_public_key master_sig { R.master= v.master_public_key; start= stamp_start; expire_withdraw= stamp_expire_withdraw; expire_spend= stamp_expire_deposit; expire_legal= stamp_expire_legal; value; fee_withdraw; fee_deposit; fee_refresh; fee_refund; denom_hash; })) in let* () = if !last_issue_date = v.list_issue_date then Ok () else Fmt.error "list_issue_date is wrong" in (* validate global-fees *) (* validate list_issue_date *) (* validate auditors *) 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 ())