diff --git a/README.md b/README.md index 77c54a6..96ecb6b 100644 --- a/README.md +++ b/README.md @@ -463,17 +463,17 @@ EncryptedECDHES = jose_jwk:box_encrypt(AliceToBob, BobPublicJWK, AlicePrivateJWK - [X] `A128KW` - [X] `A192KW` - [X] `A256KW` -- [X] `C20PKW` [draft-amringer-jose-chacha](https://tools.ietf.org/html/draft-amringer-jose-chacha-01) +- [X] `C20PKW` [draft-amringer-jose-chacha](https://datatracker.ietf.org/doc/html/draft-amringer-jose-chacha-02) - [X] `dir` -- [X] `ECDH-1PU` -- [X] `ECDH-1PU+A128GCMKW` non-standard, [draft-madden-jose-ecdh-1pu](https://tools.ietf.org/html/draft-madden-jose-ecdh-1pu-02) -- [X] `ECDH-1PU+A192GCMKW` non-standard, [draft-madden-jose-ecdh-1pu](https://tools.ietf.org/html/draft-madden-jose-ecdh-1pu-02) -- [X] `ECDH-1PU+A256GCMKW` non-standard, [draft-madden-jose-ecdh-1pu](https://tools.ietf.org/html/draft-madden-jose-ecdh-1pu-02) -- [X] `ECDH-1PU+A128KW` [draft-madden-jose-ecdh-1pu](https://tools.ietf.org/html/draft-madden-jose-ecdh-1pu-02) -- [X] `ECDH-1PU+A192KW` [draft-madden-jose-ecdh-1pu](https://tools.ietf.org/html/draft-madden-jose-ecdh-1pu-02) -- [X] `ECDH-1PU+A256KW` [draft-madden-jose-ecdh-1pu](https://tools.ietf.org/html/draft-madden-jose-ecdh-1pu-02) -- [X] `ECDH-1PU+C20PKW` [draft-amringer-jose-chacha](https://tools.ietf.org/html/draft-amringer-jose-chacha-01), [draft-madden-jose-ecdh-1pu](https://tools.ietf.org/html/draft-madden-jose-ecdh-1pu-02) -- [X] `ECDH-1PU+XC20PKW` [draft-amringer-jose-chacha](https://tools.ietf.org/html/draft-amringer-jose-chacha-01), [draft-madden-jose-ecdh-1pu](https://tools.ietf.org/html/draft-madden-jose-ecdh-1pu-02) +- [X] `ECDH-1PU` non-standard, [draft-madden-jose-ecdh-1pu](https://datatracker.ietf.org/doc/html/draft-madden-jose-ecdh-1pu-04) +- [X] `ECDH-1PU+A128GCMKW` non-standard, [draft-madden-jose-ecdh-1pu](https://datatracker.ietf.org/doc/html/draft-madden-jose-ecdh-1pu-04) +- [X] `ECDH-1PU+A192GCMKW` non-standard, [draft-madden-jose-ecdh-1pu](https://datatracker.ietf.org/doc/html/draft-madden-jose-ecdh-1pu-04) +- [X] `ECDH-1PU+A256GCMKW` non-standard, [draft-madden-jose-ecdh-1pu](https://datatracker.ietf.org/doc/html/draft-madden-jose-ecdh-1pu-04) +- [X] `ECDH-1PU+A128KW` [draft-madden-jose-ecdh-1pu](https://datatracker.ietf.org/doc/html/draft-madden-jose-ecdh-1pu-04) +- [X] `ECDH-1PU+A192KW` [draft-madden-jose-ecdh-1pu](https://datatracker.ietf.org/doc/html/draft-madden-jose-ecdh-1pu-04) +- [X] `ECDH-1PU+A256KW` [draft-madden-jose-ecdh-1pu](https://datatracker.ietf.org/doc/html/draft-madden-jose-ecdh-1pu-04) +- [X] `ECDH-1PU+C20PKW` [draft-amringer-jose-chacha](https://datatracker.ietf.org/doc/html/draft-amringer-jose-chacha-02), [draft-madden-jose-ecdh-1pu](https://datatracker.ietf.org/doc/html/draft-madden-jose-ecdh-1pu-04) +- [X] `ECDH-1PU+XC20PKW` [draft-amringer-jose-chacha](https://datatracker.ietf.org/doc/html/draft-amringer-jose-chacha-02), [draft-madden-jose-ecdh-1pu](https://datatracker.ietf.org/doc/html/draft-madden-jose-ecdh-1pu-04) - [X] `ECDH-ES` - [X] `ECDH-ES+A128GCMKW` non-standard - [X] `ECDH-ES+A192GCMKW` non-standard @@ -481,8 +481,8 @@ EncryptedECDHES = jose_jwk:box_encrypt(AliceToBob, BobPublicJWK, AlicePrivateJWK - [X] `ECDH-ES+A128KW` - [X] `ECDH-ES+A192KW` - [X] `ECDH-ES+A256KW` -- [X] `ECDH-ES+C20PKW` [draft-amringer-jose-chacha](https://tools.ietf.org/html/draft-amringer-jose-chacha-01) -- [X] `ECDH-ES+XC20PKW` [draft-amringer-jose-chacha](https://tools.ietf.org/html/draft-amringer-jose-chacha-01) +- [X] `ECDH-ES+C20PKW` [draft-amringer-jose-chacha](https://datatracker.ietf.org/doc/html/draft-amringer-jose-chacha-02) +- [X] `ECDH-ES+XC20PKW` [draft-amringer-jose-chacha](https://datatracker.ietf.org/doc/html/draft-amringer-jose-chacha-02) - [X] `PBES2-HS256+A128GCMKW` non-standard - [X] `PBES2-HS384+A192GCMKW` non-standard - [X] `PBES2-HS512+A256GCMKW` non-standard @@ -494,7 +494,7 @@ EncryptedECDHES = jose_jwk:box_encrypt(AliceToBob, BobPublicJWK, AlicePrivateJWK - [X] `RSA1_5` - [X] `RSA-OAEP` - [X] `RSA-OAEP-256` -- [X] `XC20PKW` [draft-amringer-jose-chacha](https://tools.ietf.org/html/draft-amringer-jose-chacha-01) +- [X] `XC20PKW` [draft-amringer-jose-chacha](https://datatracker.ietf.org/doc/html/draft-amringer-jose-chacha-02) #### `"enc"` [RFC 7518 Section 5](https://tools.ietf.org/html/rfc7518#section-5) @@ -504,8 +504,8 @@ EncryptedECDHES = jose_jwk:box_encrypt(AliceToBob, BobPublicJWK, AlicePrivateJWK - [X] `A128GCM` - [X] `A192GCM` - [X] `A256GCM` -- [X] `C20P` [draft-amringer-jose-chacha](https://tools.ietf.org/html/draft-amringer-jose-chacha-01) -- [X] `XC20P` [draft-amringer-jose-chacha](https://tools.ietf.org/html/draft-amringer-jose-chacha-01) +- [X] `C20P` [draft-amringer-jose-chacha](https://datatracker.ietf.org/doc/html/draft-amringer-jose-chacha-02) +- [X] `XC20P` [draft-amringer-jose-chacha](https://datatracker.ietf.org/doc/html/draft-amringer-jose-chacha-02) #### `"zip"` [RFC 7518 Section 7.3](https://tools.ietf.org/html/rfc7518#section-7.3) @@ -535,6 +535,7 @@ EncryptedECDHES = jose_jwk:box_encrypt(AliceToBob, BobPublicJWK, AlicePrivateJWK - [X] `Ed448` [RFC 8037](https://tools.ietf.org/html/rfc8037), [RFC 8032](https://tools.ietf.org/html/rfc8032#section-5.2) - [X] `Ed448ph` [RFC 8037](https://tools.ietf.org/html/rfc8037), [RFC 8032](https://tools.ietf.org/html/rfc8032#section-5.2) - [X] `EdDSA` [RFC 8037](https://tools.ietf.org/html/rfc8037), [RFC 8032](https://tools.ietf.org/html/rfc8032) +- [X] `ES256K` [RFC 8812](https://datatracker.ietf.org/doc/html/rfc8812) - [X] `ES256` - [X] `ES384` - [X] `ES512` @@ -545,6 +546,7 @@ EncryptedECDHES = jose_jwk:box_encrypt(AliceToBob, BobPublicJWK, AlicePrivateJWK - [X] `PS256` - [X] `PS384` - [X] `PS512` +- [X] `RS1` deprecated, [RFC 8812](https://datatracker.ietf.org/doc/html/rfc8812) - [X] `RS256` - [X] `RS384` - [X] `RS512` diff --git a/lib/jose/jwk.ex b/lib/jose/jwk.ex index e0aa6d6..0b42ded 100644 --- a/lib/jose/jwk.ex +++ b/lib/jose/jwk.ex @@ -22,6 +22,10 @@ defmodule JOSE.JWK do def to_record(list) when is_list(list), do: for(element <- list, into: [], do: to_record(element)) + @doc false + defp maybe_to_record(struct = %JOSE.JWK{}), do: to_record(struct) + defp maybe_to_record(other), do: other + @doc """ Converts a `:jose_jwk` record into a `JOSE.JWK`. """ @@ -391,7 +395,7 @@ defmodule JOSE.JWK do @doc """ Converts a private `JOSE.JWK` into a public `JOSE.JWK`. - iex> jwk_rsa = JOSE.JWK.generate_key({:rsa, 256}) + iex> jwk_rsa = JOSE.JWK.generate_key({:rsa, 256}) %JOSE.JWK{fields: %{}, keys: :undefined, kty: {:jose_jwk_kty_rsa, {:RSAPrivateKey, :"two-prime", @@ -499,9 +503,7 @@ defmodule JOSE.JWK do def block_encryptor(jwk = %JOSE.JWK{}), do: block_encryptor(to_record(jwk)) def block_encryptor(jwk), do: :jose_jwk.block_encryptor(jwk) - @doc """ - Key Agreement decryption of the `encrypted` binary or map using `my_private_jwk`. See `box_encrypt/2` and `JOSE.JWE.block_decrypt/2`. - """ + @deprecated "Use JOSE.JWK.box_decrypt_ecdh_es/2 or JOSE.JWK.box_decrypt_ecdh_1pu/3 instead" def box_decrypt(encrypted, my_private_jwk = %JOSE.JWK{}), do: box_decrypt(encrypted, to_record(my_private_jwk)) def box_decrypt(encrypted, {your_public_jwk = %JOSE.JWK{}, my_private_jwk}), @@ -520,52 +522,7 @@ defmodule JOSE.JWK do end end - @doc """ - Key Agreement encryption of `plain_text` by generating an ephemeral private key based on `other_public_jwk` curve. See `box_encrypt/3`. - - # bob wants alice to send him a secret, so he first sends alice his public key: - bob_public_jwk = JOSE.JWK.from(%{"crv" => "P-256", "kty" => "EC", - "x" => "6pwDpICQ8JBWdvuLuXeWILAxSEUNB_BBAswikgYKKmY", - "y" => "fEHj1ehsIJ7PP-qon-oONl_J2yZLWpUncNRedZT7xqs"}) - - # alice uses bob's public key to generate an ephemeral private key used to encrypt the secret: - iex> {enc_alice2bob_tuple, alice_private_jwk} = JOSE.JWK.box_encrypt("secret", bob_public_jwk) - {{%{alg: :jose_jwe_alg_ecdh_es, enc: :jose_jwe_enc_aes}, - %{"ciphertext" => "zcIIZLDB", "encrypted_key" => "", - "iv" => "9p8c7YJV5htz8zLI", - "protected" => "eyJhbGciOiJFQ0RILUVTIiwiYXB1IjoiaEhibEsxZlNWQ1FjTE5NQkpXMjB5Mko0VHMzcUhqR2c4ZDlocmFfc2QyZyIsImFwdiI6IlU4MkpJbFFNS0FWYWY5bXVwU0I2c0JERWpIQ1Qxdl9JU00xMUNsZHpVUGMiLCJlbmMiOiJBMTI4R0NNIiwiZXBrIjp7ImNydiI6IlAtMjU2Iiwia3R5IjoiRUMiLCJ4IjoiSUY3RTFza0hJMjBwQjRwbi0tMVZ4dVF4Vkl4Sjkzd21IaFl4VEp0VkZOVSIsInkiOiJiVDdidzdhRjVlM1hLNUh6YVM4MEFVbktKVGE2eWdYbkJDVDFxNERHSWNrIn19", - "tag" => "MHtfyNub8vG84ER0MPynuA"}}, - %JOSE.JWK{fields: %{}, keys: :undefined, - kty: {:jose_jwk_kty_ec, - {:ECPrivateKey, 1, - <<138, 8, 179, 41, 203, 0, 127, 144, 178, 132, 66, 96, 50, 161, 103, 50, 4, 119, 71, 57, 63, 63, 33, 29, 69, 201, 182, 210, 106, 37, 196, 183>>, - {:namedCurve, {1, 2, 840, 10045, 3, 1, 7}}, - <<4, 32, 94, 196, 214, 201, 7, 35, 109, 41, 7, 138, 103, 251, 237, 85, 198, 228, 49, 84, 140, 73, 247, 124, 38, 30, 22, 49, 76, 155, 85, 20, 213, 109, 62, 219, 195, 182, 133, 229, 237, 215, ...>>}}}} - - # alice compacts the encrypted message and sends it to bob which contains alice's public key: - iex> enc_alice2bob_binary = JOSE.JWE.compact(enc_alice2bob_tuple) |> elem(1) - "eyJhbGciOiJFQ0RILUVTIiwiYXB1IjoiaEhibEsxZlNWQ1FjTE5NQkpXMjB5Mko0VHMzcUhqR2c4ZDlocmFfc2QyZyIsImFwdiI6IlU4MkpJbFFNS0FWYWY5bXVwU0I2c0JERWpIQ1Qxdl9JU00xMUNsZHpVUGMiLCJlbmMiOiJBMTI4R0NNIiwiZXBrIjp7ImNydiI6IlAtMjU2Iiwia3R5IjoiRUMiLCJ4IjoiSUY3RTFza0hJMjBwQjRwbi0tMVZ4dVF4Vkl4Sjkzd21IaFl4VEp0VkZOVSIsInkiOiJiVDdidzdhRjVlM1hLNUh6YVM4MEFVbktKVGE2eWdYbkJDVDFxNERHSWNrIn19..9p8c7YJV5htz8zLI.zcIIZLDB.MHtfyNub8vG84ER0MPynuA" - - # bob can then decrypt the encrypted message using his private key: - bob_private_jwk = JOSE.JWK.from(%{"crv" => "P-256", "d" => "69sPu8znGIFuysKso-RemObfFs8bMBmkF0dfI1h6S1E", - "kty" => "EC", "x" => "6pwDpICQ8JBWdvuLuXeWILAxSEUNB_BBAswikgYKKmY", - "y" => "fEHj1ehsIJ7PP-qon-oONl_J2yZLWpUncNRedZT7xqs"}) - - iex> JOSE.JWK.box_decrypt(enc_alice2bob_binary, bob_private_jwk) - {"secret", - %JOSE.JWE{alg: {:jose_jwe_alg_ecdh_es, - {:jose_jwe_alg_ecdh_es, - {{{:ECPoint, - <<4, 32, 94, 196, 214, 201, 7, 35, 109, 41, 7, 138, 103, 251, 237, 85, 198, 228, 49, 84, 140, 73, 247, 124, 38, 30, 22, 49, 76, 155, 85, 20, 213, 109, 62, 219, 195, 182, 133, 229, 237, 215, ...>>}, - {:namedCurve, {1, 2, 840, 10045, 3, 1, 7}}}, %{}}, - <<132, 118, 229, 43, 87, 210, 84, 36, 28, 44, 211, 1, 37, 109, 180, 203, 98, 120, 78, 205, 234, 30, 49, 160, 241, 223, 97, 173, 175, 236, 119, 104>>, - <<83, 205, 137, 34, 84, 12, 40, 5, 90, 127, 217, 174, 165, 32, 122, 176, 16, 196, 140, 112, 147, 214, 255, 200, 72, 205, 117, 10, 87, 115, 80, 247>>, - :undefined}}, - enc: {:jose_jwe_enc_aes, - {:jose_jwe_enc_aes, {:aes_gcm, 128}, 128, 16, 12, :undefined, :undefined, - :undefined, :undefined}}, fields: %{}, zip: :undefined}} - - """ + @deprecated "Use JOSE.JWK.box_decrypt_ecdh_es/2 or JOSE.JWK.box_encrypt_ecdh_1pu/3 instead" def box_encrypt(plain_text, other_public_jwk = %JOSE.JWK{}), do: box_encrypt(plain_text, to_record(other_public_jwk)) def box_encrypt(plain_text, other_public_jwk) do @@ -578,9 +535,7 @@ defmodule JOSE.JWK do end end - @doc """ - Key Agreement encryption of `plain_text` using `my_private_jwk`, `other_public_jwk`, and the default `jwe` based on the key types. See `box_encrypt/4`. - """ + @deprecated "Use JOSE.JWK.box_decrypt_ecdh_es/3 or JOSE.JWK.box_encrypt_ecdh_1pu/4 instead" def box_encrypt(plain_text, other_public_jwk = %JOSE.JWK{}, my_private_jwk), do: box_encrypt(plain_text, to_record(other_public_jwk), my_private_jwk) @@ -590,11 +545,7 @@ defmodule JOSE.JWK do def box_encrypt(plain_text, other_public_jwk, my_private_jwk), do: :jose_jwk.box_encrypt(plain_text, other_public_jwk, my_private_jwk) - @doc """ - Key Agreement encryption of `plain_text` using `my_private_jwk`, `other_public_jwk`, and the algorithms specified by the `jwe`. - - # let's - """ + @deprecated "Use JOSE.JWK.box_decrypt_ecdh_es/4 or JOSE.JWK.box_encrypt_ecdh_1pu/5 instead" def box_encrypt(plain_text, jwe = %JOSE.JWE{}, other_public_jwk, my_private_jwk), do: box_encrypt(plain_text, JOSE.JWE.to_record(jwe), other_public_jwk, my_private_jwk) @@ -607,6 +558,228 @@ defmodule JOSE.JWK do def box_encrypt(plain_text, jwe, other_public_jwk, my_private_jwk), do: :jose_jwk.box_encrypt(plain_text, jwe, other_public_jwk, my_private_jwk) + @doc """ + ECDH-1PU Key Agreement decryption of the `encrypted` binary or map using `u_static_public_key` and `v_static_secret_key`. See `box_encrypt_ecdh_1pu/3` and `JOSE.JWE.block_decrypt/2`. + """ + def box_decrypt_ecdh_1pu(encrypted, u_static_public_key, v_static_secret_key) do + u_static_public_key = maybe_to_record(u_static_public_key) + v_static_secret_key = maybe_to_record(v_static_secret_key) + + case :jose_jwk.box_decrypt_ecdh_1pu(encrypted, u_static_public_key, v_static_secret_key) do + {plain_text, jwe} when is_tuple(jwe) -> + {plain_text, JOSE.JWE.from_record(jwe)} + + error -> + error + end + end + + @doc """ + ECDH-1PU Key Agreement encryption of `plain_text` by generating an ephemeral secret key based on `v_static_public_key` and `u_static_secret_key` curve. See `box_encrypt_ecdh_1pu/4`. + + # Alice (U) wants to send Bob (V) a secret message. + # They have already shared their static public keys with one another through an unspecified channel: + u_static_public_key = JOSE.JWK.from(%{ + "crv" => "X25519", + "kty" => "OKP", + "x" => "sz1JMMasNRLQfXIkvLTRaOu978QQu1roFKxBPKZdsC8" + }) + v_static_public_key = JOSE.JWK.from(%{ + "crv" => "X25519", + "kty" => "OKP", + "x" => "EFvrJluQClNDvbIjcie4UADLfirR9Fk53rcSM5gibx4" + }) + + # WARNING: Do not share secret keys. For reference purposes only, here is Alice's (U) static secret key that is used below: + u_static_secret_key = JOSE.JWK.from(%{ + "crv" => "X25519", + "d" => "SfYmE8aLpvX6Z0rZQVa5eBjLKeINUfSlu-_AcYJXCqQ", + "kty" => "OKP", + "x" => "sz1JMMasNRLQfXIkvLTRaOu978QQu1roFKxBPKZdsC8" + }) + # WARNING: Do not share secret keys. For reference purposes only, here is Bob's (V) static secret key that is used below: + v_static_secret_key = JOSE.JWK.from(%{ + "crv" => "X25519", + "d" => "8-SdA6TQ1mdFRC06U-R4-ah6YkeVcodtGuMNVngwlto", + "kty" => "OKP", + "x" => "EFvrJluQClNDvbIjcie4UADLfirR9Fk53rcSM5gibx4" + }) + + # Alice (U) uses Bob's (V) static public key along with Alice's (U) static secret key to generate an ephemeral secret key used to encrypt the message: + iex> {enc_alice2bob_tuple, u_ephemeral_secret_key} = JOSE.JWK.box_encrypt_ecdh_1pu("secret message", v_static_public_key, u_static_secret_key) + {{%{alg: :jose_jwe_alg_ecdh_1pu, enc: :jose_jwe_enc_aes}, + %{ + "ciphertext" => "Ry0I26YMnaHSmNQ86EY", + "encrypted_key" => "", + "iv" => "swK08mi6cjJ1aUQF", + "protected" => "eyJhbGciOiJFQ0RILTFQVSIsImFwdSI6IjAybXM0OF90ZmpqQXk4TXJMeDV1LW1yaVZwT24tOFpNVmtDTXlIbEtBTjAiLCJhcHYiOiJrNDR0QzE4U2tYTUVXektFQ0JSZ3VhMHpaX01MRHY1VTQ2TWNueVl6ajBBIiwiZW5jIjoiQTEyOEdDTSIsImVwayI6eyJjcnYiOiJYMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiX3JBS1l6WThJczFSRXJQTTVod29OSEZjdWZQOHpiYXpVaTN5QXJ3WnVDVSJ9LCJza2lkIjoieXNZVzZiUmRpVW1ST0NWa09oSmtWMV9JX0VEM1dESzBIN2tkbU5GelNYSSJ9", + "tag" => "-6PitRlVuXk5HT3g_y6Uxw" + }}, + %JOSE.JWK{ + keys: :undefined, + kty: {:jose_jwk_kty_okp_x25519, + <<30, 250, 220, 248, 158, 207, 86, 211, 254, 196, 78, 125, 132, 228, 186, 20, 253, 56, 226, 29, 191, 220, 131, 114, 44, 253, 72, 117, 25, 112, 209, 175, 254, 176, 10, 99, 54, 60, 34, 205, 81, 18, 179, 204, 230, 28, 40, 52, 113, 92, 185, 243, 252, 205, 182, 179, 82, 45, 242, 2, 188, 25, 184, 37>>}, + fields: %{} + }} + + # Alice (U) compacts the encrypted message and sends it to Bob (V), which contains Alice's (U) ephemeral public key: + iex> enc_alice2bob_binary = JOSE.JWE.compact(enc_alice2bob_tuple) |> elem(1) + "eyJhbGciOiJFQ0RILTFQVSIsImFwdSI6IjAybXM0OF90ZmpqQXk4TXJMeDV1LW1yaVZwT24tOFpNVmtDTXlIbEtBTjAiLCJhcHYiOiJrNDR0QzE4U2tYTUVXektFQ0JSZ3VhMHpaX01MRHY1VTQ2TWNueVl6ajBBIiwiZW5jIjoiQTEyOEdDTSIsImVwayI6eyJjcnYiOiJYMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiX3JBS1l6WThJczFSRXJQTTVod29OSEZjdWZQOHpiYXpVaTN5QXJ3WnVDVSJ9LCJza2lkIjoieXNZVzZiUmRpVW1ST0NWa09oSmtWMV9JX0VEM1dESzBIN2tkbU5GelNYSSJ9..swK08mi6cjJ1aUQF.Ry0I26YMnaHSmNQ86EY.-6PitRlVuXk5HT3g_y6Uxw" + + # Bob (V) can then decrypt the encrypted message using Alice's (U) static public key along with Bob's (V) static secret key: + iex> JOSE.JWK.box_decrypt_ecdh_1pu(enc_alice2bob_binary, u_static_public_key, v_static_secret_key) + {"secret message", + %JOSE.JWE{ + alg: {:jose_jwe_alg_ecdh_1pu, + {:jose_jwe_alg_ecdh_1pu, + {:jose_jwk, :undefined, + {:jose_jwk_kty_okp_x25519, + <<254, 176, 10, 99, 54, 60, 34, 205, 81, 18, 179, 204, 230, 28, 40, 52, 113, 92, 185, 243, 252, 205, 182, 179, 82, 45, 242, 2, 188, 25, 184, 37>>}, %{}}, + <<211, 105, 172, 227, 207, 237, 126, 56, 192, 203, 195, 43, 47, 30, 110, 250, 106, 226, 86, 147, 167, 251, 198, 76, 86, 64, 140, 200, 121, 74, 0, 221>>, + <<147, 142, 45, 11, 95, 18, 145, 115, 4, 91, 50, 132, 8, 20, 96, 185, 173, 51, 103, 243, 11, 14, 254, 84, 227, 163, 28, 159, 38, 51, 143, 64>>, :undefined, :undefined, :undefined, :undefined}}, + enc: {:jose_jwe_enc_aes, + {:jose_jwe_enc_aes, {:aes_gcm, 128}, 128, 16, 12, :undefined, :undefined, :undefined, :undefined}}, + zip: :undefined, + fields: %{"skid" => "ysYW6bRdiUmROCVkOhJkV1_I_ED3WDK0H7kdmNFzSXI"} + }} + """ + def box_encrypt_ecdh_1pu(plain_text, v_static_public_key, u_static_secret_key) do + v_static_public_key = maybe_to_record(v_static_public_key) + u_static_secret_key = maybe_to_record(u_static_secret_key) + {encrypted, u_ephemeral_secret_key} = :jose_jwk.box_encrypt_ecdh_1pu(plain_text, v_static_public_key, u_static_secret_key) + {encrypted, from_record(u_ephemeral_secret_key)} + end + + @doc """ + ECDH-1PU Key Agreement encryption of `plain_text` using `v_static_public_key`, `u_static_secret_key`, `u_ephemeral_secret_key`, and a derived `jwe` based on the input key types. See `box_encrypt_ecdh_1pu/5`. + """ + def box_encrypt_ecdh_1pu(plain_text, v_static_public_key, u_static_secret_key, u_ephemeral_secret_key) do + v_static_public_key = maybe_to_record(v_static_public_key) + u_static_secret_key = maybe_to_record(u_static_secret_key) + u_ephemeral_secret_key = maybe_to_record(u_ephemeral_secret_key) + :jose_jwk.box_encrypt_ecdh_1pu(plain_text, v_static_public_key, u_static_secret_key, u_ephemeral_secret_key) + end + + @doc """ + ECDH-1PU Key Agreement encryption of `plain_text` using `v_static_public_key`, `u_static_secret_key`, `u_ephemeral_secret_key`, and the algorithms specified by the `jwe`. + """ + def box_encrypt_ecdh_1pu(plain_text, jwe, v_static_public_key, u_static_secret_key, u_ephemeral_secret_key) do + jwe = + case jwe do + %JOSE.JWE{} -> JOSE.JWE.to_record(jwe) + {metadata, jwe = %JOSE.JWE{}} -> {metadata, JOSE.JWE.to_record(jwe)} + _ -> jwe + end + + v_static_public_key = maybe_to_record(v_static_public_key) + u_static_secret_key = maybe_to_record(u_static_secret_key) + u_ephemeral_secret_key = maybe_to_record(u_ephemeral_secret_key) + :jose_jwk.box_encrypt_ecdh_1pu(plain_text, jwe, v_static_public_key, u_static_secret_key, u_ephemeral_secret_key) + end + + @doc """ + ECDH-ES Key Agreement decryption of the `encrypted` binary or map using `v_static_secret_key`. See `box_encrypt_ecdh_es/2` and `JOSE.JWE.block_decrypt/2`. + """ + def box_decrypt_ecdh_es(encrypted, v_static_secret_key) do + v_static_secret_key = maybe_to_record(v_static_secret_key) + + case :jose_jwk.box_decrypt_ecdh_es(encrypted, v_static_secret_key) do + {plain_text, jwe} when is_tuple(jwe) -> + {plain_text, JOSE.JWE.from_record(jwe)} + + error -> + error + end + end + + @doc """ + ECDH-ES Key Agreement encryption of `plain_text` by generating an ephemeral secret key based on `v_static_public_key` curve. See `box_encrypt_ecdh_es/3`. + + # Alice (U) wants to send Bob (V) a secret message. + # Bob (V) has already shared their static public key with Alice (U) through an unspecified channel: + v_static_public_key = JOSE.JWK.from(%{ + "crv" => "X25519", + "kty" => "OKP", + "x" => "EFvrJluQClNDvbIjcie4UADLfirR9Fk53rcSM5gibx4" + }) + + # WARNING: Do not share secret keys. For reference purposes only, here is Bob's (V) static secret key that is used below: + v_static_secret_key = JOSE.JWK.from(%{ + "crv" => "X25519", + "d" => "8-SdA6TQ1mdFRC06U-R4-ah6YkeVcodtGuMNVngwlto", + "kty" => "OKP", + "x" => "EFvrJluQClNDvbIjcie4UADLfirR9Fk53rcSM5gibx4" + }) + + # Alice (U) uses Bob's (V) static public key to generate an ephemeral secret key used to encrypt the message: + iex> {enc_alice2bob_tuple, u_ephemeral_secret_key} = JOSE.JWK.box_encrypt_ecdh_es("secret message", v_static_public_key) + {{%{alg: :jose_jwe_alg_ecdh_es, enc: :jose_jwe_enc_aes}, + %{ + "ciphertext" => "AhQ3W31vvypJNubhD2U", + "encrypted_key" => "", + "iv" => "YjojFg2wPnk5JmMG", + "protected" => "eyJhbGciOiJFQ0RILUVTIiwiYXB1IjoiRHNVZlFNdUEybnZqMnRzTVp1N0o5YUFEekw3akUyRFUzRWFvVTh3YmJlVSIsImFwdiI6Ims0NHRDMThTa1hNRVd6S0VDQlJndWEwelpfTUxEdjVVNDZNY255WXpqMEEiLCJlbmMiOiJBMTI4R0NNIiwiZXBrIjp7ImNydiI6IlgyNTUxOSIsImt0eSI6Ik9LUCIsIngiOiJwRkg3YXZYQlFvUjBoZnNsUm1HaVJxREpxdUVjS0w0eTU4TEZocnc1S3dFIn19", + "tag" => "pwRKlhhXEPjwZg13455U5Q" + }}, + %JOSE.JWK{ + keys: :undefined, + kty: {:jose_jwk_kty_okp_x25519, + <<68, 178, 96, 158, 87, 182, 26, 216, 211, 230, 115, 239, 145, 244, 93, 4, 79, 231, 189, 5, 96, 164, 241, 132, 123, 151, 253, 19, 109, 246, 211, 86, 164, 81, 251, 106, 245, 193, 66, 132, 116, 133, 251, 37, 70, 97, 162, 70, 160, 201, 170, 225, 28, 40, 190, 50, 231, 194, 197, 134, 188, 57, 43, 1>>}, + fields: %{} + }} + + # Alice (U) compacts the encrypted message and sends it to Bob (V), which contains Alice's (U) ephemeral public key: + iex> enc_alice2bob_binary = JOSE.JWE.compact(enc_alice2bob_tuple) |> elem(1) + "eyJhbGciOiJFQ0RILUVTIiwiYXB1IjoiRHNVZlFNdUEybnZqMnRzTVp1N0o5YUFEekw3akUyRFUzRWFvVTh3YmJlVSIsImFwdiI6Ims0NHRDMThTa1hNRVd6S0VDQlJndWEwelpfTUxEdjVVNDZNY255WXpqMEEiLCJlbmMiOiJBMTI4R0NNIiwiZXBrIjp7ImNydiI6IlgyNTUxOSIsImt0eSI6Ik9LUCIsIngiOiJwRkg3YXZYQlFvUjBoZnNsUm1HaVJxREpxdUVjS0w0eTU4TEZocnc1S3dFIn19..YjojFg2wPnk5JmMG.AhQ3W31vvypJNubhD2U.pwRKlhhXEPjwZg13455U5Q" + + # Bob (V) can then decrypt the encrypted message with Bob's (V) static secret key: + iex> JOSE.JWK.box_decrypt_ecdh_es(enc_alice2bob_binary, v_static_secret_key) + {"secret message", + %JOSE.JWE{ + alg: {:jose_jwe_alg_ecdh_es, + {:jose_jwe_alg_ecdh_es, + {:jose_jwk, :undefined, + {:jose_jwk_kty_okp_x25519, + <<164, 81, 251, 106, 245, 193, 66, 132, 116, 133, 251, 37, 70, 97, 162, 70, 160, 201, 170, 225, 28, 40, 190, 50, 231, 194, 197, 134, 188, 57, 43, 1>>}, %{}}, + <<14, 197, 31, 64, 203, 128, 218, 123, 227, 218, 219, 12, 102, 238, 201, 245, 160, 3, 204, 190, 227, 19, 96, 212, 220, 70, 168, 83, 204, 27, 109, 229>>, + <<147, 142, 45, 11, 95, 18, 145, 115, 4, 91, 50, 132, 8, 20, 96, 185, 173, 51, 103, 243, 11, 14, 254, 84, 227, 163, 28, 159, 38, 51, 143, 64>>, :undefined, :undefined, :undefined, :undefined}}, + enc: {:jose_jwe_enc_aes, + {:jose_jwe_enc_aes, {:aes_gcm, 128}, 128, 16, 12, :undefined, :undefined, :undefined, :undefined}}, + zip: :undefined, + fields: %{} + }} + """ + def box_encrypt_ecdh_es(plain_text, v_static_public_key) do + v_static_public_key = maybe_to_record(v_static_public_key) + {encrypted, u_ephemeral_secret_key} = :jose_jwk.box_encrypt_ecdh_es(plain_text, v_static_public_key) + {encrypted, from_record(u_ephemeral_secret_key)} + end + + @doc """ + ECDH-ES Key Agreement encryption of `plain_text` using `v_static_public_key`, `u_ephemeral_secret_key`, and a derived `jwe` based on the input key types. See `box_encrypt_ecdh_es/4`. + """ + def box_encrypt_ecdh_es(plain_text, v_static_public_key, u_ephemeral_secret_key) do + v_static_public_key = maybe_to_record(v_static_public_key) + u_ephemeral_secret_key = maybe_to_record(u_ephemeral_secret_key) + :jose_jwk.box_encrypt_ecdh_es(plain_text, v_static_public_key, u_ephemeral_secret_key) + end + + @doc """ + ECDH-ES Key Agreement encryption of `plain_text` using `v_static_public_key`, `u_ephemeral_secret_key`, and the algorithms specified by the `jwe`. + """ + def box_encrypt_ecdh_es(plain_text, jwe, v_static_public_key, u_ephemeral_secret_key) do + jwe = + case jwe do + %JOSE.JWE{} -> JOSE.JWE.to_record(jwe) + {metadata, jwe = %JOSE.JWE{}} -> {metadata, JOSE.JWE.to_record(jwe)} + _ -> jwe + end + + v_static_public_key = maybe_to_record(v_static_public_key) + u_ephemeral_secret_key = maybe_to_record(u_ephemeral_secret_key) + :jose_jwk.box_encrypt_ecdh_es(plain_text, jwe, v_static_public_key, u_ephemeral_secret_key) + end + @doc """ Generates a new `JOSE.JWK` based on another `JOSE.JWK` or from initialization params provided. @@ -619,7 +792,7 @@ defmodule JOSE.JWK do The following initialization params may also be used: - * `{:ec, "P-256" | "P-384" | "P-521"}` - generates an `"EC"` key using the `"P-256"`, `"P-384"`, or `"P-521"` curves + * `{:ec, "secp256k1", "P-256" | "P-384" | "P-521"}` - generates an `"EC"` key using the `"secp256k1"`, `"P-256"`, `"P-384"`, or `"P-521"` curves * `{:oct, bytes}` - generates an `"oct"` key made of a random `bytes` number of bytes * `{:okp, :Ed25519 | :Ed25519ph | :Ed448 | :Ed448ph | :X25519 | :X448}` - generates an `"OKP"` key using the specified EdDSA or ECDH edwards curve * `{:rsa, modulus_size} | {:rsa, modulus_size, exponent_size}` - generates an `"RSA"` key using the `modulus_size` and `exponent_size` diff --git a/lib/jose/poison/lexical_encoder.ex b/lib/jose/poison/lexical_encoder.ex index 3722856..ede8f84 100644 --- a/lib/jose/poison/lexical_encoder.ex +++ b/lib/jose/poison/lexical_encoder.ex @@ -181,7 +181,7 @@ if Code.ensure_loaded?(Poison) do end defp escape(<> <> rest, mode) - when mode in [:html_safe, :javascript] and char in [0x2028, 0x2029] do + when mode in [:html_safe, :javascript] and char in [0x2028, 0x2029] do [seq(char) | escape(rest, mode)] end @@ -196,7 +196,7 @@ if Code.ensure_loaded?(Poison) do end defp chunk_size(<> <> _, _mode, acc) - when char <= 0x1F or char in '"\\' do + when char <= 0x1F or char in '"\\' do acc end @@ -213,7 +213,7 @@ if Code.ensure_loaded?(Poison) do end defp chunk_size(<> <> _, mode, acc) - when mode in [:html_safe, :javascript] and char in [0x2028, 0x2029] do + when mode in [:html_safe, :javascript] and char in [0x2028, 0x2029] do acc end diff --git a/mix.lock b/mix.lock index 39e110b..5b9e3de 100644 --- a/mix.lock +++ b/mix.lock @@ -5,12 +5,13 @@ "jason": {:hex, :jason, "1.3.0", "fa6b82a934feb176263ad2df0dbd91bf633d4a46ebfdffea0c8ae82953714946", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "53fc1f51255390e0ec7e50f9cb41e751c260d065dcba2bf0d08dc51a4002c2ac"}, "jsone": {:hex, :jsone, "1.7.0", "1e3bd7d5dd44bb2eb0797dddea1cbf2ddab8d9f29e499a467ca171c23f5984ea", [:rebar3], [], "hexpm", "a3a33712ee6bc8be10cfa21c7c425a299de4c5a8533f9f931e577a6d0e8f5dbd"}, "jsx": {:hex, :jsx, "3.1.0", "d12516baa0bb23a59bb35dccaf02a1bd08243fcbb9efe24f2d9d056ccff71268", [:rebar3], [], "hexpm", "0c5cc8fdc11b53cc25cf65ac6705ad39e54ecc56d1c22e4adb8f5a53fb9427f3"}, - "libdecaf": {:hex, :libdecaf, "2.1.0", "26e273443d75420081d4b1f76764492ee3e1d7cd601a2ab1dc8761a6943bfb46", [:rebar3], [], "hexpm", "529d493d2929cea2eea2d93464f636a1969756374c58a66ab8f95dd1c0ccffcb"}, - "libsodium": {:hex, :libsodium, "0.0.10", "24584e6f5a60aed53542054c15fb31719bbf2d811d9a84f626734faf64d41304", [:rebar3], [], "hexpm", "1f47b9b7e8f0fb18fd0ab6292ef3c5397f1237d832012f96d6e1e74fe9d104ed"}, + "libdecaf": {:hex, :libdecaf, "2.1.1", "2d2716c1c2aee870d15fbf150dca67210e2a62f11f9126f59019a5ef79f63ad3", [:rebar3], [], "hexpm", "dee36dc9a73cc2384b0f9a53ce7f76c29a1344f28c459678add231cb8a2c4c61"}, + "libsodium": {:hex, :libsodium, "2.0.0", "f0e204259548c06b21c92e39fb128f765417eab523401c90fd7fa515e07823f4", [:rebar3], [], "hexpm", "95fa57e8d133f55b7f454d286365cc458684f4a92ab035864362a42157c6e9d5"}, "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"}, "ojson": {:hex, :ojson, "1.0.0", "fd28614eadaec00a15cdb2f53f29d8717a812a508ddb80d202f2f2e2aaeabbcc", [:mix, :rebar3], [], "hexpm", "125088e64d95704194dc8b4b5095502363e0ca870b72712f61929499d3c48d00"}, "poison": {:hex, :poison, "5.0.0", "d2b54589ab4157bbb82ec2050757779bfed724463a544b6e20d79855a9e43b24", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "11dc6117c501b80c62a7594f941d043982a1bd05a1184280c0d9166eb4d8d3fc"}, + "thoas": {:hex, :thoas, "0.4.0", "86a72ccdc5ec388a13f9f843bcd6c1076640233b95440e47ffb8e3c0dbdb5a17", [:rebar3], [], "hexpm", "442296847aca11db8d25180693d7ca3073d6d7179f66952f07b16415306513b6"}, } diff --git a/src/jwe/jose_jwe_alg_ecdh_1pu.erl b/src/jwe/jose_jwe_alg_ecdh_1pu.erl index 0ff24a5..5a6fb7b 100644 --- a/src/jwe/jose_jwe_alg_ecdh_1pu.erl +++ b/src/jwe/jose_jwe_alg_ecdh_1pu.erl @@ -4,7 +4,7 @@ %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc Key Agreement with Elliptic Curve Diffie-Hellman One-Pass Unified Model (ECDH-1PU) -%%% See https://tools.ietf.org/html/draft-madden-jose-ecdh-1pu-02 +%%% See https://datatracker.ietf.org/doc/html/draft-madden-jose-ecdh-1pu-04 %%% %%% @end %%% Created : 29 Dec 2019 by Andrew Bennett diff --git a/src/jwk/jose_jwk_kty_ec.erl b/src/jwk/jose_jwk_kty_ec.erl index 6ec74e2..93de061 100644 --- a/src/jwk/jose_jwk_kty_ec.erl +++ b/src/jwk/jose_jwk_kty_ec.erl @@ -121,6 +121,8 @@ generate_key({#'ECPoint'{}, P}) -> generate_key(P); generate_key(P) when is_atom(P) -> generate_key({namedCurve, P}); +generate_key(<<"secp256k1">>) -> + generate_key(secp256k1); generate_key(<<"P-256">>) -> generate_key(secp256r1); generate_key(<<"P-384">>) -> @@ -216,6 +218,7 @@ signer(#'ECPrivateKey'{}, #{ <<"alg">> := ALG, <<"use">> := <<"sig">> }) -> signer(#'ECPrivateKey'{parameters={namedCurve, Parameters}}, _Fields) -> #{ <<"alg">> => case parameters_to_crv(Parameters) of + <<"secp256k1">> -> <<"ES256K">>; <<"P-256">> -> <<"ES256">>; <<"P-384">> -> <<"ES384">>; <<"P-521">> -> <<"ES512">> @@ -237,6 +240,7 @@ verifier(#'ECPrivateKey'{parameters=ECParameters, publicKey=Octets0}, Fields) -> verifier({#'ECPoint'{}, {namedCurve, Parameters}}, _Fields) -> [ case parameters_to_crv(Parameters) of + <<"secp256k1">> -> <<"ES256K">>; <<"P-256">> -> <<"ES256">>; <<"P-384">> -> <<"ES384">>; <<"P-521">> -> <<"ES512">> @@ -337,6 +341,8 @@ from_map_ec_private_key(binary, F = #{ <<"d">> := D }, Key) -> from_map_ec_private_key(binary, maps:remove(<<"d">>, F), Key#'ECPrivateKey'{ privateKey = jose_jwa_base64url:decode(D) }); from_map_ec_private_key(list, F = #{ <<"d">> := D }, Key) -> from_map_ec_private_key(list, maps:remove(<<"d">>, F), Key#'ECPrivateKey'{ privateKey = binary_to_list(jose_jwa_base64url:decode(D)) }); +from_map_ec_private_key(ECMode, F = #{ <<"crv">> := <<"secp256k1">> }, Key) -> + from_map_ec_private_key(ECMode, maps:remove(<<"crv">>, F), Key#'ECPrivateKey'{ parameters = {namedCurve, pubkey_cert_records:namedCurves(secp256k1)} }); from_map_ec_private_key(ECMode, F = #{ <<"crv">> := <<"P-256">> }, Key) -> from_map_ec_private_key(ECMode, maps:remove(<<"crv">>, F), Key#'ECPrivateKey'{ parameters = {namedCurve, pubkey_cert_records:namedCurves(secp256r1)} }); from_map_ec_private_key(ECMode, F = #{ <<"crv">> := <<"P-384">> }, Key) -> @@ -351,6 +357,8 @@ from_map_ec_private_key(_ECMode, F, Key) -> {Key, F}. %% @private +from_map_ec_public_key(F = #{ <<"crv">> := <<"secp256k1">> }, {Point, _Params}) -> + from_map_ec_public_key(maps:remove(<<"crv">>, F), {Point, {namedCurve, pubkey_cert_records:namedCurves(secp256k1)}}); from_map_ec_public_key(F = #{ <<"crv">> := <<"P-256">> }, {Point, _Params}) -> from_map_ec_public_key(maps:remove(<<"crv">>, F), {Point, {namedCurve, pubkey_cert_records:namedCurves(secp256r1)}}); from_map_ec_public_key(F = #{ <<"crv">> := <<"P-384">> }, {Point, _Params}) -> @@ -379,6 +387,8 @@ int_to_bin_neg(X,Ds) -> int_to_bin_neg(X bsr 8, [(X band 255)|Ds]). %% @private +jws_alg_to_digest_type(<<"secp256k1">>, 'ES256K') -> + sha256; jws_alg_to_digest_type(<<"P-256">>, 'ES256') -> sha256; jws_alg_to_digest_type(<<"P-384">>, 'ES384') -> @@ -393,6 +403,8 @@ jws_alg_to_digest_type(KeyOrCurve, ALG) -> erlang:error({not_supported, [KeyOrCurve, ALG]}). %% @private +jws_alg_to_r_s_size('ES256K') -> + 32; jws_alg_to_r_s_size('ES256') -> 32; jws_alg_to_r_s_size('ES384') -> @@ -407,6 +419,8 @@ pad(Bin, Size) -> pad(<< 0, Bin/binary >>, Size). %% @private +parameters_to_crv(secp256k1) -> + <<"secp256k1">>; parameters_to_crv(secp256r1) -> <<"P-256">>; parameters_to_crv(secp384r1) -> diff --git a/src/jwk/jose_jwk_kty_rsa.erl b/src/jwk/jose_jwk_kty_rsa.erl index 90c1124..5455331 100644 --- a/src/jwk/jose_jwk_kty_rsa.erl +++ b/src/jwk/jose_jwk_kty_rsa.erl @@ -500,6 +500,8 @@ jws_alg_to_digest_type('PS384') -> {rsa_pkcs1_pss_padding, sha384}; jws_alg_to_digest_type('PS512') -> {rsa_pkcs1_pss_padding, sha512}; +jws_alg_to_digest_type('RS1') -> + {rsa_pkcs1_padding, sha}; jws_alg_to_digest_type('RS256') -> {rsa_pkcs1_padding, sha256}; jws_alg_to_digest_type('RS384') -> diff --git a/src/jws/jose_jws_alg_ecdsa.erl b/src/jws/jose_jws_alg_ecdsa.erl index c0a4019..079465a 100644 --- a/src/jws/jose_jws_alg_ecdsa.erl +++ b/src/jws/jose_jws_alg_ecdsa.erl @@ -25,7 +25,7 @@ %% API %% Types --type alg() :: 'ES256' | 'ES384' | 'ES512'. +-type alg() :: 'ES256K' | 'ES256' | 'ES384' | 'ES512'. -export_type([alg/0]). @@ -33,6 +33,8 @@ %% jose_jws callbacks %%==================================================================== +from_map(F = #{ <<"alg">> := <<"ES256K">> }) -> + {'ES256K', maps:remove(<<"alg">>, F)}; from_map(F = #{ <<"alg">> := <<"ES256">> }) -> {'ES256', maps:remove(<<"alg">>, F)}; from_map(F = #{ <<"alg">> := <<"ES384">> }) -> @@ -40,6 +42,8 @@ from_map(F = #{ <<"alg">> := <<"ES384">> }) -> from_map(F = #{ <<"alg">> := <<"ES512">> }) -> {'ES512', maps:remove(<<"alg">>, F)}. +to_map('ES256K', F) -> + F#{ <<"alg">> => <<"ES256K">> }; to_map('ES256', F) -> F#{ <<"alg">> => <<"ES256">> }; to_map('ES384', F) -> @@ -51,6 +55,8 @@ to_map('ES512', F) -> %% jose_jws_alg callbacks %%==================================================================== +generate_key('ES256K', _Fields) -> + jose_jws_alg:generate_key({ec, <<"secp256k1">>}, <<"ES256K">>); generate_key('ES256', _Fields) -> jose_jws_alg:generate_key({ec, <<"P-256">>}, <<"ES256">>); generate_key('ES384', _Fields) -> diff --git a/src/jws/jose_jws_alg_rsa_pkcs1_v1_5.erl b/src/jws/jose_jws_alg_rsa_pkcs1_v1_5.erl index 1ca5b34..df3fc18 100644 --- a/src/jws/jose_jws_alg_rsa_pkcs1_v1_5.erl +++ b/src/jws/jose_jws_alg_rsa_pkcs1_v1_5.erl @@ -25,7 +25,7 @@ %% API %% Types --type alg() :: 'RS256' | 'RS384' | 'RS512'. +-type alg() :: 'RS1' | 'RS256' | 'RS384' | 'RS512'. -export_type([alg/0]). @@ -33,6 +33,8 @@ %% jose_jws callbacks %%==================================================================== +from_map(F = #{ <<"alg">> := <<"RS1">> }) -> + {'RS1', maps:remove(<<"alg">>, F)}; from_map(F = #{ <<"alg">> := <<"RS256">> }) -> {'RS256', maps:remove(<<"alg">>, F)}; from_map(F = #{ <<"alg">> := <<"RS384">> }) -> @@ -40,6 +42,8 @@ from_map(F = #{ <<"alg">> := <<"RS384">> }) -> from_map(F = #{ <<"alg">> := <<"RS512">> }) -> {'RS512', maps:remove(<<"alg">>, F)}. +to_map('RS1', F) -> + F#{ <<"alg">> => <<"RS1">> }; to_map('RS256', F) -> F#{ <<"alg">> => <<"RS256">> }; to_map('RS384', F) -> @@ -51,6 +55,8 @@ to_map('RS512', F) -> %% jose_jws_alg callbacks %%==================================================================== +generate_key('RS1', _Fields) -> + jose_jws_alg:generate_key({rsa, 2048}, <<"RS1">>); generate_key('RS256', _Fields) -> jose_jws_alg:generate_key({rsa, 2048}, <<"RS256">>); generate_key('RS384', _Fields) -> diff --git a/test/jose_SUITE.erl b/test/jose_SUITE.erl index be25e0f..2031b7a 100644 --- a/test/jose_SUITE.erl +++ b/test/jose_SUITE.erl @@ -317,7 +317,7 @@ jose_cfrg_curves_a_7(Config) -> % Public Key Authenticated Encryption for JOSE: ECDH-1PU % A. Example ECDH-1PU Key Agreement Computation with A256GCM -% [https://tools.ietf.org/html/draft-madden-jose-ecdh-1pu-02#appendix-A] +% [https://datatracker.ietf.org/doc/html/draft-madden-jose-ecdh-1pu-04#appendix-A] jose_ecdh_1pu_a(Config) -> C = ?config(jose_ecdh_1pu_a, Config), A_USSK_JWK = jose_jwk:from_binary(?config("a.ussk.jwk+json", C)),