diff --git a/src/secmod.ml b/src/secmod.ml new file mode 100644 index 00000000..8e238d03 --- /dev/null +++ b/src/secmod.ml @@ -0,0 +1,269 @@ +(* TODO + - key rotation + - how many signkey to use? + we just use 1 for now + - does the secmod's key as metadata/expiration date? *) +open Syntax + +type eddsa_priv = Crypto.EddsaPrivateKey.t +type eddsa_pub = Crypto.EddsaPublicKey.t +type h_denom_pub = Bin_type.DenominationHash.t + +type t = { + lock: Miou.Mutex.t; + sm_key_priv: eddsa_priv; + sm_key_pub: eddsa_pub; + sk_ht: (eddsa_pub, Signkey.t) Hashtbl.t; + sk_revoked_ht: + (eddsa_pub, Signkey.t * Bin_sig.MasterSigningKeyRevocation.t) Hashtbl.t; + dn_ht: (h_denom_pub, Denomination.t) Hashtbl.t; + dn_revoked_ht: + ( h_denom_pub, + Denomination.t * Bin_sig.MasterDenominationKeyRevocation.t ) + Hashtbl.t; +} + +let get_sm_key_priv t = t.sm_key_priv +let get_sm_key_pub t = t.sm_key_pub + +let get_signkeys t = + Miou.Mutex.protect t.lock @@ fun () -> + Hashtbl.to_seq_values t.sk_ht |> List.of_seq + +let get_denoms t = + Miou.Mutex.protect t.lock @@ fun () -> + Hashtbl.to_seq_values t.dn_ht |> List.of_seq + +let add_signkey_master_signatures conn t l = + Miou.Mutex.protect t.lock @@ fun () -> + list_iter + (fun (pub, master_sig) -> + match Hashtbl.find_opt t.sk_ht pub with + | None -> Error "secmod_signkey failure: public key not found." + | Some signkey -> + let signkey = { signkey with master_sig= Some master_sig } in + let* () = + Pg.activate_signing_key conn ~master_sig signkey |> unwrap_err_caqti + in + Hashtbl.replace t.sk_ht pub signkey; + Ok ()) + l + +let add_denom_master_signatures conn t l = + Miou.Mutex.protect t.lock @@ fun () -> + list_iter + (fun (h_denom_pub, master_sig) -> + match Hashtbl.find_opt t.dn_ht h_denom_pub with + | None -> Error "secmod_denom failure: denomination hash not found." + | Some denom -> + let denom = { denom with master_sig= Some master_sig } in + let* () = + Pg.add_denomination_key conn ~master_sig denom |> unwrap_err_caqti + in + Hashtbl.replace t.dn_ht h_denom_pub denom; + Ok ()) + l + +let revoke_signkey t exchange_pub master_sig = + Miou.Mutex.protect t.lock @@ fun () -> + match Hashtbl.find_opt t.sk_ht exchange_pub with + | None -> Error "secmod_denom failure: denomination hash not found." + | Some signkey -> + Hashtbl.remove t.sk_ht exchange_pub; + Hashtbl.replace t.sk_revoked_ht exchange_pub (signkey, master_sig); + Ok () + +let revoke_denomination t h_denom_pub master_sig = + Miou.Mutex.protect t.lock @@ fun () -> + match Hashtbl.find_opt t.dn_ht h_denom_pub with + | None -> Error "secmod_denom failure: denomination hash not found." + | Some denom -> + Hashtbl.remove t.dn_ht h_denom_pub; + Hashtbl.replace t.dn_revoked_ht h_denom_pub (denom, master_sig); + Ok () + +let dir = Fpath.(v "data" / "secmod_signkey") + +let db_lookup_signkey_data conn fname priv = + let pub = Crypto.EddsaPrivateKey.pub_of_priv priv in + let* opt = Pg.lookup_signing_key conn pub in + match opt with + | None -> + Fmt.error_msg + "load_signkey error no associated metadata found in database for \ + signkey `%s`." + (Fpath.to_string fname) + | Some (stamp_start, stamp_expire, stamp_end) -> + (* TODO master_sig *) + let master_sig = None in + let v = + Signkey.{ pub; priv; stamp_start; stamp_expire; stamp_end; master_sig } + in + Ok v + +let db_lookup_denom_data conn ~section_name priv = + let pub = Crypto.RsaPrivateKey.pub_of_priv priv in + let h_pub = + Bin_type.DenominationHash.hash (Crypto.RsaPublicKey.to_octets pub) + in + let* opt = Pg.lookup_denomination_key conn h_pub in + match opt with + | None -> + Fmt.error_msg + "load_denom error no associated metadata found in database for \ + denomination `%s`." + section_name + | Some + ( stamp_start, + stamp_expire_withdraw, + stamp_expire_deposit, + stamp_expire_legal, + value, + fee_withdraw, + fee_deposit, + fee_refresh, + fee_refund, + age_mask ) -> + let master_sig = None in + let v = + Denomination. + { + pub; + priv; + section_name; + value; + stamp_start; + stamp_expire_withdraw; + stamp_expire_deposit; + stamp_expire_legal; + fee_withdraw; + fee_deposit; + fee_refresh; + fee_refund; + age_mask; + h_pub; + master_sig; + } + in + Ok v + +let load_signkey conn fname = + let* opt = Data_file.read_eddsa fname in + match opt with + | None -> Ok None + | Some priv -> + let* signkey_data = db_lookup_signkey_data conn fname priv in + Ok (Some signkey_data) + +let _store t = + let* () = Data_file.write_eddsa Fpath.(dir / "sm_key") t.sm_key_priv in + let* () = + get_signkeys t + |> List.mapi (fun i key -> + let fname = Fpath.(dir / string_of_int i) in + (fname, key.Signkey.priv)) + |> list_iter (fun (fname, key) -> Data_file.write_eddsa fname key) + in + let* () = + get_denoms t + |> List.mapi (fun i key -> + let fname = Fpath.(dir / string_of_int i) in + (fname, key.Denomination.priv)) + |> list_iter (fun (fname, key) -> Data_file.write_rsa fname key) + in + Ok () + +let load conn = + let error_invalid_state = + Fmt.error_msg "secmod_signkey load error: invalid store state." + in + let* sm_key_priv = Data_file.read_eddsa Fpath.(dir / "sm_key") in + let* signkeys = + let l = List.init 1 (fun i -> Fpath.(dir / string_of_int i)) in + let* l = list_map (fun fname -> load_signkey conn fname) l in + match opt_list l with Error () -> error_invalid_state | Ok opt -> Ok opt + in + let* denoms = + let* l = + let open Config.Coin in + list_map + (fun coin -> + let section_name = coin.section_name in + let fname = Fpath.(dir / section_name) in + let* opt = Data_file.read_rsa fname in + match opt with + | None -> Ok None + | Some priv -> + (* todo: could check that coin config match db values *) + let* denom_data = db_lookup_denom_data conn ~section_name priv in + Ok (Some denom_data)) + all_coins + in + match Syntax.opt_list l with + | Error () -> error_invalid_state + | Ok opt -> Ok opt + in + match (sm_key_priv, signkeys, denoms) with + | None, None, None -> Ok None + | Some sm_key_priv, Some signkeys, Some denoms -> + let sm_key_pub = Crypto.EddsaPrivateKey.pub_of_priv sm_key_priv in + let lock = Miou.Mutex.create () in + let sk_ht = + signkeys + |> List.map (fun v -> (v.Signkey.pub, v)) + |> List.to_seq + |> Hashtbl.of_seq + in + let dn_ht = + denoms + |> List.map (fun v -> (v.Denomination.h_pub, v)) + |> List.to_seq + |> Hashtbl.of_seq + in + let sk_revoked_ht = Hashtbl.create 0xff in + let dn_revoked_ht = Hashtbl.create 0xff in + Ok + (Some + { + lock; + sm_key_priv; + sm_key_pub; + sk_ht; + sk_revoked_ht; + dn_ht; + dn_revoked_ht; + }) + | _, _ -> error_invalid_state + +let make_new () = + let lock = Miou.Mutex.create () in + let sm_key_priv, sm_key_pub = Mirage_crypto_ec.Ed25519.generate () in + let sk_ht = + [ Signkey.make () ] + |> List.map (fun v -> (v.Signkey.pub, v)) + |> List.to_seq + |> Hashtbl.of_seq + in + let dn_ht = + Config.Coin.all_coins + |> List.map Denomination.make + |> List.map (fun v -> (v.Denomination.h_pub, v)) + |> List.to_seq + |> Hashtbl.of_seq + in + let sk_revoked_ht = Hashtbl.create 0xff in + let dn_revoked_ht = Hashtbl.create 0xff in + { lock; sm_key_priv; sm_key_pub; sk_ht; sk_revoked_ht; dn_ht; dn_revoked_ht } + +let init conn = + match load conn with + | Ok None -> + let t = make_new () in + Logs.info (fun m -> m "secmod_signkey initialized with fresh keys"); + t + | Ok (Some v) -> + Logs.info (fun m -> m "secmod_signkey initialized from storage"); + v + | Error _ -> + (* TODO error: pretty print *) + Fmt.failwith "secmod_signkey init failure." diff --git a/src/secmod_signkey.mli b/src/secmod.mli similarity index 55% rename from src/secmod_signkey.mli rename to src/secmod.mli index 2149f943..2da919ef 100644 --- a/src/secmod_signkey.mli +++ b/src/secmod.mli @@ -1,22 +1,36 @@ -type t type eddsa_priv = Crypto.EddsaPrivateKey.t type eddsa_pub = Crypto.EddsaPublicKey.t +type h_denom_pub = Bin_type.DenominationHash.t +type t val get_sm_key_priv : t -> eddsa_priv val get_sm_key_pub : t -> eddsa_pub val get_signkeys : t -> Signkey.t list +val get_denoms : t -> Denomination.t list val init : (module Pg.CONN) -> t (* - management operations - *) -val add_master_signatures : +val add_signkey_master_signatures : (module Pg.CONN) -> t -> (eddsa_pub * Bin_sig.ExchangeSigningKeyValidity.t) list -> (unit, string) result +val add_denom_master_signatures : + (module Pg.CONN) -> + t -> + (h_denom_pub * Bin_sig.DenominationKeyValidity.t) list -> + (unit, string) result + val revoke_signkey : t -> eddsa_pub -> Bin_sig.MasterSigningKeyRevocation.t -> (unit, string) result + +val revoke_denomination : + t -> + h_denom_pub -> + Bin_sig.MasterDenominationKeyRevocation.t -> + (unit, string) result diff --git a/src/secmod_signkey.ml b/src/secmod_signkey.ml deleted file mode 100644 index db0df105..00000000 --- a/src/secmod_signkey.ml +++ /dev/null @@ -1,135 +0,0 @@ -(* TODO - - key rotation - - how many signkey to use? - we just use 1 for now - - does the secmod's key as metadata/expiration date? *) -open Syntax - -type eddsa_priv = Crypto.EddsaPrivateKey.t -type eddsa_pub = Crypto.EddsaPublicKey.t - -type t = { - lock: Miou.Mutex.t; - sm_key_priv: eddsa_priv; - sm_key_pub: eddsa_pub; - signkey_ht: (eddsa_pub, Signkey.t) Hashtbl.t; - revoked_ht: - (eddsa_pub, Signkey.t * Bin_sig.MasterSigningKeyRevocation.t) Hashtbl.t; -} - -let get_sm_key_priv t = t.sm_key_priv -let get_sm_key_pub t = t.sm_key_pub - -let get_signkeys t = - Miou.Mutex.protect t.lock @@ fun () -> - Hashtbl.to_seq_values t.signkey_ht |> List.of_seq - -let add_master_signatures conn t l = - Miou.Mutex.protect t.lock @@ fun () -> - list_iter - (fun (pub, master_sig) -> - match Hashtbl.find_opt t.signkey_ht pub with - | None -> Error "secmod_signkey failure: public key not found." - | Some signkey -> - let signkey = { signkey with master_sig= Some master_sig } in - let* () = - Pg.activate_signing_key conn ~master_sig signkey |> unwrap_err_caqti - in - Hashtbl.replace t.signkey_ht pub signkey; - Ok ()) - l - -let revoke_signkey t exchange_pub master_sig = - Miou.Mutex.protect t.lock @@ fun () -> - match Hashtbl.find_opt t.signkey_ht exchange_pub with - | None -> Error "secmod_denom failure: denomination hash not found." - | Some signkey -> - Hashtbl.remove t.signkey_ht exchange_pub; - Hashtbl.replace t.revoked_ht exchange_pub (signkey, master_sig); - Ok () - -let dir = Fpath.(v "data" / "secmod_signkey") - -let db_lookup_signkey_data conn fname priv = - let pub = Crypto.EddsaPrivateKey.pub_of_priv priv in - let* opt = Pg.lookup_signing_key conn pub in - match opt with - | None -> - Fmt.error_msg - "load_signkey error no associated metadata found in database for \ - signkey `%s`." - (Fpath.to_string fname) - | Some (stamp_start, stamp_expire, stamp_end) -> - (* TODO master_sig *) - let master_sig = None in - let v = - Signkey.{ pub; priv; stamp_start; stamp_expire; stamp_end; master_sig } - in - Ok v - -let load_signkey conn fname = - let* opt = Data_file.read_eddsa fname in - match opt with - | None -> Ok None - | Some priv -> - let* signkey_data = db_lookup_signkey_data conn fname priv in - Ok (Some signkey_data) - -let _store_secmod_data t = - let* () = Data_file.write_eddsa Fpath.(dir / "sm_key") t.sm_key_priv in - get_signkeys t - |> List.mapi (fun i key -> - let fname = Fpath.(dir / string_of_int i) in - (fname, key.Signkey.priv)) - |> list_iter (fun (fname, key) -> Data_file.write_eddsa fname key) - -let load conn = - let error_invalid_state = - Fmt.error_msg "secmod_signkey load error: invalid store state." - in - let* sm_key_priv = Data_file.read_eddsa Fpath.(dir / "sm_key") in - let* signkeys = - let l = List.init 1 (fun i -> Fpath.(dir / string_of_int i)) in - let* l = list_map (fun fname -> load_signkey conn fname) l in - match opt_list l with Error () -> error_invalid_state | Ok opt -> Ok opt - in - match (sm_key_priv, signkeys) with - | None, None -> Ok None - | Some sm_key_priv, Some signkeys -> - let sm_key_pub = Crypto.EddsaPrivateKey.pub_of_priv sm_key_priv in - let lock = Miou.Mutex.create () in - let signkey_ht = - signkeys - |> List.map (fun v -> (v.Signkey.pub, v)) - |> List.to_seq - |> Hashtbl.of_seq - in - let revoked_ht = Hashtbl.create 0xff in - Ok (Some { lock; sm_key_priv; sm_key_pub; signkey_ht; revoked_ht }) - | _, _ -> error_invalid_state - -let make_new () = - let lock = Miou.Mutex.create () in - let sm_key_priv, sm_key_pub = Mirage_crypto_ec.Ed25519.generate () in - let signkeys = [ Signkey.make () ] in - let signkey_ht = - signkeys - |> List.map (fun v -> (v.Signkey.pub, v)) - |> List.to_seq - |> Hashtbl.of_seq - in - let revoked_ht = Hashtbl.create 0xff in - { lock; sm_key_priv; sm_key_pub; signkey_ht; revoked_ht } - -let init conn = - match load conn with - | Ok None -> - let t = make_new () in - Logs.info (fun m -> m "secmod_signkey initialized with fresh keys"); - t - | Ok (Some v) -> - Logs.info (fun m -> m "secmod_signkey initialized from storage"); - v - | Error _ -> - (* TODO error: pretty print *) - Fmt.failwith "secmod_signkey init failure."