Giter Club home page Giter Club logo

ex_crypto's People

Contributors

barttenbrinke avatar bglusman avatar denispeplin avatar joustava avatar kianmeng avatar lukaszsamson avatar marcantoine-arnaud avatar narnach avatar ntrepid8 avatar obrok avatar quatermain avatar sheharyarn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

ex_crypto's Issues

using anything but aes_256 as the key fails

I'm trying to do basic AES encryption on some text

{:ok, aes_128_key} = ExCrypto.generate_aes_key(:aes_128, :bytes)
{:ok, {_iv, cipher_text}} = ExCrypto.encrypt(aes_128_key, "hi")

and I'm getting this error

** (MatchError) no match of right hand side value: %ErlangError{original: :notsup}
    (ex_crypto) lib/ex_crypto.ex:28: ExCrypto.normalize_error/3

If I use aes_256 everything works.

Loading private key from PEM file fails

This is the same issue as reported here:
https://stackoverflow.com/questions/41336947/erlang-generate-rsa-keys-from-pem-files

In this case it causes ExPublicKey.load/2 to return an {:error, _} tuple.

Loading the corresponding public key produces the :RSAPublicKey as expected.

Steps to reproduce

I'm using OpenSSL 1.0.2o

  1. Generate keys
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 -out test_private.pem
openssl pkey -pubout -inform PEM -outform PEM -in test_private.pem -out test_public.pem
  1. Load keys
iex(1)> ExPublicKey.load("test_public.pem")
{:ok, #ExPublicKey.RSAPublicKey<
   fingerprint_sha256=
     a5...>}
iex(2)> ExPublicKey.load("test_private.pem")
{:error,
 "invalid argument, expected one of[ExPublicKey.RSAPublicKey, ExPublicKey.RSAPrivateKey], found: PrivateKeyInfo"}

Possible solution

Use :public_key.der_decode/2 with the data provided from the PrivateKeyInfo tuple. I'm assuming the the algorithm identifier is contained inside the PrivateKeyInfo data, but I don't know how to extract it so I've hard-coded :RSAPrivateKey here.

defmodule Key do
  def load_priv_key() do
    {:PrivateKeyInfo, :v1, _, private_key_binary, _} =
      File.read!("test_private.pem")
      |> :public_key.pem_decode() |> hd
      |> :public_key.pem_entry_decode()

    :public_key.der_decode(:RSAPrivateKey, private_key_binary)
    |> ExPublicKey.RSAPrivateKey.from_sequence()
  end
end
iex(1)> Key.load_priv_key
#ExPublicKey.RSAPrivateKey<
  fingerprint_sha256=
    a5...>

Load RSA keys with passphrase

I have RSA keys that are encrypted using a password. How would I go about loading them from disk and using them? Currently, when trying to load them, gives this error:

iex(11)> ExPublicKey.load("/Users/Psy/crap/crypto/testkey.pem")
{:error,
 %FunctionClauseError{arity: 1, function: :pem_entry_decode,
  module: :public_key},
 [{:public_key, :pem_entry_decode,
   [{:RSAPrivateKey,
     <<69, 103, 57, 185, 26, 114, 20, 34, 100, 84, 103, 123, 157, 128, 197, 116,
       174, 125, 94, 20, 234, 243, 138, 198, 8, 108, 207, 220, 65, 70, 93, 198,
       113, 84, 163, 100, 154, 48, 60, 9, 237, 3, ...>>,
     {'AES-128-CBC',
      <<44, 86, 151, 113, 171, 22, 39, 28, 21, 99, 173, 229, 4, 100, 138,
        3>>}}], [file: 'public_key.erl', line: 140]},
  {ExPublicKey, :load_pem_entry, 1, [file: 'lib/ex_public_key.ex', line: 63]},
  {ExPublicKey, :loads, 1, [file: 'lib/ex_public_key.ex', line: 49]},
  {:erl_eval, :do_apply, 6, [file: 'erl_eval.erl', line: 670]},
  {:elixir, :erl_eval, 3, [file: 'src/elixir.erl', line: 229]},
  {:elixir, :eval_forms, 4, [file: 'src/elixir.erl', line: 217]},
  {IEx.Evaluator, :handle_eval, 6, [file: 'lib/iex/evaluator.ex', line: 182]},
  {IEx.Evaluator, :do_eval, 4, [file: 'lib/iex/evaluator.ex', line: 175]},
  {IEx.Evaluator, :eval, 4, [file: 'lib/iex/evaluator.ex', line: 155]},
  {IEx.Evaluator, :loop, 3, [file: 'lib/iex/evaluator.ex', line: 61]}, 
  {IEx.Evaluator, :init, 4, [file: 'lib/iex/evaluator.ex', line: 21]},
  {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 247]}]}

