diff --git a/flake.lock b/flake.lock index 5a465b0..055482e 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1688466019, - "narHash": "sha256-VeM2akYrBYMsb4W/MmBo1zmaMfgbL4cH3Pu8PGyIwJ0=", + "lastModified": 1693611461, + "narHash": "sha256-aPODl8vAgGQ0ZYFIRisxYG5MOGSkIczvu2Cd8Gb9+1Y=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "8e8d955c22df93dbe24f19ea04f47a74adbdc5ec", + "rev": "7f53fdb7bdc5bb237da7fefef12d099e4fd611ca", "type": "github" }, "original": { @@ -22,11 +22,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1689068808, - "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", + "lastModified": 1694529238, + "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", "owner": "numtide", "repo": "flake-utils", - "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", + "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", "type": "github" }, "original": { @@ -37,11 +37,11 @@ }, "nix-filter": { "locked": { - "lastModified": 1687178632, - "narHash": "sha256-HS7YR5erss0JCaUijPeyg2XrisEb959FIct3n2TMGbE=", + "lastModified": 1694857738, + "narHash": "sha256-bxxNyLHjhu0N8T3REINXQ2ZkJco0ABFPn6PIe2QUfqo=", "owner": "numtide", "repo": "nix-filter", - "rev": "d90c75e8319d0dd9be67d933d8eb9d0894ec9174", + "rev": "41fd48e00c22b4ced525af521ead8792402de0ea", "type": "github" }, "original": { @@ -56,11 +56,11 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1690144192, - "narHash": "sha256-EbOs95JaFAs53D/7jVNvbB2KWQgaG0BRrjWH4Eu9nxQ=", + "lastModified": 1695138105, + "narHash": "sha256-L1xVzf9PWVs4HNhEZMeeABEEV1KzIg8GnPBl2i2nkv8=", "owner": "nix-ocaml", "repo": "nix-overlays", - "rev": "8552afd78ee14a287ee283a4bf713087bf4e0317", + "rev": "81635bb16c283fbf40974c52e2a68fd26e1999a8", "type": "github" }, "original": { @@ -72,11 +72,11 @@ "nixpkgs-lib": { "locked": { "dir": "lib", - "lastModified": 1688049487, - "narHash": "sha256-100g4iaKC9MalDjUW9iN6Jl/OocTDtXdeAj7pEGIRh4=", + "lastModified": 1693471703, + "narHash": "sha256-0l03ZBL8P1P6z8MaSDS/MvuU8E75rVxe5eE1N6gxeTo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "4bc72cae107788bf3f24f30db2e2f685c9298dc9", + "rev": "3e52e76b70d5508f3cec70b882a29199f4d1ee85", "type": "github" }, "original": { @@ -89,17 +89,17 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1690040837, - "narHash": "sha256-RWN1jKsQ10GdVGOpddDXkMpsRn4qQNg+lQR+yESgOT0=", + "lastModified": 1695043561, + "narHash": "sha256-ajrDIUJA5RB6Y2I1G4suDhiDMJuwg1WarNuasshRobE=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "ad274bf46a418c5db3e3e94ef9295f8824fc2729", + "rev": "089313d7c7c864b21648d78fb8700062dafab1f2", "type": "github" }, "original": { "owner": "NixOS", "repo": "nixpkgs", - "rev": "ad274bf46a418c5db3e3e94ef9295f8824fc2729", + "rev": "089313d7c7c864b21648d78fb8700062dafab1f2", "type": "github" } }, @@ -133,11 +133,11 @@ ] }, "locked": { - "lastModified": 1689620039, - "narHash": "sha256-BtNwghr05z7k5YMdq+6nbue+nEalvDepuA7qdQMAKoQ=", + "lastModified": 1694528738, + "narHash": "sha256-aWMEjib5oTqEzF9f3WXffC1cwICo6v/4dYKjwNktV8k=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "719c2977f958c41fa60a928e2fbc50af14844114", + "rev": "7a49c388d7a6b63bb551b1ddedfa4efab8f400d8", "type": "github" }, "original": { diff --git a/jose/Header.ml b/jose/Header.ml index 74869d3..31af963 100644 --- a/jose/Header.ml +++ b/jose/Header.ml @@ -69,8 +69,8 @@ let of_json json = jwk = json |> Json.member "jwk" |> Json.to_option (fun jwk_json -> - Jwk.of_pub_json jwk_json |> U_Result.to_opt) - |> U_Opt.flatten; + Jwk.of_pub_json jwk_json |> Result.to_option) + |> Option.join; kid = json |> Json.member "kid" |> Json.to_string_option; x5t = json |> Json.member "x5t" |> Json.to_string_option; x5t256 = json |> Json.member "x5t#256" |> Json.to_string_option; @@ -78,7 +78,7 @@ let of_json json = cty = json |> Json.member "cty" |> Json.to_string_option; enc = json |> Json.member "enc" |> Json.to_string_option - |> U_Opt.map Jwa.enc_of_string; + |> Option.map Jwa.enc_of_string; extra = get_extra_headers json; } with Json.Type_error (s, _) -> Error (`Msg s) @@ -89,21 +89,22 @@ let to_json t = RJson.to_json_string_opt "typ" t.typ; Some ("alg", Jwa.alg_to_json t.alg); RJson.to_json_string_opt "kid" t.kid; - U_Opt.map Jwk.to_pub_json t.jwk |> U_Opt.map (fun jwk -> ("jwk", jwk)); + Option.map Jwk.to_pub_json t.jwk |> Option.map (fun jwk -> ("jwk", jwk)); RJson.to_json_string_opt "x5t" t.x5t; RJson.to_json_string_opt "x5t#256" t.x5t256; RJson.to_json_string_opt "cty" t.cty; t.enc - |> U_Opt.map Jwa.enc_to_string - |> U_Opt.map (fun enc -> ("enc", `String enc)); + |> Option.map Jwa.enc_to_string + |> Option.map (fun enc -> ("enc", `String enc)); ] in - `Assoc (U_List.filter_map (fun x -> x) values @ t.extra) + `Assoc (List.filter_map Fun.id values @ t.extra) let of_string header_str = - U_Base64.url_decode header_str - |> U_Result.flat_map (fun decoded_header -> - Yojson.Safe.from_string decoded_header |> of_json) + let s = U_Base64.url_decode header_str + in + Result.bind s (fun decoded_header -> + Yojson.Safe.from_string decoded_header |> of_json) let to_string header = to_json header |> Yojson.Safe.to_string |> U_Base64.url_encode_string diff --git a/jose/Jwe.ml b/jose/Jwe.ml index 8a1a5ca..f177412 100644 --- a/jose/Jwe.ml +++ b/jose/Jwe.ml @@ -31,25 +31,26 @@ let make_cek (header : Header.t) = | Some enc -> let key_length = Jwa.enc_to_length enc in Mirage_crypto_rng.generate (key_length / 8) - |> Cstruct.to_string |> U_Result.return + |> Cstruct.to_string |> Result.ok | None -> Error `Missing_enc let make_iv (header : Header.t) = match header.alg with | `RSA_OAEP -> Mirage_crypto_rng.generate Mirage_crypto.Cipher_block.AES.GCM.block_size - |> Cstruct.to_string |> U_Result.return + |> Cstruct.to_string |> Result.ok | `RSA1_5 -> Mirage_crypto_rng.generate Mirage_crypto.Cipher_block.AES.CBC.block_size - |> Cstruct.to_string |> U_Result.return + |> Cstruct.to_string |> Result.ok | _ -> Error `Unsupported_alg let make ~header payload = - let open U_Result.Infix in - make_cek header >>= fun cek -> - make_iv header >>= fun iv -> - let aad = None in - Ok { header; cek; iv; aad; payload } + let cek = make_cek header in + Result.bind cek (fun cek -> + let iv = make_iv header in + Result.bind iv (fun iv -> + let aad = None in + Ok { header; cek; iv; aad; payload })) let encrypt_payload ?enc ~cek ~iv ~aad payload = let iv = Cstruct.of_string iv in @@ -97,53 +98,55 @@ let encrypt_payload ?enc ~cek ~iv ~aad payload = | _ -> Error `Unsupported_enc let encrypt_cek (type a) alg (cek : string) ~(jwk : a Jwk.t) = - let open U_Result.Infix in - (match jwk with - | Rsa_priv rsa -> Ok (Mirage_crypto_pk.Rsa.pub_of_priv rsa.key) - | Rsa_pub rsa -> Ok rsa.key - | Oct _ -> Error `Unsupported_kty - | Es256_priv _ -> Error `Unsupported_kty - | Es256_pub _ -> Error `Unsupported_kty - | Es384_priv _ -> Error `Unsupported_kty - | Es384_pub _ -> Error `Unsupported_kty - | Es512_priv _ -> Error `Unsupported_kty - | Es512_pub _ -> Error `Unsupported_kty - | Ed25519_priv _ -> Error `Unsupported_kty - | Ed25519_pub _ -> Error `Unsupported_kty) - >>= fun key -> - match alg with - | `RSA1_5 -> - let ecek = - cek |> Cstruct.of_string - |> Mirage_crypto_pk.Rsa.PKCS1.encrypt ~key - |> Cstruct.to_string - in - Ok ecek - | `RSA_OAEP -> - let cek = Cstruct.of_string cek in - let jek = RSA_OAEP.encrypt ~key cek |> Cstruct.to_string in - Ok jek - | _ -> Error `Invalid_alg + let key = match jwk with + | Rsa_priv rsa -> Ok (Mirage_crypto_pk.Rsa.pub_of_priv rsa.key) + | Rsa_pub rsa -> Ok rsa.key + | Oct _ -> Error `Unsupported_kty + | Es256_priv _ -> Error `Unsupported_kty + | Es256_pub _ -> Error `Unsupported_kty + | Es384_priv _ -> Error `Unsupported_kty + | Es384_pub _ -> Error `Unsupported_kty + | Es512_priv _ -> Error `Unsupported_kty + | Es512_pub _ -> Error `Unsupported_kty + | Ed25519_priv _ -> Error `Unsupported_kty + | Ed25519_pub _ -> Error `Unsupported_kty + in + Result.bind key (fun key -> + match alg with + | `RSA1_5 -> + let ecek = + cek |> Cstruct.of_string + |> Mirage_crypto_pk.Rsa.PKCS1.encrypt ~key + |> Cstruct.to_string + in + Ok ecek + | `RSA_OAEP -> + let cek = Cstruct.of_string cek in + let jek = RSA_OAEP.encrypt ~key cek |> Cstruct.to_string in + Ok jek + | _ -> Error `Invalid_alg) let encrypt (type a) ~(jwk : a Jwk.t) t = - let open U_Result.Infix in let header_string = Header.to_string t.header in - - encrypt_cek t.header.alg t.cek ~jwk >|= U_Base64.url_encode_string - >>= fun ecek -> - let eiv = U_Base64.url_encode_string t.iv in - encrypt_payload ?enc:t.header.enc ~cek:t.cek ~iv:t.iv ~aad:header_string - t.payload - >>= fun (ciphertext, auth_tag) -> - Ok - (String.concat "." - [ - header_string; - ecek; - eiv; - U_Base64.url_encode_string ciphertext; - U_Base64.url_encode_string auth_tag; - ]) + let ecek = + encrypt_cek t.header.alg t.cek ~jwk |> Result.map U_Base64.url_encode_string + in + Result.bind ecek (fun ecek -> + let eiv = U_Base64.url_encode_string t.iv in + let ciphertext = + encrypt_payload ?enc:t.header.enc ~cek:t.cek ~iv:t.iv ~aad:header_string + t.payload + in + Result.bind ciphertext (fun (ciphertext, auth_tag) -> + Ok + (String.concat "." + [ + header_string; + ecek; + eiv; + U_Base64.url_encode_string ciphertext; + U_Base64.url_encode_string auth_tag; + ]))) let decrypt_cek alg str ~(jwk : Jwk.priv Jwk.t) = let of_opt_cstruct = function @@ -152,71 +155,83 @@ let decrypt_cek alg str ~(jwk : Jwk.priv Jwk.t) = in match (alg, jwk) with | `RSA1_5, Jwk.Rsa_priv rsa -> - Utils.U_Base64.url_decode str - |> U_Result.map Cstruct.of_string - |> U_Result.map (Mirage_crypto_pk.Rsa.PKCS1.decrypt ~key:rsa.key) - |> U_Result.flat_map of_opt_cstruct + let decoded = Utils.U_Base64.url_decode str + |> Result.map (fun decoded -> + Cstruct.of_string decoded + |> Mirage_crypto_pk.Rsa.PKCS1.decrypt ~key:rsa.key) + in + Result.bind decoded of_opt_cstruct | `RSA_OAEP, Jwk.Rsa_priv rsa -> - Utils.U_Base64.url_decode str - |> U_Result.map Cstruct.of_string - |> U_Result.map (RSA_OAEP.decrypt ~key:rsa.key) - |> U_Result.flat_map of_opt_cstruct + let decoded = + Utils.U_Base64.url_decode str + |> Result.map (fun decoded -> + Cstruct.of_string decoded + |> RSA_OAEP.decrypt ~key:rsa.key) + in + Result.bind decoded of_opt_cstruct | _ -> Error `Invalid_JWK (* Move to Jwa? *) let decrypt_ciphertext enc ~cek ~iv ~auth_tag ~aad ciphertext = let iv = Cstruct.of_string iv in - let open Utils.U_Result.Infix in - U_Base64.url_decode ciphertext >>= fun encrypted -> - let encrypted = Cstruct.of_string encrypted in - match enc with - | Some `A128CBC_HS256 -> - (* RFC 7516 appendix B.1: first 128 bit hmac, last 128 bit aes *) - let hmac_key, aes_key = Cstruct.(split (of_string cek) 16) in - let key = Mirage_crypto.Cipher_block.AES.CBC.of_secret aes_key in + let encrypted = U_Base64.url_decode ciphertext in + Result.bind encrypted (fun encrypted -> + let encrypted = Cstruct.of_string encrypted in + match enc with + | Some `A128CBC_HS256 -> + (* RFC 7516 appendix B.1: first 128 bit hmac, last 128 bit aes *) + let hmac_key, aes_key = Cstruct.(split (of_string cek) 16) in + let key = Mirage_crypto.Cipher_block.AES.CBC.of_secret aes_key in - (* B.5 input to HMAC computation *) - let hmac_input = - (* B.3 64 bit big-endian AAD length (in bits!) *) - let aal = Cstruct.create 8 in - Cstruct.BE.set_uint64 aal 0 Int64.(mul 8L (of_int (String.length aad))); - Cstruct.(concat [ of_string aad; iv; encrypted; aal ]) - in - let computed_auth_tag = - let full = Mirage_crypto.Hash.SHA256.hmac ~key:hmac_key hmac_input in - (* B.7 truncate to 128 bit *) - Cstruct.sub full 0 16 |> Cstruct.to_string - in - if not (String.equal computed_auth_tag auth_tag) then - Error (`Msg "invalid auth tag") - else - (* B.2 encryption in CBC mode *) - Mirage_crypto.Cipher_block.AES.CBC.decrypt ~key ~iv encrypted - |> Pkcs7.unpad - >>= fun data -> Ok (Cstruct.to_string data) - | Some `A256GCM -> - let module GCM = Mirage_crypto.Cipher_block.AES.GCM in - let cek = Cstruct.of_string cek in - let key = GCM.of_secret cek in - let adata = Cstruct.of_string aad in - let encrypted = Cstruct.append encrypted (Cstruct.of_string auth_tag) in - Mirage_crypto.Cipher_block.AES.GCM.authenticate_decrypt ~key ~nonce:iv - ~adata encrypted - |> fun message -> - U_Opt.( - map (fun x -> Ok (Cstruct.to_string x)) message - |> get_with_default ~default:(Error (`Msg "invalid auth tag"))) - | _ -> Error (`Msg "unsupported encryption") + (* B.5 input to HMAC computation *) + let hmac_input = + (* B.3 64 bit big-endian AAD length (in bits!) *) + let aal = Cstruct.create 8 in + Cstruct.BE.set_uint64 aal 0 Int64.(mul 8L (of_int (String.length aad))); + Cstruct.(concat [ of_string aad; iv; encrypted; aal ]) + in + let computed_auth_tag = + let full = Mirage_crypto.Hash.SHA256.hmac ~key:hmac_key hmac_input in + (* B.7 truncate to 128 bit *) + Cstruct.sub full 0 16 |> Cstruct.to_string + in + if not (String.equal computed_auth_tag auth_tag) then + Error (`Msg "invalid auth tag") + else + (* B.2 encryption in CBC mode *) + let data = + Mirage_crypto.Cipher_block.AES.CBC.decrypt ~key ~iv encrypted + |> Pkcs7.unpad + in + Result.bind data (fun data -> Ok (Cstruct.to_string data)) + | Some `A256GCM -> + let module GCM = Mirage_crypto.Cipher_block.AES.GCM in + let cek = Cstruct.of_string cek in + let key = GCM.of_secret cek in + let adata = Cstruct.of_string aad in + let encrypted = Cstruct.append encrypted (Cstruct.of_string auth_tag) in + Mirage_crypto.Cipher_block.AES.GCM.authenticate_decrypt ~key ~nonce:iv + ~adata encrypted + |> fun message -> + message + |> Option.map (fun x -> Ok (Cstruct.to_string x)) + |> Option.value ~default:(Error (`Msg "invalid auth tag")) + | _ -> Error (`Msg "unsupported encryption")) let decrypt ~(jwk : Jwk.priv Jwk.t) jwe = - let open Utils.U_Result.Infix in String.split_on_char '.' jwe |> function | [ enc_header; enc_cek; enc_iv; ciphertext; auth_tag ] -> - Header.of_string enc_header >>= fun header -> - decrypt_cek header.Header.alg ~jwk enc_cek >>= fun cek -> - U_Base64.url_decode enc_iv >>= fun iv -> - U_Base64.url_decode auth_tag >>= fun auth_tag -> - decrypt_ciphertext header.Header.enc ~cek ~iv ~auth_tag ~aad:enc_header - ciphertext - >>= fun payload -> Ok { header; cek; iv; payload; aad = None } + let header = Header.of_string enc_header in + Result.bind header (fun header -> + let cek = decrypt_cek header.Header.alg ~jwk enc_cek in + Result.bind cek (fun cek -> + let iv = U_Base64.url_decode enc_iv in + Result.bind iv (fun iv -> + let auth_tag = U_Base64.url_decode auth_tag in + Result.bind auth_tag (fun auth_tag -> + let payload = + decrypt_ciphertext header.Header.enc ~cek ~iv ~auth_tag ~aad:enc_header ciphertext + in + Result.bind payload + (fun payload -> Ok { header; cek; iv; payload; aad = None }))))) | _ -> Error `Invalid_JWE diff --git a/jose/Jwk.ml b/jose/Jwk.ml index 83d09af..0ee5c5b 100644 --- a/jose/Jwk.ml +++ b/jose/Jwk.ml @@ -7,8 +7,8 @@ module Util = struct let get_component ?(pad = false) e = U_Base64.url_decode ~pad e - |> U_Result.map (fun x -> - U_String.pad 8 ~c:'\000' x |> U_String.rev |> Z.of_bits) + |> Result.map (fun x -> + U_String.pad ~len:8 ~c:'\000' x |> U_String.rev |> Z.of_bits) let kid_of_json json = Yojson.Safe.to_string json |> Cstruct.of_string @@ -27,15 +27,20 @@ module Util = struct (x, y) let make_ESXXX_of_x_y ~pub_of_cstruct (x, y) = - let x = U_Base64.url_decode x |> U_Result.map Cstruct.of_string in - let y = U_Base64.url_decode y |> U_Result.map Cstruct.of_string in - U_Result.both x y - |> U_Result.map (fun (x, y) -> - let four = Cstruct.create 1 in - Cstruct.set_uint8 four 0 4; - let point = Cstruct.concat [ four; x; y ] in - let k = pub_of_cstruct point in - k |> U_Result.get_exn) + let x = U_Base64.url_decode x |> Result.map Cstruct.of_string in + let y = U_Base64.url_decode y |> Result.map Cstruct.of_string in + match x,y with + | Ok x, Ok y -> + let four = + let cs = Cstruct.create 1 in + Cstruct.set_uint8 cs 0 4; + cs + in + let point = Cstruct.concat [ four; x; y ] in + pub_of_cstruct point + |> Result.get_ok + |> Result.ok + | Error e, _ | _, Error e -> Error e let get_ES256_x_y = get_ESXXX_x_y ~split_at:32 (* 64 octets split in 2 *) @@ -356,8 +361,11 @@ let of_pub_x509 ?use (x509 : X509.Public_key.t) : | _ -> Error `Unsupported_kty let of_pub_pem ?use pem : (public t, [> `Unsupported_kty ]) result = - Cstruct.of_string pem |> X509.Public_key.decode_pem - |> U_Result.flat_map (of_pub_x509 ?use) + let pem = + let pem_cs = Cstruct.of_string pem in + X509.Public_key.decode_pem pem_cs + in + Result.bind pem (fun pem -> of_pub_x509 ?use pem) let to_pub_pem (type a) (jwk : a t) = match jwk with @@ -366,33 +374,36 @@ let to_pub_pem (type a) (jwk : a t) = | Rsa_priv rsa -> rsa.key |> Mirage_crypto_pk.Rsa.pub_of_priv |> (fun key -> X509.Public_key.encode_pem (`RSA key)) - |> Cstruct.to_string |> U_Result.return + |> Cstruct.to_string |> Result.ok | Es256_pub ec -> Ok (X509.Public_key.encode_pem (`P256 ec.key) |> Cstruct.to_string) | Es256_priv ec -> ec.key |> Mirage_crypto_ec.P256.Dsa.pub_of_priv |> (fun key -> X509.Public_key.encode_pem (`P256 key) |> Cstruct.to_string) - |> U_Result.return + |> Result.ok | Es384_pub ec -> Ok (X509.Public_key.encode_pem (`P384 ec.key) |> Cstruct.to_string) | Es384_priv ec -> ec.key |> Mirage_crypto_ec.P384.Dsa.pub_of_priv |> (fun key -> X509.Public_key.encode_pem (`P384 key) |> Cstruct.to_string) - |> U_Result.return + |> Result.ok | Es512_pub ec -> Ok (X509.Public_key.encode_pem (`P521 ec.key) |> Cstruct.to_string) | Es512_priv ec -> ec.key |> Mirage_crypto_ec.P521.Dsa.pub_of_priv |> (fun key -> X509.Public_key.encode_pem (`P521 key) |> Cstruct.to_string) - |> U_Result.return + |> Result.ok | _ -> Error `Unsupported_kty let of_priv_pem ?use pem : (priv t, [> `Unsupported_kty ]) result = - Cstruct.of_string pem |> X509.Private_key.decode_pem - |> U_Result.flat_map (of_priv_x509 ?use) + let pem = + let pem_cs = Cstruct.of_string pem in + X509.Private_key.decode_pem pem_cs + in + Result.bind pem (fun pem -> of_priv_x509 ?use pem) let to_priv_pem (jwk : priv t) = match jwk with @@ -415,7 +426,7 @@ let oct_to_json (oct : oct) = RJson.to_json_string_opt "kid" oct.kid; ] in - `Assoc (U_List.filter_map (fun x -> x) values) + `Assoc (List.filter_map Fun.id values) let pub_rsa_to_json pub_rsa = (* Should I make this a result? It feels like our well-formed key should @@ -433,10 +444,10 @@ let pub_rsa_to_json pub_rsa = Option.map (fun use -> ("use", `String (use_to_string use))) pub_rsa.use; RJson.to_json_string_opt "x5t" (Util.get_JWK_x5t (X509.Public_key.fingerprint ~hash:`SHA1 public_key) - |> U_Result.to_opt); + |> Result.to_option); ] in - `Assoc (U_List.filter_map (fun x -> x) values) + `Assoc (List.filter_map Fun.id values) let pub_of_priv_rsa (priv_rsa : priv_rsa) : pub_rsa = { priv_rsa with key = Mirage_crypto_pk.Rsa.pub_of_priv priv_rsa.key } @@ -486,7 +497,7 @@ let priv_rsa_to_priv_json (priv_rsa : priv_rsa) : Yojson.Safe.t = RJson.to_json_string_opt "kid" priv_rsa.kid; ] in - `Assoc (U_List.filter_map (fun x -> x) values) + `Assoc (List.filter_map Fun.id values) let pub_esXXX_to_pub_json ~get_ESXXX_x_y ~crv (pub : 'a) : Yojson.Safe.t = let x, y = get_ESXXX_x_y pub.key in @@ -501,7 +512,7 @@ let pub_esXXX_to_pub_json ~get_ESXXX_x_y ~crv (pub : 'a) : Yojson.Safe.t = RJson.to_json_string_opt "kid" pub.kid; ] in - `Assoc (U_List.filter_map (fun x -> x) values) + `Assoc (List.filter_map Fun.id values) let priv_esXXX_to_priv_json ~get_ESXXX_x_y ~pub_of_priv ~priv_to_cstruct ~crv (priv : 'a) : Yojson.Safe.t = @@ -521,7 +532,7 @@ let priv_esXXX_to_priv_json ~get_ESXXX_x_y ~pub_of_priv ~priv_to_cstruct ~crv RJson.to_json_string_opt "kid" priv.kid; ] in - `Assoc (U_List.filter_map (fun x -> x) values) + `Assoc (List.filter_map Fun.id values) let pub_es256_to_pub_json = pub_esXXX_to_pub_json ~get_ESXXX_x_y:Util.get_ES256_x_y ~crv:"P-256" @@ -620,42 +631,44 @@ let pub_rsa_of_json json : (public t, 'error) result = try let e = json |> Json.member "e" |> Json.to_string |> Util.get_component in let n = json |> Json.member "n" |> Json.to_string |> Util.get_component in - U_Result.both e n - |> U_Result.flat_map (fun (e, n) -> Mirage_crypto_pk.Rsa.pub ~e ~n) - |> U_Result.flat_map (fun key -> - let alg = - json |> Json.member "alg" |> Json.to_string_option - |> U_Opt.map Jwa.alg_of_string - in - let use = - json |> Json.member "use" |> Json.to_string_option - |> U_Opt.map use_of_string - in - let kid = json |> Json.member "kid" |> Json.to_string_option in - let kty = `RSA in - match (alg, use) with - | Some _, Some _ -> Ok (Rsa_pub { alg; kty; use; key; kid }) - | Some alg, None -> - Ok - (Rsa_pub - { - alg = Some alg; - kty; - use = Some (use_of_alg alg); - key; - kid; - }) - | None, Some use -> - Ok - (Rsa_pub - { - alg = Some (alg_of_use_and_kty ~use kty); - kty; - use = Some use; - key; - kid; - }) - | alg, use -> Ok (Rsa_pub { alg; kty; use; key; kid })) + match e, n with + | Error e, _ | _, Error e -> Error e + | Ok e, Ok n -> + let pub = Mirage_crypto_pk.Rsa.pub ~e ~n in + Result.bind pub (fun key -> + let alg = + json |> Json.member "alg" |> Json.to_string_option + |> Option.map Jwa.alg_of_string + in + let use = + json |> Json.member "use" |> Json.to_string_option + |> Option.map use_of_string + in + let kid = json |> Json.member "kid" |> Json.to_string_option in + let kty = `RSA in + match (alg, use) with + | Some _, Some _ -> Ok (Rsa_pub { alg; kty; use; key; kid }) + | Some alg, None -> + Ok + (Rsa_pub + { + alg = Some alg; + kty; + use = Some (use_of_alg alg); + key; + kid; + }) + | None, Some use -> + Ok + (Rsa_pub + { + alg = Some (alg_of_use_and_kty ~use kty); + kty; + use = Some use; + key; + kid; + }) + | alg, use -> Ok (Rsa_pub { alg; kty; use; key; kid })) with Json.Type_error (s, _) -> Error (`Json_parse_failed s) let priv_rsa_of_json json : (priv t, 'error) result = @@ -669,43 +682,44 @@ let priv_rsa_of_json json : (priv t, 'error) result = let dp = json |> Json.member "dp" |> Json.to_string |> Util.get_component in let dq = json |> Json.member "dq" |> Json.to_string |> Util.get_component in let qi = json |> Json.member "qi" |> Json.to_string |> Util.get_component in - U_Result.all8 e n d p q dp dq qi - |> U_Result.flat_map (fun (e, n, d, p, q, dp, dq, qi) -> - Mirage_crypto_pk.Rsa.priv ~e ~n ~d ~p ~q ~dp ~dq ~q':qi) - |> U_Result.flat_map (fun key -> - let alg = - json |> Json.member "alg" |> Json.to_string_option - |> U_Opt.map Jwa.alg_of_string - in - let use = - json |> Json.member "use" |> Json.to_string_option - |> U_Opt.map use_of_string - in - let kid = json |> Json.member "kid" |> Json.to_string_option in - let kty = `RSA in - match (alg, use) with - | Some _, Some _ -> Ok (Rsa_priv { alg; kty; use; key; kid }) - | Some alg, None -> - Ok - (Rsa_priv - { - alg = Some alg; - kty; - use = Some (use_of_alg alg); - key; - kid; - }) - | None, Some use -> - Ok - (Rsa_priv - { - alg = Some (alg_of_use_and_kty ~use kty); - kty; - use = Some use; - key; - kid; - }) - | None, None -> Ok (Rsa_priv { alg; kty; use; key; kid })) + let all8 = U_Result.all8 e n d p q dp dq qi in + let priv = Result.bind all8 (fun (e, n, d, p, q, dp, dq, qi) -> + Mirage_crypto_pk.Rsa.priv ~e ~n ~d ~p ~q ~dp ~dq ~q':qi) + in + Result.bind priv (fun key -> + let alg = + json |> Json.member "alg" |> Json.to_string_option + |> Option.map Jwa.alg_of_string + in + let use = + json |> Json.member "use" |> Json.to_string_option + |> Option.map use_of_string + in + let kid = json |> Json.member "kid" |> Json.to_string_option in + let kty = `RSA in + match (alg, use) with + | Some _, Some _ -> Ok (Rsa_priv { alg; kty; use; key; kid }) + | Some alg, None -> + Ok + (Rsa_priv + { + alg = Some alg; + kty; + use = Some (use_of_alg alg); + key; + kid; + }) + | None, Some use -> + Ok + (Rsa_priv + { + alg = Some (alg_of_use_and_kty ~use kty); + kty; + use = Some use; + key; + kid; + }) + | None, None -> Ok (Rsa_priv { alg; kty; use; key; kid })) with Json.Type_error (s, _) -> Error (`Json_parse_failed s) let oct_of_json json = @@ -713,7 +727,7 @@ let oct_of_json json = try let alg = json |> Json.member "alg" |> Json.to_string_option - |> U_Opt.map Jwa.alg_of_string + |> Option.map Jwa.alg_of_string in Ok (Oct @@ -723,7 +737,7 @@ let oct_of_json json = (* Shortcut since that is the only thing we handle *) use = json |> Json.member "use" |> Json.to_string_option - |> U_Opt.map use_of_string; + |> Option.map use_of_string; key = json |> Json.member "k" |> Json.to_string; kid = json |> Json.member "kid" |> Json.to_string_option; }) @@ -734,7 +748,7 @@ let pub_ec_of_json json = try let alg = json |> Json.member "alg" |> Json.to_string_option - |> U_Opt.map Jwa.alg_of_string + |> Option.map Jwa.alg_of_string in let crv = json |> Json.member "crv" |> Json.to_string in let x = json |> Json.member "x" |> Json.to_string in @@ -746,7 +760,7 @@ let pub_ec_of_json json = (* Shortcut since that is the only thing we handle *) use = json |> Json.member "use" |> Json.to_string_option - |> U_Opt.map use_of_string; + |> Option.map use_of_string; key; kid = json |> Json.member "kid" |> Json.to_string_option; } @@ -754,13 +768,13 @@ let pub_ec_of_json json = match crv with | "P-256" -> Util.make_ES256_of_x_y (x, y) - |> U_Result.map (fun key -> Es256_pub (make_jwk key)) + |> Result.map (fun key -> Es256_pub (make_jwk key)) | "P-384" -> Util.make_ES384_of_x_y (x, y) - |> U_Result.map (fun key -> Es384_pub (make_jwk key)) + |> Result.map (fun key -> Es384_pub (make_jwk key)) | "P-521" -> Util.make_ES512_of_x_y (x, y) - |> U_Result.map (fun key -> Es512_pub (make_jwk key)) + |> Result.map (fun key -> Es512_pub (make_jwk key)) | _ -> Error (`Msg "kty and alg doesn't match") with Json.Type_error (s, _) -> Error (`Json_parse_failed s) @@ -769,12 +783,12 @@ let priv_ec_of_json json = try let alg = json |> Json.member "alg" |> Json.to_string_option - |> U_Opt.map Jwa.alg_of_string + |> Option.map Jwa.alg_of_string in let crv = json |> Json.member "crv" |> Json.to_string in let d = json |> Json.member "d" |> Json.to_string |> U_Base64.url_decode - |> U_Result.map Cstruct.of_string + |> Result.map Cstruct.of_string in let make_jwk key = { @@ -783,7 +797,7 @@ let priv_ec_of_json json = (* Shortcut since that is the only thing we handle *) use = json |> Json.member "use" |> Json.to_string_option - |> U_Opt.map use_of_string; + |> Option.map use_of_string; key; kid = json |> Json.member "kid" |> Json.to_string_option; } @@ -791,16 +805,16 @@ let priv_ec_of_json json = match (crv, d) with | "P-256", Ok d -> Mirage_crypto_ec.P256.Dsa.priv_of_cstruct d - |> U_Result.map_error (fun _ -> `Msg "Could not create key") - |> U_Result.map (fun key -> Es256_priv (make_jwk key)) + |> Result.map_error (fun _ -> `Msg "Could not create key") + |> Result.map (fun key -> Es256_priv (make_jwk key)) | "P-384", Ok d -> Mirage_crypto_ec.P384.Dsa.priv_of_cstruct d - |> U_Result.map_error (fun _ -> `Msg "Could not create key") - |> U_Result.map (fun key -> Es384_priv (make_jwk key)) + |> Result.map_error (fun _ -> `Msg "Could not create key") + |> Result.map (fun key -> Es384_priv (make_jwk key)) | "P-521", Ok d -> Mirage_crypto_ec.P521.Dsa.priv_of_cstruct d - |> U_Result.map_error (fun _ -> `Msg "Could not create key") - |> U_Result.map (fun key -> Es512_priv (make_jwk key)) + |> Result.map_error (fun _ -> `Msg "Could not create key") + |> Result.map (fun key -> Es512_priv (make_jwk key)) | _ -> Error (`Msg "kty and alg doesn't match") with Json.Type_error (s, _) -> Error (`Json_parse_failed s) @@ -809,13 +823,13 @@ let pub_okp_of_json json = try let alg = json |> Json.member "alg" |> Json.to_string_option - |> U_Opt.map Jwa.alg_of_string + |> Option.map Jwa.alg_of_string in (* TODO: This is needed if we want more curves *) let _crv = json |> Json.member "crv" |> Json.to_string in let x = json |> Json.member "x" |> Json.to_string |> U_Base64.url_decode - |> U_Result.map Cstruct.of_string + |> Result.map Cstruct.of_string in let make_jwk key = { @@ -824,15 +838,14 @@ let pub_okp_of_json json = (* Shortcut since that is the only thing we handle *) use = json |> Json.member "use" |> Json.to_string_option - |> U_Opt.map use_of_string; + |> Option.map use_of_string; key; kid = json |> Json.member "kid" |> Json.to_string_option; } in - x - |> U_Result.flat_map (fun cstruct -> - Mirage_crypto_ec.Ed25519.pub_of_cstruct cstruct - |> U_Result.map_error (fun _ -> `Msg "Could not create key")) + Result.bind x (fun cstruct -> + Mirage_crypto_ec.Ed25519.pub_of_cstruct cstruct + |> Result.map_error (fun _ -> `Msg "Could not create key")) |> Result.map (fun key -> Ed25519_pub (make_jwk key)) with Json.Type_error (s, _) -> Error (`Json_parse_failed s) @@ -841,13 +854,13 @@ let priv_okp_of_json json = try let alg = json |> Json.member "alg" |> Json.to_string_option - |> U_Opt.map Jwa.alg_of_string + |> Option.map Jwa.alg_of_string in (* TODO: This is needed if we want more curves *) let _crv = json |> Json.member "crv" |> Json.to_string in let d = json |> Json.member "d" |> Json.to_string |> U_Base64.url_decode - |> U_Result.map Cstruct.of_string + |> Result.map Cstruct.of_string in let make_jwk key = { @@ -856,15 +869,14 @@ let priv_okp_of_json json = (* Shortcut since that is the only thing we handle *) use = json |> Json.member "use" |> Json.to_string_option - |> U_Opt.map use_of_string; + |> Option.map use_of_string; key; kid = json |> Json.member "kid" |> Json.to_string_option; } in - d - |> U_Result.flat_map (fun cstruct -> + Result.bind d (fun cstruct -> Mirage_crypto_ec.Ed25519.priv_of_cstruct cstruct - |> U_Result.map_error (fun _ -> `Msg "Could not create key")) + |> Result.map_error (fun _ -> `Msg "Could not create key")) |> Result.map (fun key -> Ed25519_priv (make_jwk key)) with Json.Type_error (s, _) -> Error (`Json_parse_failed s) @@ -906,11 +918,11 @@ let pub_of_priv (jwk : priv t) : public t = | Ed25519_priv okt -> Ed25519_pub (pub_of_priv_ed25519 okt) let oct_to_sign_key (oct : oct) : (Cstruct.t, [> `Msg of string ]) result = - U_Base64.url_decode oct.key |> U_Result.map Cstruct.of_string + U_Base64.url_decode oct.key |> Result.map Cstruct.of_string let hash_values hash values = let module Hash = (val Mirage_crypto.Hash.module_of hash) in - `Assoc (U_List.filter_map (fun x -> x) values) + `Assoc (List.filter_map Fun.id values) |> Yojson.to_string |> Cstruct.of_string |> Hash.digest let pub_rsa_to_thumbprint hash (pub_rsa : Mirage_crypto_pk.Rsa.pub jwk) = diff --git a/jose/Jwks.ml b/jose/Jwks.ml index 99cc68a..abdfb22 100644 --- a/jose/Jwks.ml +++ b/jose/Jwks.ml @@ -10,14 +10,14 @@ let of_json json = json |> Yojson.Safe.Util.member "keys" |> Yojson.Safe.Util.to_list |> List.map Jwk.of_pub_json - |> Utils.U_List.filter_map Utils.U_Result.to_opt; + |> List.filter_map Result.to_option; } let to_string t = to_json t |> Yojson.Safe.to_string let of_string str = Yojson.Safe.from_string str |> of_json let find_key jwks kid = - Utils.U_List.find_opt + List.find_opt (fun (jwk : Jwk.public Jwk.t) -> let curr_kid = Jwk.get_kid jwk in match curr_kid with Some curr_kid -> curr_kid = kid | None -> false) diff --git a/jose/Jws.ml b/jose/Jws.ml index ab6b018..3a1943f 100644 --- a/jose/Jws.ml +++ b/jose/Jws.ml @@ -16,9 +16,10 @@ let of_compact_string token = | [ header_str; payload_str; signature ] -> let header = Header.of_string header_str in let payload = payload_str |> U_Base64.url_decode in - U_Result.both header payload - |> U_Result.map (fun (header, payload) -> - { header; raw_header = header_str; payload; signature }) + (match header, payload with + | Ok header, Ok payload -> + Ok { header; raw_header = header_str; payload; signature } + | Error e, _ | _, Error e -> Error e) | _ -> Error (`Msg "token didn't include header, payload or signature") let of_json_string token = @@ -26,9 +27,11 @@ let of_json_string token = let module Json = Yojson.Safe.Util in let json = Yojson.Safe.from_string token in let payload = - Json.member "payload" json |> Json.to_string_option - |> Option.to_result ~none:(`Msg "no payload") - |> U_Result.flat_map U_Base64.url_decode + let payload = + Json.member "payload" json |> Json.to_string_option + |> Option.to_result ~none:(`Msg "no payload") + in + Result.bind payload U_Base64.url_decode in match (payload, Json.member "signature" json |> Json.to_string_option) with @@ -36,7 +39,7 @@ let of_json_string token = let protected = Json.member "protected" json |> Json.to_string in Ok { - header = Header.of_string protected |> U_Result.get_exn; + header = Header.of_string protected |> Result.get_ok; raw_header = protected; payload; signature; @@ -101,18 +104,18 @@ let verify_jwk (type a) ~(jwk : a Jwk.t) ~input_str str = | None -> Error `Invalid_signature | Some message -> Ok message) | Jwk.Oct jwk -> - Jwk.oct_to_sign_key jwk - |> U_Result.flat_map (fun key -> - let computed_signature = - Mirage_crypto.Hash.SHA256.hmac ~key (Cstruct.of_string input_str) - in - (* From RFC7518§3.2: - * The comparison of the computed HMAC value to the JWS Signature - * value MUST be done in a constant-time manner to thwart timing - * attacks. *) - if Eqaf_cstruct.equal str computed_signature then - Ok computed_signature - else Error `Invalid_signature) + let key = Jwk.oct_to_sign_key jwk in + Result.bind key (fun key -> + let computed_signature = + Mirage_crypto.Hash.SHA256.hmac ~key (Cstruct.of_string input_str) + in + (* From RFC7518§3.2: + * The comparison of the computed HMAC value to the JWS Signature + * value MUST be done in a constant-time manner to thwart timing + * attacks. *) + if Eqaf_cstruct.equal str computed_signature then + Ok computed_signature + else Error `Invalid_signature) | Jwk.Es256_pub pub_jwk -> let r, s = Cstruct.split str 32 in let message = @@ -177,25 +180,27 @@ let verify_jwk (type a) ~(jwk : a Jwk.t) ~input_str str = let verify_internal (type a) ~(jwk : a Jwk.t) t = let payload_str = U_Base64.url_encode_string t.payload in let input_str = Printf.sprintf "%s.%s" t.raw_header payload_str in - U_Base64.url_decode t.signature - |> U_Result.map Cstruct.of_string - |> U_Result.flat_map (verify_jwk ~jwk ~input_str) + let unverified_jwk = + U_Base64.url_decode t.signature |> Result.map Cstruct.of_string + in + Result.bind unverified_jwk (verify_jwk ~jwk ~input_str) let validate (type a) ~(jwk : a Jwk.t) t = let header = t.header in - (match header.alg with - | `RS256 -> Ok header.alg - | `HS256 -> Ok header.alg - | `ES256 -> Ok header.alg - | `ES384 -> Ok header.alg - | `ES512 -> Ok header.alg - | `EdDSA -> Ok header.alg - | `Unsupported _ | `RSA_OAEP | `RSA1_5 | `None -> - Error (`Msg "alg not supported for signing")) - |> U_Result.flat_map (fun _alg -> - match verify_internal ~jwk t with - | Ok _sig -> Ok t - | Error e -> Error e) + let alg = match header.alg with + | `RS256 -> Ok header.alg + | `HS256 -> Ok header.alg + | `ES256 -> Ok header.alg + | `ES384 -> Ok header.alg + | `ES512 -> Ok header.alg + | `EdDSA -> Ok header.alg + | `Unsupported _ | `RSA_OAEP | `RSA1_5 | `None -> + Error (`Msg "alg not supported for signing") + in + Result.bind alg (fun _alg -> + match verify_internal ~jwk t with + | Ok _sig -> Ok t + | Error e -> Error e) (* Assumes a well formed header. *) let sign ?header ~payload (jwk : Jwk.priv Jwk.t) = @@ -240,7 +245,7 @@ let sign ?header ~payload (jwk : Jwk.priv Jwk.t) = | `Digest _ -> raise (Invalid_argument "Digest")) | Jwk.Oct oct -> Jwk.oct_to_sign_key oct - |> U_Result.map (fun key -> function + |> Result.map (fun key -> function | `Message x -> Mirage_crypto.Hash.SHA256.hmac ~key x | `Digest _ -> raise (Invalid_argument "Digest")) in diff --git a/jose/Jwt.ml b/jose/Jwt.ml index d3c78fc..c8a7171 100644 --- a/jose/Jwt.ml +++ b/jose/Jwt.ml @@ -18,7 +18,7 @@ let payload_to_string payload = let payload_of_string payload_str = let payload = U_Base64.url_decode payload_str in - U_Result.map Yojson.Safe.from_string payload + Result.map Yojson.Safe.from_string payload type t = { header : Header.t; @@ -66,7 +66,7 @@ let to_string ?serialization t = let jws = to_jws t in Jws.to_string ?serialization jws -let unsafe_of_string token = Jws.of_string token |> U_Result.map of_jws +let unsafe_of_string token = Jws.of_string token |> Result.map of_jws let check_expiration ~(now : Ptime.t) t = let module Json = Yojson.Safe.Util in @@ -83,7 +83,7 @@ let check_expiration ~(now : Ptime.t) t = | None -> Ok t let validate_signature (type a) ~(jwk : a Jwk.t) (t : t) : (t, 'error) result = - Jws.validate ~jwk (to_jws t) |> U_Result.map of_jws + Jws.validate ~jwk (to_jws t) |> Result.map of_jws let validate (type a) ~(jwk : a Jwk.t) ~now (t : t) : (t, 'error) result = match validate_signature ~jwk t with @@ -91,7 +91,7 @@ let validate (type a) ~(jwk : a Jwk.t) ~now (t : t) : (t, 'error) result = | Error e -> Error e let of_string ~jwk ~now s = - U_Result.bind (unsafe_of_string s) (validate ~jwk ~now) + Result.bind (unsafe_of_string s) (validate ~jwk ~now) let sign ?header ~payload (jwk : Jwk.priv Jwk.t) = let header = @@ -104,5 +104,5 @@ let sign ?header ~payload (jwk : Jwk.priv Jwk.t) = with _ -> Error (`Msg "Can't serialize payload") in match payload with - | Ok payload -> Jws.sign ~header ~payload jwk |> U_Result.map of_jws + | Ok payload -> Jws.sign ~header ~payload jwk |> Result.map of_jws | Error e -> Error e diff --git a/jose/Utils.ml b/jose/Utils.ml index ab123ac..e094f16 100644 --- a/jose/Utils.ml +++ b/jose/Utils.ml @@ -1,63 +1,22 @@ module U_Result = struct - let map fn r = match r with Ok v -> Ok (fn v) | Error e -> Error e - let flat_map fn r = match r with Ok v -> fn v | Error e -> Error e - let map_error fn r = match r with Ok v -> Ok v | Error e -> Error (fn e) - let to_opt = function Ok v -> Some v | Error _ -> None - let return v = Ok v - let get_exn = function Ok v -> v | Error _ -> raise Not_found - - let both a b = - match (a, b) with - | Ok a, Ok b -> Ok (a, b) - | Error e, _ -> Error e - | _, Error e -> Error e - - let bind v f = match v with Ok v -> f v | Error _ as e -> e - let all8 a b c d e f g h = match (a, b, c, d, e, f, g, h) with | Ok a, Ok b, Ok c, Ok d, Ok e, Ok f, Ok g, Ok h -> Ok (a, b, c, d, e, f, g, h) | _ -> Error (`Msg "all 8 was not Ok") - - module Infix = struct - let ( >>= ) = bind - let ( >|= ) r fn = match r with Ok v -> Ok (fn v) | Error e -> Error e - end -end - -module U_Opt = struct - let flatten o = match o with Some v -> v | None -> None - let map fn o = match o with Some v -> Some (fn v) | None -> None - let get_with_default ~default o = match o with Some v -> v | None -> default - let return x = Some x -end - -module U_List = struct - let filter_map f = - let rec aux accu = function - | [] -> List.rev accu - | x :: l -> ( - match f x with None -> aux accu l | Some v -> aux (v :: accu) l) - in - aux [] - - let rec find_opt p = function - | [] -> None - | x :: l -> if p x then Some x else find_opt p l end module U_String = struct let rev s = - let len = Astring.String.length s in - Astring.String.mapi (fun i _ -> s.[len - (i + 1)]) s + let len = String.length s in + String.mapi (fun i _ -> s.[len - (i + 1)]) s - let pad ~c length s = - let len = Astring.String.length s in - if len >= length then s + let pad ~c ~len:pad_length s = + let len = String.length s in + if len >= pad_length then s else - let diff = length - len in - Astring.String.v ~len:length (fun i -> + let diff = pad_length - len in + Astring.String.v ~len:pad_length (fun i -> if i < diff then c else s.[i - diff]) let trim_leading_null s = @@ -65,13 +24,13 @@ module U_String = struct end module U_Base64 = struct - let url_encode_string ?(pad = false) payload = + let url_encode_string ?(pad=false) payload = Base64.encode_string ~pad ~alphabet:Base64.uri_safe_alphabet payload - let url_encode ?(pad = false) ?off ?len payload = + let url_encode ?(pad=false) ?off ?len payload = Base64.encode ~pad ~alphabet:Base64.uri_safe_alphabet ?off ?len payload - let url_decode ?(pad = false) ?off ?len payload = + let url_decode ?(pad=false) ?off ?len payload = Base64.decode ~pad ~alphabet:Base64.uri_safe_alphabet ?off ?len payload end