diff --git a/src/handler.ml b/src/handler.ml index 17cc203f..55ed8336 100644 --- a/src/handler.ml +++ b/src/handler.ml @@ -88,3 +88,17 @@ let management_signkey_revoke req exchange_pub server _env = Ok "" in respond_with_res res req + +let management_auditors_jsont = AuditorSetupMessage.jsont + +let management_auditors req server _env = + let open Syntax in + let open Management in + let db_conn = Vif.Server.device Devices.db_connection server in + let res = + let* v = Vif.Request.of_json req |> unwrap_err_msg in + let* () = verify_auditor_add_sig v in + let* () = update_auditor ~db_conn v in + Ok "" + in + respond_with_res res req diff --git a/src/management.ml b/src/management.ml index de55ae39..63480e6a 100644 --- a/src/management.ml +++ b/src/management.ml @@ -182,15 +182,9 @@ let check_master_signatures_update ~db_conn ~sm_denom = match opt with None -> error | Some v -> Ok v in let check = function false -> error | true -> Ok () in - (* TODO time *) - let timestamp_equal a b = - let timestamp_to_int64 = function - | None -> None - | Some v -> v |> Ptime.to_float_s |> Int64.of_float |> Option.some - in - timestamp_to_int64 a = timestamp_to_int64 b + let* () = + check (Timestamp.compare valid_from denom.stamp_start = Some 0) in - let* () = check (timestamp_equal valid_from denom.stamp_start) in let* () = check (coin = denom.value) in let* () = check (fee_refund = denom.fee_refund) in let* () = check (age_mask = denom.age_mask) in @@ -243,3 +237,43 @@ let update_signkey_revocation ~db_conn ~sm_signkey exchange_pub |> unwrap_err_caqti in Ok () + +let verify_auditor_add_sig + AuditorSetupMessage. + { auditor_url; auditor_name= _; auditor_pub; master_sig; validity_start } + = + let open Bin_sig.MasterAddAuditor in + verify ~key:Config.Exchange.master_public_key master_sig + { + start_date= validity_start; + auditor_pub; + h_auditor_url= Bin_type.Hash_64_cstr.hash auditor_url; + } + +(* todo: there is something about use of monotonic time + protection against replay attack that I don't understand *) +let update_auditor ~db_conn + AuditorSetupMessage. + { auditor_url; auditor_name; auditor_pub; master_sig= _; validity_start } + = + let open Syntax in + let* last_date_opt = + Pg.lookup_auditor_timestamp db_conn auditor_pub |> unwrap_err_caqti + in + match last_date_opt with + | None -> + let+ () = + Pg.insert_auditor db_conn ~auditor_pub ~auditor_url ~auditor_name + ~start_date:validity_start + |> unwrap_err_caqti + in + () + | Some last_date -> + let cmp = Timestamp.compare last_date validity_start |> Option.get in + if cmp > 0 then Error "more recent management auditor already present" + else + let+ () = + Pg.update_auditor db_conn ~auditor_pub ~auditor_url ~auditor_name + ~enabled:true ~change_date:validity_start + |> unwrap_err_caqti + in + () diff --git a/src/mte.ml b/src/mte.ml index f6265ca4..530826a2 100644 --- a/src/mte.ml +++ b/src/mte.ml @@ -43,6 +43,8 @@ let routes = (v "management" / "signkeys" /% string `Path / "revoke") management_signkey_revoke_jsont --> management_signkey_revoke; + post (v "management" / "auditors") management_auditors_jsont + --> management_auditors; ] let () = diff --git a/src/pg.ml b/src/pg.ml index af06fc11..a02efab9 100644 --- a/src/pg.ml +++ b/src/pg.ml @@ -28,9 +28,9 @@ module Caqti_type = struct let ptime : Ptime.t option t = Timestamp.caqti let time = Timestamp.caqti let age_mask : int t = Caqti_type.int - let rsa_public = RsaPublicKey.caqti - let eddsa_public = EddsaPublicKey.caqti - let eddsa_signature = EddsaSignature.caqti + let rsa_pub = RsaPublicKey.caqti + let eddsa_pub = EddsaPublicKey.caqti + let eddsa_sig = EddsaSignature.caqti include struct (* alias for hash *) @@ -68,7 +68,7 @@ let preflight = let lookup_signing_key = let lookup_signing_key = - Caqti_type.(eddsa_public ->? t3 time time time) + Caqti_type.(eddsa_pub ->? t3 time time time) "SELECT valid_from, expire_sign, expire_legal FROM exchange_sign_keys \ WHERE exchange_pub=$1" in @@ -78,7 +78,7 @@ let lookup_signing_key = let activate_signing_key = let insert_signkey = let master_sig = Bin_sig.ExchangeSigningKeyValidity.caqti in - Caqti_type.(t5 eddsa_public time time time master_sig ->. unit) + Caqti_type.(t5 eddsa_pub time time time master_sig ->. unit) "INSERT INTO exchange_sign_keys (exchange_pub, valid_from, expire_sign, \ expire_legal, master_sig) VALUES ($1, $2, $3, $4, $5)" in @@ -114,8 +114,8 @@ let add_denomination_key = let denomination_insert = let master_sig = Bin_sig.DenominationKeyValidity.caqti in Caqti_type.( - t12 denomination_hash rsa_public master_sig time time time time amount - amount amount amount (t2 amount age_mask) + t12 denomination_hash rsa_pub master_sig time time time time amount amount + amount amount (t2 amount age_mask) ->. unit) "INSERT INTO denominations (denom_pub_hash, denom_pub, master_sig, \ valid_from, expire_withdraw, expire_deposit, expire_legal, coin, \ @@ -173,9 +173,48 @@ let insert_denomination_revocation = let insert_signkey_revocation = let signkey_revocation_insert = let master_sig = Bin_sig.MasterSigningKeyRevocation.caqti in - Caqti_type.(t2 eddsa_public master_sig ->. unit) + Caqti_type.(t2 eddsa_pub master_sig ->. unit) "INSERT INTO signkey_revocations (esk_serial, master_sig) SELECT \ esk_serial, $2 FROM exchange_sign_keys WHERE exchange_pub=$1" in fun (module Conn : CONN) exchange_pub master_sig -> Conn.exec signkey_revocation_insert (exchange_pub, master_sig) + +let lookup_auditor_timestamp = + let lookup_auditor_timestamp = + Caqti_type.(eddsa_pub ->? time) + "lookup_auditor_timestamp SELECT last_change FROM auditors WHERE \ + auditor_pub=$1" + in + fun (module Conn : CONN) auditor_pub -> + Conn.find_opt lookup_auditor_timestamp auditor_pub + +let insert_auditor = + let insert_auditor = + Caqti_type.(t4 eddsa_pub string string time ->. unit) + "INSERT INTO auditors (auditor_pub, auditor_name, auditor_url, \ + is_active, last_change) VALUES ($1, $2, $3, true, $4)" + in + fun (module Conn : CONN) + ~auditor_pub + ~auditor_url + ~auditor_name + ~start_date + -> + Conn.exec insert_auditor (auditor_pub, auditor_name, auditor_url, start_date) + +let update_auditor = + let update_auditor = + Caqti_type.(t5 eddsa_pub string string bool time ->. unit) + "UPDATE auditors SET auditor_url=$2, auditor_name=$3, is_active=$4, \ + last_change=$5 WHERE auditor_pub=$1" + in + fun (module Conn : CONN) + ~auditor_pub + ~auditor_url + ~auditor_name + ~change_date + ~enabled + -> + Conn.exec update_auditor + (auditor_pub, auditor_url, auditor_name, enabled, change_date) diff --git a/src/timestamp.ml b/src/timestamp.ml index 710ac992..d01bf83c 100644 --- a/src/timestamp.ml +++ b/src/timestamp.ml @@ -67,3 +67,13 @@ let caqti : Ptime.t option Caqti_type.t = let encode v = Ok (encode_int64 v) in let decode v = Ok (decode_int64 v) in Caqti_type.custom ~encode ~decode Caqti_type.int64 + +let compare a b = + match (a, b) with + | None, None -> Some 0 + | None, _ | _, None -> None + | Some a, Some b -> + let a = ptime_to_int64 a in + let b = ptime_to_int64 b in + let c = Int64.compare a b in + Some c diff --git a/src/timestamp.mli b/src/timestamp.mli index ae81b3a3..d5d10c5b 100644 --- a/src/timestamp.mli +++ b/src/timestamp.mli @@ -1,6 +1,8 @@ (* TODO time uhuh! - need to be int64 for binary/pg round trip *) + need to be int64 for binary/pg round trip + + really need to fix this module *) type t = Ptime.t option type span = Ptime.Span.t option @@ -8,6 +10,7 @@ type span = Ptime.Span.t option val diff : t -> t -> span val add_span_exn : t -> span -> t val of_span_exn : span -> t +val compare : t -> t -> int option (* - *) val jsont : t Jsont.t