Help handling aes256cbc

I'm struggling currently to get clean, easy encryption interoperability between ruby and elixir, which looks easy on paper as both use OpenSSL under the hood, but think elixir community could use some better abstractions around Erlang crypto, which looks like exactly what this library is out to offer!

I got a simple zero IV working using this code:
https://gist.github.com/bglusman/7e1df9efad80845977b60d44d335f3df

But it's brittle and awkward and I thought perhaps we can include some elixir version here that will:
a) help avoid invalid IV values
b) perhaps provide easy options to decrypt existing widely used options from ruby and/or other standard libraries

In ruby we're using attr_encrypted for DB encryption, though still currently stuck on 1.4 until we can find time to use explicit IV's instead of "single_iv_and_salt" which I think corresponds to using pkcs5_keyivgen in ruby OpenSSL, but I'm not 100% sure. Being able to support both that and explicit IV's would be nice, but I also wasted a lot of time not realizing that all IV's had to be 128 bits which a good error message would help, and whatever I've screwed up now that erlang just says "** (ErlangError) erlang error: :notsup" in response to should also hopefully be able to have a more illuminating/useful error message as well :-)

Folks in the #security channel on elixir slack may be up for helping, they helped me past the 128 bit issue, I'll post this issue there as well.

Decrypting can return {:ok, :error}

For example, if you provide a different Auth data for decryption:

  iex> clear_text = "my-clear-text"
  iex> auth_data = "my-auth-data"
  iex> bad_auth_data = "bad-auth-data"
  iex> {:ok, aes_256_key} = ExCrypto.generate_aes_key(:aes_256, :bytes)
  iex> {:ok, {_ad, payload}} = ExCrypto.encrypt(aes_256_key, auth_data, clear_text)
  iex> {init_vec, cipher_text, cipher_tag} = payload
  iex> {:ok, :error} = ExCrypto.decrypt(aes_256_key, bad_auth_data, init_vec, cipher_text, cipher_tag)

Add better documentation on the auth_data

I'm trying to understand what auth_data for GCM mode is. Between https://crypto.stackexchange.com/questions/35727/does-aad-make-gcm-encryption-more-secure which says that it's optional, and https://en.wikipedia.org/wiki/Galois/Counter_Mode has this blurb: As with any message authentication code, if the adversary chooses a t-bit tag at random, it is expected to be correct for given data with probability 2โˆ’t. With GCM, however, an adversary can choose tags that increase this probability, proportional to the total length of the ciphertext and additional authenticated data (AAD). Consequently, GCM is not well-suited for use with very short tag lengths or very long messages.

Basically, can I omit this data if I control my metadata elsewhere (e.g. protocol negotiation takes place elsewhere)?

Either way, can you update the documentation to be a little more descriptive here? The samples just have "other auth data" which doesn't help me understand how it could be used practically.

Breaks on OTP Release 24

iex(4)> System.otp_release()
"24"
iex(5)> clear_text = "my-clear-text"
"my-clear-text"
iex(6)> auth_data = "my-auth-data"
"my-auth-data"
iex(7)> {:ok, aes_256_key} = ExCrypto.generate_aes_key(:aes_256, :bytes)
{:ok,
<<16, 212, 156, 208, 202, 106, 240, 39, 18, 138, 213, 17, 35, 15, 50, 53, 156,
60, 141, 243, 97, 61, 145, 27, 178, 31, 106, 172, 167, 224, 213, 119>>}
iex(8)> {:ok, {ad, {init_vec, cipher_text, cipher_tag}}} = ExCrypto.encrypt(aes_256_key, auth_data, clear_text)
** (UndefinedFunctionError) function :crypto.block_encrypt/4 is undefined or private, use crypto:crypto_one_time/5, crypto:crypto_one_time_aead/6,7 or crypto:crypto
(dyn_iv)?init + crypto:crypto(dyn_iv)?_update + crypto:crypto_final instead
(crypto 5.0.2) :crypto.block_encrypt(:aes_gcm, <<16, 212, 156, 208, 202, 106, 240, 39, 18, 138, 213, 17, 35, 15, 50, 53, 156, 60, 141, 243, 97, 61, 145, 27, 178, 31, 106, 172, 167, 224, 213, 119>>, <<75, 167, 47, 63, 228, 39, 177, 233, 98, 107, 47, 119, 119, 193, 82, 54>>, {"my-auth-data", "my-clear-text"})
(ex_crypto 0.10.0) lib/ex_crypto.ex:317: ExCrypto._encrypt/4

