%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
%%
%%% Purpose : Base 64 encoding and decoding.
-module(ssl_base64).
-export([encode/1, encode_split/1, decode/1, join_decode/1]).
-define(st(X,A), ((X-A+256) div 256)).
-define(CHARS, 64).
%% A PEM encoding consists of characters A-Z, a-z, 0-9, +, / and
%% =. Each character encodes a 6 bits value from 0 to 63 (A = 0, / =
%% 63); = is a padding character.
%%
%%
%% encode(Bytes|Binary) -> Chars
%%
%% Take 3 bytes a time (3 x 8 = 24 bits), and make 4 characters out of
%% them (4 x 6 = 24 bits).
%%
encode(Bs) when is_list(Bs) ->
encode(list_to_binary(Bs));
encode(<<B:3/binary, Bs/binary>>) ->
<<C1:6, C2:6, C3:6, C4:6>> = B,
[enc(C1), enc(C2), enc(C3), enc(C4)| encode(Bs)];
encode(<<B:2/binary>>) ->
<<C1:6, C2:6, C3:6, _:6>> = <<B/binary, 0>>,
[enc(C1), enc(C2), enc(C3), $=];
encode(<<B:1/binary>>) ->
<<C1:6, C2:6, _:12>> = <<B/binary, 0, 0>>,
[enc(C1), enc(C2), $=, $=];
encode(<<>>) ->
[].
%%
%% encode_split(Bytes|Binary) -> Lines
%%
%% The encoding is divided into lines separated by <NL>, and each line
%% is precisely 64 characters long (excluding the <NL> characters,
%% except the last line which 64 characters long or shorter. <NL> may
%% follow the last line.
%%
encode_split(Bs) ->
split(encode(Bs)).
%%
%% decode(Chars) -> Binary
%%
decode(Cs) ->
list_to_binary(decode1(Cs)).
decode1([C1, C2, $=, $=]) ->
<<B1, _:16>> = <<(dec(C1)):6, (dec(C2)):6, 0:12>>,
[B1];
decode1([C1, C2, C3, $=]) ->
<<B1, B2, _:8>> = <<(dec(C1)):6, (dec(C2)):6, (dec(C3)):6, (dec(0)):6>>,
[B1, B2];
decode1([C1, C2, C3, C4| Cs]) ->
Bin = <<(dec(C1)):6, (dec(C2)):6, (dec(C3)):6, (dec(C4)):6>>,
[Bin| decode1(Cs)];
decode1([]) ->
[].
%%
%% join_decode(Lines) -> Binary
%%
%% Remove <NL> before decoding.
%%
join_decode(Cs) ->
decode(join(Cs)).
%%
%% Locals
%%
%% enc/1 and dec/1
%%
%% Mapping: 0-25 -> A-Z, 26-51 -> a-z, 52-61 -> 0-9, 62 -> +, 63 -> /
%%
enc(C) ->
65 + C + 6*?st(C,26) - 75*?st(C,52) -15*?st(C,62) + 3*?st(C,63).
dec(C) ->
62*?st(C,43) + ?st(C,47) + (C-59)*?st(C,48) - 69*?st(C,65) - 6*?st(C,97).
%% split encoding into lines
%%
split(Cs) ->
split(Cs, ?CHARS).
split([], _N) ->
[$\n];
split(Cs, 0) ->
[$\n| split(Cs, ?CHARS)];
split([C| Cs], N) ->
[C| split(Cs, N-1)].
%% join lines of encodings
%%
join([$\r, $\n| Cs]) ->
join(Cs);
join([$\n| Cs]) ->
join(Cs);
join([C| Cs]) ->
[C| join(Cs)];
join([]) ->
[].