cow_base64url.erl (2408B)
1 %% Copyright (c) 2017-2018, Loïc Hoguin <essen@ninenines.eu> 2 %% 3 %% Permission to use, copy, modify, and/or distribute this software for any 4 %% purpose with or without fee is hereby granted, provided that the above 5 %% copyright notice and this permission notice appear in all copies. 6 %% 7 %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 15 %% This module implements "base64url" following the algorithm 16 %% found in Appendix C of RFC7515. The option #{padding => false} 17 %% must be given to reproduce this variant exactly. The default 18 %% will leave the padding characters. 19 -module(cow_base64url). 20 21 -export([decode/1]). 22 -export([decode/2]). 23 -export([encode/1]). 24 -export([encode/2]). 25 26 -ifdef(TEST). 27 -include_lib("proper/include/proper.hrl"). 28 -endif. 29 30 decode(Enc) -> 31 decode(Enc, #{}). 32 33 decode(Enc0, Opts) -> 34 Enc1 = << << case C of 35 $- -> $+; 36 $_ -> $/; 37 _ -> C 38 end >> || << C >> <= Enc0 >>, 39 Enc = case Opts of 40 #{padding := false} -> 41 case byte_size(Enc1) rem 4 of 42 0 -> Enc1; 43 2 -> << Enc1/binary, "==" >>; 44 3 -> << Enc1/binary, "=" >> 45 end; 46 _ -> 47 Enc1 48 end, 49 base64:decode(Enc). 50 51 encode(Dec) -> 52 encode(Dec, #{}). 53 54 encode(Dec, Opts) -> 55 encode(base64:encode(Dec), Opts, <<>>). 56 57 encode(<<$+, R/bits>>, Opts, Acc) -> encode(R, Opts, <<Acc/binary, $->>); 58 encode(<<$/, R/bits>>, Opts, Acc) -> encode(R, Opts, <<Acc/binary, $_>>); 59 encode(<<$=, _/bits>>, #{padding := false}, Acc) -> Acc; 60 encode(<<C, R/bits>>, Opts, Acc) -> encode(R, Opts, <<Acc/binary, C>>); 61 encode(<<>>, _, Acc) -> Acc. 62 63 -ifdef(TEST). 64 65 rfc7515_test() -> 66 Dec = <<3,236,255,224,193>>, 67 Enc = <<"A-z_4ME">>, 68 Pad = <<"A-z_4ME=">>, 69 Dec = decode(<<Enc/binary,$=>>), 70 Dec = decode(Enc, #{padding => false}), 71 Pad = encode(Dec), 72 Enc = encode(Dec, #{padding => false}), 73 ok. 74 75 prop_identity() -> 76 ?FORALL(B, binary(), B =:= decode(encode(B))). 77 78 prop_identity_no_padding() -> 79 ?FORALL(B, binary(), B =:= decode(encode(B, #{padding => false}), #{padding => false})). 80 81 -endif.