fix check for replay attack + revoke 1sec in the future

This commit is contained in:
swrup 2026-02-26 17:52:30 +01:00
parent 30d500fe7d
commit bba5484da2
4 changed files with 66 additions and 46 deletions

View file

@ -165,8 +165,7 @@ module Auditors = struct
() ()
| Some auditor -> | Some auditor ->
if Timestamp.geq auditor.last_change validity_start then if Timestamp.geq auditor.last_change validity_start then
Error Error "replay detected on enable-auditor"
"database has more recent auditor data for this auditor public key"
else else
let+ () = Pg.update_auditor db_conn auditor |> unwrap_err_caqti in let+ () = Pg.update_auditor db_conn auditor |> unwrap_err_caqti in
Logs.info (fun m -> m "updated auditor"); Logs.info (fun m -> m "updated auditor");
@ -200,27 +199,22 @@ module Auditors_disable = struct
match opt with match opt with
| None -> Error "auditor not found" | None -> Error "auditor not found"
| Some auditor -> ( | Some auditor -> (
match Timestamp.geq auditor.last_change validity_end with if Timestamp.geq auditor.last_change validity_end then
| true -> Error "replay detected on disable-auditor"
Error else
"database has more recent auditor data for this auditor public \ match auditor.is_active with
key" | false ->
| false -> ( Logs.info (fun m -> m "auditor was already revoked");
match auditor.is_active with Ok ()
| false -> | true ->
Logs.info (fun m -> m "auditor was already revoked"); let auditor =
Ok () { auditor with last_change= validity_end; is_active= false }
| true -> in
let auditor = let+ () = Pg.update_auditor db_conn auditor |> unwrap_err_caqti in
{ auditor with last_change= validity_end; is_active= false } Logs.info (fun m ->
in m "revoked auditor `%s`"
let+ () = (Crypto.EddsaPublicKey.to_b32 auditor_pub));
Pg.update_auditor db_conn auditor |> unwrap_err_caqti ())
in
Logs.info (fun m ->
m "revoked auditor `%s`"
(Crypto.EddsaPublicKey.to_b32 auditor_pub));
()))
let jsont = AuditorTeardownMessage.jsont let jsont = AuditorTeardownMessage.jsont
@ -436,14 +430,6 @@ module Wire = struct
} = } =
let* opt = Pg.find_wire db_conn ~payto_uri |> unwrap_err_caqti in let* opt = Pg.find_wire db_conn ~payto_uri |> unwrap_err_caqti in
match opt with match opt with
| Some wire ->
let+ () =
Pg.update_wire db_conn ~is_active:true ~last_change:validity_start
wire
|> unwrap_err_caqti
in
Logs.info (fun m -> m "updated wire method");
()
| None -> | None ->
let wire = let wire =
ExchangeWireAccount. ExchangeWireAccount.
@ -464,6 +450,17 @@ module Wire = struct
in in
Logs.info (fun m -> m "added wire method"); Logs.info (fun m -> m "added wire method");
() ()
| Some (wire, _is_active, last_change) ->
if Timestamp.geq last_change validity_start then
Error "replay detected on enable-wire"
else
let+ () =
Pg.update_wire db_conn ~is_active:true ~last_change:validity_start
wire
|> unwrap_err_caqti
in
Logs.info (fun m -> m "updated wire method");
()
let jsont = WireSetupMessage.jsont let jsont = WireSetupMessage.jsont
@ -492,13 +489,17 @@ module Wire_disable = struct
let* opt = Pg.find_wire db_conn ~payto_uri |> unwrap_err_caqti in let* opt = Pg.find_wire db_conn ~payto_uri |> unwrap_err_caqti in
match opt with match opt with
| None -> Error "wire not found" | None -> Error "wire not found"
| Some wire -> | Some (wire, _is_active, last_change) ->
let+ () = if Timestamp.geq last_change validity_end then
Pg.update_wire db_conn ~is_active:false ~last_change:validity_end wire Error "replay detected on disable-wire"
|> unwrap_err_caqti else
in let+ () =
Logs.info (fun m -> m "disabled wire method"); Pg.update_wire db_conn ~is_active:false ~last_change:validity_end
() wire
|> unwrap_err_caqti
in
Logs.info (fun m -> m "disabled wire method");
()
let jsont = WireTeardownMessage.jsont let jsont = WireTeardownMessage.jsont

