From baedf3974926a8fe8ef26a9b222134aaa4970da5 Mon Sep 17 00:00:00 2001 From: Justin Garcia Date: Thu, 2 May 2024 00:42:20 +0800 Subject: [PATCH] refactor: move client_random to user model --- lib/backend/Routes/Api/Login.ml | 12 ++-- lib/backend/Routes/Api/Register.ml | 4 +- lib/backend/Routes/Api/Secrets.ml | 11 ++-- lib/frontend/js/hooks/Session.re | 1 + .../js/pages/GetStartedPageGenerateHooks.re | 2 - lib/models/Secrets.ml | 11 ---- lib/models/Secrets.mli | 6 -- lib/models/User.ml | 18 ++++-- lib/models/User.mli | 7 +++ lib/types/definitions.atd | 2 +- test/backend/Register_Api_test.ml | 27 +++++++-- test/backend/Secrets_Api_test.ml | 8 ++- test/models/Secrets_test.ml | 56 +++++++++---------- test/models/User_test.ml | 25 ++++++--- 14 files changed, 105 insertions(+), 85 deletions(-) diff --git a/lib/backend/Routes/Api/Login.ml b/lib/backend/Routes/Api/Login.ml index 2660f1b..e68110d 100644 --- a/lib/backend/Routes/Api/Login.ml +++ b/lib/backend/Routes/Api/Login.ml @@ -2,20 +2,18 @@ open HigherOrderHandlers open Types_native.Definitions_j open Vault_native -let get_secrets request username = +let get_user request username = Dream.sql request @@ fun db -> - match%lwt Models.Secrets.get_by_username ~username db with + match%lwt Models.User.get_by_username ~username db with | Ok user -> Lwt.return_ok user | Error e -> Lwt.return_error e let handler request = let handle_salt username = - match%lwt get_secrets request username with - | Ok (Some { client_random_value; _ }) -> + match%lwt get_user request username with + | Ok (Some { client_random; _ }) -> Dream.info (fun log -> log "User exists, serving client salt."); - let salt = - client_random_value |> Bytes.of_string |> Salt.compute_digest - in + let salt = client_random |> Bytes.of_string |> Salt.compute_digest in Dream.json @@ string_of_login_salt_response { salt } | Ok None -> Dream.info (fun log -> log "User does not exist, serving server salt."); diff --git a/lib/backend/Routes/Api/Register.ml b/lib/backend/Routes/Api/Register.ml index cbd6c92..8c8edcd 100644 --- a/lib/backend/Routes/Api/Register.ml +++ b/lib/backend/Routes/Api/Register.ml @@ -2,13 +2,13 @@ open HigherOrderHandlers open Types_native.Definitions_j let handler request = - let inner ({ username; auth_token } : register_user_payload) = + let inner ({ username; auth_token; client_random } : register_user_payload) = match ValidationUsername.validate username with | Validated Success -> ( let auth_token = Cipher.double_hmac auth_token in let%lwt insert_result = Dream.sql request (fun connection -> - Models.User.insert ~username ~auth_token connection) + Models.User.insert ~username ~auth_token ~client_random connection) in match insert_result with | Ok (id, public_id) -> diff --git a/lib/backend/Routes/Api/Secrets.ml b/lib/backend/Routes/Api/Secrets.ml index 2d40b0c..5528387 100644 --- a/lib/backend/Routes/Api/Secrets.ml +++ b/lib/backend/Routes/Api/Secrets.ml @@ -19,7 +19,6 @@ let get_secrets request (user_id : int32) = | Some { user_id = _; - client_random_value; encrypted_master_key; master_key_iv; encrypted_protection_key; @@ -32,7 +31,6 @@ let get_secrets request (user_id : int32) = Lwt.return_ok @@ Some { - client_random_value; encrypted_master_key; master_key_iv; encrypted_protection_key; @@ -46,7 +44,6 @@ let get_secrets request (user_id : int32) = let insert_secrets request (user_id : int32) ({ - client_random_value; encrypted_master_key; master_key_iv; encrypted_protection_key; @@ -66,10 +63,10 @@ let insert_secrets request (user_id : int32) if has_keys then Lwt.return_ok false else let* () = - Models.Secrets.insert ~user_id ~client_random_value ~encrypted_master_key - ~master_key_iv ~encrypted_protection_key ~protection_key_iv - ~exported_verification_key ~encrypted_verification_key - ~verification_key_iv ~exported_protection_key connection + Models.Secrets.insert ~user_id ~encrypted_master_key ~master_key_iv + ~encrypted_protection_key ~protection_key_iv ~exported_verification_key + ~encrypted_verification_key ~verification_key_iv + ~exported_protection_key connection in Lwt.return_ok true diff --git a/lib/frontend/js/hooks/Session.re b/lib/frontend/js/hooks/Session.re index 25b557a..9d3860f 100644 --- a/lib/frontend/js/hooks/Session.re +++ b/lib/frontend/js/hooks/Session.re @@ -38,6 +38,7 @@ let useRegister = () => { ApiRegister.post({ username, auth_token: Salt.toHash(freshDerivedKey.hashedAuthenticationKey), + client_random: Base64_js.Uint8Array.encode(clientRandom), }); let* _ = diff --git a/lib/frontend/js/pages/GetStartedPageGenerateHooks.re b/lib/frontend/js/pages/GetStartedPageGenerateHooks.re index 58eb098..8d4fdf7 100644 --- a/lib/frontend/js/pages/GetStartedPageGenerateHooks.re +++ b/lib/frontend/js/pages/GetStartedPageGenerateHooks.re @@ -77,8 +77,6 @@ let useGenerateKeys = () => { push({kind: Loading, message: "Submitting"}); let registerKeysPayload: Types_universal.Definitions_t.register_keys_payload = { - client_random_value: - clientSecrets.clientRandom |> Base64_js.Uint8Array.encode, encrypted_master_key: wrappedMasterKey |> Base64_js.ArrayBuffer.encode, master_key_iv: clientSecrets.masterKeyIv |> Base64_js.Uint8Array.encode, diff --git a/lib/models/Secrets.ml b/lib/models/Secrets.ml index 93e3522..7db06ac 100644 --- a/lib/models/Secrets.ml +++ b/lib/models/Secrets.ml @@ -2,7 +2,6 @@ open Utils type t = { user_id : int32; - client_random_value : string; encrypted_master_key : string; master_key_iv : string; encrypted_protection_key : string; @@ -20,8 +19,6 @@ let create_table = CREATE TABLE IF NOT EXISTS secrets ( user_id INT PRIMARY KEY REFERENCES users(id), - client_random_value BYTEA NOT NULL, - encrypted_master_key BYTEA NOT NULL, master_key_iv BYTEA NOT NULL, @@ -43,8 +40,6 @@ let get_by_user_id = SELECT @int32{user_id}, - @Base64Octets{client_random_value}, - @Base64Octets{encrypted_master_key}, @Base64Octets{master_key_iv}, @@ -69,8 +64,6 @@ let get_by_username = SELECT @int32{user_id}, - @Base64Octets{client_random_value}, - @Base64Octets{encrypted_master_key}, @Base64Octets{master_key_iv}, @@ -99,8 +92,6 @@ let insert = INSERT INTO secrets ( user_id, - client_random_value, - encrypted_master_key, master_key_iv, @@ -115,8 +106,6 @@ INSERT INTO secrets ( VALUES( %int32{user_id}, - %Base64Octets{client_random_value}, - %Base64Octets{encrypted_master_key}, %Base64Octets{master_key_iv}, diff --git a/lib/models/Secrets.mli b/lib/models/Secrets.mli index 5fdd7eb..ba492cf 100644 --- a/lib/models/Secrets.mli +++ b/lib/models/Secrets.mli @@ -2,11 +2,6 @@ type t = { user_id : int32; (** References a {!User.t.id}. *) - client_random_value : string; - (** A random value generated by a CSPRNG in the client. - - Typically 16 string in length, this is used as part of the salt - for deriving the bits in the "[DerivedKey]". *) encrypted_master_key : string; (** The symmetric key used to encrypt the {!encrypted_protection_key} and {!encrypted_verification_key}. *) @@ -50,7 +45,6 @@ val get_by_username : val insert : user_id:int32 -> - client_random_value:string -> encrypted_master_key:string -> master_key_iv:string -> encrypted_protection_key:string -> diff --git a/lib/models/User.ml b/lib/models/User.ml index 7d949ca..4b45af2 100644 --- a/lib/models/User.ml +++ b/lib/models/User.ml @@ -1,8 +1,11 @@ +open Utils + type t = { id : int32; public_id : string; username : string; auth_token : string; + client_random : string; } let create_table = @@ -13,7 +16,8 @@ CREATE TABLE IF NOT EXISTS users ( id SERIAL PRIMARY KEY, public_id VARCHAR(12) DEFAULT nanoid(12, '0123456789abcdefghijklmnopqrstuvwxyz'), username VARCHAR(16) UNIQUE NOT NULL, - auth_token VARCHAR(128) NOT NULL + auth_token VARCHAR(128) NOT NULL, + client_random BYTEA NOT NULL ); |sql}] () @@ -26,7 +30,8 @@ SELECT @int32{id}, @string{public_id}, @string{username}, - @string{auth_token} + @string{auth_token}, + @Base64Octets{client_random} FROM users WHERE @@ -42,7 +47,8 @@ SELECT @int32{id}, @string{public_id}, @string{username}, - @string{auth_token} + @string{auth_token}, + @Base64Octets{client_random} FROM users WHERE @@ -56,11 +62,13 @@ let insert = {sql| INSERT INTO users ( username, - auth_token + auth_token, + client_random ) VALUES( %string{username}, - %string{auth_token} + %string{auth_token}, + %Base64Octets{client_random} ) RETURNING @int32{id}, diff --git a/lib/models/User.mli b/lib/models/User.mli index 3c6e360..bab6816 100644 --- a/lib/models/User.mli +++ b/lib/models/User.mli @@ -9,6 +9,12 @@ type t = { auth_token : string; (** A SHA-512 hash representing the authentication token derived from the user's password. *) + client_random : string; + (** A random value generated by a CSPRNG in the client. + + Typically 16 string in length, this is used as part of the salt + for deriving the bits in the "[DerivedKey]" and consequently + the {!auth_token}. *) } val create_table : @@ -32,6 +38,7 @@ val get_by_username : val insert : username:string -> auth_token:string -> + client_random:string -> (module Rapper_helper.CONNECTION) -> (int32 * string, [> Caqti_error.call_or_retrieve ]) result Lwt.t (** [insert ~username ~auth_token db] insert a new row in the [users] table, diff --git a/lib/types/definitions.atd b/lib/types/definitions.atd index 0874536..1958382 100644 --- a/lib/types/definitions.atd +++ b/lib/types/definitions.atd @@ -14,10 +14,10 @@ type 'json base_error_response = { type register_user_payload = { username: string; auth_token: string; + client_random: string; } type register_keys_payload = { - client_random_value: string; encrypted_master_key: string; master_key_iv: string; encrypted_protection_key: string; diff --git a/test/backend/Register_Api_test.ml b/test/backend/Register_Api_test.ml index 2edc63f..0e8dda1 100644 --- a/test/backend/Register_Api_test.ml +++ b/test/backend/Register_Api_test.ml @@ -16,7 +16,11 @@ let it_works = let%lwt response, body = let json = string_of_register_user_payload - { username = "purefunctor"; auth_token = double_hmac "auth_token" } + { + username = "purefunctor"; + auth_token = String.make 128 'J'; + client_random = String.make 16 'K'; + } in post_json cookie_headers json "http://localhost:8080/api/register" in @@ -59,7 +63,11 @@ let already_registered = let%lwt _, body = let json = string_of_register_user_payload - { username = "purefunctor"; auth_token = double_hmac "auth_token" } + { + username = "purefunctor"; + auth_token = String.make 128 'J'; + client_random = String.make 16 'K'; + } in post_json cookie_headers json "http://localhost:8080/api/register" in @@ -68,7 +76,11 @@ let already_registered = let%lwt response, body = let json = string_of_register_user_payload - { username = "purefunctor"; auth_token = double_hmac "auth_token" } + { + username = "purefunctor"; + auth_token = String.make 128 'J'; + client_random = String.make 16 'K'; + } in post_json cookie_headers json "http://localhost:8080/api/register" in @@ -105,7 +117,11 @@ let creates_session = let%lwt response, body = let json = string_of_register_user_payload - { username = "purefunctor"; auth_token = double_hmac "auth_token" } + { + username = "purefunctor"; + auth_token = String.make 128 'J'; + client_random = String.make 16 'K'; + } in post_json cookie_headers json "http://localhost:8080/api/register" in @@ -134,7 +150,8 @@ let invalid_username = string_of_register_user_payload { username = "invalid&username"; - auth_token = double_hmac "auth_token"; + auth_token = String.make 128 'J'; + client_random = String.make 16 'K'; } in post_json cookie_headers json "http://localhost:8080/api/register" diff --git a/test/backend/Secrets_Api_test.ml b/test/backend/Secrets_Api_test.ml index b828f9d..e5af71e 100644 --- a/test/backend/Secrets_Api_test.ml +++ b/test/backend/Secrets_Api_test.ml @@ -8,7 +8,11 @@ let register_fake_user () = let%lwt response, body = let json = string_of_register_user_payload - { username = "purefunctor"; auth_token = "auth_token" } + { + username = "purefunctor"; + auth_token = String.make 128 'J'; + client_random = String.make 16 'K'; + } in post_json cookie_headers json "http://localhost:8080/api/register" in @@ -30,7 +34,6 @@ let register_fake_user () = Lwt.return cookie_headers let make_payload generate = - let client_random_value = generate () in let encrypted_master_key = generate () in let master_key_iv = generate () in let encrypted_protection_key = generate () in @@ -41,7 +44,6 @@ let make_payload generate = let exported_verification_key = generate () in string_of_register_keys_payload { - client_random_value; encrypted_master_key; master_key_iv; encrypted_protection_key; diff --git a/test/models/Secrets_test.ml b/test/models/Secrets_test.ml index 4facf92..0fe2518 100644 --- a/test/models/Secrets_test.ml +++ b/test/models/Secrets_test.ml @@ -10,7 +10,6 @@ module Secrets = struct record [ field "user_id" (fun k -> k.user_id) int32; - field "client_random_value" (fun k -> k.client_random_value) string; field "encrypted_master_key" (fun k -> k.encrypted_master_key) string; field "master_key_iv" (fun k -> k.master_key_iv) string; field "encrypted_protection_key" @@ -33,7 +32,6 @@ module Secrets = struct List.for_all Fun.id [ Int32.equal x.user_id y.user_id; - String.equal x.client_random_value y.client_random_value; String.equal x.encrypted_master_key y.encrypted_master_key; String.equal x.master_key_iv y.master_key_iv; String.equal x.encrypted_protection_key y.encrypted_protection_key; @@ -70,13 +68,14 @@ let insert = let* user_id, _ = let username = "insert.test" in let auth_token = String.make 128 ' ' in - User.insert ~username ~auth_token db + let client_random = String.make 16 ' ' in + User.insert ~username ~auth_token ~client_random db in let* _ = - Secrets.insert ~user_id ~client_random_value ~encrypted_master_key - ~master_key_iv ~encrypted_protection_key ~protection_key_iv - ~exported_protection_key ~encrypted_verification_key - ~verification_key_iv ~exported_verification_key db + Secrets.insert ~user_id ~encrypted_master_key ~master_key_iv + ~encrypted_protection_key ~protection_key_iv ~exported_protection_key + ~encrypted_verification_key ~verification_key_iv + ~exported_verification_key db in Lwt.return_ok () in @@ -89,19 +88,20 @@ let insert_existing = let* user_id, _ = let username = "insert.test" in let auth_token = String.make 128 ' ' in - User.insert ~username ~auth_token db + let client_random = String.make 16 ' ' in + User.insert ~username ~auth_token ~client_random db in let* _ = - Secrets.insert ~user_id ~client_random_value ~encrypted_master_key - ~master_key_iv ~encrypted_protection_key ~protection_key_iv - ~exported_protection_key ~encrypted_verification_key - ~verification_key_iv ~exported_verification_key db + Secrets.insert ~user_id ~encrypted_master_key ~master_key_iv + ~encrypted_protection_key ~protection_key_iv ~exported_protection_key + ~encrypted_verification_key ~verification_key_iv + ~exported_verification_key db in let errorful = - Secrets.insert ~user_id ~client_random_value ~encrypted_master_key - ~master_key_iv ~encrypted_protection_key ~protection_key_iv - ~exported_protection_key ~encrypted_verification_key - ~verification_key_iv ~exported_verification_key db + Secrets.insert ~user_id ~encrypted_master_key ~master_key_iv + ~encrypted_protection_key ~protection_key_iv ~exported_protection_key + ~encrypted_verification_key ~verification_key_iv + ~exported_verification_key db in Lwt.bind errorful (function | Ok _ -> Alcotest.fail "Expected an error." @@ -116,19 +116,19 @@ let get_by_user_id = let* user_id, _ = let username = "insert.test" in let auth_token = String.make 128 ' ' in - User.insert ~username ~auth_token db + let client_random = String.make 16 ' ' in + User.insert ~username ~auth_token ~client_random db in let* _ = - Secrets.insert ~user_id ~client_random_value ~encrypted_master_key - ~master_key_iv ~encrypted_protection_key ~protection_key_iv - ~exported_protection_key ~encrypted_verification_key - ~verification_key_iv ~exported_verification_key db + Secrets.insert ~user_id ~encrypted_master_key ~master_key_iv + ~encrypted_protection_key ~protection_key_iv ~exported_protection_key + ~encrypted_verification_key ~verification_key_iv + ~exported_verification_key db in let expected = Secrets. { user_id; - client_random_value; encrypted_master_key; master_key_iv; encrypted_protection_key; @@ -155,19 +155,19 @@ let get_by_username = let username = "insert.test" in let* user_id, _ = let auth_token = String.make 128 ' ' in - User.insert ~username ~auth_token db + let client_random = String.make 16 ' ' in + User.insert ~username ~auth_token ~client_random db in let* _ = - Secrets.insert ~user_id ~client_random_value ~encrypted_master_key - ~master_key_iv ~encrypted_protection_key ~protection_key_iv - ~exported_protection_key ~encrypted_verification_key - ~verification_key_iv ~exported_verification_key db + Secrets.insert ~user_id ~encrypted_master_key ~master_key_iv + ~encrypted_protection_key ~protection_key_iv ~exported_protection_key + ~encrypted_verification_key ~verification_key_iv + ~exported_verification_key db in let expected = Secrets. { user_id; - client_random_value; encrypted_master_key; master_key_iv; encrypted_protection_key; diff --git a/test/models/User_test.ml b/test/models/User_test.ml index 2155238..009a161 100644 --- a/test/models/User_test.ml +++ b/test/models/User_test.ml @@ -36,12 +36,14 @@ let insert = let* first_id, _ = let username = "maho.akashi" in let auth_token = String.make 128 ' ' in - User.insert ~username ~auth_token db + let client_random = String.make 16 ' ' in + User.insert ~username ~auth_token ~client_random db in let* second_id, _ = let username = "rinku.aimoto" in let auth_token = String.make 128 ' ' in - User.insert ~username ~auth_token db + let client_random = String.make 16 ' ' in + User.insert ~username ~auth_token ~client_random db in let _ = Alcotest.(check int32) "first_id = 1" first_id 1l in let _ = Alcotest.(check int32) "second_id = 2" second_id 2l in @@ -55,8 +57,9 @@ let insert_existing = let* _ = Initialize.initialize db in let username = "maho.akashi" in let auth_token = String.make 128 ' ' in - let* _ = User.insert ~username ~auth_token db in - let errorful = User.insert ~username ~auth_token db in + let client_random = String.make 16 ' ' in + let* _ = User.insert ~username ~auth_token ~client_random db in + let errorful = User.insert ~username ~auth_token ~client_random db in Lwt.bind errorful @@ function | Ok _ -> Alcotest.fail "Expected an error." | Error _ -> Lwt.return_ok () @@ -69,8 +72,11 @@ let get_by_id = let* _ = Initialize.initialize db in let username = "maho.akashi" in let auth_token = String.make 128 ' ' in - let* id, public_id = User.insert ~username ~auth_token db in - let expected = User.{ id; public_id; username; auth_token } in + let client_random = String.make 16 ' ' in + let* id, public_id = User.insert ~username ~auth_token ~client_random db in + let expected = + User.{ id; public_id; username; auth_token; client_random } + in let* actual = User.get_by_id ~id db in let _ = Alcotest.(check @@ option (module User)) @@ -86,8 +92,11 @@ let get_by_username = let* _ = Initialize.initialize db in let username = "maho.akashi" in let auth_token = String.make 128 ' ' in - let* id, public_id = User.insert ~username ~auth_token db in - let expected = User.{ id; public_id; username; auth_token } in + let client_random = String.make 16 ' ' in + let* id, public_id = User.insert ~username ~auth_token ~client_random db in + let expected = + User.{ id; public_id; username; auth_token; client_random } + in let* actual = User.get_by_username ~username db in let _ = Alcotest.(check @@ option (module User))