Warnings with Elixir 1.5.2 and version 0.7.1

Hello,

I got couple of warnings with Elixir 1.5.2 and version 0.7.1:

warning: clauses for the same def should be grouped together, def encrypt/3 was previously defined (lib/ex_crypto.ex:274)
  lib/ex_crypto.ex:321

warning: crypto:rand_uniform/2 is deprecated and will be removed in a future release; use rand:uniform/1
  lib/ex_crypto.ex:102

warning: clauses for the same def should be grouped together, def generate_key/3 was previously defined (lib/ex_public_key.ex:246)
  lib/ex_public_key.ex:267

warning: default arguments in load_pem_entry/2 are never used
  lib/ex_public_key.ex:112

warning: this clause cannot match because a previous clause at line 244 always matches
  lib/ex_public_key.ex:245

warning: variable "public_exp" is unused
  lib/ex_public_key.ex:247

Key Strength

The documentation shows

{:ok, aes_256_key} = ExCrypto.generate_aes_key(:aes_256, :base64)
{:ok,
 "Bs_BzhuwseEA8ZUvuEY0mq9Rmlv6cSoU_RaYD14Q62HiN_kJ4FiaW0YYppf1ffYPQ56xuitxQtYAnaeP-Q5l1WPh5aExdwCG_PUm5g-MlOUA1XSSP2RvuQqAiHzazIzjGVSIcl0Gr7TSLPOoIQrPshMNaA4j3SGZ3lAOqO1quvXtDn-9Sxwr5dwV7VzOIvXRwb0GbZeYp8lnVJgeqHl8cEhUTfT_h9Pm7tU2CFeHZCDK8ntFT_t4q6VlcBcvw_Pj3CGcVSmpmCHMKW1brt6jXGBijqSTdbjYDZnCx2Q44VoYqMMZ1U2GnVyjc-ZuwugwGGqQ7UEqV_TOMjbK6Oxx-Q=="}

But when I ran this locally, I get these much shorter results:

iex(1)> ExCrypto.generate_aes_key(:aes_256, :base64)
{:ok, "7MCZHilcDU7C3JTIicel0HTbMdoewepNLbjf0Is5fpI="}
iex(2)> ExCrypto.generate_aes_key(:aes_256, :base64)
{:ok, "8rPe6WBHKsWzRB1bTlwVyaMqNWmo1X9kO33l36Twc1I="}
iex(3)> ExCrypto.generate_aes_key(:aes_256, :base64)
{:ok, "n4LPbbqZ2Zopp7XKM7WBlq-1CwHRU96ljkAm3y8S-tQ="}
iex(4)> ExCrypto.generate_aes_key(:aes_256, :base64)
{:ok, "nNa1fpWJ7lqtXHbQ3dMLl5Fg8dyaJTdrPKl2iHaIzTg="}
iex(5)> ExCrypto.generate_aes_key(:aes_256, :base64)
{:ok, "FyoaY2gPzaz-3DEHdqtoArdp2lf203B0tuf5grYY_9w="}

The versions I have on my Mac 10.15.3 is:

$ elixir --version
Erlang/OTP 22 [erts-10.6.4] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe]

Elixir 1.9.4 (compiled with Erlang/OTP 20)

$ openssl version
LibreSSL 2.8.3

What am I missing that these keys seem shorter than they should be.

Add type specifications to all public functions

It would be nice if all public functions had a @spec type specification so it becomes more obvious from the documentation what types can be given to a function without reading the actual code.

