PageRenderTime 9ms CodeModel.GetById 0ms RepoModel.GetById 1ms app.codeStats 0ms

/src/utils/ewgi_util_crypto.erl

http://github.com/skarab/ewgi
Erlang | 69 lines | 36 code | 6 blank | 27 comment | 0 complexity | 8b0007d2222b69cfe83a7fa60d2f9112 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception
  1. %% @author Hunter Morris <hunter.morris@smarkets.com>
  2. %% @author Davide Marquęs <nesrait@gmail.com>
  3. %% @copyright 2009 Smarkets Limited.
  4. %%
  5. %% @doc Secure data transformations for data storage in unsecure locations.
  6. %% Based on smak_sn_cookie by Hunter Morris.
  7. %% Differences from smak_sn_cookie: removed the timestamping operation on
  8. %% data encoding and and the timeout validation on decoding data.
  9. %%
  10. %% The binary data is first compressed, then encrypted using AES in CFB mode
  11. %% with a random initialization vector. After that the data is signed
  12. %% using HMAC-SHA1 with the same key.
  13. %%
  14. %% When the data is decoded, the signature is first checked for validity and
  15. %% then the data is decrypted, decompressed, and deserialized.
  16. %%
  17. %% @end
  18. %%
  19. %% Licensed under the MIT license:
  20. %% http://www.opensource.org/licenses/mit-license.php
  21. -module(ewgi_util_crypto).
  22. -author('Hunter Morris <hunter.morris@smarkets.com>').
  23. -author('Davide Marquęs <nesrait@gmail.com>').
  24. -define(MAX_SIZE, 4096). %% Most browsers only allow 4K of cookies
  25. -define(MAX_SIZE_REPR, ?MAX_SIZE/2). %% Assume a 2-character representation of each byte in the final cookie.
  26. -export([encode/2, encode/3, decode/2]).
  27. %% @spec encode(Key::binary(), Data::binary()) -> binary() | {error, Reason::atom()}
  28. %% @doc Encodes and signs the data using the specified secret key.
  29. %% @see encode/3
  30. -spec encode(binary(), binary()) -> binary() | {error, atom()}.
  31. encode(Key, Data) ->
  32. encode(Key, Data, ?MAX_SIZE_REPR).
  33. %% @spec encode(Key::binary(), Data::binary(), MaxSize::integer()) -> binary() | {error, Reason::atom()}
  34. %% @doc Encodes and signs the data using the specified secret key and provides an error if it exceeds the maximum size.
  35. -spec encode(binary(), binary(), integer()) -> binary() | {error, atom()}.
  36. encode(<<Key:16/binary>>, Data, MaxSize) when MaxSize > 0, is_binary(Data) ->
  37. IV = crypto:rand_bytes(16),
  38. DataCompressed = zlib:zip(Data),
  39. DataCrypted = crypto:aes_cfb_128_encrypt(Key, IV, DataCompressed),
  40. HMACSignature = crypto:sha_mac(Key, <<IV:16/binary, DataCrypted/bits>>),
  41. Result = <<HMACSignature:20/binary, IV:16/binary, DataCrypted/bits>>,
  42. case size(Result) of
  43. Sz when Sz >= MaxSize ->
  44. {error, session_too_large};
  45. _ ->
  46. Result
  47. end;
  48. encode(_Key, _, _) ->
  49. {error, invalid_key}.
  50. %% @spec decode(Key::binary(), Cookie::binary()) -> binary() | {error, Reason::atom()}
  51. %% @doc Checks the signature and timeout and decrypts the cookie if it is valid.
  52. -spec decode(binary(), binary()) -> binary() | {error, atom()}.
  53. decode(<<Key:16/binary>>, <<HMACSignature:20/binary, IV:16/binary, DataCrypted/bits>>) ->
  54. case crypto:sha_mac(Key, <<IV:16/binary, DataCrypted/bits>>) of
  55. HMACSignature ->
  56. DataCompressed = crypto:aes_cfb_128_decrypt(Key, IV, DataCrypted),
  57. Data = zlib:unzip(DataCompressed),
  58. Data;
  59. _ ->
  60. {error, data_tampered}
  61. end;
  62. decode(_Key, _) ->
  63. {error, invalid_key}.