From 2f8834c49f7223dee05bd4fefa50747a0ee3f98f Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 18 Oct 2022 11:54:20 +0100 Subject: [PATCH 1/9] release latest version of all dependencies as v2.10.0 see: https://github.com/dwyl/fields/issues/116 --- README.md | 209 ++++++++++++++++++++++++++---------------------------- mix.exs | 7 +- mix.lock | 2 +- 3 files changed, 104 insertions(+), 114 deletions(-) diff --git a/README.md b/README.md index 325e3e6..0970fa2 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,15 @@ # Fields A **collection of frequently used fields** implemented as custom **`Ecto` types**
-with -[validation](https://hexdocs.pm/fields/Fields.Validate.html), -[sanitising](https://hexdocs.pm/fields/Fields.HtmlBody.html) -and +with +[validation](https://hexdocs.pm/fields/Fields.Validate.html), +[sanitising](https://hexdocs.pm/fields/Fields.HtmlBody.html) +and [encryption](https://hexdocs.pm/fields/Fields.AES.html) -/ +/ [hashing](https://hexdocs.pm/fields/Fields.Password.html) to **build `Phoenix` Apps _much_ faster!** πŸš€
+ @@ -40,19 +42,18 @@ with built-in validation, sanitising and security. **`Fields`** makes defining Ecto Schemas faster and more precise. - # _What_? πŸ’­ An Elixir package that helps you add popular custom types to your Phoenix/Ecto schemas so you can build apps faster! > **@dwyl** we are firm believers that personal data -(_Personally Identifiable Information_ (PII)) should be encrypted "at rest" -i.e. all "user" data should be encrypted _before_ being stored in the database. -This project makes hashing, encryption and _decryption_ for secure data storage -_much_ easier for everyone. +> (_Personally Identifiable Information_ (PII)) should be encrypted "at rest" +> i.e. all "user" data should be encrypted _before_ being stored in the database. +> This project makes hashing, encryption and _decryption_ for secure data storage +> _much_ easier for everyone. -> This package was born out of our research +> This package was born out of our research > into the best/easiest way to encrypt data in **`Phoenix`**: > [dwyl/phoenix-ecto-encryption-example](https://github.com/dwyl/phoenix-ecto-encryption-example) @@ -62,15 +63,14 @@ This module is for people building Elixir/Phoenix apps who want to ship _simpler_ and more maintainable code. > We've attempted to make **`Fields`** -as **beginner-friendly** as possible.
-If you get stuck using it or anything is unclear, please ask for -[help!](https://github.com/dwyl/fields/issues) +> as **beginner-friendly** as possible.
+> If you get stuck using it or anything is unclear, please ask for +> [help!](https://github.com/dwyl/fields/issues) # _How_? βœ… Start using **`Fields`** in your Phoenix App today with these 3 easy steps: - ## 1. Add the `fields` hex package to `deps` in `mix.exs` πŸ“¦ Add the `fields` package to your list of dependencies in your `mix.exs` file: @@ -78,7 +78,7 @@ Add the `fields` package to your list of dependencies in your `mix.exs` file: ```elixir def deps do [ - {:fields, "~> 2.9.1"} + {:fields, "~> 2.10.0"} ] end ``` @@ -86,7 +86,6 @@ end Once you have saved the `mix.exs` file, run **`mix deps.get`** in your terminal to download. - ## 2. Ensure you have the necessary environment variables πŸ”‘ In order to use Encryption and Hashing, @@ -103,18 +102,16 @@ If you need to create a secure `SECRET_KEY_BASE` value, please see: And for `ENCRYPTION_KEYS`, see: [How to create encryption keys](https://github.com/dwyl/phoenix-ecto-encryption-example#how-to-generate-aes-encryption-keys) - > In our case we use a **`.env`** file -to manage our environment variables. -See: -[github.com/dwyl/**learn-environment-variables**](https://git.io/JeMLg)
-This allows us to securely manage our secret keys in dev -without the risk of accidentally publishing them on Github.
-When we _deploy_ our Apps, we use our service provider's -built-in key management service to securely store Environment Variables. -e.g: -[Environment Variables on Heroku](https://github.com/dwyl/learn-environment-variables#environment-variables-on-heroku) - +> to manage our environment variables. +> See: +> [github.com/dwyl/**learn-environment-variables**](https://git.io/JeMLg)
+> This allows us to securely manage our secret keys in dev +> without the risk of accidentally publishing them on Github.
+> When we _deploy_ our Apps, we use our service provider's +> built-in key management service to securely store Environment Variables. +> e.g: +> [Environment Variables on Heroku](https://github.com/dwyl/learn-environment-variables#environment-variables-on-heroku) ## 3. Apply the relevant field(s) to your schema πŸ“ @@ -183,39 +180,39 @@ e.g: ## Available `Fields` πŸ“– -+ [`Address`](lib/address.ex) - an address for a physical location. -Validated and stored as a (`plaintext`) `String`. -+ [`AddressEncrypted`](lib/address_encrypted.ex) - an address for a customer -or user which should be stored encrypted for data protection. -+ [`DescriptionPlaintextUnlimited`](lib/description_plaintext_unlimited.ex) - -filters any HTML/JS to avoid security issues. Perfect for blog post comments. -+ [`Encrypted`](lib/encrypted.ex) - a general purpose encrypted field. +- [`Address`](lib/address.ex) - an address for a physical location. + Validated and stored as a (`plaintext`) `String`. +- [`AddressEncrypted`](lib/address_encrypted.ex) - an address for a customer + or user which should be stored encrypted for data protection. +- [`DescriptionPlaintextUnlimited`](lib/description_plaintext_unlimited.ex) - + filters any HTML/JS to avoid security issues. Perfect for blog post comments. +- [`Encrypted`](lib/encrypted.ex) - a general purpose encrypted field. converts any type of data `to_string` and then encrypts it. -+ [`EmailEncrypted`](lib/email_encrypted.ex) - validate and strongly encrypt -email address to ensure they are kept private and secure. -+ [`EmailHash`](lib/email_hash.ex) - when an email needs to be looked up fast -without decrypting. Salted and hashed with `:sha256`. -+ [`EmailPlaintext`](lib/email_plaintext.ex) - when an email address is `public` -there's no advantage to encrypting it. e.g. a customer support email. -+ [`Hash`](lib/hash.ex) - a general-purpose hash field using `:sha256`, -useful if you need to store the hash of a value. (_one way_) -+ [`HtmlBody`](lib/html-body.ex) - useful for storing HTML data e.g in a CMS. -+ [`Name`](lib/html-body.ex) - used for personal names -that need to be kept private/secure. Max length 35 characters. AES Encrypted. -+ [`Password`](lib/password.ex) - passwords hashed using `argon2`. -+ [`PhoneNumberEncrypted`](lib/phone_number_encrypted.ex) - a phone number that should be kept private gets validated and encrypted. -+ [`PhoneNumber`](lib/phone_number.ex) - when a phone number is _not_ -sensitive information and can be stored in plaintext. -+ [`Postcode`](lib/postcode.ex) - validated postcode stored as `plaintext`. -+ [`PostcodeEncrypted`](lib/postcode_encrypted.ex) - validated and encrypted. -+ [`Url`](lib/url.ex) - validate a URL and store as `plaintext` -(_not encrypted_) `String` -+ [`UrlEncrypted`](lib/url_encrypted.ex) - validate a URL and store as AES _encrypted_ `Binary` -+ [`IpAddressPlaintext`](lib/ip_address_plaintext.ex) - validate an ipv4 and ipv6 address and store as `plaintext` -+ [`IpAddressHash`](lib/ip_address_hash.ex) - hash for ipv4 or ipv6 -+ [`IpAddressEncrypted`](lib/ip_address_encrypted.ex) - validate an ipv4 and ipv6 address and store as AES _encrypted_ `Binary` - -***Detailed documentation*** available on **HexDocs**: +- [`EmailEncrypted`](lib/email_encrypted.ex) - validate and strongly encrypt + email address to ensure they are kept private and secure. +- [`EmailHash`](lib/email_hash.ex) - when an email needs to be looked up fast + without decrypting. Salted and hashed with `:sha256`. +- [`EmailPlaintext`](lib/email_plaintext.ex) - when an email address is `public` + there's no advantage to encrypting it. e.g. a customer support email. +- [`Hash`](lib/hash.ex) - a general-purpose hash field using `:sha256`, + useful if you need to store the hash of a value. (_one way_) +- [`HtmlBody`](lib/html-body.ex) - useful for storing HTML data e.g in a CMS. +- [`Name`](lib/html-body.ex) - used for personal names + that need to be kept private/secure. Max length 35 characters. AES Encrypted. +- [`Password`](lib/password.ex) - passwords hashed using `argon2`. +- [`PhoneNumberEncrypted`](lib/phone_number_encrypted.ex) - a phone number that should be kept private gets validated and encrypted. +- [`PhoneNumber`](lib/phone_number.ex) - when a phone number is _not_ + sensitive information and can be stored in plaintext. +- [`Postcode`](lib/postcode.ex) - validated postcode stored as `plaintext`. +- [`PostcodeEncrypted`](lib/postcode_encrypted.ex) - validated and encrypted. +- [`Url`](lib/url.ex) - validate a URL and store as `plaintext` + (_not encrypted_) `String` +- [`UrlEncrypted`](lib/url_encrypted.ex) - validate a URL and store as AES _encrypted_ `Binary` +- [`IpAddressPlaintext`](lib/ip_address_plaintext.ex) - validate an ipv4 and ipv6 address and store as `plaintext` +- [`IpAddressHash`](lib/ip_address_hash.ex) - hash for ipv4 or ipv6 +- [`IpAddressEncrypted`](lib/ip_address_encrypted.ex) - validate an ipv4 and ipv6 address and store as AES _encrypted_ `Binary` + +**_Detailed documentation_** available on **HexDocs**: [hexdocs.pm/**fields**](https://hexdocs.pm/fields)
@@ -226,7 +223,6 @@ sensitive information and can be stored in plaintext. mix t ``` - ### _Coverage_ ``` @@ -242,7 +238,6 @@ please open an issue so we can add it!
-
## Background / Further Reading πŸ”— @@ -255,7 +250,6 @@ If you are rusty/new on Binaries in Elixir, take a look at this post by @blackode:
https://medium.com/blackode/playing-with-elixir-binaries-strings-dd01a40039d5 - # Questions? If you have questions, please open an issue: @@ -270,23 +264,23 @@ and [`EmailHash`](https://github.com/dwyl/fields/blob/main/lib/email_hash.ex) serve very different purposes. Briefly: -with +with [**encryption**](https://en.wikipedia.org/wiki/Encryption) -the output is **_always_ different** +the output is **_always_ different** is meant for safely storing sensitive data that we want to **_decrypt_** later -whereas with +whereas with [**hash**](https://en.wikipedia.org/wiki/Hash_function) the output is **_always_ the same** -it cannot be "unhashed" but +it cannot be "unhashed" but can be used to -[***check***](https://en.wikipedia.org/wiki/Checksum) a value, +[**_check_**](https://en.wikipedia.org/wiki/Checksum) a value, i.e. you can lookup a _hashed_ value in a database. -The best way to understand how these work -is to see it for yourself. -Start an -[`IEx`](https://hexdocs.pm/iex/1.1.1/IEx.html) +The best way to understand how these work +is to see it for yourself. +Start an +[`IEx`](https://hexdocs.pm/iex/1.1.1/IEx.html) session in your terminal: ```sh @@ -302,6 +296,7 @@ Compiling 23 files (.ex) Generated fields app Interactive Elixir (1.12.3) - press Ctrl+C to exit (type h() ENTER for help) ``` + That confirms the `fields` module has compiled. #### Encryption @@ -321,18 +316,18 @@ iex(2)> encrypted = Fields.AES.encrypt(email) 80, 194, 241, 31, 28, 24, 155, 172, 208, 185, 142, 64, 65, 127>> ``` -> **Note**: the `Fields.EmailEncrypted` -uses the `AES.encrypt/1` behind the scenes, -that's why we are using it here directly. -You could just as easily have written: -`{:ok, encrypted} = Fields.EmailEncrypted.dump(email)` -this is just a shorthand. +> **Note**: the `Fields.EmailEncrypted` +> uses the `AES.encrypt/1` behind the scenes, +> that's why we are using it here directly. +> You could just as easily have written: +> `{:ok, encrypted} = Fields.EmailEncrypted.dump(email)` +> this is just a shorthand. -That output `<<48, 48, 48 ... 64, 65, 127>>` is a +That output `<<48, 48, 48 ... 64, 65, 127>>` is a [**bitstring**](https://elixir-lang.org/getting-started/binaries-strings-and-char-lists.html#bitstrings) which is the sequence of bits in memory. -The encrypted data - usually called -["ciphertext"](https://en.wikipedia.org/wiki/Ciphertext) - +The encrypted data - usually called +["ciphertext"](https://en.wikipedia.org/wiki/Ciphertext) - is not human readable, that's a feature. But if you want to _decrypt_ it back to its human-readable form, simply run: @@ -346,13 +341,13 @@ iex(3)> decrypted = Fields.AES.decrypt(encrypted) So we know that an encrypted value can be decrypted. In the case of `EmailEncrypted` this is useful when we want to send someone an email message. -For security/privacy, +For security/privacy, we want their sensitive personal data to be stored _encrypted_ in the Database, but when we need to decrypt it to send them a message, it's easy enough. -If you run the `Fields.AES.encrypt/1` function +If you run the `Fields.AES.encrypt/1` function multiple times in your terminal, you will _always_ see different output: @@ -375,24 +370,22 @@ iex(7)> Fields.AES.encrypt(email) 235, 162, 186, 132, 23, 34, 174, 171, 157, 115, 54, 211, 124, 247, ...>> ``` -The first 4 bytes `<<48, 48, 48, 49,` are the same +The first 4 bytes `<<48, 48, 48, 49,` are the same because we are using the same encryption key. -But the rest is _always_ different. - +But the rest is _always_ different. #### Hashing -A `hash` function -can be used to map data of arbitrary size +A `hash` function +can be used to map data of arbitrary size to fixed-size values. -i.e. _any_ length of `plaintext` will +i.e. _any_ length of `plaintext` will result in the _same_ length `hash` _value_. A `hash` function is _one-way_, it cannot be reversed or "un-hashed". The `hash` _value_ is _always_ the same for a given string of plaintext. - Try it in `IEx`: ```elixir @@ -410,11 +403,11 @@ iex(4)> Fields.Helpers.hash(:sha256, email) 17, 97, 146, 103, 115, 3, 185, 230, 137, 218, 137, 209, 111, 48, 236>> ``` -The hash _value_ is identical for the given input text +The hash _value_ is identical for the given input text in this case the email address `"alex@gmail.com"`. If you use the `Fields.EmailHash.dump/1` function, -you will see the same hash value +you will see the same hash value (_because the same helper function is invoked_): ```elixir @@ -430,13 +423,12 @@ iex(6)> Fields.EmailHash.dump(email) When the `EmailHash` is stored in a database we can lookup an email address by hashing it -and comparing it to the list. +and comparing it to the list. -The best way of _visualizing_ this -is to convert the hash value (bitstring) +The best way of _visualizing_ this +is to convert the hash value (bitstring) to `base64` so that it is _human-readable_: - ```elixir iex(1)> email = "alex@gmail.com" "alex@gmail.com" @@ -450,14 +442,13 @@ iex(3)> Fields.Helpers.hash(:sha256, email) |> :base64.encode Imagine you have a database table called `people` that has just 3 columns: `id`, `email_hash` and `email_encrypted` +| `id` | `email_hash` | `email_encrypted` | +| :--: | -------------------------------------------- | -------------------------------------- | +| 1 | X/v7zLU77wTawSMU34PbZR4RYZJncwO55onaidFvMOw= | MDAwMc57Y1j0nhwOdw7EvNeUVEfYQoAr7aT6oX | +| 2 | +zXMhia/Z2I64nul6pqoDZTVM1q2K21Pby6GtPcm9iE= | MDAwMXnS1uwGN/cZRFkQgArm2Sbj9y+hnUJIS7 | +| 3 | maY4IxoRSOSqm6qyJDrnEN1JQssJRqRGhzwOown4DPU= | MDAwMa4v0FBko++zqfAkfisXOLosQfrDLAdPax | -| `id` | `email_hash` | `email_encrypted` | -|:-----:| ------------- | ----------------- | -| 1 | X/v7zLU77wTawSMU34PbZR4RYZJncwO55onaidFvMOw= | MDAwMc57Y1j0nhwOdw7EvNeUVEfYQoAr7aT6oX | -| 2 | +zXMhia/Z2I64nul6pqoDZTVM1q2K21Pby6GtPcm9iE= | MDAwMXnS1uwGN/cZRFkQgArm2Sbj9y+hnUJIS7 | -| 3 | maY4IxoRSOSqm6qyJDrnEN1JQssJRqRGhzwOown4DPU= | MDAwMa4v0FBko++zqfAkfisXOLosQfrDLAdPax | - -With this "database" table, +With this "database" table, we can now _lookup_ an email address to find out their `id`: ```elixir @@ -471,22 +462,22 @@ therefore **Alice's** `id` is `2` in the database. \ No newline at end of file +--> diff --git a/mix.exs b/mix.exs index f1a0a48..11498b9 100644 --- a/mix.exs +++ b/mix.exs @@ -5,7 +5,7 @@ defmodule Fields.MixProject do [ app: :fields, description: "A collection of useful fields for building Phoenix apps faster!", - version: "2.9.1", + version: "2.10.0", elixir: ">= 1.10.0", start_permanent: Mix.env() == :prod, deps: deps(), @@ -19,7 +19,7 @@ defmodule Fields.MixProject do coveralls: :test, "coveralls.json": :test, "coveralls.html": :test, - t: :test, + t: :test ] ] end @@ -49,7 +49,6 @@ defmodule Fields.MixProject do {:envar, "~> 1.0.8"}, # strip noise from html field {:html_sanitize_ex, "~> 1.4.2"}, - # stream_data for property based testing {:stream_data, "~> 0.5.0", only: :test}, @@ -76,7 +75,7 @@ defmodule Fields.MixProject do defp aliases do [ c: ["coveralls.html"], - t: ["test"], + t: ["test"] ] end end diff --git a/mix.lock b/mix.lock index 378e09c..306e90e 100644 --- a/mix.lock +++ b/mix.lock @@ -1,6 +1,6 @@ %{ "argon2_elixir": {:hex, :argon2_elixir, "3.0.0", "fd4405f593e77b525a5c667282172dd32772d7c4fa58cdecdaae79d2713b6c5f", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "8b753b270af557d51ba13fcdebc0f0ab27a2a6792df72fd5a6cf9cfaffcedc57"}, - "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"}, + "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"}, "certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"}, "comeonin": {:hex, :comeonin, "5.3.3", "2c564dac95a35650e9b6acfe6d2952083d8a08e4a89b93a481acb552b325892e", [:mix], [], "hexpm", "3e38c9c2cb080828116597ca8807bb482618a315bfafd98c90bc22a821cc84df"}, "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"}, From 42adfccd8abd295fc70b3bca906fe1882c8b1f97 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 18 Oct 2022 12:08:22 +0100 Subject: [PATCH 2/9] clarify GPL license in mix.exs --- mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 11498b9..c9395d3 100644 --- a/mix.exs +++ b/mix.exs @@ -64,7 +64,7 @@ defmodule Fields.MixProject do defp package do [ maintainers: ["dwyl"], - licenses: ["GNU GPL v2.0"], + licenses: ["GPL-2.0-or-later"], links: %{github: "https://github.com/dwyl/fields"}, files: ~w(lib LICENSE mix.exs README.md .formatter.exs) ] From 2bec12514c8296e731781489f5e62d31bb6da331 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 18 Oct 2022 12:24:42 +0100 Subject: [PATCH 3/9] downgrade required version of ecto to ensure phoenix compatibility #116 https://github.com/dwyl/learn-elixir/issues/182#issuecomment-1282219179 --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 306e90e..c6a2a3b 100644 --- a/mix.lock +++ b/mix.lock @@ -5,7 +5,7 @@ "comeonin": {:hex, :comeonin, "5.3.3", "2c564dac95a35650e9b6acfe6d2952083d8a08e4a89b93a481acb552b325892e", [:mix], [], "hexpm", "3e38c9c2cb080828116597ca8807bb482618a315bfafd98c90bc22a821cc84df"}, "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"}, "earmark_parser": {:hex, :earmark_parser, "1.4.28", "0bf6546eb7cd6185ae086cbc5d20cd6dbb4b428aad14c02c49f7b554484b4586", [:mix], [], "hexpm", "501cef12286a3231dc80c81352a9453decf9586977f917a96e619293132743fb"}, - "ecto": {:hex, :ecto, "3.9.1", "67173b1687afeb68ce805ee7420b4261649d5e2deed8fe5550df23bab0bc4396", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c80bb3d736648df790f7f92f81b36c922d9dd3203ca65be4ff01d067f54eb304"}, + "ecto": {:hex, :ecto, "3.4.6", "08f7afad3257d6eb8613309af31037e16c36808dfda5a3cd0cb4e9738db030e4", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6f13a9e2a62e75c2dcfc7207bfc65645ab387af8360db4c89fee8b5a4bf3f70b"}, "elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"}, "envar": {:hex, :envar, "1.0.9", "b51976b00035efd254c3f51ee7f3cf2e22f91350ef104da393d1d71286eb4fdc", [:mix], [], "hexpm", "bfc3a73f97910c744e0d9e53722ad2d1f73bbb392d2dd1cac63e0af27776fde3"}, "ex_doc": {:hex, :ex_doc, "0.28.6", "2bbd7a143d3014fc26de9056793e97600ae8978af2ced82c2575f130b7c0d7d7", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "bca1441614654710ba37a0e173079273d619f9160cbcc8cd04e6bd59f1ad0e29"}, From 46bb8e7bb2d860ef587acd0612cc3bee4892ad09 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 18 Oct 2022 12:25:25 +0100 Subject: [PATCH 4/9] bump version to 2.10.1 to lower the required version of ecto #116 --- mix.exs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index c9395d3..2414af0 100644 --- a/mix.exs +++ b/mix.exs @@ -5,7 +5,7 @@ defmodule Fields.MixProject do [ app: :fields, description: "A collection of useful fields for building Phoenix apps faster!", - version: "2.10.0", + version: "2.10.1", elixir: ">= 1.10.0", start_permanent: Mix.env() == :prod, deps: deps(), @@ -44,7 +44,9 @@ defmodule Fields.MixProject do # password hashing {:argon2_elixir, "~> 3.0.0"}, # ecto types - {:ecto, "~> 3.8"}, + # require older version of ecto for Phoenix compatibility ... + # + {:ecto, "~> 3.3"}, # Check/get Environment Variables: https://github.com/dwyl/envar {:envar, "~> 1.0.8"}, # strip noise from html field From 39688a8b6bcbadda44fbac4662a2f859098ce67a Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 18 Oct 2022 12:28:51 +0100 Subject: [PATCH 5/9] why does this this have to be so difficult?! #116 --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index c6a2a3b..306e90e 100644 --- a/mix.lock +++ b/mix.lock @@ -5,7 +5,7 @@ "comeonin": {:hex, :comeonin, "5.3.3", "2c564dac95a35650e9b6acfe6d2952083d8a08e4a89b93a481acb552b325892e", [:mix], [], "hexpm", "3e38c9c2cb080828116597ca8807bb482618a315bfafd98c90bc22a821cc84df"}, "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"}, "earmark_parser": {:hex, :earmark_parser, "1.4.28", "0bf6546eb7cd6185ae086cbc5d20cd6dbb4b428aad14c02c49f7b554484b4586", [:mix], [], "hexpm", "501cef12286a3231dc80c81352a9453decf9586977f917a96e619293132743fb"}, - "ecto": {:hex, :ecto, "3.4.6", "08f7afad3257d6eb8613309af31037e16c36808dfda5a3cd0cb4e9738db030e4", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6f13a9e2a62e75c2dcfc7207bfc65645ab387af8360db4c89fee8b5a4bf3f70b"}, + "ecto": {:hex, :ecto, "3.9.1", "67173b1687afeb68ce805ee7420b4261649d5e2deed8fe5550df23bab0bc4396", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c80bb3d736648df790f7f92f81b36c922d9dd3203ca65be4ff01d067f54eb304"}, "elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"}, "envar": {:hex, :envar, "1.0.9", "b51976b00035efd254c3f51ee7f3cf2e22f91350ef104da393d1d71286eb4fdc", [:mix], [], "hexpm", "bfc3a73f97910c744e0d9e53722ad2d1f73bbb392d2dd1cac63e0af27776fde3"}, "ex_doc": {:hex, :ex_doc, "0.28.6", "2bbd7a143d3014fc26de9056793e97600ae8978af2ced82c2575f130b7c0d7d7", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "bca1441614654710ba37a0e173079273d619f9160cbcc8cd04e6bd59f1ad0e29"}, From 0620ad9a9de15d20b8723793e794246a41d9e3db Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 18 Oct 2022 14:37:30 +0100 Subject: [PATCH 6/9] update build badge from Travis-CI to GitHub Actions https://github.com/dwyl/learn-travis/issues/67 --- README.md | 2 +- lib/aes.ex | 20 +++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 0970fa2..a01253c 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ with built-in validation, testing, sanitising and encryption. See below for examples! --> -[![Build Status](https://img.shields.io/travis/dwyl/fields/master.svg?style=flat-square)](https://travis-ci.org/dwyl/fields) +![GitHub Workflow Status](https://img.shields.io/github/workflow/status/dwyl/fields/Elixir%20CI?label=build&style=flat-square) [![codecov.io](https://img.shields.io/codecov/c/github/dwyl/fields/master.svg?style=flat-square)](http://codecov.io/github/dwyl/fields?branch=master) [![Hex.pm](https://img.shields.io/hexpm/v/fields?color=brightgreen&style=flat-square)](https://hex.pm/packages/fields) [![docs](https://img.shields.io/badge/docs-maintained-brightgreen?style=flat-square)](https://hexdocs.pm/fields/api-reference.html) diff --git a/lib/aes.ex b/lib/aes.ex index 77c8207..9ca45a3 100644 --- a/lib/aes.ex +++ b/lib/aes.ex @@ -9,8 +9,7 @@ defmodule Fields.AES do # Use AES 256 Bit Keys for Encryption. @aad "AES256GCM" @doc """ - Encrypt Using AES Galois/Counter Mode (GCM) - https://en.wikipedia.org/wiki/Galois/Counter_Mode + Encrypt Using AES GCM. Uses a random IV for each call, and prepends the IV and Tag to the ciphertext. This means that `encrypt/1` will never return the same ciphertext for the same value. This makes "cracking" (bruteforce decryption) much harder! @@ -33,7 +32,10 @@ defmodule Fields.AES do # get *specific* key (by id) from list of keys. key_id = get_key_id() key = get_key(key_id) - {ciphertext, tag} = :crypto.crypto_one_time_aead(:aes_256_gcm, key, iv, to_string(plaintext), @aad, true) + + {ciphertext, tag} = + :crypto.crypto_one_time_aead(:aes_256_gcm, key, iv, to_string(plaintext), @aad, true) + # 1 >> "0001" key_id_str = String.pad_leading(to_string(key_id), 4, "0") # "return" key_id_str with the iv, cipher tag & ciphertext @@ -87,11 +89,11 @@ defmodule Fields.AES do # consider optimizing if benchmarking shows it is. defp fetch_keys() do Envar.get("ENCRYPTION_KEYS", Application.fetch_env!(:fields, :encryption_keys)) - # remove single-quotes around key list in .env - |> String.replace("'", "") - # Β split the CSV list of keys - |> String.split(",") - # decode the keys - |> Enum.map(fn key -> :base64.decode(key) end) + # remove single-quotes around key list in .env + |> String.replace("'", "") + # Β split the CSV list of keys + |> String.split(",") + # decode the keys + |> Enum.map(fn key -> :base64.decode(key) end) end end From 810d070e002abcddf89e6cafc8a95586b28e22f4 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 18 Oct 2022 15:20:42 +0100 Subject: [PATCH 7/9] replace :aes_256_gcm with :aes_gcm #118 --- README.md | 2 +- lib/aes.ex | 5 +++-- mix.exs | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a01253c..29965bf 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ Add the `fields` package to your list of dependencies in your `mix.exs` file: ```elixir def deps do [ - {:fields, "~> 2.10.0"} + {:fields, "~> 2.10.2"} ] end ``` diff --git a/lib/aes.ex b/lib/aes.ex index 9ca45a3..d79ebe0 100644 --- a/lib/aes.ex +++ b/lib/aes.ex @@ -6,8 +6,9 @@ defmodule Fields.AES do this makes "bruteforce" decryption much more difficult. See `encrypt/1` and `decrypt/1` for more details. """ - # Use AES 256 Bit Keys for Encryption. + # Use AES 256 Bit Keys for Encryption. (aad = "Associated Authenticated Data") @aad "AES256GCM" + @cipher :aes_gcm @doc """ Encrypt Using AES GCM. Uses a random IV for each call, and prepends the IV and Tag to the @@ -34,7 +35,7 @@ defmodule Fields.AES do key = get_key(key_id) {ciphertext, tag} = - :crypto.crypto_one_time_aead(:aes_256_gcm, key, iv, to_string(plaintext), @aad, true) + :crypto.crypto_one_time_aead(@cipher, key, iv, to_string(plaintext), @aad, true) # 1 >> "0001" key_id_str = String.pad_leading(to_string(key_id), 4, "0") diff --git a/mix.exs b/mix.exs index 2414af0..d9150ca 100644 --- a/mix.exs +++ b/mix.exs @@ -5,7 +5,7 @@ defmodule Fields.MixProject do [ app: :fields, description: "A collection of useful fields for building Phoenix apps faster!", - version: "2.10.1", + version: "2.10.2", elixir: ">= 1.10.0", start_permanent: Mix.env() == :prod, deps: deps(), From b645bc6c30a87d711efd68473f34582ea124760e Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 18 Oct 2022 16:18:19 +0100 Subject: [PATCH 8/9] add regression test for #118 --- README.md | 2 +- lib/aes.ex | 2 +- test/aes_test.exs | 7 +++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 29965bf..9ec685f 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ Add the `fields` package to your list of dependencies in your `mix.exs` file: ```elixir def deps do [ - {:fields, "~> 2.10.2"} + {:fields, "~> 2.10.3"} ] end ``` diff --git a/lib/aes.ex b/lib/aes.ex index d79ebe0..9e8d6ab 100644 --- a/lib/aes.ex +++ b/lib/aes.ex @@ -8,7 +8,7 @@ defmodule Fields.AES do """ # Use AES 256 Bit Keys for Encryption. (aad = "Associated Authenticated Data") @aad "AES256GCM" - @cipher :aes_gcm + @cipher :aes_256_gcm @doc """ Encrypt Using AES GCM. Uses a random IV for each call, and prepends the IV and Tag to the diff --git a/test/aes_test.exs b/test/aes_test.exs index 14f1a24..8cacc71 100644 --- a/test/aes_test.exs +++ b/test/aes_test.exs @@ -33,4 +33,11 @@ defmodule Fields.AESTest do plaintext = "hello" |> AES.encrypt() |> AES.decrypt() assert plaintext == "hello" end + + test "encrypt/1 auth regression test #118" do + email = "admin@dwylauth.com" + ciphertext = email |> AES.encrypt() + plaintext = ciphertext |> AES.decrypt() + assert plaintext == email + end end From 4d5db2b4ba4267a792f81601e0878e4cf184f7f7 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 18 Oct 2022 16:27:56 +0100 Subject: [PATCH 9/9] bump version to 2.10.3 #118 --- mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index d9150ca..6939ae9 100644 --- a/mix.exs +++ b/mix.exs @@ -5,7 +5,7 @@ defmodule Fields.MixProject do [ app: :fields, description: "A collection of useful fields for building Phoenix apps faster!", - version: "2.10.2", + version: "2.10.3", elixir: ">= 1.10.0", start_permanent: Mix.env() == :prod, deps: deps(),