This would also make it possible for dialyzer to do type inference on the code, e.g. I just came across an issue where a RSAPrivateKey was given to ExPublicKey.encrypt_public but the code ended up failing inside ExPublicKey.RSAPublicKey (https://github.com/ntrepid8/ex_crypto/blob/master/lib/ex_public_key/ex_rsa_public_key.ex#L31) with the error

  ** (Protocol.UndefinedError) protocol String.Chars not implemented for #ExPublicKey.RSAPrivateKey<fingerprint_sha256=....>. This protocol is implemented for: Ecto.Time, Ecto.DateTime, Ecto.Date, Decimal, Version, Atom, Integer, Version.Requirement, NaiveDateTime, URI, Date, DateTime, BitString, List, Float, Time

because the line

{:error, "invalid ExPublicKey.RSAPublicKey: #{rsa_public_key}"}

did not use #{inspect rsa_public_key}.

problem with AES-256-CBC private key

Hi there,
I've found your very useful Elixir lib but I'm facing with an issue when I'm using AES-256-CBC private keys

Test case that fails:
test/ex_public_key_test.exs
insert at line : 32
"-aes256",

so instead of

# generate a passphrase protected RSA private key with openssl
    System.cmd("openssl", [
      "genrsa",
      "-out",
      rsa_secure_private_key_path,
      "-passout",
      "pass:#{rand_string}",
      "2048"
    ])

we have now

# generate a passphrase protected RSA private key with openssl
    System.cmd("openssl", [
      "genrsa",
      "-aes256",
      "-out",
      rsa_secure_private_key_path,
      "-passout",
      "pass:#{rand_string}",
      "2048"
    ])

and the test
mix test test/ex_public_key_test.exs:79
fails

  1) test read secure RSA keys (ExPublicKeyTest)
     test/ex_public_key_test.exs:79
     ** (CaseClauseError) no case clause matching: {:error, %FunctionClauseError{args: nil, arity: 4, clauses: nil, function: :decode, kind: nil, module: :pubkey_pbe}, [{:pubkey_pbe, :decode, [<<43, 180, 39, 134, 179, 160, 115, 204, 207, 162, 100, 17, 226, 84, 197, 254, 3, 10, 68, 212, 144, 49, 120, 150, 239, 201, 223, 214, 213, 157, 39, 151, 17, 109, 42, 106, 193, 217, 176, 117, 244, 187, ...>>, 'KLvk', 'AES-256-CBC', <<246, 36, 62, 81, 225, 191, 64, 102, 55, 197, 38, 121, 69, 244, 140, 46>>], [file: 'pubkey_pbe.erl', line: 59]}, {:public_key, :do_pem_entry_decode, 2, [file: 'public_key.erl', line: 1103]}, {ExPublicKey, :load_pem_entry, 2, [file: 'lib/ex_public_key.ex', line: 128]}, {ExPublicKey, :load_pem_entry, 2, [file: 'lib/ex_public_key.ex', line: 122]}, {ExPublicKey, :loads, 2, [file: 'lib/ex_public_key.ex', line: 94]}, {ExPublicKey, :loads!, 2, [file: 'lib/ex_public_key.ex', line: 107]}, {ExPublicKeyTest, :"test read secure RSA keys", 1, [file: 'test/ex_public_key_test.exs', line: 81]}, {ExUnit.Runner, :exec_test, 1, [file: 'lib/ex_unit/runner.ex', line: 306]}, {:timer, :tc, 1, [file: 'timer.erl', line: 166]}, {ExUnit.Runner, :"-spawn_test/3-fun-1-", 4, [file: 'lib/ex_unit/runner.ex', line: 245]}]}
     code: secure_rsa_priv_key = ExPublicKey.loads!(secure_priv_key_string, context[:passphrase])
     stacktrace:
       (ex_crypto) lib/ex_public_key.ex:107: ExPublicKey.loads!/2
       test/ex_public_key_test.exs:81: (test)

if you replace "-aes256" with "-aes128" . test/ex_public_key_test.exs is OK

Any idea?
Many thanks in advance
Daniel

ExPublicKey.generate_key/2 fails.

Straight from the docs:

{:ok, key} = ExPublicKey.generate_key(:rsa, 2048)

But this is what I get when running it:

** (ArgumentError) argument error
    (crypto) :crypto.rsa_generate_key_nif(:rsa, <<8, 0>>)
    (crypto) crypto.erl:543: :crypto.generate_key/3
    (public_key) public_key.erl:426: :public_key.generate_key/1
    (ex_crypto) lib/ex_public_key.ex:287: ExPublicKey.generate_key/4

Using 0.9.0.

Cannot encrypt a long text using RSA private key

I found a possible bug on ExPublicKey.encrypt_private/3.

iex> System.otp_release()
"24"
iex> short_text = "Hello"
"Hello"
iex> long_text = String.duplicate("a", 10000)
"aaa..."
iex> rsa_priv_key = ExPublicKey.load!("/path/to/private_key.pem")
#ExPublicKey.RSAPrivateKey<...>
iex> ExPublicKey.encrypt_private(short_text, rsa_priv_key)
{:ok,
 "hlnaQvo5Onskl1dlI95RSoBAZlUDVMfHXmM5J3nuB2D7er02AivkOz2l9POaH8KgN4KbVFzbnla4-i8YUmWrOQ=="}
iex> ExPublicKey.encrypt_private(long_text, rsa_priv_key)
{:error, %ErlangError{original: :encrypt_failed}, []}

In fact, an error occurs even if the text is not so long. For example, replacing 10000 with 100 causes the same problem.

However, if I replace 10000 with 50, the error does not occur.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.