···212212let sign ~privkey ~msg : bytes =
213213 let privkey, (module Curve : CURVE) = privkey in
214214 Curve.sign ~privkey ~msg
215215+216216+let pubkey_to_did_key pubkey : string =
217217+ let pubkey, (module Curve : CURVE) = pubkey in
218218+ Curve.pubkey_to_did_key pubkey
+1-23
pegasus/lib/api/identity/updateHandle.ml
···11type request = {handle: string} [@@deriving yojson]
2233-let validate_handle handle =
44- if not @@ String.ends_with ~suffix:("." ^ Env.hostname) handle then
55- Error (Errors.InvalidRequestError ("InvalidHandle", "invalid handle suffix"))
66- else
77- let front =
88- String.sub handle 0
99- (String.length handle - (String.length Env.hostname + 1))
1010- in
1111- if String.contains front '.' then
1212- Error
1313- (Errors.InvalidRequestError
1414- ("InvalidHandle", "invalid characters in handle") )
1515- else
1616- match String.length front with
1717- | l when l < 3 ->
1818- Error
1919- (Errors.InvalidRequestError ("InvalidHandle", "handle too short"))
2020- | l when l > 18 ->
2121- Error (Errors.InvalidRequestError ("InvalidHandle", "handle too long"))
2222- | _ ->
2323- Ok ()
2424-253let handler =
264 Xrpc.handler ~auth:Auth.Verifiers.authorization (fun {req; auth; db} ->
275 let did = Auth.get_authed_did_exn auth in
···3311 | Error _ ->
3412 Errors.invalid_request "invalid request body"
3513 in
3636- match validate_handle handle with
1414+ match Util.validate_handle handle with
3715 | Error e ->
3816 raise e
3917 | Ok () -> (
+102
pegasus/lib/api/repo/createAccount.ml
···11+type request =
22+ { email: string
33+ ; handle: string
44+ ; did: string option
55+ ; password: string
66+ ; invite_code: string option [@key "inviteCode"]
77+ ; recovery_key: string option [@key "recoveryKey"] }
88+[@@deriving yojson {strict= false}]
99+1010+type response =
1111+ { access_jwt: string [@key "accessJwt"]
1212+ ; refresh_jwt: string [@key "refreshJwt"]
1313+ ; handle: string
1414+ ; did: string }
1515+[@@deriving yojson]
1616+1717+let handler =
1818+ Xrpc.handler (fun ctx ->
1919+ let%lwt input = Xrpc.parse_body ctx.req request_of_yojson in
2020+ if input.invite_code = None && Env.invite_required = true then
2121+ Errors.invalid_request ~name:"InvalidInviteCode"
2222+ "no invite code provided" ;
2323+ let () =
2424+ match Util.validate_handle input.handle with
2525+ | Ok _ ->
2626+ ()
2727+ | Error e ->
2828+ raise e
2929+ in
3030+ let%lwt () =
3131+ match%lwt
3232+ Lwt.all
3333+ [ Data_store.get_actor_by_identifier input.email ctx.db
3434+ ; Data_store.get_actor_by_identifier input.handle ctx.db ]
3535+ with
3636+ | [Some _; _] ->
3737+ Errors.invalid_request "an account with that email already exists"
3838+ | [_; Some _] ->
3939+ Errors.invalid_request "an account with that handle already exists"
4040+ | _ ->
4141+ Lwt.return ()
4242+ in
4343+ let signing_key, signing_pubkey = Kleidos.P256.generate_keypair () in
4444+ let%lwt did =
4545+ match input.did with
4646+ | Some did -> (
4747+ match%lwt Data_store.get_actor_by_identifier did ctx.db with
4848+ | Some _ ->
4949+ Errors.invalid_request "an account with that did already exists"
5050+ | None ->
5151+ Lwt.return did )
5252+ | None -> (
5353+ let sk_did = Kleidos.P256.pubkey_to_did_key signing_pubkey in
5454+ let rotation_did_keys =
5555+ [Kleidos.pubkey_to_did_key Env.rotation_key]
5656+ in
5757+ let rotation_did_keys =
5858+ match input.recovery_key with
5959+ | Some rk ->
6060+ rk :: rotation_did_keys
6161+ | None ->
6262+ rotation_did_keys
6363+ in
6464+ match%lwt
6565+ Plc.submit_genesis Env.rotation_key sk_did ~rotation_did_keys
6666+ input.handle
6767+ with
6868+ | Ok did ->
6969+ Lwt.return did
7070+ | Error e ->
7171+ failwith e )
7272+ in
7373+ let sk_priv_mk = Kleidos.P256.privkey_to_multikey signing_key in
7474+ let%lwt () =
7575+ Data_store.create_actor ~did ~handle:input.handle ~email:input.email
7676+ ~password:input.password ~signing_key:sk_priv_mk ctx.db
7777+ in
7878+ let%lwt _ = User_store.connect ~create:true ~write:true did in
7979+ let%lwt repo = Repository.load ~write:true ~ds:ctx.db did in
8080+ let%lwt _ = Repository.put_initial_commit repo in
8181+ let%lwt _ =
8282+ Sequencer.sequence_identity ctx.db ~did ~handle:input.handle ()
8383+ in
8484+ let%lwt _ = Sequencer.sequence_account ctx.db ~did ~active:true () in
8585+ let%lwt {commit= commit_cid, commit; _} =
8686+ Repository.apply_writes repo [] None
8787+ in
8888+ let open User_store.Types in
8989+ let commit_block =
9090+ commit |> User_store.Types.signed_commit_to_yojson
9191+ |> Dag_cbor.encode_yojson
9292+ in
9393+ let block_stream = Lwt_seq.of_list [(commit_cid, commit_block)] in
9494+ let%lwt blocks =
9595+ Car.blocks_to_stream commit_cid block_stream |> Car.collect_stream
9696+ in
9797+ let%lwt _ =
9898+ Sequencer.sequence_sync ctx.db ~did ~rev:commit.rev ~blocks ()
9999+ in
100100+ let access_jwt, refresh_jwt = Auth.generate_jwt did in
101101+ Dream.json @@ Yojson.Safe.to_string
102102+ @@ response_to_yojson {access_jwt; refresh_jwt; did; handle= input.handle} )
+1-1
pegasus/lib/data_store.ml
···196196197197let create_actor ~did ~handle ~email ~password ~signing_key conn =
198198 let password_hash = Bcrypt.hash password |> Bcrypt.string_of_hash in
199199- let now = Unix.gettimeofday () *. 1000. |> int_of_float in
199199+ let now = Util.now_ms () in
200200 let$! () =
201201 Queries.create_actor ~did ~handle ~email ~password_hash ~signing_key
202202 ~created_at:now
+39
pegasus/lib/plc.ml
···134134 | _ ->
135135 failwith "unexpected json structure" )
136136137137+let signed_operation_of_yojson (json : Yojson.Safe.t) =
138138+ let open Yojson.Safe.Util in
139139+ let type' = json |> member "type" |> to_string in
140140+ match type' with
141141+ | "plc_operation" ->
142142+ let rotation_keys =
143143+ json |> member "rotationKeys" |> to_list |> List.map to_string
144144+ in
145145+ let verification_methods =
146146+ json
147147+ |> member "verificationMethods"
148148+ |> to_assoc
149149+ |> List.map (fun (k, v) -> (k, v |> to_string))
150150+ in
151151+ let also_known_as =
152152+ json |> member "alsoKnownAs" |> to_list |> List.map to_string
153153+ in
154154+ let services =
155155+ json |> member "services" |> to_assoc
156156+ |> List.map (fun (k, v) -> (k, Result.get_ok @@ service_of_yojson v))
157157+ in
158158+ let prev = json |> member "prev" |> to_string_option in
159159+ let signature = json |> member "sig" |> to_string in
160160+ Ok
161161+ (Operation
162162+ { type'
163163+ ; rotation_keys
164164+ ; verification_methods
165165+ ; also_known_as
166166+ ; services
167167+ ; prev
168168+ ; signature } )
169169+ | "plc_tombstone" ->
170170+ let prev = json |> member "prev" |> to_string in
171171+ let signature = json |> member "sig" |> to_string in
172172+ Ok (Tombstone {type'; prev; signature})
173173+ | t ->
174174+ Error ("unexpected operation type " ^ t)
175175+137176type audit_log_operation =
138177 { signature: string [@key "sig"]
139178 ; prev: string option
+22
pegasus/lib/util.ml
···261261262262(* returns whether the value is None *)
263263let is_none = function None -> true | _ -> false
264264+265265+let validate_handle handle =
266266+ if not @@ String.ends_with ~suffix:("." ^ Env.hostname) handle then
267267+ Error (Errors.InvalidRequestError ("InvalidHandle", "invalid handle suffix"))
268268+ else
269269+ let front =
270270+ String.sub handle 0
271271+ (String.length handle - (String.length Env.hostname + 1))
272272+ in
273273+ if String.contains front '.' then
274274+ Error
275275+ (Errors.InvalidRequestError
276276+ ("InvalidHandle", "invalid characters in handle") )
277277+ else
278278+ match String.length front with
279279+ | l when l < 3 ->
280280+ Error
281281+ (Errors.InvalidRequestError ("InvalidHandle", "handle too short"))
282282+ | l when l > 18 ->
283283+ Error (Errors.InvalidRequestError ("InvalidHandle", "handle too long"))
284284+ | _ ->
285285+ Ok ()