From 0eb0db3653bd89809acf528a0a3234c2c3cdde22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Mon, 10 Jul 2017 13:28:26 +0200 Subject: 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. --- ebin/cowlib.app | 2 +- src/cow_base64url.erl | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/cow_multipart.erl | 2 +- 3 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 src/cow_base64url.erl 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 +%% +%% 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, <>); +encode(<<$/, R/bits>>, Opts, Acc) -> encode(R, Opts, <>); +encode(<<$=, _/bits>>, #{padding := false}, Acc) -> Acc; +encode(<>, Opts, Acc) -> encode(R, Opts, <>); +encode(<<>>, _, Acc) -> Acc. + +-ifdef(TEST). + +rfc7515_test() -> + Dec = <<3,236,255,224,193>>, + Enc = <<"A-z_4ME">>, + Pad = <<"A-z_4ME=">>, + Dec = decode(<>), + 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. %% -- cgit v1.2.3