aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2017-07-10 13:28:26 +0200
committerLoïc Hoguin <[email protected]>2017-07-10 13:28:26 +0200
commit0eb0db3653bd89809acf528a0a3234c2c3cdde22 (patch)
tree7d7aa3f3503e487fb75f8948aab664fadf951882
parent8210fd3c195cc04e51a50986fceff316be05d155 (diff)
downloadcowlib-0eb0db3653bd89809acf528a0a3234c2c3cdde22.tar.gz
cowlib-0eb0db3653bd89809acf528a0a3234c2c3cdde22.tar.bz2
cowlib-0eb0db3653bd89809acf528a0a3234c2c3cdde22.zip
Add new module cow_base64url
It implements the algorithm found in RFC 7515 Appendix C https://tools.ietf.org/html/rfc7515#appendix-C with an option to switch between with/no padding variants. Relace the cow_multipart:boundary function to use this and fix issues with agents which do not support slashes in the boundary characters.
-rw-r--r--ebin/cowlib.app2
-rw-r--r--src/cow_base64url.erl81
-rw-r--r--src/cow_multipart.erl2
3 files changed, 83 insertions, 2 deletions
diff --git a/ebin/cowlib.app b/ebin/cowlib.app
index 8d7e059..5ebff11 100644
--- a/ebin/cowlib.app
+++ b/ebin/cowlib.app
@@ -1,7 +1,7 @@
{application, 'cowlib', [
{description, "Support library for manipulating Web protocols."},
{vsn, "2.0.0-pre.1"},
- {modules, ['cow_cookie','cow_date','cow_hpack','cow_http','cow_http2','cow_http_hd','cow_http_te','cow_mimetypes','cow_multipart','cow_qs','cow_spdy','cow_sse','cow_uri','cow_ws']},
+ {modules, ['cow_base64url','cow_cookie','cow_date','cow_hpack','cow_http','cow_http2','cow_http_hd','cow_http_te','cow_mimetypes','cow_multipart','cow_qs','cow_spdy','cow_sse','cow_uri','cow_ws']},
{registered, []},
{applications, [kernel,stdlib,crypto]},
{env, []}
diff --git a/src/cow_base64url.erl b/src/cow_base64url.erl
new file mode 100644
index 0000000..ff375f7
--- /dev/null
+++ b/src/cow_base64url.erl
@@ -0,0 +1,81 @@
+%% Copyright (c) 2017, Loïc Hoguin <[email protected]>
+%%
+%% Permission to use, copy, modify, and/or distribute this software for any
+%% purpose with or without fee is hereby granted, provided that the above
+%% copyright notice and this permission notice appear in all copies.
+%%
+%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+%% This module implements "base64url" following the algorithm
+%% found in Appendix C of RFC7515. The option #{padding => false}
+%% must be given to reproduce this variant exactly. The default
+%% will leave the padding characters.
+-module(cow_base64url).
+
+-export([decode/1]).
+-export([decode/2]).
+-export([encode/1]).
+-export([encode/2]).
+
+-ifdef(TEST).
+-include_lib("proper/include/proper.hrl").
+-endif.
+
+decode(Enc) ->
+ decode(Enc, #{}).
+
+decode(Enc0, Opts) ->
+ Enc1 = << << case C of
+ $- -> $+;
+ $_ -> $/;
+ _ -> C
+ end >> || << C >> <= Enc0 >>,
+ Enc = case Opts of
+ #{padding := false} ->
+ case byte_size(Enc1) rem 4 of
+ 0 -> Enc1;
+ 2 -> << Enc1/binary, "==" >>;
+ 3 -> << Enc1/binary, "=" >>
+ end;
+ _ ->
+ Enc1
+ end,
+ base64:decode(Enc).
+
+encode(Dec) ->
+ encode(Dec, #{}).
+
+encode(Dec, Opts) ->
+ encode(base64:encode(Dec), Opts, <<>>).
+
+encode(<<$+, R/bits>>, Opts, Acc) -> encode(R, Opts, <<Acc/binary, $->>);
+encode(<<$/, R/bits>>, Opts, Acc) -> encode(R, Opts, <<Acc/binary, $_>>);
+encode(<<$=, _/bits>>, #{padding := false}, Acc) -> Acc;
+encode(<<C, R/bits>>, Opts, Acc) -> encode(R, Opts, <<Acc/binary, C>>);
+encode(<<>>, _, Acc) -> Acc.
+
+-ifdef(TEST).
+
+rfc7515_test() ->
+ Dec = <<3,236,255,224,193>>,
+ Enc = <<"A-z_4ME">>,
+ Pad = <<"A-z_4ME=">>,
+ Dec = decode(<<Enc/binary,$=>>),
+ Dec = decode(Enc, #{padding => false}),
+ Pad = encode(Dec),
+ Enc = encode(Dec, #{padding => false}),
+ ok.
+
+prop_identity() ->
+ ?FORALL(B, binary(), B =:= decode(encode(B))).
+
+prop_identity_no_padding() ->
+ ?FORALL(B, binary(), B =:= decode(encode(B, #{padding => false}), #{padding => false})).
+
+-endif.
diff --git a/src/cow_multipart.erl b/src/cow_multipart.erl
index f573be5..7ccda0d 100644
--- a/src/cow_multipart.erl
+++ b/src/cow_multipart.erl
@@ -424,7 +424,7 @@ horse_parse() ->
-spec boundary() -> binary().
boundary() ->
- base64:encode(crypto:strong_rand_bytes(48)).
+ cow_base64url:encode(crypto:strong_rand_bytes(48), #{padding => false}).
%% @doc Return the first part's head.
%%