diff --git a/.circleci/config.yml b/.circleci/config.yml index 89f74e8..d7bc92e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2 jobs: build: docker: - - image: grappigpanda/elixir-libsodium:1.13_otp24 + - image: cimg/elixir:1.13.4-erlang-24.3 environment: MIX_ENV: test @@ -19,6 +19,7 @@ jobs: - v1-build-cache-{{ .Branch }} - v1-build-cache + - run: mix local.hex --force - run: mix do deps.get --only test, compile - run: mix test - run: mix credo diff --git a/.tool-versions b/.tool-versions index aabe162..a167df6 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ elixir 1.13 -erlang 24.1 +erlang 24.3.4.14 diff --git a/README.md b/README.md index f1fe935..965d05c 100644 --- a/README.md +++ b/README.md @@ -25,15 +25,12 @@ use Paseto in [an insecure way](https://auth0.com/blog/critical-vulnerabilities- ## Considerations for using this library -There are a few library/binary requirements required in order for the Paseto +There are a few library/binary requirements required in order for the Paseto library to work on any computer: -1. Erlang version >= 20.1 +1. Erlang version >= 24.3 * This is required because this was the first Erlang version to introduce crypto:sign/5. -2. libsodium >= 1.0.13 - * This is required for cryptography used in Paseto. - * This can be found at https://github.com/jedisct1/libsodium -3. openssl >= 1.1 +2. openssl >= 1.1 * This is needed for XChaCha-Poly1305 used for V2.Local Paseto ## Want to use this library through Guardian or Plugs? @@ -72,8 +69,8 @@ This decodes to: ``` * Key used in this example (hex-encoded): ``` - 707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f - ``` + 707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f + ``` * Footer: ``` Paragon Initiative Enterprises @@ -112,27 +109,26 @@ To learn what each version means, please see [this page in the documentation](ht ### Generating a token ```elixir -iex> {:ok, pk, sk} = Salty.Sign.Ed25519.keypair() -iex> keypair = {pk, sk} -iex> token = Paseto.generate_token("v2", "public", "This is a test message", keypair) +iex> {:ok, sk, _pk} = Paseto.Crypto.Ed25519.generate_keypair() +iex> token = Paseto.generate_token("v2", "public", "This is a test message", sk) "v2.public.VGhpcyBpcyBhIHRlc3QgbWVzc2FnZSe-sJyD2x_fCDGEUKDcvjU9y3jRHxD4iEJ8iQwwfMUq5jUR47J15uPbgyOmBkQCxNDydR0yV1iBR-GPpyE-NQw" ``` -In short, we generate a keypair using [libsalty2](https://github.com/Ianleeclark/libsalty2) (libsodium elixir bindings) and generate the token using that keypair. +In short, first we generate a keypair and then a token using the secret key. P.S. If you're confused about how to serialize the above keys, you can use functions from the [`Base`](https://hexdocs.pm/elixir/Base.html) module: ```elixir -iex> {:ok, pk, sk} = Salty.Sign.Ed25519.keypair() -iex> pk |> Base.encode16(case: :lower) +iex> {:ok, sk, pk} = Paseto.Crypto.Ed25519.generate_keypair() +iex> Base.encode16(pk, case: :lower) "a17c258ffdd864b3614bd445465ff96e0b16e8509e28e7ba60734f7c433ab7e8" ``` ### Parsing a token ```elixir iex> token = "v2.public.VGhpcyBpcyBhIHRlc3QgbWVzc2FnZSe-sJyD2x_fCDGEUKDcvjU9y3jRHxD4iEJ8iQwwfMUq5jUR47J15uPbgyOmBkQCxNDydR0yV1iBR-GPpyE-NQw" -iex> Paseto.parse_token(token, keypair) +iex> Paseto.parse_token(token, pk) {:ok, %Paseto.Token{ footer: nil, @@ -147,26 +143,7 @@ More info can be found in the [HexDocs][]. ## Installation -You need libsodium installed on your machine. - -```bash -# Installing on FreeBSD -$ cd /usr/ports/security/libsodium/ && make install clean - -# Installing on Ubuntu -$ sudo apt install libsodium-dev - -# Installing on Fedora -$ dnf install libsodium-devel - -# Redhat & Cent OS -$ yum install libsodium-devel - -# Installing on OSX -$ brew install libsodium -``` - -The package can be installed by adding `paseto` to your list of +The package can be installed by adding `paseto` to your list of dependencies in `mix.exs`: ```elixir diff --git a/lib/paseto.ex b/lib/paseto.ex index 5d61e3f..4f0a3e9 100644 --- a/lib/paseto.ex +++ b/lib/paseto.ex @@ -98,7 +98,7 @@ defmodule Paseto do * footer: An optional value, often used for storing keyIDs or other similar info. # Examples: - iex> {:ok, pk, sk} = Salty.Sign.Ed25519.keypair() + iex> {:ok, sk, pk} = Paseto.Crypto.Ed25519.generate_keypair() iex> token = generate_token("v2", "public", "This is a test message", sk) "v2.public.VGhpcyBpcyBhIHRlc3QgbWVzc2FnZSe-sJyD2x_fCDGEUKDcvjU9y3jRHxD4iEJ8iQwwfMUq5jUR47J15uPbgyOmBkQCxNDydR0yV1iBR-GPpyE-NQw" iex> Paseto.parse_token(token, pk) @@ -110,7 +110,8 @@ defmodule Paseto do version: "v2" }} """ - @spec generate_token(String.t(), String.t(), String.t(), binary, String.t()) :: String.t() | {:error, String.t()} + @spec generate_token(String.t(), String.t(), String.t(), binary, String.t()) :: + String.t() | {:error, String.t()} def generate_token(version, purpose, payload, secret_key, footer \\ "") do _generate_token(version, purpose, payload, secret_key, footer) end @@ -132,8 +133,9 @@ defmodule Paseto do try do {:ok, String.to_existing_atom("Elixir.Paseto.#{version}")} - rescue RuntimeError -> - {:error, "Invalid version selected. Only v1 & v2 supported."} + rescue + RuntimeError -> + {:error, "Invalid version selected. Only v1 & v2 supported."} end end end diff --git a/lib/paseto/crypto/ed25519.ex b/lib/paseto/crypto/ed25519.ex new file mode 100644 index 0000000..424e295 --- /dev/null +++ b/lib/paseto/crypto/ed25519.ex @@ -0,0 +1,36 @@ +defmodule Paseto.Crypto.Ed25519 do + @moduledoc """ + Wrap crypto erlang functions to work with ED25519 + """ + + @dialyzer {:nowarn_function, sign: 2} + @spec sign(binary(), binary()) :: {:ok, binary()} + def sign(message, private_key) when byte_size(private_key) == 32 do + {:ok, ^private_key, public_key} = seed_keypair(private_key) + signature = :public_key.sign(message, :none, {:ed_pri, :ed25519, public_key, private_key}) + {:ok, signature} + end + + @dialyzer {:nowarn_function, verify_detached: 3} + @spec verify_detached(binary(), binary(), binary()) :: :ok | {:error, :invalid_signature} + def verify_detached(message, signature, public_key) + when byte_size(signature) == 64 and byte_size(public_key) == 32 do + if :public_key.verify(message, :none, signature, {:ed_pub, :ed25519, public_key}) do + :ok + else + {:error, :invalid_signature} + end + end + + @spec seed_keypair(binary()) :: {:ok, binary(), binary()} + def seed_keypair(private_key) when byte_size(private_key) == 32 do + {pk, sk} = :crypto.generate_key(:eddsa, :ed25519, private_key) + {:ok, sk, pk} + end + + @spec generate_keypair() :: {:ok, binary(), binary()} + def generate_keypair do + {pk, sk} = :crypto.generate_key(:eddsa, :ed25519) + {:ok, sk, pk} + end +end diff --git a/lib/paseto/crypto/xchacha20_poly1305.ex b/lib/paseto/crypto/xchacha20_poly1305.ex new file mode 100644 index 0000000..e879b5a --- /dev/null +++ b/lib/paseto/crypto/xchacha20_poly1305.ex @@ -0,0 +1,101 @@ +defmodule Paseto.Crypto.XChaCha20Poly1305 do + @moduledoc """ + Implement XChaCha20Poly1305 encryption and decryption. + """ + + @spec encrypt(binary(), binary(), binary(), binary()) :: {:ok, binary()} + def encrypt(message, aad, <>, <>) do + # Perform the HChaCha20 operation to generate the subkey and nonce + {subkey, nonce} = xchacha20_subkey_and_nonce(key, iv) + + # Perform the ChaCha20 operation to encrypt the message + block_encrypt(subkey, nonce, {aad, message}) + end + + @spec decrypt(binary(), binary(), binary(), binary()) :: {:ok, binary()} + def decrypt(encrypted, aad, <>, <>) do + cipher_text_size = byte_size(encrypted) - 16 + <> = encrypted + + # Perform the HChaCha20 operation to generate the subkey and nonce + {subkey, nonce} = xchacha20_subkey_and_nonce(key, iv) + + # Perform the ChaCha20 operation to decrypt the message + block_decrypt(:chacha20_poly1305, subkey, nonce, {aad, cipher_text, cipher_tag}) + end + + defp xchacha20_subkey_and_nonce(key, <>) do + subkey = hchacha20(key, nonce0) + nonce = <<0::32, nonce1::64-bits>> + {subkey, nonce} + end + + defp hchacha20(key, nonce) do + # ChaCha20 has an internal blocksize of 512-bits (64-bytes). + # Let's use a Mask of random 64-bytes to blind the intermediate keystream. + mask = <> = :crypto.strong_rand_bytes(64) + + <> = + :crypto.crypto_one_time(:chacha20, key, nonce, mask, true) + + << + x00::32-unsigned-little-integer, + x01::32-unsigned-little-integer, + x02::32-unsigned-little-integer, + x03::32-unsigned-little-integer, + x12::32-unsigned-little-integer, + x13::32-unsigned-little-integer, + x14::32-unsigned-little-integer, + x15::32-unsigned-little-integer + >> = + :crypto.exor( + <>, + <> + ) + + ## The final step of ChaCha20 is `State2 = State0 + State1', so let's + ## recover `State1' with subtraction: `State1 = State2 - State0' + << + y00::32-unsigned-little-integer, + y01::32-unsigned-little-integer, + y02::32-unsigned-little-integer, + y03::32-unsigned-little-integer, + y12::32-unsigned-little-integer, + y13::32-unsigned-little-integer, + y14::32-unsigned-little-integer, + y15::32-unsigned-little-integer + >> = <<"expand 32-byte k", nonce::128-bits>> + + << + x00 - y00::32-unsigned-little-integer, + x01 - y01::32-unsigned-little-integer, + x02 - y02::32-unsigned-little-integer, + x03 - y03::32-unsigned-little-integer, + x12 - y12::32-unsigned-little-integer, + x13 - y13::32-unsigned-little-integer, + x14 - y14::32-unsigned-little-integer, + x15 - y15::32-unsigned-little-integer + >> + end + + defp block_encrypt(key, iv, {aad, payload}) do + {cipher_text, cipher_tag} = + :crypto.crypto_one_time_aead(:chacha20_poly1305, key, iv, payload, aad, true) + + {:ok, cipher_text <> cipher_tag} + catch + :error, :notsup -> raise_notsup() + end + + defp block_decrypt(cipher, key, iv, {aad, payload, tag}) do + plain = :crypto.crypto_one_time_aead(cipher, key, iv, payload, aad, tag, false) + {:ok, plain} + catch + :error, :notsup -> raise_notsup() + end + + defp raise_notsup do + raise "The algorithm chacha20_poly1305 is not supported by your Erlang/OTP installation. " <> + "Please make sure it was compiled with the correct OpenSSL/BoringSSL bindings" + end +end diff --git a/lib/paseto/utils/crypto.ex b/lib/paseto/utils/crypto.ex index daf91be..9ae2e91 100644 --- a/lib/paseto/utils/crypto.ex +++ b/lib/paseto/utils/crypto.ex @@ -1,7 +1,7 @@ defmodule Paseto.Utils.Crypto do @moduledoc false - alias Salty.Aead.Xchacha20poly1305Ietf + alias Paseto.Crypto.XChaCha20Poly1305 @doc """ AES-256 in counter mode for encrypting. Used for v1 local. @@ -43,8 +43,7 @@ defmodule Paseto.Utils.Crypto do def xchacha20_poly1305_encrypt(message, aad, nonce, key) when byte_size(nonce) == 24 and byte_size(key) == 32 do - # NOTE: nsec (the `nil` value here, isn't used in libsodium.) - Xchacha20poly1305Ietf.encrypt(message, aad, nil, nonce, key) + XChaCha20Poly1305.encrypt(message, aad, nonce, key) end @doc """ @@ -70,8 +69,7 @@ defmodule Paseto.Utils.Crypto do def xchacha20_poly1305_decrypt(message, aad, nonce, key) when byte_size(nonce) == 24 and byte_size(key) == 32 do - # NOTE: Again, `nsec` isn't used. - Xchacha20poly1305Ietf.decrypt(nil, message, aad, nonce, key) + XChaCha20Poly1305.decrypt(message, aad, nonce, key) rescue err -> {:error, "Decrypt failed due to #{inspect(err)}"} end diff --git a/lib/paseto/v2.ex b/lib/paseto/v2.ex index 0f2b5b2..f3f454f 100644 --- a/lib/paseto/v2.ex +++ b/lib/paseto/v2.ex @@ -14,7 +14,7 @@ defmodule Paseto.V2 do alias Paseto.Token alias Paseto.Utils alias Paseto.Utils.Crypto - alias Salty.Sign.Ed25519 + alias Paseto.Crypto.Ed25519 import Paseto.Utils, only: [b64_decode!: 1] @@ -74,15 +74,15 @@ defmodule Paseto.V2 do Handles signing the token for public use. # Examples: - iex> {:ok, pk, sk} = Salty.Sign.Ed25519.keypair() + iex> {:ok, sk, pk} = Paseto.Crypto.Ed25519.generate_keypair() iex> Paseto.V2.sign("Test Message", sk) "v2.public.VGVzdAJxQsXSrgYBkcwiOnWamiattqhhhNN_1jsY-LR_YbsoYpZ18-ogVSxWv7d8DlqzLSz9csqNtSzDk4y0JV5xaAE" """ @spec sign(String.t(), String.t(), String.t()) :: String.t() | {:error, String.t()} - def sign(data, secret_key, footer \\ "") when byte_size(secret_key) == 64 do + def sign(data, secret_key, footer \\ "") when byte_size(secret_key) == 32 do pre_auth_encode = Utils.pre_auth_encode([@header_public, data, footer]) - {:ok, sig} = Ed25519.sign_detached(pre_auth_encode, secret_key) + {:ok, sig} = Ed25519.sign(pre_auth_encode, secret_key) Utils.b64_encode_token(@header_public, data <> sig, footer) rescue @@ -93,7 +93,7 @@ defmodule Paseto.V2 do Handles verifying the signature belongs to the provided key. # Examples: - iex> {:ok, pk, sk} = Salty.Sign.Ed25519.keypair() + iex> {:ok, sk, pk} = Paseto.Crypto.Ed25519.generate_keypair() iex> Paseto.V2.sign("Test Message", sk) "v2.public.VGVzdAJxQsXSrgYBkcwiOnWamiattqhhhNN_1jsY-LR_YbsoYpZ18-ogVSxWv7d8DlqzLSz9csqNtSzDk4y0JV5xaAE" iex> Paseto.V2.verify("VGVzdAJxQsXSrgYBkcwiOnWamiattqhhhNN_1jsY-LR_YbsoYpZ18-ogVSxWv7d8DlqzLSz9csqNtSzDk4y0JV5xaAE", pk) @@ -109,7 +109,7 @@ defmodule Paseto.V2 do pre_auth_encode = Utils.pre_auth_encode([@header_public, data, decoded_footer]) - :ok = Ed25519.verify_detached(sig, pre_auth_encode, public_key) + :ok = Ed25519.verify_detached(pre_auth_encode, sig, public_key) {:ok, data} rescue _ -> {:error, "Failed to verify signature."} diff --git a/mix.exs b/mix.exs index cef760f..1b4df11 100644 --- a/mix.exs +++ b/mix.exs @@ -19,7 +19,7 @@ defmodule Paseto.MixProject do def application do [ - extra_applications: [:logger] + extra_applications: [:logger, :public_key] ] end @@ -28,7 +28,6 @@ defmodule Paseto.MixProject do {:credo, "~> 1.4", only: [:dev, :test], runtime: false}, {:hkdf, "~> 0.2.0"}, {:blake2, "~> 1.0"}, - {:libsalty2, "~> 0.3.0"}, {:ex_doc, "~> 0.19", only: :dev, runtime: false}, {:stream_data, "~> 0.5.0", only: :test} ] diff --git a/mix.lock b/mix.lock index cd06168..4e6a38a 100644 --- a/mix.lock +++ b/mix.lock @@ -1,22 +1,15 @@ %{ "blake2": {:hex, :blake2, "1.0.4", "8263c69a191142922bc2510f1ffc0de0ae96e8c3bd5e2ad3fac7e87aed94c8b1", [:mix], [], "hexpm", "e9f4120d163ba14d86304195e50745fa18483e6ad2be94c864ae449bbdd6a189"}, - "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"}, - "credo": {:hex, :credo, "1.6.4", "ddd474afb6e8c240313f3a7b0d025cc3213f0d171879429bf8535d7021d9ad78", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "c28f910b61e1ff829bffa056ef7293a8db50e87f2c57a9b5c3f57eee124536b7"}, - "earmark": {:hex, :earmark, "1.3.5", "0db71c8290b5bc81cb0101a2a507a76dca659513984d683119ee722828b424f6", [:mix], [], "hexpm", "762b999fd414fb41e297944228aa1de2cd4a3876a07f968c8b11d1e9a2190d07"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.25", "2024618731c55ebfcc5439d756852ec4e85978a39d0d58593763924d9a15916f", [:mix], [], "hexpm", "56749c5e1c59447f7b7a23ddb235e4b3defe276afc220a6227237f3efe83f51e"}, - "elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"}, - "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, - "ex_doc": {:hex, :ex_doc, "0.28.3", "6eea2f69995f5fba94cd6dd398df369fe4e777a47cd887714a0976930615c9e6", [: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", "05387a6a2655b5f9820f3f627450ed20b4325c25977b2ee69bed90af6688e718"}, - "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, + "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, + "credo": {:hex, :credo, "1.7.6", "b8f14011a5443f2839b04def0b252300842ce7388f3af177157c86da18dfbeea", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "146f347fb9f8cbc5f7e39e3f22f70acbef51d441baa6d10169dd604bfbc55296"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, + "ex_doc": {:hex, :ex_doc, "0.33.0", "690562b153153c7e4d455dc21dab86e445f66ceba718defe64b0ef6f0bd83ba0", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "3f69adc28274cb51be37d09b03e4565232862a4b10288a3894587b0131412124"}, + "file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"}, "hkdf": {:hex, :hkdf, "0.2.0", "be3c4f88b3bbc565b75e039befb057de1eed8d794dd241bed264e53271e704c8", [:mix], [], "hexpm", "ab23e116b734b72a031d20cbf922ba424a0fb8ea824747edb060b0bb18b9eca6"}, - "jason": {:hex, :jason, "1.3.0", "fa6b82a934feb176263ad2df0dbd91bf633d4a46ebfdffea0c8ae82953714946", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "53fc1f51255390e0ec7e50f9cb41e751c260d065dcba2bf0d08dc51a4002c2ac"}, - "libsalty2": {:hex, :libsalty2, "0.3.0", "c83cd97f7096fc9104ee23c3ab22a9db55d48ca3fe028e6e6bfb8ec47fed9294", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "99cea3f717df672d518536fda7abadee692bac92a76d418db4683d5fb54c1f9a"}, - "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"}, - "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, - "nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"}, - "poison": {:hex, :poison, "4.0.1", "bcb755a16fac91cad79bfe9fc3585bb07b9331e50cfe3420a24bcc2d735709ae", [:mix], [], "hexpm"}, - "sality": {:git, "https://github.com/Ianleeclark/libsalty.git", "009058286e30ae12795c5751d1bc0dc347b0dac2", []}, - "salty": {:git, "https://github.com/Ianleeclark/libsalty.git", "009058286e30ae12795c5751d1bc0dc347b0dac2", []}, + "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, + "makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, + "makeup_erlang": {:hex, :makeup_erlang, "1.0.0", "6f0eff9c9c489f26b69b61440bf1b238d95badae49adac77973cbacae87e3c2e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "ea7a9307de9d1548d2a72d299058d1fd2339e3d398560a0e46c27dab4891e4d2"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "stream_data": {:hex, :stream_data, "0.5.0", "b27641e58941685c75b353577dc602c9d2c12292dd84babf506c2033cd97893e", [:mix], [], "hexpm", "012bd2eec069ada4db3411f9115ccafa38540a3c78c4c0349f151fc761b9e271"}, } diff --git a/test/fixtures/test_vectors/v2_public.exs b/test/fixtures/test_vectors/v2_public.exs index dfcd950..08af670 100644 --- a/test/fixtures/test_vectors/v2_public.exs +++ b/test/fixtures/test_vectors/v2_public.exs @@ -3,8 +3,7 @@ vector: "v2-S-1", token: "v2.public.eyJkYXRhIjoidGhpcyBpcyBhIHNpZ25lZCBtZXNzYWdlIiwiZXhwIjoiMjAxOS0wMS0wMVQwMDowMDowMCswMDowMCJ9HQr8URrGntTu7Dz9J2IF23d1M7-9lH9xiqdGyJNvzp4angPW5Esc7C5huy_M8I8_DjJK2ZXC2SUYuOFM-Q_5Cw", - private_key: - "b4cbfb43 df4ce210 727d953e 4a713307 fa19bb7d 9f850414 38d9e11b 942a3774 1eb9dbbb bc047c03 fd70604e 0071f098 7e16b28b 757225c1 1f00415d 0e20b1a2", + private_key: "b4cbfb43 df4ce210 727d953e 4a713307 fa19bb7d 9f850414 38d9e11b 942a3774", public_key: "1eb9dbbb bc047c03 fd70604e 0071f098 7e16b28b 757225c1 1f00415d 0e20b1a2", payload: ~s({"data":"this is a signed message","exp":"2019-01-01T00:00:00+00:00"}) ], @@ -12,8 +11,7 @@ vector: "v2-S-2", token: "v2.public.eyJkYXRhIjoidGhpcyBpcyBhIHNpZ25lZCBtZXNzYWdlIiwiZXhwIjoiMjAxOS0wMS0wMVQwMDowMDowMCswMDowMCJ9flsZsx_gYCR0N_Ec2QxJFFpvQAs7h9HtKwbVK2n1MJ3Rz-hwe8KUqjnd8FAnIJZ601tp7lGkguU63oGbomhoBw.eyJraWQiOiJ6VmhNaVBCUDlmUmYyc25FY1Q3Z0ZUaW9lQTlDT2NOeTlEZmdMMVc2MGhhTiJ9", - private_key: - "b4cbfb43 df4ce210 727d953e 4a713307 fa19bb7d 9f850414 38d9e11b 942a3774 1eb9dbbb bc047c03 fd70604e 0071f098 7e16b28b 757225c1 1f00415d 0e20b1a2", + private_key: "b4cbfb43 df4ce210 727d953e 4a713307 fa19bb7d 9f850414 38d9e11b 942a3774", public_key: "1eb9dbbb bc047c03 fd70604e 0071f098 7e16b28b 757225c1 1f00415d 0e20b1a2", payload: ~s({"data":"this is a signed message","exp":"2019-01-01T00:00:00+00:00"}), footer: ~s({"kid":"zVhMiPBP9fRf2snEcT7gFTioeA9COcNy9DfgL1W60haN"}) diff --git a/test/paseto/v2_test.exs b/test/paseto/v2_test.exs index 67047f7..b33cc78 100644 --- a/test/paseto/v2_test.exs +++ b/test/paseto/v2_test.exs @@ -4,7 +4,7 @@ defmodule Paseto.V2Test do alias Paseto.V2 alias Paseto.Utils alias Paseto.Token - alias Salty.Sign.Ed25519 + alias Paseto.Crypto.Ed25519 describe "Encryption/Decryption tests" do test "Simple encrypt/decrypt, footerless" do @@ -67,7 +67,7 @@ defmodule Paseto.V2Test do describe "Sign/Verify tests" do test "Simple sign/verify, footerless" do message = "Test Message" - {:ok, pk, sk} = Ed25519.keypair() + {:ok, sk, pk} = Ed25519.generate_keypair() {:ok, %Token{payload: signed_payload}} = message @@ -80,7 +80,7 @@ defmodule Paseto.V2Test do test "Simple sign/verify, with footer" do message = "Test Message" footer = "key-id:533434" - {:ok, pk, sk} = Ed25519.keypair() + {:ok, sk, pk} = Ed25519.generate_keypair() {:ok, %Token{payload: signed_payload, footer: encoded_footer}} = message @@ -92,8 +92,8 @@ defmodule Paseto.V2Test do test "Invalid PK fails to verify, footerless" do message = "Test Message" - {:ok, _pk1, sk1} = Ed25519.keypair() - {:ok, pk2, _sk2} = Ed25519.keypair() + {:ok, sk1, _pk1} = Ed25519.generate_keypair() + {:ok, _sk2, pk2} = Ed25519.generate_keypair() {:ok, %Token{payload: signed_payload}} = message @@ -106,8 +106,8 @@ defmodule Paseto.V2Test do test "Invalid PK fails to verify, with footer" do message = "Test Message" footer = "key-id:533434" - {:ok, _pk1, sk1} = Ed25519.keypair() - {:ok, pk2, _sk2} = Ed25519.keypair() + {:ok, sk1, _pk1} = Ed25519.generate_keypair() + {:ok, _sk2, pk2} = Ed25519.generate_keypair() {:ok, %Token{payload: signed_payload, footer: encoded_footer}} = message @@ -126,11 +126,10 @@ defmodule Paseto.V2Test do # $secretKey = AsymmetricSecretKey(new Version2()); # echo Base64UrlSafe::encodeUnpadded($secretKey->raw()); # => 9xCi52l6M59Lix7EvEZHqceBjD5-10R7n_jpc2P0BREUZzW4SfV8ft_LAs7gw0avSVvhWhns1rf6BbOERCP2XQ - encoded_secret_key = + encoded_keypair = "9xCi52l6M59Lix7EvEZHqceBjD5-10R7n_jpc2P0BREUZzW4SfV8ft_LAs7gw0avSVvhWhns1rf6BbOERCP2XQ" - secret_key = Utils.b64_decode!(encoded_secret_key) - public_key = Ed25519.sk_to_pk(secret_key) + <<_secret_key::256-bits, public_key::256-bits>> = Utils.b64_decode!(encoded_keypair) # $plaintext = "v2 public example" # $footer = "v2 public footer" diff --git a/test/paseto_test.exs b/test/paseto_test.exs index ca739f1..4c863f9 100644 --- a/test/paseto_test.exs +++ b/test/paseto_test.exs @@ -2,7 +2,7 @@ defmodule PasetoTest do use ExUnit.Case use ExUnitProperties - alias Salty.Sign.Ed25519 + alias Paseto.Crypto.Ed25519 @public_exponent 65_537 @@ -37,7 +37,7 @@ defmodule PasetoTest do {key, key} "public" -> - {:ok, pk, sk} = Ed25519.keypair() + {:ok, sk, pk} = Ed25519.generate_keypair() {pk, sk} end end