1%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*-
2%% vim: ts=4 sw=4 ft=erlang noet
3%%%-------------------------------------------------------------------
4%%% @author Andrew Bennett <potatosaladx@gmail.com>
5%%% @copyright 2014-2015, Andrew Bennett
6%%% @doc
7%%%
8%%% @end
9%%% Created :  22 Jul 2015 by Andrew Bennett <potatosaladx@gmail.com>
10%%%-------------------------------------------------------------------
11-module(jose_jwe_enc_aes).
12-behaviour(jose_jwe).
13-behaviour(jose_jwe_enc).
14
15%% jose_jwe callbacks
16-export([from_map/1]).
17-export([to_map/2]).
18%% jose_jwe_enc callbacks
19-export([algorithm/1]).
20-export([bits/1]).
21-export([block_decrypt/4]).
22-export([block_encrypt/4]).
23-export([next_cek/1]).
24-export([next_iv/1]).
25%% API
26-export([cipher_supported/0]).
27-export([hmac_supported/0]).
28
29%% Types
30-type cipher()   :: aes_cbc | aes_gcm.
31-type key_size() :: 128 | 192 | 256.
32
33-record(jose_jwe_enc_aes, {
34	cipher  = undefined :: undefined | {cipher(), key_size()},
35	bits    = undefined :: undefined | pos_integer(),
36	cek_len = undefined :: undefined | pos_integer(),
37	iv_len  = undefined :: undefined | pos_integer(),
38	enc_len = undefined :: undefined | pos_integer(),
39	mac_len = undefined :: undefined | pos_integer(),
40	tag_len = undefined :: undefined | pos_integer(),
41	hmac    = undefined :: undefined | sha256 | sha384 | sha512
42}).
43
44-type enc() :: #jose_jwe_enc_aes{}.
45
46-export_type([enc/0]).
47
48-define(AES_128_CBC_HMAC_SHA_256, #jose_jwe_enc_aes{
49	cipher  = {aes_cbc, 128},
50	bits    = 256,
51	cek_len = 32,
52	iv_len  = 16,
53	enc_len = 16,
54	mac_len = 16,
55	tag_len = 16,
56	hmac    = sha256
57}).
58
59-define(AES_192_CBC_HMAC_SHA_384, #jose_jwe_enc_aes{
60	cipher  = {aes_cbc, 192},
61	bits    = 384,
62	cek_len = 48,
63	iv_len  = 16,
64	enc_len = 24,
65	mac_len = 24,
66	tag_len = 24,
67	hmac    = sha384
68}).
69
70-define(AES_256_CBC_HMAC_SHA_512, #jose_jwe_enc_aes{
71	cipher  = {aes_cbc, 256},
72	bits    = 512,
73	cek_len = 64,
74	iv_len  = 16,
75	enc_len = 32,
76	mac_len = 32,
77	tag_len = 32,
78	hmac    = sha512
79}).
80
81-define(AES_128_GCM, #jose_jwe_enc_aes{
82	cipher  = {aes_gcm, 128},
83	bits    = 128,
84	cek_len = 16,
85	iv_len  = 12
86}).
87
88-define(AES_192_GCM, #jose_jwe_enc_aes{
89	cipher  = {aes_gcm, 192},
90	bits    = 192,
91	cek_len = 24,
92	iv_len  = 12
93}).
94
95-define(AES_256_GCM, #jose_jwe_enc_aes{
96	cipher  = {aes_gcm, 256},
97	bits    = 256,
98	cek_len = 32,
99	iv_len  = 12
100}).
101
102%%====================================================================
103%% jose_jwe callbacks
104%%====================================================================
105
106from_map(F = #{ <<"enc">> := <<"A128CBC-HS256">> }) ->
107	{?AES_128_CBC_HMAC_SHA_256, maps:remove(<<"enc">>, F)};
108from_map(F = #{ <<"enc">> := <<"A192CBC-HS384">> }) ->
109	{?AES_192_CBC_HMAC_SHA_384, maps:remove(<<"enc">>, F)};
110from_map(F = #{ <<"enc">> := <<"A256CBC-HS512">> }) ->
111	{?AES_256_CBC_HMAC_SHA_512, maps:remove(<<"enc">>, F)};
112from_map(F = #{ <<"enc">> := <<"A128GCM">> }) ->
113	{?AES_128_GCM, maps:remove(<<"enc">>, F)};
114from_map(F = #{ <<"enc">> := <<"A192GCM">> }) ->
115	{?AES_192_GCM, maps:remove(<<"enc">>, F)};
116from_map(F = #{ <<"enc">> := <<"A256GCM">> }) ->
117	{?AES_256_GCM, maps:remove(<<"enc">>, F)}.
118
119to_map(?AES_128_CBC_HMAC_SHA_256, F) ->
120	F#{ <<"enc">> => <<"A128CBC-HS256">> };
121to_map(?AES_192_CBC_HMAC_SHA_384, F) ->
122	F#{ <<"enc">> => <<"A192CBC-HS384">> };
123to_map(?AES_256_CBC_HMAC_SHA_512, F) ->
124	F#{ <<"enc">> => <<"A256CBC-HS512">> };
125to_map(?AES_128_GCM, F) ->
126	F#{ <<"enc">> => <<"A128GCM">> };
127to_map(?AES_192_GCM, F) ->
128	F#{ <<"enc">> => <<"A192GCM">> };
129to_map(?AES_256_GCM, F) ->
130	F#{ <<"enc">> => <<"A256GCM">> }.
131
132%%====================================================================
133%% jose_jwe_enc callbacks
134%%====================================================================
135
136algorithm(?AES_128_CBC_HMAC_SHA_256) -> <<"A128CBC-HS256">>;
137algorithm(?AES_192_CBC_HMAC_SHA_384) -> <<"A192CBC-HS384">>;
138algorithm(?AES_256_CBC_HMAC_SHA_512) -> <<"A256CBC-HS512">>;
139algorithm(?AES_128_GCM)              -> <<"A128GCM">>;
140algorithm(?AES_192_GCM)              -> <<"A192GCM">>;
141algorithm(?AES_256_GCM)              -> <<"A256GCM">>.
142
143bits(#jose_jwe_enc_aes{bits=Bits}) -> Bits.
144
145block_decrypt({AAD, CipherText, CipherTag}, CEK, IV, #jose_jwe_enc_aes{
146		cipher=Cipher,
147		cek_len=CEKLen,
148		hmac=undefined})
149			when byte_size(CEK) =:= CEKLen
150			andalso bit_size(IV) > 0 ->
151	jose_jwa:block_decrypt(Cipher, CEK, IV, {AAD, CipherText, CipherTag});
152block_decrypt({AAD, CipherText, CipherTag}, CEK, IV, #jose_jwe_enc_aes{
153		cipher=Cipher,
154		cek_len=CEKLen,
155		iv_len=IVLen,
156		enc_len=EncLen,
157		mac_len=MacLen,
158		tag_len=TagLen,
159		hmac=HMAC})
160			when byte_size(CEK) =:= CEKLen
161			andalso byte_size(IV) =:= IVLen ->
162	<< MacKey:MacLen/binary, EncKey:EncLen/binary >> = CEK,
163	AADLength = << (bit_size(AAD)):1/unsigned-big-integer-unit:64 >>,
164	MacData = << AAD/binary, IV/binary, CipherText/binary, AADLength/binary >>,
165	case jose_crypto_compat:mac(hmac, HMAC, MacKey, MacData) of
166		<< CipherTag:TagLen/binary, _/binary >> ->
167			PlainText = jose_jwa_pkcs7:unpad(jose_jwa:block_decrypt(Cipher, EncKey, IV, CipherText)),
168			PlainText;
169		_ ->
170			error
171	end.
172
173block_encrypt({AAD, PlainText}, CEK, IV, #jose_jwe_enc_aes{
174		cipher=Cipher,
175		cek_len=CEKLen,
176		hmac=undefined})
177			when byte_size(CEK) =:= CEKLen
178			andalso bit_size(IV) > 0 ->
179	jose_jwa:block_encrypt(Cipher, CEK, IV, {AAD, PlainText});
180block_encrypt({AAD, PlainText}, CEK, IV, #jose_jwe_enc_aes{
181		cipher=Cipher,
182		cek_len=CEKLen,
183		iv_len=IVLen,
184		enc_len=EncLen,
185		mac_len=MacLen,
186		tag_len=TagLen,
187		hmac=HMAC})
188			when byte_size(CEK) =:= CEKLen
189			andalso byte_size(IV) =:= IVLen ->
190	<< MacKey:MacLen/binary, EncKey:EncLen/binary, _/binary >> = CEK,
191	CipherText = jose_jwa:block_encrypt(Cipher, EncKey, IV, jose_jwa_pkcs7:pad(PlainText)),
192	AADLength = << (bit_size(AAD)):1/unsigned-big-integer-unit:64 >>,
193	MacData = << AAD/binary, IV/binary, CipherText/binary, AADLength/binary >>,
194	<< CipherTag:TagLen/binary, _/binary >> = jose_crypto_compat:mac(hmac, HMAC, MacKey, MacData),
195	{CipherText, CipherTag}.
196
197next_cek(#jose_jwe_enc_aes{cek_len=CEKLen}) ->
198	crypto:strong_rand_bytes(CEKLen).
199
200next_iv(#jose_jwe_enc_aes{iv_len=IVLen}) ->
201	crypto:strong_rand_bytes(IVLen).
202
203%%====================================================================
204%% API functions
205%%====================================================================
206
207cipher_supported() ->
208	[aes_cbc, aes_gcm].
209
210hmac_supported() ->
211	[sha256, sha384, sha512].
212
213%%%-------------------------------------------------------------------
214%%% Internal functions
215%%%-------------------------------------------------------------------
216