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