atproto libraries implementation in ocaml
at main 5.4 kB view raw
1(** Identity Tool - Resolve handles/DIDs and verify AT Protocol identities. 2 Usage: identity_tool [--resolve-handle|--resolve-did|--verify] <identifier> 3*) 4 5open Atproto_syntax 6open Atproto_identity 7 8let http_get ~hcs_client uri = 9 let url = Uri.to_string uri in 10 match Hcs.Client.request hcs_client url with 11 | Ok resp -> Did_resolver.{ status = resp.status; body = resp.body } 12 | Error e -> 13 let msg = 14 match e with 15 | Hcs.Client.Connection_failed s -> "Connection failed: " ^ s 16 | Hcs.Client.Tls_error s -> "TLS error: " ^ s 17 | Hcs.Client.Protocol_error s -> "Protocol error: " ^ s 18 | Hcs.Client.Timeout -> "Timeout" 19 | Hcs.Client.Invalid_response s -> "Invalid response: " ^ s 20 | Hcs.Client.Too_many_redirects -> "Too many redirects" 21 in 22 Did_resolver.{ status = 0; body = msg } 23 24let run_with_hcs ~hcs_client f = 25 Effect.Deep.try_with f () 26 { 27 effc = 28 (fun (type a) (eff : a Effect.t) -> 29 match eff with 30 | Did_resolver.Http_get uri -> 31 Some 32 (fun (k : (a, _) Effect.Deep.continuation) -> 33 Effect.Deep.continue k (http_get ~hcs_client uri)) 34 | Handle_resolver.Http_get uri -> 35 Some 36 (fun k -> 37 let r = http_get ~hcs_client uri in 38 Effect.Deep.continue k 39 Handle_resolver.{ status = r.status; body = r.body }) 40 | Handle_resolver.Dns_txt _ -> 41 Some 42 (fun k -> Effect.Deep.continue k Handle_resolver.Dns_not_found) 43 | _ -> None); 44 } 45 46let resolve_handle ~hcs_client h = 47 match Handle.of_string h with 48 | Error _ -> 49 Printf.printf "Error: Invalid handle\n"; 50 1 51 | Ok handle -> 52 run_with_hcs ~hcs_client (fun () -> 53 match Handle_resolver.resolve handle with 54 | Error e -> 55 Printf.printf "Error: %s\n" (Handle_resolver.error_to_string e); 56 1 57 | Ok did -> 58 Printf.printf "Handle: %s\nDID: %s\n" (Handle.to_string handle) 59 (Did.to_string did); 60 0) 61 62let resolve_did ~hcs_client d = 63 match Did.of_string d with 64 | Error _ -> 65 Printf.printf "Error: Invalid DID\n"; 66 1 67 | Ok did -> 68 run_with_hcs ~hcs_client (fun () -> 69 match Did_resolver.resolve_did did with 70 | Error e -> 71 Printf.printf "Error: %s\n" (Did_resolver.error_to_string e); 72 1 73 | Ok doc -> 74 Printf.printf "DID: %s\n" doc.id; 75 List.iter 76 (fun a -> Printf.printf "Handle: %s\n" a) 77 doc.also_known_as; 78 List.iter 79 (fun (v : Did_resolver.verification_method) -> 80 Printf.printf "Key: %s (%s)\n" 81 (Option.value ~default:"-" v.public_key_multibase) 82 v.type_) 83 doc.verification_method; 84 List.iter 85 (fun (s : Did_resolver.service) -> 86 Printf.printf "Service: %s -> %s\n" s.type_ s.service_endpoint) 87 doc.service; 88 0) 89 90let verify ~hcs_client id = 91 let is_did = String.length id > 4 && String.sub id 0 4 = "did:" in 92 run_with_hcs ~hcs_client (fun () -> 93 let result = 94 if is_did then 95 match Did.of_string id with 96 | Error _ -> Error "Invalid DID" 97 | Ok d -> 98 Identity.verify_did d |> Result.map_error Identity.error_to_string 99 else 100 match Handle.of_string id with 101 | Error _ -> Error "Invalid handle" 102 | Ok h -> 103 Identity.verify_handle h 104 |> Result.map_error Identity.error_to_string 105 in 106 match result with 107 | Error e -> 108 Printf.printf "FAILED: %s\n" e; 109 1 110 | Ok v -> 111 Printf.printf "PASSED\nDID: %s\nHandle: %s\n" (Did.to_string v.did) 112 (Handle.to_string v.handle); 113 Option.iter (Printf.printf "Key: %s\n") v.signing_key; 114 Option.iter 115 (fun u -> Printf.printf "PDS: %s\n" (Uri.to_string u)) 116 v.pds_endpoint; 117 0) 118 119type mode = Resolve_handle | Resolve_did | Verify 120 121let cli = 122 let open Climate.Arg_parser in 123 let+ id = 124 pos_req 0 string ~value_name:"IDENTIFIER" ~doc:"Handle or DID to look up" 125 and+ rh = flag [ "H"; "resolve-handle" ] ~doc:"Resolve handle to DID" 126 and+ rd = flag [ "d"; "resolve-did" ] ~doc:"Resolve DID to document" 127 and+ _vf = flag [ "v"; "verify" ] ~doc:"Verify bidirectional identity" in 128 let mode = 129 if rh then Resolve_handle else if rd then Resolve_did else Verify 130 in 131 (mode, id) 132 133let () = 134 Mirage_crypto_rng_unix.use_default (); 135 Eio_main.run @@ fun env -> 136 Eio.Switch.run @@ fun sw -> 137 let config = Hcs.Client.default_config |> Hcs.Client.with_insecure_tls in 138 let hcs_client = 139 Hcs.Client.create ~sw ~net:(Eio.Stdenv.net env) 140 ~clock:(Eio.Stdenv.clock env) ~config () 141 in 142 let mode, id = 143 Climate.Command.run 144 ~program_name:(Climate.Program_name.Literal "identity_tool") 145 (Climate.Command.singleton ~doc:"AT Protocol identity lookup tool" cli) 146 in 147 let result = 148 match mode with 149 | Resolve_handle -> resolve_handle ~hcs_client id 150 | Resolve_did -> resolve_did ~hcs_client id 151 | Verify -> verify ~hcs_client id 152 in 153 Hcs.Client.close hcs_client; 154 exit result