View file

@ -257,10 +257,10 @@ let insert_global_fees =
let find_wire = let find_wire =
let req = let req =
Caqti_type.(payto_uri ->? exchange_wire_account) Caqti_type.(payto_uri ->? t3 exchange_wire_account bool time)
"SELECT payto_uri, conversion_url, debit_restrictions::TEXT, \ "SELECT payto_uri, conversion_url, debit_restrictions::TEXT, \
credit_restrictions::TEXT, master_sig, bank_label, priority FROM \ credit_restrictions::TEXT, master_sig, bank_label, priority, is_active, \
wire_accounts WHERE payto_uri=$1" last_change FROM wire_accounts WHERE payto_uri=$1"
in in
fun (module Conn : CONN) ~payto_uri -> Conn.find_opt req payto_uri fun (module Conn : CONN) ~payto_uri -> Conn.find_opt req payto_uri

View file

@ -1,6 +1,11 @@
(* not done: (* TODO
/management/aml-officers (for /aml) don't trust any public keys
/management/partners (for /wads) *) keep trusted exchange/secmod keys
either from config or TOFU (Trust On First Use)
not done:
/management/aml-officers (for /aml)
/management/partners (for /wads) *)
open Cmdliner open Cmdliner
open Cmdliner.Term.Syntax open Cmdliner.Term.Syntax
open Offline_impl open Offline_impl

View file

@ -299,7 +299,14 @@ let enable_auditor ~output ~master_key ~auditor_url ~auditor_name ~auditor_pub =
let disable_auditor ~output ~master_key ~auditor_pub = let disable_auditor ~output ~master_key ~auditor_pub =
let* key = read_master_key_file master_key in let* key = read_master_key_file master_key in
let ns = Mtime_clock.now_ns () in let ns = Mtime_clock.now_ns () in
let validity_end = Timestamp.of_s @@ Int64.unsigned_div ns 1_000_000_000L in let open Time in
let validity_end =
TimeAbsolute.of_s @@ Int64.unsigned_div ns 1_000_000_000L
in
(* hack for tests: +1sec to be sure it overwrite previous timestamp *)
let validity_end =
Timestamp.of_absolute (TimeAbsolute.add validity_end TimeRelative.(of_s 1L))
in
let master_sig = let master_sig =
let open Signatures.MasterDelAuditor in let open Signatures.MasterDelAuditor in
signf (EddsaSignature.sign ~key) { end_date= validity_end; auditor_pub } signf (EddsaSignature.sign ~key) { end_date= validity_end; auditor_pub }
@ -397,7 +404,14 @@ let enable_wire ~output ~master_key ~payto_uri ~bank_label ~priority =
let disable_wire ~output ~master_key ~payto_uri = let disable_wire ~output ~master_key ~payto_uri =
let* key = read_master_key_file master_key in let* key = read_master_key_file master_key in
let ns = Mtime_clock.now_ns () in let ns = Mtime_clock.now_ns () in
let validity_end = Timestamp.of_s @@ Int64.unsigned_div ns 1_000_000_000L in let open Time in
let validity_end =
TimeAbsolute.of_s @@ Int64.unsigned_div ns 1_000_000_000L
in
(* hack for tests: +1sec to be sure it overwrite previous timestamp *)
let validity_end =
Timestamp.of_absolute (TimeAbsolute.add validity_end TimeRelative.(of_s 1L))
in
let h_wire = Hash.FullPaytoHash.hash payto_uri in let h_wire = Hash.FullPaytoHash.hash payto_uri in
let master_sig_del = let master_sig_del =
let open Signatures.MasterDelWire in let open Signatures.MasterDelWire in