objective categorical abstract machine language personal data server

use existing jwt logic

futur.blue fee8144c 987aad0a

verified
+40 -42
+31 -25
pegasus/lib/jwt.ml
··· 19 19 let b64_decode str = 20 20 match Base64.decode ~pad:false ~alphabet:Base64.uri_safe_alphabet str with 21 21 | Ok s -> 22 - Ok s 22 + s 23 23 | Error (`Msg e) -> 24 - Error e 24 + failwith e 25 25 26 26 let extract_signature_components signature = 27 27 if Bytes.length signature <> 64 then failwith "expected 64 byte jwt signature" ··· 30 30 let s = Bytes.sub signature 32 32 in 31 31 (r, s) 32 32 33 - let sign_jwt payload signing_key = 33 + let sign_jwt payload ?(typ = "JWT") signing_key = 34 34 let _, (module Curve : Kleidos.CURVE) = signing_key in 35 35 let alg = 36 36 match Curve.name with ··· 51 51 failwith "invalid curve" 52 52 in 53 53 let header_json = 54 - `Assoc [("alg", `String alg); ("crv", `String crv); ("typ", `String "JWT")] 54 + `Assoc [("alg", `String alg); ("crv", `String crv); ("typ", `String typ)] 55 55 in 56 56 let encoded_header = header_json |> Yojson.Safe.to_string |> b64_encode in 57 57 let encoded_payload = payload |> Yojson.Safe.to_string |> b64_encode in ··· 65 65 let decode_jwt jwt = 66 66 match String.split_on_char '.' jwt with 67 67 | [header_b64; payload_b64; _] -> ( 68 - match (b64_decode header_b64, b64_decode payload_b64) with 69 - | Ok header_str, Ok payload_str -> ( 70 - try 71 - let header = Yojson.Safe.from_string header_str in 72 - let payload = Yojson.Safe.from_string payload_str in 73 - Ok (header, payload) 74 - with _ -> Error "invalid json in jwt" ) 75 - | Error e, _ | _, Error e -> 76 - Error e ) 68 + try 69 + let header = Yojson.Safe.from_string (b64_decode header_b64) in 70 + let payload = Yojson.Safe.from_string (b64_decode payload_b64) in 71 + Ok (header, payload) 72 + with _ -> Error "invalid jwt" ) 77 73 | _ -> 78 74 Error "invalid jwt format" 79 75 80 76 let verify_jwt jwt pubkey = 81 77 match String.split_on_char '.' jwt with 82 - | [header_b64; payload_b64; signature_b64] -> ( 83 - match b64_decode signature_b64 with 84 - | Error e -> 85 - Error e 86 - | Ok signature_str -> 87 - let signature = Bytes.of_string signature_str in 88 - let signing_input = header_b64 ^ "." ^ payload_b64 in 89 - let verified = 90 - Kleidos.verify ~pubkey ~msg:(Bytes.of_string signing_input) ~signature 91 - in 92 - if verified then decode_jwt jwt 93 - else Error "jwt signature verification failed" ) 78 + | [header_b64; payload_b64; signature_b64] -> 79 + let signature = Bytes.of_string (b64_decode signature_b64) in 80 + let signing_input = header_b64 ^ "." ^ payload_b64 in 81 + let verified = 82 + Kleidos.verify ~pubkey ~msg:(Bytes.of_string signing_input) ~signature 83 + in 84 + if verified then decode_jwt jwt 85 + else Error "jwt signature verification failed" 94 86 | _ -> 95 87 Error "invalid jwt format" 96 88 ··· 126 118 let exp = now_s + Defaults.service_token_exp in 127 119 let payload = service_jwt_to_yojson {iss= did; aud; lxm; exp} in 128 120 sign_jwt payload signing_key 121 + 122 + let extract_claim claims key = 123 + try 124 + let open Yojson.Safe.Util in 125 + let rec find_nested json keys = 126 + match keys with 127 + | [] -> 128 + Some json 129 + | k :: rest -> 130 + find_nested (json |> member k) rest 131 + in 132 + let keys = String.split_on_char '.' key in 133 + find_nested claims keys 134 + with _ -> None
+9 -17
pegasus/lib/oauth/dpop.ml
··· 29 29 Bytes.set_int64_be data 0 counter ; 30 30 Digestif.SHA256.( 31 31 hmac_bytes ~key:(Bytes.to_string secret) data 32 - |> to_raw_string 33 - |> Base64.encode_exn ~pad:false ) 32 + |> to_raw_string |> Jwt.b64_encode ) 34 33 35 34 let create_nonce_state secret = 36 35 let counter = ··· 79 78 ?port:(Uri.port uri) ~path:(Uri.path uri) () 80 79 |> Uri.to_string 81 80 82 - let b64url_decode s = 83 - Base64.decode_exn ~alphabet:Base64.uri_safe_alphabet ~pad:false s 84 - 85 81 let compute_jwk_thumbprint jwk = 86 82 let open Yojson.Safe.Util in 87 83 let crv = jwk |> member "crv" |> to_string in ··· 92 88 (* keys must be in lexicographic order *) 93 89 Printf.sprintf {|{"crv":"%s","kty":"%s","x":"%s","y":"%s"}|} crv kty x y 94 90 in 95 - Digestif.SHA256.( 96 - digest_string tp |> to_raw_string |> Base64.encode_exn ~pad:false ) 91 + Digestif.SHA256.(digest_string tp |> to_raw_string |> Jwt.b64_encode) 97 92 98 93 let verify_signature jwt jwk = 99 94 let open Yojson.Safe.Util in ··· 101 96 match parts with 102 97 | [header_b64; payload_b64; sig_b64] -> 103 98 let signing_input = header_b64 ^ "." ^ payload_b64 in 104 - let msg = 105 - Digestif.SHA256.(digest_string signing_input |> to_raw_string) 106 - |> Bytes.of_string 107 - in 99 + let msg = Bytes.of_string signing_input in 108 100 let x = 109 - jwk |> member "x" |> to_string |> b64url_decode |> Bytes.of_string 101 + jwk |> member "x" |> to_string |> Jwt.b64_decode |> Bytes.of_string 110 102 in 111 103 let y = 112 - jwk |> member "y" |> to_string |> b64url_decode |> Bytes.of_string 104 + jwk |> member "y" |> to_string |> Jwt.b64_decode |> Bytes.of_string 113 105 in 114 106 let crv = jwk |> member "crv" |> to_string in 115 107 let pubkey = Bytes.cat (Bytes.of_string "\x04") (Bytes.cat x y) in ··· 123 115 | _ -> 124 116 failwith "unsupported algorithm" ) 125 117 in 126 - let sig_bytes = b64url_decode sig_b64 |> Bytes.of_string in 118 + let sig_bytes = Jwt.b64_decode sig_b64 |> Bytes.of_string in 127 119 let r = Bytes.sub sig_bytes 0 32 in 128 120 let s = Bytes.sub sig_bytes 32 32 in 129 121 let signature = Bytes.cat r s in ··· 139 131 let open Yojson.Safe.Util in 140 132 match String.split_on_char '.' jwt with 141 133 | [header_b64; payload_b64; _] -> ( 142 - let header = Yojson.Safe.from_string (b64url_decode header_b64) in 143 - let payload = Yojson.Safe.from_string (b64url_decode payload_b64) in 134 + let header = Yojson.Safe.from_string (Jwt.b64_decode header_b64) in 135 + let payload = Yojson.Safe.from_string (Jwt.b64_decode payload_b64) in 144 136 let typ = header |> member "typ" |> to_string in 145 137 if typ <> "dpop+jwt" then Lwt.return_error "invalid typ in dpop proof" 146 138 else ··· 202 194 let expected_ath = 203 195 Digestif.SHA256.( 204 196 digest_string token |> to_raw_string 205 - |> Base64.encode_exn ~pad:false ) 197 + |> Jwt.b64_encode ) 206 198 in 207 199 if Some expected_ath <> ath_claim then 208 200 Lwt.return_error "ath mismatch"