@@ -221,7 +221,7 @@ is quite simple; (_only 4 lines_):<br />
221
221
``` elixir
222
222
def encrypt (plaintext) do
223
223
iv = :crypto .strong_rand_bytes (16 ) # create random Initialization Vector
224
- state = :crypto .stream_init (:aes_ctr , key (), iv) # Initialise crypto stream
224
+ state = :crypto .stream_init (:aes_ctr , get_key (), iv) # create crypto stream
225
225
# peform the encryption:
226
226
{_state , ciphertext} = :crypto .stream_encrypt (state, to_string (plaintext))
227
227
iv <> ciphertext # "return" iv concatenated with the ciphertext
@@ -245,9 +245,10 @@ it accepts a "blob" of `ciphertext` (_which as you may recall_),
245
245
has the IV prepended to it, and returns the original ` plaintext ` .
246
246
247
247
``` elixir
248
- def decrypt (ciphertext) do
249
- << iv:: binary- 16 , ciphertext:: binary>> = ciphertext # split iv from ciphertext
250
- state = :crypto .stream_init (:aes_ctr , key (), iv) # create crypto stream
248
+ def decrypt (ciphertext, key_id) do
249
+ << iv:: binary- 16 , ciphertext:: binary>> = ciphertext # split iv & ciphertext
250
+ # get encryption key based on key_id & Initialise crypto stream:
251
+ state = :crypto .stream_init (:aes_ctr , get_key (key_id), iv)
251
252
# perform decryption
252
253
{_state , plaintext} = :crypto .stream_decrypt (state, ciphertext)
253
254
plaintext # "return" just the plaintext
@@ -257,34 +258,66 @@ end
257
258
The fist step (line) is to "split" the IV from the ` ciphertext `
258
259
using Elixir's binary pattern matching.
259
260
260
- To _ understand_ the ` <<iv::binary-16, ciphertext::binary>> `
261
- pattern matching line, read the following guide:
261
+ > If you are unfamiliar with Elixir binary pattern matching syntax
262
+ `<<iv::binary-16, ciphertext::binary>> `
263
+ read the following guide:
262
264
https://elixir-lang.org/getting-started/binaries-strings-and-char-lists.html
263
265
264
- The ` state = :crypto.stream_init(:aes_ctr, key(), iv) ` line is the _ same_
265
- as in the ` encrypt ` function, this should reassure you that it's the
266
- _ same_ setup (init) to ` decrypt ` as it was to ` encrypt ` .
266
+ The ` state = :crypto.stream_init(:aes_ctr, get_key(key_id), iv) ` line
267
+ is the _ same_ as in the ` encrypt ` function,
268
+ _ except_ that this time we initialise the stream with a _ specific_ key
269
+ (_ the key that was used to ` encrypt ` the data originally_ ).
267
270
268
- Then ` ciphertext ` is decrypted using ` stream_decrypt `
271
+ ` ciphertext ` is decrypted using ` stream_decrypt `
269
272
http://erlang.org/doc/man/crypto.html#stream_decrypt-2
270
273
271
- Finally the original ` plaintext ` is returned.
272
-
274
+ Finally the original ` plaintext ` is _ returned_ .
273
275
274
276
275
277
276
278
#### 3.3 Get (Encryption) Key
277
279
278
- You will have noticed that both ` encrypt ` and ` decrypt ` functions
279
- call a ` key ()` function on the ` state ` line.
280
+ You will have noticed that _ both _ ` encrypt ` and ` decrypt ` functions
281
+ call a ` get_key ()` function on the ` state = ... ` line.
280
282
It is not a "built-in" function, we are about to define it!
281
283
284
+
285
+
286
+
287
+ ##### ` ENCRYPTION_KEYS ` Environment Variable
288
+
289
+ In order for our ` get_key ` function to work,
290
+ it needs to be know how to "read" the encryption keys.
291
+
292
+ > _ ** Note** : we prefer to store our Encryption Keys as
293
+ ** Environment Variables**
294
+
295
+ e need to "export" an Environment Variable
296
+ containing a (_ comma-separated_ ) list of (_ one or more_ )
297
+ encryption key(s).
298
+ _ Copy-paste_ (_ and run_ ) the following command into your terminal:
299
+
300
+ ``` elixir
301
+ echo " export ENCRYPTION_KEYS='nMdayQpR0aoasLaq1g94FLba+A+wB44JLko47sVQXMg=,L+ZVX8iheoqgqb22mUpATmMDsvVGtafoAeb0KN5uWf0='" >> .env && echo " .env" >> .gitignore
302
+ ```
303
+ > For _ now_ , copy paste this command exactly as it is.<br />
304
+ > When you are deploying your own App,
305
+ > generate your own AES encryption key(s)
306
+ > see below for how to do this.
307
+ > _ ** Note** : there are ** two** encryption keys separated by a comma.
308
+ This is to ** demonstrate** that it's ** possible** to use ** multiple keys** ._
309
+
310
+
282
311
``` elixir
283
312
284
313
```
285
314
286
315
287
316
317
+ https://elixirschool.com/en/lessons/specifics/ets/
318
+ https://elixir-lang.org/getting-started/mix-otp/ets.html
319
+
320
+
288
321
289
322
In your terminal run the following ** _ three_ commands** :
290
323
@@ -365,32 +398,45 @@ far less likely_) as `bcrypt` has a CPU-bound "work-factor".
365
398
366
399
367
400
401
+ ### How To Generate AES Encryption Keys?
368
402
369
- <br /> <br />
403
+ Encryption keys should be the appropriate length (in bits)
404
+ as required by the chosen algorithm.
370
405
371
- ## Credits
406
+ > An ** AES 128-bit** key can be expressed
407
+ as a hexadecimal string with 32 characters.
408
+ It will require ** 24 characters** in ** base64** .
372
409
373
- Credit for this example goes to [ @ danielberkompas ] ( https://github.com/danielberkompas )
374
- for his post:
375
- https://blog.danielberkompas.com/2015/07/03/encrypting-data-with-ecto < br />
410
+ > An ** AES 256-bit ** key can be expressed
411
+ as a hexadecimal string with 64 characters.
412
+ It will require ** 44 characters ** in ** base64 ** .
376
413
377
- Daniel's post is for [ Phoenix ` v0.14.0 ` ] ( https://github.com/danielberkompas/danielberkompas.github.io/blob/c6eb249e5019e782e891bfeb591bc75f084fd97c/_posts/2015-07-03-encrypting-data-with-ecto.md ) which is quite "old" now ...
378
- therefore a few changes/updates are required.
379
- e.g: There are no more "** Models** " in Phoenix 1.3 or Ecto callbacks.
414
+ see: https://security.stackexchange.com/a/45334/117318
380
415
381
- _ Also_ his post only includes the "sample code"
382
- and is _ not_ a _ complete_ example. <br />
383
- Which means anyone following the post needs to _ manually_ copy-paste the code...
384
- We prefer to include the _ complete_ "end state" of any tutorial so that
385
- people can ` git clone ` and _ ` run ` _ the code locally.
416
+ Open ` iex ` in your Terminal and paste the following line (_ then press enter_ )
417
+ ``` elixir
418
+ :crypto .strong_rand_bytes (32 ) |> :base64 .encode
419
+ ```
420
+
421
+ You should see terminal output similar to the following:
422
+
423
+ ![ elixir-generate-encryption-key] ( https://user-images.githubusercontent.com/194400/38561017-dd93d186-3cce-11e8-91cd-c70f920ac79a.png )
424
+
425
+ We generated 3 keys for demonstration purposes:
426
+ + "h6pUk0ZccS0pYsibHZZ4Cd+PRO339rMA7sMz7FnmcGs="
427
+ + "nMd/yQpR0aoasLaq1g94FL/a+A+wB44JLko47sVQXMg="
428
+ + "L+ZVX8iheoqgqb22mUpATmMDsvVGt/foAe/0KN5uWf0="
429
+
430
+
431
+
432
+ These two Erlang functions are described in:
433
+ + http://erlang.org/doc/man/crypto.html#strong_rand_bytes-1
434
+ + http://erlang.org/doc/man/base64.html#encode-1
435
+
436
+ Base64 encoding the bytes generated by ` strong_rand_bytes `
437
+ will make the output human-readable
438
+ (_ whereas bytes are less user-friendly_ ).
386
439
387
- I reached out to Daniel on Twitter
388
- asking if he would accept a Pull Request
389
- updating the post to latest version of Phoenix: <br />
390
- [ ![ credit-tweet] ( https://user-images.githubusercontent.com/194400/35771850-32b1cfba-092b-11e8-9bbf-0e693016bb76.png )] ( https://twitter.com/nelsonic/status/959901100760498181 ) <br />
391
- If he replies I will _ gladly_ create a PR.
392
- _ Meanwhile_ this example will fill in the gaps
393
- and provide a more up-to-date example.
394
440
395
441
<br /> <br />
396
442
@@ -403,19 +449,36 @@ https://crypto.stackexchange.com/questions/3615/what-is-the-effect-of-the-differ
403
449
+ How is decryption done in AES CTR mode?: https://crypto.stackexchange.com/questions/34918/how-is-decryption-done-in-aes-ctr-mode
404
450
+ Block Cipher Counter (CTR) Mode:
405
451
https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29
452
+ + Is AES-256 weaker than 192 and 128 bit versions?
453
+ https://crypto.stackexchange.com/questions/5118/is-aes-256-weaker-than-192-and-128-bit-versions
454
+ + What are the practical differences between 256-bit, 192-bit, and 128-bit AES encryption?
455
+ https://crypto.stackexchange.com/questions/20/what-are-the-practical-differences-between-256-bit-192-bit-and-128-bit-aes-enc
456
+ + How to choose an Authenticated Encryption mode
457
+ (_ by Matthew Green cryptography professor at Johns Hopkins University_ ):
458
+ https://blog.cryptographyengineering.com/2012/05/19/how-to-choose-authenticated-encryption/
459
+ + How to choose an AES encryption mode (CBC ECB CTR OCB CFB)? (v. long answers, but good comparison!)
460
+ https://stackoverflow.com/questions/1220751/how-to-choose-an-aes-encryption-mode-cbc-ecb-ctr-ocb-cfb
461
+ + AES GCM vs CTR+HMAC tradeoffs:
462
+ https://crypto.stackexchange.com/questions/14747/gcm-vs-ctrhmac-tradeoffs
463
+ + Galois/Counter Mode for symmetric key cryptographic block ciphers: https://en.wikipedia.org/wiki/Galois/Counter_Mode
464
+ + How long (in letters) are encryption keys for AES?
465
+ https://security.stackexchange.com/questions/45318/how-long-in-letters-are-encryption-keys-for-aes
466
+ + Generate random alphanumeric string (_ used for AES keys_ )
467
+ https://stackoverflow.com/questions/12788799/how-to-generate-a-random-alphanumeric-string-with-erlang
406
468
+ Singular or Plural controller names?: https://stackoverflow.com/questions/35882394/phoenix-controllers-singular-or-plural
407
- + Postgres Data Type for storing ` bcrypt ` hashed passwords: https://stackoverflow.com/questions/33944199/bcrypt-and-postgresql-what-data-type-should-be-used >> ` bytea `
408
- + https://security.stackexchange.com/questions/4781/do-any-security-experts-recommend-bcrypt-for-password-storage/6415#6415
469
+ + Postgres Data Type for storing ` bcrypt ` hashed passwords: https://stackoverflow.com/questions/33944199/bcrypt-and-postgresql-what-data-type-should-be-used >> ` bytea ` ( _ byte _ )
470
+ + Do security experts recommend bcrypt? https://security.stackexchange.com/questions/4781/do-any-security-experts-recommend-bcrypt-for-password-storage/6415#6415
409
471
+ Hacker News discussion thread "*** Don't use ` bcrypt ` *** ":
410
472
https://news.ycombinator.com/item?id=3724560
411
473
412
474
413
475
## Troubleshooting
414
476
415
- If you get "stuck", please open an issue describing the issue you are facing.
477
+ If _ you _ get "stuck", please open an issue describing the issue you are facing.
416
478
417
479
TIL: app names in Phoneix _ must_ be lowercase letters: <br />
418
480
![ lower-case-app-names] ( https://user-images.githubusercontent.com/194400/35360087-73d69d88-0154-11e8-9f47-d9a9333d1e6c.png )
481
+ (_ basic, I know, now..._ )
419
482
420
483
Works with lowercase: <br />
421
484
![ second-time-lucky] ( https://user-images.githubusercontent.com/194400/35360183-c522063c-0154-11e8-994a-7516bc0e5c1e.png )
@@ -426,3 +489,32 @@ To run a _single_ test while debugging, use the following syntax:
426
489
``` sh
427
490
mix test test/user/user_test.exs:9
428
491
```
492
+
493
+ <br /> <br />
494
+
495
+ ## Credits
496
+
497
+ Credit for this example goes to [ @danielberkompas ] ( https://github.com/danielberkompas )
498
+ for his post:
499
+ https://blog.danielberkompas.com/2015/07/03/encrypting-data-with-ecto <br />
500
+
501
+ Daniel's post is for [ Phoenix ` v0.14.0 ` ] ( https://github.com/danielberkompas/danielberkompas.github.io/blob/c6eb249e5019e782e891bfeb591bc75f084fd97c/_posts/2015-07-03-encrypting-data-with-ecto.md ) which is quite "old" now ...
502
+ therefore a few changes/updates are required.
503
+ e.g: There are no more "** Models** " in Phoenix 1.3 or Ecto callbacks.
504
+
505
+ _ Also_ his post only includes the "sample code"
506
+ and is _ not_ a _ complete_ example
507
+ _ explaining_ the functions & Custom Ecto Types. <br />
508
+ Which means anyone following the post needs to _ manually_ copy-paste the code...
509
+ We prefer to include the _ complete_ "end state" of any tutorial so that
510
+ people can ` git clone ` and _ ` run ` _ the code locally.
511
+
512
+ <!--
513
+ I reached out to Daniel on Twitter
514
+ asking if he would accept a Pull Request
515
+ updating the post to latest version of Phoenix: <br />
516
+ [](https://twitter.com/nelsonic/status/959901100760498181) <br />
517
+ If he replies I will _gladly_ create a PR.
518
+ _Meanwhile_ this example will fill in the gaps
519
+ and provide a more up-to-date example.
520
+ -->
0 commit comments