atproto libraries implementation in ocaml
at main 7.1 kB view raw
1(** Identity Verification for AT Protocol. 2 3 This module provides bidirectional verification of identities, ensuring that 4 DIDs and handles are properly linked. Verification confirms that: 5 6 1. A DID document includes the expected handle in alsoKnownAs 2. The handle 7 resolves back to the same DID 8 9 This is crucial for security as it prevents impersonation attacks. *) 10 11open Atproto_syntax 12 13(** {1 Types} *) 14 15type verified_identity = { 16 did : Did.t; 17 handle : Handle.t; 18 signing_key : string option; (** Multibase-encoded public key *) 19 pds_endpoint : Uri.t option; 20} 21(** A fully verified identity *) 22 23type verification_error = 24 | Did_resolution_failed of Did_resolver.error 25 | Handle_resolution_failed of Handle_resolver.error 26 | Handle_mismatch of { expected : Handle.t; found : Handle.t option } 27 | Did_mismatch of { expected : Did.t; found : Did.t } 28 | No_handle_in_document 29 | Invalid_did of string 30 | Invalid_handle of string 31 32let error_to_string = function 33 | Did_resolution_failed e -> 34 Printf.sprintf "DID resolution failed: %s" 35 (Did_resolver.error_to_string e) 36 | Handle_resolution_failed e -> 37 Printf.sprintf "Handle resolution failed: %s" 38 (Handle_resolver.error_to_string e) 39 | Handle_mismatch { expected; found } -> 40 let found_str = 41 match found with None -> "none" | Some h -> Handle.to_string h 42 in 43 Printf.sprintf "Handle mismatch: expected %s, found %s" 44 (Handle.to_string expected) 45 found_str 46 | Did_mismatch { expected; found } -> 47 Printf.sprintf "DID mismatch: expected %s, found %s" 48 (Did.to_string expected) (Did.to_string found) 49 | No_handle_in_document -> "No handle found in DID document" 50 | Invalid_did s -> Printf.sprintf "Invalid DID: %s" s 51 | Invalid_handle s -> Printf.sprintf "Invalid handle: %s" s 52 53(** {1 Verification Functions} *) 54 55(** Verify a DID by: 1. Resolving the DID to get the document 2. Extracting the 56 handle from alsoKnownAs 3. Resolving the handle to verify it points back to 57 the DID *) 58let verify_did did = 59 (* Step 1: Resolve DID *) 60 match Did_resolver.resolve_did did with 61 | Error e -> Error (Did_resolution_failed e) 62 | Ok doc -> ( 63 (* Step 2: Extract handle from document *) 64 match Did_resolver.get_handle doc with 65 | None -> Error No_handle_in_document 66 | Some handle -> ( 67 (* Step 3: Resolve handle back to DID *) 68 match Handle_resolver.resolve handle with 69 | Error e -> Error (Handle_resolution_failed e) 70 | Ok resolved_did -> 71 (* Step 4: Verify DIDs match *) 72 if Did.equal did resolved_did then 73 Ok 74 { 75 did; 76 handle; 77 signing_key = Did_resolver.get_signing_key doc; 78 pds_endpoint = Did_resolver.get_pds_endpoint doc; 79 } 80 else Error (Did_mismatch { expected = did; found = resolved_did }) 81 )) 82 83(** Verify a DID string *) 84let verify_did_string did_str = 85 match Did.of_string did_str with 86 | Error _ -> Error (Invalid_did did_str) 87 | Ok did -> verify_did did 88 89(** Verify a handle by: 1. Resolving the handle to get the DID 2. Resolving the 90 DID to get the document 3. Verifying the handle is in alsoKnownAs *) 91let verify_handle handle = 92 (* Step 1: Resolve handle to DID *) 93 match Handle_resolver.resolve handle with 94 | Error e -> Error (Handle_resolution_failed e) 95 | Ok did -> ( 96 (* Step 2: Resolve DID to document *) 97 match Did_resolver.resolve_did did with 98 | Error e -> Error (Did_resolution_failed e) 99 | Ok doc -> ( 100 (* Step 3: Verify handle is in document *) 101 match Did_resolver.get_handle doc with 102 | None -> Error No_handle_in_document 103 | Some doc_handle -> 104 if Handle.equal handle doc_handle then 105 Ok 106 { 107 did; 108 handle; 109 signing_key = Did_resolver.get_signing_key doc; 110 pds_endpoint = Did_resolver.get_pds_endpoint doc; 111 } 112 else 113 Error 114 (Handle_mismatch 115 { expected = handle; found = Some doc_handle }))) 116 117(** Verify a handle string *) 118let verify_handle_string handle_str = 119 match Handle.of_string handle_str with 120 | Error _ -> Error (Invalid_handle handle_str) 121 | Ok handle -> verify_handle handle 122 123(** Verify that a DID and handle are bidirectionally linked. Both must resolve 124 to each other. *) 125let verify_bidirectional did handle = 126 (* Verify from DID side *) 127 match Did_resolver.resolve_did did with 128 | Error e -> Error (Did_resolution_failed e) 129 | Ok doc -> ( 130 (* Check handle is in document *) 131 match Did_resolver.get_handle doc with 132 | None -> Error No_handle_in_document 133 | Some doc_handle -> ( 134 if not (Handle.equal handle doc_handle) then 135 Error 136 (Handle_mismatch { expected = handle; found = Some doc_handle }) 137 else 138 (* Verify from handle side *) 139 match Handle_resolver.resolve handle with 140 | Error e -> Error (Handle_resolution_failed e) 141 | Ok resolved_did -> 142 if not (Did.equal did resolved_did) then 143 Error (Did_mismatch { expected = did; found = resolved_did }) 144 else 145 Ok 146 { 147 did; 148 handle; 149 signing_key = Did_resolver.get_signing_key doc; 150 pds_endpoint = Did_resolver.get_pds_endpoint doc; 151 })) 152 153(** Verify bidirectional link from strings *) 154let verify_bidirectional_strings did_str handle_str = 155 match (Did.of_string did_str, Handle.of_string handle_str) with 156 | Error _, _ -> Error (Invalid_did did_str) 157 | _, Error _ -> Error (Invalid_handle handle_str) 158 | Ok did, Ok handle -> verify_bidirectional did handle 159 160(** {1 Quick Checks} *) 161 162(** Check if a DID has a valid handle (without full verification). Only checks 163 that the handle is present in the document. *) 164let did_has_handle did = 165 match Did_resolver.resolve_did did with 166 | Error _ -> false 167 | Ok doc -> Option.is_some (Did_resolver.get_handle doc) 168 169(** Check if a handle resolves to a valid DID. Does not verify the reverse 170 direction. *) 171let handle_resolves handle = 172 match Handle_resolver.resolve handle with Error _ -> false | Ok _ -> true 173 174(** Get identity info without full verification. Useful for display purposes 175 when verification is not critical. *) 176let get_identity_info did = 177 match Did_resolver.resolve_did did with 178 | Error e -> Error (Did_resolution_failed e) 179 | Ok doc -> 180 Ok 181 { 182 did; 183 handle = 184 (match Did_resolver.get_handle doc with 185 | Some h -> h 186 | None -> Handle.of_string_exn "unknown.invalid"); 187 signing_key = Did_resolver.get_signing_key doc; 188 pds_endpoint = Did_resolver.get_pds_endpoint doc; 189 }