potatosalad-erlang-jose-30ac019/0000775000232200023220000000000015267717064017142 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/.dialyzer_ignore.exs0000664000232200023220000000531115267717064023127 0ustar debalancedebalance[ # Initially all files are ignored # TODO: When fixing / completing specs & types of files, please remove the # corresponding entry below {"lib/jose/jwa.ex", :invalid_contract}, {"lib/jose/jwe.ex", :call}, {"lib/jose/jwe.ex", :pattern_match_cov}, {"lib/jose/jwe.ex", :invalid_contract}, {"lib/jose/jwk.ex", :call}, {"lib/jose/jwk.ex", :pattern_match_cov}, {"lib/jose/jws.ex", :pattern_match_cov}, {"lib/jose/jwt.ex", :call}, {"lib/jose/jwt.ex", :guard_fail}, {"src/jose_public_key.erl", :pattern_match}, {"src/jose_public_key.erl", :pattern_match_cov}, {"src/jose_public_key.erl", :call}, {"src/jose_public_key.erl", :no_return}, {"src/jose_server.erl", :pattern_match}, {"src/jose_server.erl", :pattern_match_cov}, {"src/json/jose_json_jiffy.erl", :unknown_function}, {"src/jwa/curve25519/jose_curve25519_crypto.erl", :callback_arg_type_mismatch}, {"src/jwa/curve25519/jose_curve25519_libdecaf.erl", :callback_arg_type_mismatch}, {"src/jwa/curve25519/jose_curve25519_libsodium.erl", :callback_arg_type_mismatch}, {"src/jwa/curve448/jose_curve448_crypto.erl", :callback_arg_type_mismatch}, {"src/jwa/curve448/jose_curve448_fallback.erl", :callback_spec_arg_type_mismatch}, {"src/jwa/curve448/jose_curve448_fallback.erl", :callback_spec_type_mismatch}, {"src/jwa/curve448/jose_curve448_libdecaf.erl", :callback_arg_type_mismatch}, {"src/jwa/curve448/jose_curve448_libdecaf.erl", :callback_type_mismatch}, {"src/jwa/jose_jwa_curve25519.erl", :callback_arg_type_mismatch}, {"src/jwa/jose_jwa_curve448.erl", :callback_arg_type_mismatch}, {"src/jwa/jose_jwa_curve448.erl", :callback_type_mismatch}, {"src/jwa/jose_jwa_pkcs1.erl", :invalid_contract}, {"src/jwa/jose_jwa_pkcs1.erl", :pattern_match_cov}, {"src/jwa/jose_jwa_pkcs1.erl", :guard_fail}, {"src/jwa/sha3/jose_sha3_keccakf1600_driver.erl", :unknown_function}, {"src/jwa/sha3/jose_sha3_keccakf1600_nif.erl", :unknown_function}, {"src/jwe/jose_jwe_alg_ecdh_1pu.erl", :record_match}, {"src/jwe/jose_jwe_alg_ecdh_1pu.erl", :record_constr}, {"src/jwe/jose_jwe_alg_ecdh_es.erl", :record_match}, {"src/jwe/jose_jwe_alg_ecdh_es.erl", :record_constr}, {"src/jwe/jose_jwe_alg_ecdh_ss.erl", :record_match}, {"src/jwe/jose_jwe_alg_ecdh_ss.erl", :record_constr}, {"src/jwk/jose_jwk_kty_okp_ed25519.erl", :call}, {"src/jwk/jose_jwk_kty_okp_ed25519ph.erl", :call}, {"src/jwk/jose_jwk_kty_okp_ed448.erl", :call}, {"src/jwk/jose_jwk_kty_okp_ed448ph.erl", :call}, {"src/jwk/jose_jwk_kty_rsa.erl", :unknown_function}, {"src/jws/jose_jws_alg_poly1305.erl", :call}, {"src/jws/jose_jws_alg_poly1305.erl", :record_constr}, {"src/jws/jose_jws_alg_poly1305.erl", :guard_fail_pat}, {"src/jws/jose_jws_alg_poly1305.erl", :no_return} ] potatosalad-erlang-jose-30ac019/include/0000775000232200023220000000000015267717064020565 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/include/jose_jwk.hrl0000664000232200023220000000122215267717064023104 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2015, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 21 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -ifndef(JOSE_JWK_HRL). -record(jose_jwk, { keys = undefined :: undefined | {module(), any()}, kty = undefined :: undefined | {module(), any()}, fields = #{} :: map() }). -define(JOSE_JWK_HRL, 1). -endif. potatosalad-erlang-jose-30ac019/include/jose_public_key.hrl0000664000232200023220000000522715267717064024450 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 12 May 2017 by Andrew Bennett %%%------------------------------------------------------------------- -ifndef(JOSE_PUBLIC_KEY_HRL). -include_lib("public_key/include/public_key.hrl"). -ifndef('id-aes128-CBC'). -define('id-aes128-CBC', {2,16,840,1,101,3,4,1,2}). -endif. -ifndef('id-aes192-CBC'). -define('id-aes192-CBC', {2,16,840,1,101,3,4,1,22}). -endif. -ifndef('id-aes256-CBC'). -define('id-aes256-CBC', {2,16,840,1,101,3,4,1,42}). -endif. %% PBES2 key-derivation defaults used when exporting a password-protected %% PKCS#8 private key. Shared by jose_jwk_der:to_binary/3 and %% jose_jwk_pem:to_binary/3 so that both export paths stay in sync. %% %% The OWASP "Password Storage Cheat Sheet" recommends 600,000 iterations for %% PBKDF2-HMAC-SHA256, and NIST SP 800-132 requires a salt of at least 128 %% bits. Decoding is unaffected by these values: the salt and iteration count %% are always read back out of the PBES2 parameters carried in the blob, so %% keys exported by earlier releases keep importing unchanged. -ifndef(JOSE_PBES2_SALT_BYTES). -define(JOSE_PBES2_SALT_BYTES, 16). -endif. -ifndef(JOSE_PBES2_ITERATION_COUNT). -define(JOSE_PBES2_ITERATION_COUNT, 600000). -endif. -define('jose_id-X25519', {1,3,101,110}). -define('jose_id-X448', {1,3,101,111}). -define('jose_id-EdDSA25519', {1,3,101,112}). -define('jose_id-EdDSA448', {1,3,101,113}). -record(jose_EdDSA25519PublicKey, { publicKey = undefined :: undefined | << _:256 >> }). -record(jose_EdDSA25519PrivateKey, { publicKey = undefined :: undefined | #jose_EdDSA25519PublicKey{}, privateKey = undefined :: undefined | << _:256 >> }). -record(jose_EdDSA448PublicKey, { publicKey = undefined :: undefined | << _:456 >> }). -record(jose_EdDSA448PrivateKey, { publicKey = undefined :: undefined | #jose_EdDSA448PublicKey{}, privateKey = undefined :: undefined | << _:456 >> }). -record(jose_X25519PublicKey, { publicKey = undefined :: undefined | << _:256 >> }). -record(jose_X25519PrivateKey, { publicKey = undefined :: undefined | #jose_X25519PublicKey{}, privateKey = undefined :: undefined | << _:256 >> }). -record(jose_X448PublicKey, { publicKey = undefined :: undefined | << _:448 >> }). -record(jose_X448PrivateKey, { publicKey = undefined :: undefined | #jose_X448PublicKey{}, privateKey = undefined :: undefined | << _:448 >> }). -define(JOSE_PUBLIC_KEY_HRL, 1). -endif. potatosalad-erlang-jose-30ac019/include/jose_jwt.hrl0000664000232200023220000000104015267717064023113 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2015, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 29 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -ifndef(JOSE_JWT_HRL). -record(jose_jwt, { fields = #{} :: map() }). -define(JOSE_JWT_HRL, 1). -endif. potatosalad-erlang-jose-30ac019/include/jose_jws.hrl0000664000232200023220000000121215267717064023113 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2015, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 23 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -ifndef(JOSE_JWS_HRL). -record(jose_jws, { alg = undefined :: undefined | {module(), any()}, b64 = undefined :: undefined | boolean(), fields = #{} :: map() }). -define(JOSE_JWS_HRL, 1). -endif. potatosalad-erlang-jose-30ac019/include/jose_compat.hrl0000664000232200023220000000161515267717064023602 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2018, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 19 Nov 2018 by Emil Falk %%%------------------------------------------------------------------- -ifndef(JOSE_COMPAT_HRL). -ifdef(OTP_RELEASE). %% this implies OTP 21 or higher -define(COMPAT_CATCH(Class, Reason, Stacktrace), Class:Reason:Stacktrace). -define(COMPAT_GET_STACKTRACE(Stacktrace), Stacktrace). -if(?OTP_RELEASE >= 23). -define(JOSE_CRYPTO_OTP_23, 1). -endif. -else. -define(COMPAT_CATCH(Class, Reason, _), Class:Reason). -define(COMPAT_GET_STACKTRACE(_), erlang:get_stacktrace()). -endif. -define(JOSE_COMPAT_HRL, 1). -endif. potatosalad-erlang-jose-30ac019/include/jose_base.hrl0000664000232200023220000000136115267717064023227 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2017-2019, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 11 May 2017 by Andrew Bennett %%%------------------------------------------------------------------- -ifndef(JOSE_BASE_HRL). -define(bnotzero(X), ((((X) bor ((bnot (X)) + 1)) bsr 7) band 1)). -define(is_iodata(I), (is_binary(I) orelse is_list(I))). -define(to_binary(I), (case I of _ when is_binary(I) -> I; _ when is_list(I) -> erlang:iolist_to_binary(I) end)). -define(JOSE_BASE_HRL, 1). -endif. potatosalad-erlang-jose-30ac019/include/jose.hrl0000664000232200023220000000112115267717064022227 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2015, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 23 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -ifndef(JOSE_HRL). -include("jose_jwe.hrl"). -include("jose_jwk.hrl"). -include("jose_jws.hrl"). -include("jose_jwt.hrl"). -define(JOSE_HRL, 1). -endif. potatosalad-erlang-jose-30ac019/include/jose_jwe.hrl0000664000232200023220000000131015267717064023074 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2015, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 22 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -ifndef(JOSE_JWE_HRL). -record(jose_jwe, { alg = undefined :: undefined | {module(), any()}, enc = undefined :: undefined | {module(), any()}, zip = undefined :: undefined | {module(), any()}, fields = #{} :: map() }). -define(JOSE_JWE_HRL, 1). -endif. potatosalad-erlang-jose-30ac019/README.md0000664000232200023220000006771315267717064020437 0ustar debalancedebalance# JOSE [![Build Status](https://github.com/potatosalad/erlang-jose/actions/workflows/main.yml/badge.svg?branch=main)](https://github.com/potatosalad/erlang-jose/actions) [![Hex.pm](https://img.shields.io/hexpm/v/jose.svg)](https://hex.pm/packages/jose) JSON Object Signing and Encryption (JOSE) for Erlang and Elixir. ## Installation Add `jose` to your project's dependencies in `mix.exs` ```elixir defp deps() do [ {:jose, "~> 1.11"} ] end ``` If you are using deployment tools (`exrm`, etc.) and your app depends on `jose` directly, you will need to include `jose` in your applications list in `mix.exs` to ensure they get compiled into your release: ```elixir def application() do [ mod: {YourApp, []}, applications: [:jose] ] end ``` Add `jose` to your project's dependencies in your `Makefile` for [`erlang.mk`](https://github.com/ninenines/erlang.mk) or the following to your `rebar.config` ```erlang {deps, [ jose ]}. ``` #### JSON Encoder/Decoder _NOTE:_ The following only applies to versions prior to Erlang/OTP 27 when the `json` module was included. If present, the builtin `json` module will be used unless overridden as described below. You will also need to specify either [jiffy](https://github.com/davisp/jiffy), [jsone](https://github.com/sile/jsone), [jsx](https://github.com/talentdeficit/jsx), [ojson](https://github.com/potatosalad/erlang-ojson), [Poison](https://github.com/devinus/poison), or [Jason](https://github.com/michalmuskala/jason) as a dependency. For example, with Elixir and `mix.exs` ```elixir defp deps() do [ {:jose, "~> 1.11"}, {:jason, "~> 1.4"} ] end ``` Or with Erlang and `rebar.config` ```erlang {deps, [ jose, ojson ]}. ``` `jose` will attempt to find a suitable JSON encoder/decoder and will try to use (in order) ojson, Jason, Poison, jiffy, jsone, or jsx. You may also specify a different `json_module` as an application environment variable to `jose` or by using `jose:json_module/1` or `JOSE.json_module/1`. #### ChaCha20/Poly1305 Support ChaCha20/Poly1305 encryption and one-time message authentication functions are experimentally supported based on [RFC 7539](https://tools.ietf.org/html/rfc7539). Fallback support for `ChaCha20/Poly1305` encryption and `Poly1305` signing is also provided. See [`crypto_fallback`](#cryptographic-algorithm-fallback) below. External support is also provided by the following libraries: * [libsodium](https://github.com/potatosalad/erlang-libsodium) - `ChaCha20/Poly1305` encryption and `Poly1305` signing Other modules which implement the `jose_chacha20_poly1305` behavior may also be used as follows: ```elixir # ChaCha20/Poly1305 JOSE.chacha20_poly1305_module(:libsodium) # uses a fast Erlang port driver for libsodium JOSE.chacha20_poly1305_module(:jose_jwa_chacha20_poly1305) # uses the pure Erlang implementation (slow) ``` #### Curve25519 and Curve448 Support Curve25519 and Curve448 and their associated signing/key exchange functions are supported now that [RFC 8037](https://tools.ietf.org/html/rfc8037) has been published. Fallback support for `Ed25519`, `Ed25519ph`, `Ed448`, `Ed448ph`, `X25519`, and `X448` is provided. See [`crypto_fallback`](#cryptographic-algorithm-fallback) below. External support is also provided by the following libraries: * [libdecaf](https://github.com/potatosalad/erlang-libdecaf) - `Ed25519`, `Ed25519ph`, `Ed448`, `Ed448ph`, `X25519`, `X448` * [libsodium](https://github.com/potatosalad/erlang-libsodium) - `Ed25519`, `Ed25519ph`, `X25519` If both libraries are present, libdecaf will be used by default. Other modules which implement the `jose_curve25519` or `jose_curve448` behaviors may also be used as follows: ```elixir # Curve25519 JOSE.curve25519_module(:libdecaf) # uses a fast Erlang NIF for libdecaf JOSE.curve25519_module(:jose_jwa_curve25519) # uses the pure Erlang implementation (slow) # Curve448 JOSE.curve448_module(:libdecaf) # uses a fast Erlang NIF for libdecaf JOSE.curve448_module(:jose_jwa_curve448) # uses the pure Erlang implementation (slow) ``` #### SHA-3 Support SHA-3 is experimentally supported for use with `Ed448` and `Ed448ph` signing functions. Fallback support for SHA-3 is provided. See [`crypto_fallback`](#cryptographic-algorithm-fallback) below. External support for SHA-3 is provided by the [keccakf1600](https://github.com/potatosalad/erlang-keccakf1600) and [libdecaf](https://github.com/potatosalad/erlang-libdecaf) libraries. If present, keccakf1600 will be used by default. Other modules which implement the `jose_sha3` behaviors may also be used as follows: ```elixir JOSE.sha3_module(:keccakf1600) # uses a NIF written in C with timeslice reductions JOSE.sha3_module(:jose_jwa_sha3) # uses the pure Erlang implementation (slow) ``` #### Cryptographic Algorithm Fallback `jose` strives to support [all](#algorithm-support) of the cryptographic algorithms specified in the [JOSE RFCs](https://tools.ietf.org/wg/jose/). However, not all of the required algorithms are supported natively by Erlang/Elixir. For algorithms unsupported by the native [`crypto`](http://www.erlang.org/doc/man/crypto.html) and [`public_key`](http://www.erlang.org/doc/man/public_key.html), `jose` has a pure Erlang implementation that may be used as a fallback. See [ALGORITHMS.md](https://github.com/potatosalad/erlang-jose/blob/main/ALGORITHMS.md) for more information about algorithm support for specific OTP versions. By default, the algorithm fallback is disabled, but can be enabled by setting the `crypto_fallback` application environment variable for `jose` to `true` or by calling `jose:crypto_fallback/1` or `JOSE.crypto_fallback/1` with `true`. > [!WARNING] > **The pure Erlang fallback implementations are not constant-time.** > > The pure Erlang elliptic curve modules (`jose_jwa_curve25519`/`jose_jwa_x25519` and `jose_jwa_curve448`/`jose_jwa_x448`) perform scalar multiplication with Erlang bignum arithmetic, whose execution time depends on operand values. They therefore leak timing information about the private scalar and should not be used with long-lived private keys in any context where an attacker can observe operation timing. > > These modules exist as a last-resort fallback for platforms with no native implementation. `jose` probes `libdecaf`, `libsodium`, and then OTP `crypto` for Curve25519, and `libdecaf` and OTP `crypto` for Curve448. The selected provider handles X25519/X448 when it supports those operations. The OTP provider still delegates unsupported EdDSA context and prehash operations to the fallback. Setting `crypto_fallback` to `true` enables the pure Erlang implementation for unavailable operations; it does not override a working native module. Release 1.11.13 corrects the shipped application environment to disable fallback by default, as documented above. > > Selecting the pure Erlang curve modules explicitly (for example `jose:curve25519_module(jose_jwa_curve25519)` or `JOSE.curve25519_module(:jose_jwa_curve25519)`) opts out of the native provider path. Do not do this with long-lived keys in production. For new JWE applications, prefer `RSA-OAEP` or `RSA-OAEP-256` over `RSA1_5`. JWE decryption substitutes a random content encryption key on RSA unwrap failure and authenticates the content before returning plaintext. This normalizes the unwrap error result; it is not a guarantee against timing attacks on the underlying RSA provider. The low-level `jose_jwa_pkcs1` RSA padding helpers report padding failures and must not be exposed as a decryption service for untrusted ciphertexts. RSA private JWKs containing only `(n, e, d)` require CRT factor recovery. This search accepts moduli up to 8192 bits and uses at most 32 random trials and 256 modular squares. It checks a 200 ms work deadline before each expensive operation, or 500 ms for moduli of 6145–8192 bits. An in-progress native operation can finish after the deadline. Very large keys, unusually difficult keys or a slow runtime can exhaust this budget even for a valid key; supply `p`, `q`, `dp`, `dq` and `qi` to import without recovery. Random trials preserve keys that a fixed small-base search can miss. These limits bound recovery work, not total request CPU or wall time on every platform. You may also review which algorithms are currently supported with the `jose_jwa:supports/0` or `JOSE.JWA.supports/0` functions. For example, on Elixir 1.9.4 and OTP 22: ```elixir # crypto_fallback defaults to false JOSE.JWA.supports() [ {:jwe, {:alg, ["A128GCMKW", "A128KW", "A192GCMKW", "A192KW", "A256GCMKW", "A256KW", "C20PKW", "ECDH-1PU", "ECDH-1PU+A128GCMKW", "ECDH-1PU+A128KW", "ECDH-1PU+A192GCMKW", "ECDH-1PU+A192KW", "ECDH-1PU+A256GCMKW", "ECDH-1PU+A256KW", "ECDH-1PU+C20PKW", "ECDH-1PU+XC20PKW", "ECDH-ES", "ECDH-ES+A128GCMKW", "ECDH-ES+A128KW", "ECDH-ES+A192GCMKW", "ECDH-ES+A192KW", "ECDH-ES+A256GCMKW", "ECDH-ES+A256KW", "ECDH-ES+C20PKW", "ECDH-ES+XC20PKW", "ECDH-SS", "ECDH-SS+A128GCMKW", "ECDH-SS+A128KW", "ECDH-SS+A192GCMKW", "ECDH-SS+A192KW", "ECDH-SS+A256GCMKW", "ECDH-SS+A256KW", "ECDH-SS+C20PKW", "ECDH-SS+XC20PKW", "PBES2-HS256+A128GCMKW", "PBES2-HS256+A128KW", "PBES2-HS384+A192GCMKW", "PBES2-HS384+A192KW", "PBES2-HS512+A256GCMKW", "PBES2-HS512+A256KW", "PBES2-HS512+C20PKW", "PBES2-HS512+XC20PKW", "RSA-OAEP", "RSA-OAEP-256", "RSA1_5", "XC20PKW", "dir"]}, {:enc, ["A128CBC-HS256", "A128GCM", "A192CBC-HS384", "A192GCM", "A256CBC-HS512", "A256GCM", "C20P", "XC20P"]}, {:zip, ["DEF"]}}, {:jwk, {:kty, ["EC", "OKP", "RSA", "oct"]}, {:kty_OKP_crv, ["Ed25519", "Ed25519ph", "Ed448", "Ed448ph", "X25519", "X448"]}}, {:jws, {:alg, ["ES256", "ES256K", "ES384", "ES512", "Ed25519", "Ed25519ph", "Ed448", "Ed448ph", "EdDSA", "HS256", "HS384", "HS512", "PS256", "PS384", "PS512", "Poly1305", "RS1", "RS256", "RS384", "RS512"]}} ] # setting crypto_fallback to true JOSE.crypto_fallback(true) # additional algorithms are now available for use JOSE.JWA.supports() [ {:jwe, {:alg, ["A128GCMKW", "A128KW", "A192GCMKW", "A192KW", "A256GCMKW", "A256KW", "C20PKW", "ECDH-1PU", "ECDH-1PU+A128GCMKW", "ECDH-1PU+A128KW", "ECDH-1PU+A192GCMKW", "ECDH-1PU+A192KW", "ECDH-1PU+A256GCMKW", "ECDH-1PU+A256KW", "ECDH-1PU+C20PKW", "ECDH-1PU+XC20PKW", "ECDH-ES", "ECDH-ES+A128GCMKW", "ECDH-ES+A128KW", "ECDH-ES+A192GCMKW", "ECDH-ES+A192KW", "ECDH-ES+A256GCMKW", "ECDH-ES+A256KW", "ECDH-ES+C20PKW", "ECDH-ES+XC20PKW", "ECDH-SS", "ECDH-SS+A128GCMKW", "ECDH-SS+A128KW", "ECDH-SS+A192GCMKW", "ECDH-SS+A192KW", "ECDH-SS+A256GCMKW", "ECDH-SS+A256KW", "ECDH-SS+C20PKW", "ECDH-SS+XC20PKW", "PBES2-HS256+A128GCMKW", "PBES2-HS256+A128KW", "PBES2-HS384+A192GCMKW", "PBES2-HS384+A192KW", "PBES2-HS512+A256GCMKW", "PBES2-HS512+A256KW", "PBES2-HS512+C20PKW", "PBES2-HS512+XC20PKW", "RSA-OAEP", "RSA-OAEP-256", "RSA1_5", "XC20PKW", "dir"]}, {:enc, ["A128CBC-HS256", "A128GCM", "A192CBC-HS384", "A192GCM", "A256CBC-HS512", "A256GCM", "C20P", "XC20P"]}, {:zip, ["DEF"]}}, {:jwk, {:kty, ["EC", "OKP", "RSA", "oct"]}, {:kty_OKP_crv, ["Ed25519", "Ed25519ph", "Ed448", "Ed448ph", "X25519", "X448"]}}, {:jws, {:alg, ["ES256", "ES256K", "ES384", "ES512", "Ed25519", "Ed25519ph", "Ed448", "Ed448ph", "EdDSA", "HS256", "HS384", "HS512", "PS256", "PS384", "PS512", "Poly1305", "RS1", "RS256", "RS384", "RS512"]}} ] ``` #### Unsecured Signing Vulnerability The [`"none"`](https://tools.ietf.org/html/rfc7515#appendix-A.5) signing algorithm is disabled by default to prevent accidental verification of empty signatures (read about the vulnerability [here](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/)). If you want to further restrict the signature algorithms allowed for a token, use `JOSE.JWT.verify_strict/3`: ```elixir # Signed Compact JSON Web Token (JWT) with HS256 token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlLCJpc3MiOiJqb2UifQ.shLcxOl_HBBsOTvPnskfIlxHUibPN7Y9T4LhPB-iBwM" # JSON Web Key (JWK) jwk = %{ "kty" => "oct", "k" => :jose_base64url.encode("symmetric key") } {verified, _, _} = JOSE.JWT.verify_strict(jwk, ["HS256"], token) # {true, _, _} {verified, _, _} = JOSE.JWT.verify_strict(jwk, ["RS256"], token) # {false, _, _} ``` If you need to inspect the contents of a JSON Web token (JWT) prior to verifying it, use `JOSE.JWT.peek_payload/1` or `JOSE.JWT.peek_protected/1`: ```elixir token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlLCJpc3MiOiJqb2UifQ.shLcxOl_HBBsOTvPnskfIlxHUibPN7Y9T4LhPB-iBwM" payload = JOSE.JWT.peek_payload(token) # %JOSE.JWT{fields: %{"exp" => 1300819380, "http://example.com/is_root" => true, # "iss" => "joe"}} protected = JOSE.JWT.peek_protected(token) # %JOSE.JWS{alg: {:jose_jws_alg_hmac, {:jose_jws_alg_hmac, :sha256}}, # b64: :undefined, fields: %{"typ" => "JWT"}} # If you want to inspect the JSON, you can convert it back to a regular map: {_, protected_map} = JOSE.JWS.to_map(protected) # {_, %{"alg" => "HS256", "typ" => "JWT"}} ``` You may also enable the `"none"` algorithm as an application environment variable for `jose` or by using `jose:unsecured_signing/1` or `JOSE.unsecured_signing/1`. ```elixir # unsecured_signing defaults to false JOSE.JWA.supports[:jws] {:alg, ["ES256", "ES256K", "ES384", "ES512", "Ed25519", "Ed25519ph", "Ed448", "Ed448ph", "EdDSA", "HS256", "HS384", "HS512", "PS256", "PS384", "PS512", "Poly1305", "RS1", "RS256", "RS384", "RS512"]} # setting unsecured_signing to true JOSE.unsecured_signing(true) # the "none" algorithm is now available for use JOSE.JWA.supports[:jws] {:alg, ["ES256", "ES256K", "ES384", "ES512", "Ed25519", "Ed25519ph", "Ed448", "Ed448ph", "EdDSA", "HS256", "HS384", "HS512", "PS256", "PS384", "PS512", "Poly1305", "RS1", "RS256", "RS384", "RS512", "none"]} ``` ## Usage ##### JSON Web Signature (JWS) of JSON Web Token (JWT) using HMAC using SHA-256 (HS256) with JSON Web Key (JWK) _Elixir_ ```elixir # JSON Web Key (JWK) jwk = %{ "kty" => "oct", "k" => :jose_base64url.encode("symmetric key") } # JSON Web Signature (JWS) jws = %{ "alg" => "HS256" } # JSON Web Token (JWT) jwt = %{ "iss" => "joe", "exp" => 1300819380, "http://example.com/is_root" => true } signed = JOSE.JWT.sign(jwk, jws, jwt) # {%{alg: :jose_jws_alg_hmac}, # %{"payload" => "eyJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlLCJpc3MiOiJqb2UifQ", # "protected" => "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9", # "signature" => "shLcxOl_HBBsOTvPnskfIlxHUibPN7Y9T4LhPB-iBwM"}} compact_signed = JOSE.JWS.compact(signed) # {%{alg: :jose_jws_alg_hmac}, # "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlLCJpc3MiOiJqb2UifQ.shLcxOl_HBBsOTvPnskfIlxHUibPN7Y9T4LhPB-iBwM"} verified = JOSE.JWT.verify(jwk, compact_signed) # {true, # %JOSE.JWT{fields: %{"exp" => 1300819380, "http://example.com/is_root" => true, # "iss" => "joe"}}, # %JOSE.JWS{alg: {:jose_jws_alg_hmac, :HS256}, b64: :undefined, # fields: %{"typ" => "JWT"}}} verified == JOSE.JWT.verify(jwk, signed) # true ``` _Erlang_ ```erlang % JSON Web Key (JWK) JWK = #{ <<"kty">> => <<"oct">>, <<"k">> => jose_base64url:encode(<<"symmetric key">>) }. % JSON Web Signature (JWS) JWS = #{ <<"alg">> => <<"HS256">> }. % JSON Web Token (JWT) JWT = #{ <<"iss">> => <<"joe">>, <<"exp">> => 1300819380, <<"http://example.com/is_root">> => true }. Signed = jose_jwt:sign(JWK, JWS, JWT). % {#{alg => jose_jws_alg_hmac}, % #{<<"payload">> => <<"eyJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlLCJpc3MiOiJqb2UifQ">>, % <<"protected">> => <<"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9">>, % <<"signature">> => <<"shLcxOl_HBBsOTvPnskfIlxHUibPN7Y9T4LhPB-iBwM">>}} CompactSigned = jose_jws:compact(Signed). % {#{alg => jose_jws_alg_hmac}, % <<"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlLCJpc3MiOiJqb2UifQ.shLcxOl_HBBsOTvPnskfIlxHUibPN7Y9T4LhPB-iBwM">>} Verified = jose_jwt:verify(JWK, CompactSigned). % {true, % #jose_jwt{ % fields = % #{<<"exp">> => 1300819380, % <<"http://example.com/is_root">> => true, % <<"iss">> => <<"joe">>}}, % #jose_jws{ % alg = {jose_jws_alg_hmac,'HS256'}, % b64 = undefined, % fields = #{<<"typ">> => <<"JWT">>}}} Verified =:= jose_jwt:verify(JWK, Signed). % true ``` ##### Reading JSON Web Keys (JWK) from PEM files The examples below use three keys created with `openssl`: ```bash # RSA Private Key openssl genrsa -out rsa-2048.pem 2048 # EC Private Key (Alice) openssl ecparam -name secp256r1 -genkey -noout -out ec-secp256r1-alice.pem # EC Private Key (Bob) openssl ecparam -name secp256r1 -genkey -noout -out ec-secp256r1-bob.pem ``` _Elixir_ ```elixir # RSA examples rsa_private_jwk = JOSE.JWK.from_pem_file("rsa-2048.pem") rsa_public_jwk = JOSE.JWK.to_public(rsa_private_jwk) ## Sign and Verify (defaults to PS256) message = "my message" signed = JOSE.JWK.sign(message, rsa_private_jwk) {true, ^message, _} = JOSE.JWK.verify(signed, rsa_public_jwk) ## Sign and Verify (specify RS256) signed = JOSE.JWK.sign(message, %{ "alg" => "RS256" }, rsa_private_jwk) {true, ^message, _} = JOSE.JWK.verify(signed, rsa_public_jwk) ## Encrypt and Decrypt (defaults to RSA-OAEP with A128CBC-HS256) plain_text = "my plain text" encrypted = JOSE.JWK.block_encrypt(plain_text, rsa_public_jwk) {^plain_text, _} = JOSE.JWK.block_decrypt(encrypted, rsa_private_jwk) ## Encrypt and Decrypt (specify RSA-OAEP-256 with A128GCM) encrypted = JOSE.JWK.block_encrypt(plain_text, %{ "alg" => "RSA-OAEP-256", "enc" => "A128GCM" }, rsa_public_jwk) {^plain_text, _} = JOSE.JWK.block_decrypt(encrypted, rsa_private_jwk) # EC examples alice_private_jwk = JOSE.JWK.from_pem_file("ec-secp256r1-alice.pem") alice_public_jwk = JOSE.JWK.to_public(alice_private_jwk) bob_private_jwk = JOSE.JWK.from_pem_file("ec-secp256r1-bob.pem") bob_public_jwk = JOSE.JWK.to_public(bob_private_jwk) ## Sign and Verify (defaults to ES256) message = "my message" signed = JOSE.JWK.sign(message, alice_private_jwk) {true, ^message, _} = JOSE.JWK.verify(signed, alice_public_jwk) ## Encrypt and Decrypt (defaults to ECDH-ES with A128GCM) ### Alice sends Bob a secret message using Bob's public key and Alice's private key alice_to_bob = "For Bob's eyes only." encrypted = JOSE.JWK.box_encrypt(alice_to_bob, bob_public_jwk, alice_private_jwk) ### Only Bob can decrypt the message using his private key (Alice's public key is embedded in the JWE header) {^alice_to_bob, _} = JOSE.JWK.box_decrypt(encrypted, bob_private_jwk) ``` _Erlang_ ```erlang % RSA examples RSAPrivateJWK = jose_jwk:from_pem_file("rsa-2048.pem"), RSAPublicJWK = jose_jwk:to_public(RSAPrivateJWK). %% Sign and Verify (defaults to PS256) Message = <<"my message">>, SignedPS256 = jose_jwk:sign(Message, RSAPrivateJWK), {true, Message, _} = jose_jwk:verify(SignedPS256, RSAPublicJWK). %% Sign and Verify (specify RS256) SignedRS256 = jose_jwk:sign(Message, #{ <<"alg">> => <<"RS256">> }, RSAPrivateJWK), {true, Message, _} = jose_jwk:verify(SignedRS256, RSAPublicJWK). %% Encrypt and Decrypt (defaults to RSA-OAEP with A128CBC-HS256) PlainText = <<"my plain text">>, EncryptedRSAOAEP = jose_jwk:block_encrypt(PlainText, RSAPublicJWK), {PlainText, _} = jose_jwk:block_decrypt(EncryptedRSAOAEP, RSAPrivateJWK). %% Encrypt and Decrypt (specify RSA-OAEP-256 with A128GCM) EncryptedRSAOAEP256 = jose_jwk:block_encrypt(PlainText, #{ <<"alg">> => <<"RSA-OAEP-256">>, <<"enc">> => <<"A128GCM">> }, RSAPublicJWK), {PlainText, _} = jose_jwk:block_decrypt(EncryptedRSAOAEP256, RSAPrivateJWK). % EC examples AlicePrivateJWK = jose_jwk:from_pem_file("ec-secp256r1-alice.pem"), AlicePublicJWK = jose_jwk:to_public(AlicePrivateJWK), BobPrivateJWK = jose_jwk:from_pem_file("ec-secp256r1-bob.pem"), BobPublicJWK = jose_jwk:to_public(BobPrivateJWK). %% Sign and Verify (defaults to ES256) Message = <<"my message">>, SignedES256 = jose_jwk:sign(Message, AlicePrivateJWK), {true, Message, _} = jose_jwk:verify(SignedES256, AlicePublicJWK). %% Encrypt and Decrypt (defaults to ECDH-ES with A128GCM) %%% Alice sends Bob a secret message using Bob's public key and Alice's private key AliceToBob = <<"For Bob's eyes only.">>, EncryptedECDHES = jose_jwk:box_encrypt(AliceToBob, BobPublicJWK, AlicePrivateJWK), %%% Only Bob can decrypt the message using his private key (Alice's public key is embedded in the JWE header) {AliceToBob, _} = jose_jwk:box_decrypt(EncryptedECDHES, BobPrivateJWK). ``` ## Algorithm Support ### JSON Web Encryption (JWE) [RFC 7516](https://tools.ietf.org/html/rfc7516) #### `"alg"` [RFC 7518 Section 4](https://tools.ietf.org/html/rfc7518#section-4) - [X] `A128GCMKW` - [X] `A192GCMKW` - [X] `A256GCMKW` - [X] `A128KW` - [X] `A192KW` - [X] `A256KW` - [X] `C20PKW` [draft-amringer-jose-chacha](https://datatracker.ietf.org/doc/html/draft-amringer-jose-chacha-02) - [X] `dir` - [X] `ECDH-1PU` [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 - [X] `ECDH-ES+A256GCMKW` non-standard - [X] `ECDH-ES+A128KW` - [X] `ECDH-ES+A192KW` - [X] `ECDH-ES+A256KW` - [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] `ECDH-SS` [draft-amringer-jose-ecdh-ss](https://datatracker.ietf.org/doc/html/draft-amringer-jose-ecdh-ss-00) - [X] `ECDH-SS+A128GCMKW` non-standard, [draft-amringer-jose-ecdh-ss](https://datatracker.ietf.org/doc/html/draft-amringer-jose-ecdh-ss-00) - [X] `ECDH-SS+A192GCMKW` non-standard, [draft-amringer-jose-ecdh-ss](https://datatracker.ietf.org/doc/html/draft-amringer-jose-ecdh-ss-00) - [X] `ECDH-SS+A256GCMKW` non-standard, [draft-amringer-jose-ecdh-ss](https://datatracker.ietf.org/doc/html/draft-amringer-jose-ecdh-ss-00) - [X] `ECDH-SS+A128KW` [draft-amringer-jose-ecdh-ss](https://datatracker.ietf.org/doc/html/draft-amringer-jose-ecdh-ss-00) - [X] `ECDH-SS+A192KW` [draft-amringer-jose-ecdh-ss](https://datatracker.ietf.org/doc/html/draft-amringer-jose-ecdh-ss-00) - [X] `ECDH-SS+A256KW` [draft-amringer-jose-ecdh-ss](https://datatracker.ietf.org/doc/html/draft-amringer-jose-ecdh-ss-00) - [X] `ECDH-SS+C20PKW` [draft-amringer-jose-chacha](https://datatracker.ietf.org/doc/html/draft-amringer-jose-chacha-02), [draft-amringer-jose-ecdh-ss](https://datatracker.ietf.org/doc/html/draft-amringer-jose-ecdh-ss-00) - [X] `ECDH-SS+XC20PKW` [draft-amringer-jose-chacha](https://datatracker.ietf.org/doc/html/draft-amringer-jose-chacha-02), [draft-amringer-jose-ecdh-ss](https://datatracker.ietf.org/doc/html/draft-amringer-jose-ecdh-ss-00) - [X] `PBES2-HS256+A128GCMKW` non-standard - [X] `PBES2-HS384+A192GCMKW` non-standard - [X] `PBES2-HS512+A256GCMKW` non-standard - [X] `PBES2-HS256+A128KW` - [X] `PBES2-HS384+A192KW` - [X] `PBES2-HS512+A256KW` - [X] `PBES2-HS512+C20PKW` non-standard - [X] `PBES2-HS512+XC20PKW` non-standard - [X] `RSA1_5` - [X] `RSA-OAEP` - [X] `RSA-OAEP-256` - [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) - [X] `A128CBC-HS256` - [X] `A192CBC-HS384` - [X] `A256CBC-HS512` - [X] `A128GCM` - [X] `A192GCM` - [X] `A256GCM` - [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) - [X] `DEF` ### JSON Web Key (JWK) [RFC 7517](https://tools.ietf.org/html/rfc7517) #### `"alg"` [RFC 7518 Section 6](https://tools.ietf.org/html/rfc7518#section-6) - [X] `EC` - [X] `oct` - [X] `OKP` [RFC 8037](https://tools.ietf.org/html/rfc8037) - [X] `OKP` with `{"crv":"Ed25519"}` [RFC 8037](https://tools.ietf.org/html/rfc8037), [RFC 8032](https://tools.ietf.org/html/rfc8032#section-5.1) - [X] `OKP` with `{"crv":"Ed25519ph"}` [RFC 8037](https://tools.ietf.org/html/rfc8037), [RFC 8032](https://tools.ietf.org/html/rfc8032#section-5.1) - [X] `OKP` with `{"crv":"Ed448"}` [RFC 8037](https://tools.ietf.org/html/rfc8037), [RFC 8032](https://tools.ietf.org/html/rfc8032#section-5.2) - [X] `OKP` with `{"crv":"Ed448ph"}` [RFC 8037](https://tools.ietf.org/html/rfc8037), [RFC 8032](https://tools.ietf.org/html/rfc8032#section-5.2) - [X] `OKP` with `{"crv":"X25519"}` [RFC 8037](https://tools.ietf.org/html/rfc8037), [RFC 7748](https://tools.ietf.org/html/rfc7748#section-5) - [X] `OKP` with `{"crv":"X448"}` [RFC 8037](https://tools.ietf.org/html/rfc8037), [RFC 7748](https://tools.ietf.org/html/rfc7748#section-5) - [X] `RSA` ### JSON Web Signature (JWS) [RFC 7515](https://tools.ietf.org/html/rfc7515) #### `"alg"` [RFC 7518 Section 3](https://tools.ietf.org/html/rfc7518#section-3) - [X] `Ed25519` [RFC 8037](https://tools.ietf.org/html/rfc8037), [RFC 8032](https://tools.ietf.org/html/rfc8032#section-5.1) - [X] `Ed25519ph` [RFC 8037](https://tools.ietf.org/html/rfc8037), [RFC 8032](https://tools.ietf.org/html/rfc8032#section-5.1) - [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` - [X] `HS256` - [X] `HS384` - [X] `HS512` - [X] `Poly1305` non-standard - [X] `PS256` - [X] `PS384` - [X] `PS512` - [X] `RS1` deprecated, [RFC 8812](https://datatracker.ietf.org/doc/html/rfc8812) - [X] `RS256` - [X] `RS384` - [X] `RS512` - [X] `none` [unsecured](#footnote-unsecured) ### Additional Specifications - [X] JSON Web Key (JWK) Thumbprint [RFC 7638](https://tools.ietf.org/html/rfc7638) - [X] JWS Unencoded Payload Option [RFC 7797](https://tools.ietf.org/html/rfc7797) unsecured This algorithm is disabled by default due to the unsecured signing vulnerability. Use the [`unsecured_signing`](#unsecured-signing-vulnerability) setting to enable this algorithm. potatosalad-erlang-jose-30ac019/codegen/0000775000232200023220000000000015267717064020546 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/codegen/jose_base.erl0000664000232200023220000001501315267717064023204 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: nil; st-rulers: [132] -*- %% vim: ts=4 sw=4 ft=erlang et %%% % @format %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 24 Feb 2018 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_base). %% API -export([calculate_shift/1]). -export([calculate_pairs/2]). %% parse_transform callbacks -export([parse_transform/2]). %%%=================================================================== %%% API functions %%%=================================================================== calculate_shift(Table) when is_list(Table) -> erlang:round(math:log2(length(Table))). calculate_pairs(Table, Type) when Type == sensitive orelse Type == upper -> Shift = calculate_shift(Table), do_calculate_pairs(Table, Table, Shift, []). %% @private do_calculate_pairs([{V0, C0} | Rest], Table, Shift, Acc0) -> Acc1 = do_calculate_pairs_permutation(V0 bsl Shift, C0 bsl 8, Table, Acc0), do_calculate_pairs(Rest, Table, Shift, Acc1); do_calculate_pairs([], _Table, _Shift, Acc) -> lists:reverse(Acc). %% @private do_calculate_pairs_permutation(V0, C0, [{V1, C1} | Rest], Acc0) -> V = V0 + V1, C = C0 + C1, Acc1 = [{V, C} | Acc0], do_calculate_pairs_permutation(V0, C0, Rest, Acc1); do_calculate_pairs_permutation(_, _, [], Acc) -> Acc. %%%=================================================================== %%% parse_transform callbacks %%%=================================================================== parse_transform(Ast0, _Options) -> Ast1 = walk_ast([], Ast0), [Module] = [Module || {attribute, _Line, module, Module} <- Ast1], % io:format("Module = ~p~n", [Module]), Forms = epp:restore_typed_record_fields( revert([ Form || Form = {function, _Line, Name, _Arity, _Clauses} <- Ast1, Name =:= encode_char orelse Name =:= encode_pair ]) ), ok = lists:foreach( fun(Form = {function, _Line, Name, _Arity, _Clauses}) -> Filename = unicode:characters_to_list(io_lib:format("~s.~s.erl", [Module, Name])), Body = erlang:iolist_to_binary( binary:split(unicode:characters_to_binary(erl_pp:form(Form)), <<"\n">>, [global, trim_all]) ), ok = file:write_file(Filename, <<"%%% % @format\n", Body/binary>>), io:format("Wrote to ~s~n", [Filename]), ok end, Forms ), % Str = [ % io_lib:fwrite( % "~s~n", % [ % lists:flatten([ % erl_pp:form(Fm) % || Fm <- Forms % ]) % ] % ) % ], % io:format("~s~n", [Str]), Ast1. revert(Tree) -> [erl_syntax:revert(T) || T <- lists:flatten(Tree)]. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private walk_ast(Acc, []) -> lists:reverse(Acc); walk_ast(Acc, [{function, Line, Name, Arity, Clauses} | Ast]) -> walk_ast([{function, Line, Name, Arity, walk_clauses([], Clauses)} | Acc], Ast); walk_ast(Acc, [Form | Ast]) -> walk_ast([Form | Acc], Ast). %% @private walk_clauses(Acc, []) -> lists:reverse(Acc); walk_clauses(Acc, [{clause, Line, Arguments, Guards, Body} | Ast]) -> walk_clauses([{clause, Line, Arguments, Guards, walk_body([], Body)} | Acc], Ast). %% @private walk_body(Acc, []) -> lists:reverse(Acc); walk_body(Acc, [Statement | Ast]) -> walk_body([transform_statement(Statement) | Acc], Ast). %% @private transform_statement( Statement = {call, Line, {remote, _Line1, {atom, _Line2, ?MODULE}, {atom, _Line3, Function}}, Arguments} ) -> case transform_call(Function, Line, Arguments) of {ok, NewStatement} -> NewStatement; error -> Statement end; transform_statement({match, Line, Pattern, Expression}) -> {match, Line, Pattern, transform_expression(Expression)}; transform_statement(Statement) -> Statement. %% @private transform_expression({bc, Line1, {bin, Line2, Elements}, Qualifiers}) -> {bc, Line1, {bin, Line2, walk_elements([], Elements)}, Qualifiers}; transform_expression(Expression) -> Expression. %% @private walk_elements(Acc, []) -> lists:reverse(Acc); walk_elements(Acc, [{bin_element, Line, Statement, Size, Type} | Ast]) -> walk_elements([{bin_element, Line, transform_statement(Statement), Size, Type} | Acc], Ast); walk_elements(Acc, [Element | Ast]) -> walk_elements([Element | Acc], Ast). %% @private transform_call(encode_char_as_case, _Line, [ Case = {'case', _Line1, _Var, [{clause, _Line2, [{integer, _Line3, 0}], [], [{char, _Line4, _}]} | _]} ]) -> {ok, Case}; transform_call(encode_pair_as_case, Line, [ {'case', _Line1, Var, Clauses0 = [{clause, _Line2, [{integer, _Line3, 0}], [], [{char, _Line4, _}]} | _]}, {atom, _Line5, Type} ]) -> Table = calculate_pairs( [{Index, Value} || {clause, _, [{integer, _, Index}], [], [{char, _, Value}]} <- Clauses0], Type ), Clauses1 = [{clause, Line, [{integer, Line, Index}], [], [{integer, Line, Value}]} || {Index, Value} <- Table], Case = {'case', Line, Var, Clauses1}, {ok, Case}; transform_call(encode_char_as_tuple_element_call, Line, [ {'case', _Line1, Var, Clauses0 = [{clause, _Line2, [{integer, _Line3, 0}], [], [{char, _Line4, _}]} | _]} ]) -> Pairs = [{Index, Value} || {clause, _, [{integer, _, Index}], [], [{char, _, Value}]} <- Clauses0], encode_as_tuple_element_call(Line, Var, Pairs); transform_call(encode_pair_as_tuple_element_call, Line, [ {'case', _Line1, Var, Clauses0 = [{clause, _Line2, [{integer, _Line3, 0}], [], [{char, _Line4, _}]} | _]}, {atom, _Line5, Type} ]) -> Pairs = calculate_pairs( [{Index, Value} || {clause, _, [{integer, _, Index}], [], [{char, _, Value}]} <- Clauses0], Type ), encode_as_tuple_element_call(Line, Var, Pairs); transform_call(_Function, _Line, _Arguments) -> error. %% @private encode_as_tuple_element_call(Line, Var, Pairs) -> Table = maps:from_list(Pairs), Elements = [{char, Line, maps:get(Index, Table)} || Index <- lists:seq(0, maps:size(Table) - 1)], VarPlusOne = {op, Line, '+', Var, {integer, Line, 1}}, Tuple = {tuple, Line, Elements}, Call = {call, Line, {atom, Line, element}, [VarPlusOne, Tuple]}, {ok, Call}. potatosalad-erlang-jose-30ac019/codegen/jose_base64url.erl0000664000232200023220000002006515267717064024104 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc RFC 4648, Section 5: https://tools.ietf.org/html/rfc4648#section-5 %%% %%% @end %%% Created : 11 May 2017 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_base64url). -compile({parse_transform, jose_base}). -include("jose_base.hrl"). %% API -export([decode/1]). -export([decode/2]). -export(['decode!'/1]). -export(['decode!'/2]). -export([encode/1]). -export([encode/2]). -export([random/1]). -export([random/2]). % Macros -define(B64URL_TO_INT(C), case C of $A -> 16#00; $B -> 16#01; $C -> 16#02; $D -> 16#03; $E -> 16#04; $F -> 16#05; $G -> 16#06; $H -> 16#07; $I -> 16#08; $J -> 16#09; $K -> 16#0A; $L -> 16#0B; $M -> 16#0C; $N -> 16#0D; $O -> 16#0E; $P -> 16#0F; $Q -> 16#10; $R -> 16#11; $S -> 16#12; $T -> 16#13; $U -> 16#14; $V -> 16#15; $W -> 16#16; $X -> 16#17; $Y -> 16#18; $Z -> 16#19; $a -> 16#1A; $b -> 16#1B; $c -> 16#1C; $d -> 16#1D; $e -> 16#1E; $f -> 16#1F; $g -> 16#20; $h -> 16#21; $i -> 16#22; $j -> 16#23; $k -> 16#24; $l -> 16#25; $m -> 16#26; $n -> 16#27; $o -> 16#28; $p -> 16#29; $q -> 16#2A; $r -> 16#2B; $s -> 16#2C; $t -> 16#2D; $u -> 16#2E; $v -> 16#2F; $w -> 16#30; $x -> 16#31; $y -> 16#32; $z -> 16#33; $0 -> 16#34; $1 -> 16#35; $2 -> 16#36; $3 -> 16#37; $4 -> 16#38; $5 -> 16#39; $6 -> 16#3A; $7 -> 16#3B; $8 -> 16#3C; $9 -> 16#3D; $- -> 16#3E; $_ -> 16#3F end). -define(INT_TO_B64URL(C), case C of 16#00 -> $A; 16#01 -> $B; 16#02 -> $C; 16#03 -> $D; 16#04 -> $E; 16#05 -> $F; 16#06 -> $G; 16#07 -> $H; 16#08 -> $I; 16#09 -> $J; 16#0A -> $K; 16#0B -> $L; 16#0C -> $M; 16#0D -> $N; 16#0E -> $O; 16#0F -> $P; 16#10 -> $Q; 16#11 -> $R; 16#12 -> $S; 16#13 -> $T; 16#14 -> $U; 16#15 -> $V; 16#16 -> $W; 16#17 -> $X; 16#18 -> $Y; 16#19 -> $Z; 16#1A -> $a; 16#1B -> $b; 16#1C -> $c; 16#1D -> $d; 16#1E -> $e; 16#1F -> $f; 16#20 -> $g; 16#21 -> $h; 16#22 -> $i; 16#23 -> $j; 16#24 -> $k; 16#25 -> $l; 16#26 -> $m; 16#27 -> $n; 16#28 -> $o; 16#29 -> $p; 16#2A -> $q; 16#2B -> $r; 16#2C -> $s; 16#2D -> $t; 16#2E -> $u; 16#2F -> $v; 16#30 -> $w; 16#31 -> $x; 16#32 -> $y; 16#33 -> $z; 16#34 -> $0; 16#35 -> $1; 16#36 -> $2; 16#37 -> $3; 16#38 -> $4; 16#39 -> $5; 16#3A -> $6; 16#3B -> $7; 16#3C -> $8; 16#3D -> $9; 16#3E -> $-; 16#3F -> $_ end). %%%=================================================================== %%% API functions %%%=================================================================== decode(Input) when ?is_iodata(Input) -> decode(Input, #{}). decode(Input, Opts) when ?is_iodata(Input) andalso is_map(Opts) -> try 'decode!'(Input, Opts) of Output when is_binary(Output) -> {ok, Output} catch _:_ -> error end; decode(Input, Opts) when ?is_iodata(Input) andalso is_list(Opts) -> decode(Input, maps:from_list(Opts)). 'decode!'(Input) when ?is_iodata(Input) -> 'decode!'(Input, #{}). 'decode!'([], #{}) -> <<>>; 'decode!'(<<>>, #{}) -> <<>>; 'decode!'(Input, Opts) when ?is_iodata(Input) andalso is_map(Opts) -> Padding = maps:get('padding', Opts, nil), Size = erlang:iolist_size(Input), Offset = case Padding of _ when (Padding == false orelse Padding == nil) andalso Size =< 4 -> 0; _ when (Padding == false orelse Padding == nil) andalso (Size rem 4) =/= 0 -> Size - (Size rem 4); _ when (Padding == false orelse Padding == nil) -> Size - 4; _ when (Padding == true orelse Padding == nil) andalso Size >= 4 -> Size - 4; _ -> erlang:error({badarg, [Input, Opts]}) end, << Head0:Offset/binary, Tail0/binary >> = ?to_binary(Input), Head = << << (?B64URL_TO_INT(V)):6 >> || << V >> <= Head0 >>, Tail = case Padding of false -> case Tail0 of << T0:8, T1:8 >> -> << (?B64URL_TO_INT(T0)):6, (?B64URL_TO_INT(T1) bsr 4):2 >>; << T0:8, T1:8, T2:8 >> -> << (?B64URL_TO_INT(T0)):6, (?B64URL_TO_INT(T1)):6, (?B64URL_TO_INT(T2) bsr 2):4 >>; << T:4/binary >> -> << << (?B64URL_TO_INT(V)):6 >> || << V >> <= T >>; <<>> -> <<>>; _ -> erlang:error({badarg, [Input, Opts]}) end; nil -> case Tail0 of << T0:8, T1:8, $=, $= >> -> << (?B64URL_TO_INT(T0)):6, (?B64URL_TO_INT(T1) bsr 4):2 >>; << T0:8, T1:8, T2:8, $= >> -> << (?B64URL_TO_INT(T0)):6, (?B64URL_TO_INT(T1)):6, (?B64URL_TO_INT(T2) bsr 2):4 >>; << T0:8, T1:8 >> -> << (?B64URL_TO_INT(T0)):6, (?B64URL_TO_INT(T1) bsr 4):2 >>; << T0:8, T1:8, T2:8 >> -> << (?B64URL_TO_INT(T0)):6, (?B64URL_TO_INT(T1)):6, (?B64URL_TO_INT(T2) bsr 2):4 >>; << T:4/binary >> -> << << (?B64URL_TO_INT(V)):6 >> || << V >> <= T >>; <<>> -> <<>> end; true -> case Tail0 of << T0:8, T1:8, $=, $= >> -> << (?B64URL_TO_INT(T0)):6, (?B64URL_TO_INT(T1) bsr 4):2 >>; << T0:8, T1:8, T2:8, $= >> -> << (?B64URL_TO_INT(T0)):6, (?B64URL_TO_INT(T1)):6, (?B64URL_TO_INT(T2) bsr 2):4 >>; << T:4/binary >> -> << << (?B64URL_TO_INT(V)):6 >> || << V >> <= T >>; <<>> -> <<>>; _ -> erlang:error({badarg, [Input, Opts]}) end end, << Head/binary, Tail/binary >>; 'decode!'(Input, Opts) when ?is_iodata(Input) andalso is_list(Opts) -> 'decode!'(Input, maps:from_list(Opts)). encode(Input) when ?is_iodata(Input) -> encode(Input, #{}). encode(Input, Opts) when ?is_iodata(Input) andalso is_map(Opts) -> Padding = maps:get('padding', Opts, true), Offset = 6 * (erlang:iolist_size(Input) div 6), << Head:Offset/binary, Tail/binary >> = ?to_binary(Input), H = << << (encode_pair(V0)):16, (encode_pair(V1)):16, (encode_pair(V2)):16, (encode_pair(V3)):16 >> || << V0:12, V1:12, V2:12, V3:12 >> <= Head >>, {T, Pad} = case Tail of << T0:12, T1:12, T2:12, T3:4 >> -> {<< (encode_pair(T0)):16, (encode_pair(T1)):16, (encode_pair(T2)):16, (encode_char(T3 bsl 2)):8 >>, << $= >>}; << T0:12, T1:12, T2:8 >> -> {<< (encode_pair(T0)):16, (encode_pair(T1)):16, (encode_pair(T2 bsl 4)):16 >>, << $=, $= >>}; << T0:12, T1:12 >> -> {<< (encode_pair(T0)):16, (encode_pair(T1)):16 >>, <<>>}; << T0:12, T1:4 >> -> {<< (encode_pair(T0)):16, (encode_char(T1 bsl 2)):8 >>, <<>>}; << T0:8 >> -> {<< (encode_pair(T0 bsl 4)):16 >>, << $=, $= >>}; <<>> -> {<<>>, <<>>} end, case Padding of true -> << H/binary, T/binary, Pad/binary >>; false -> << H/binary, T/binary >>; _ -> erlang:error({badarg, [Input, Opts]}) end; encode(Input, Opts) when ?is_iodata(Input) andalso is_list(Opts) -> encode(Input, maps:from_list(Opts)). random(Bytes) when is_integer(Bytes) andalso Bytes >= 0 -> random(Bytes, #{}). random(0, Opts) when is_map(Opts) -> <<>>; random(Bytes, Opts) when (Bytes =:= 1) andalso is_map(Opts) -> erlang:error({badarg, [Bytes, Opts]}); random(Bytes, Opts) when is_integer(Bytes) andalso Bytes > 0 andalso is_map(Opts) -> Padding = maps:get('padding', Opts, true), R = (Bytes rem 4), Size = case Padding of true when R =:= 0 -> (Bytes * 3) div 4; false when R =:= 0 orelse R =:= 2 orelse R =:= 3 -> (Bytes * 3) div 4; _ -> erlang:error({badarg, [Bytes, Opts]}) end, Binary = crypto:strong_rand_bytes(Size), encode(Binary, Opts); random(Bytes, Opts) when is_integer(Bytes) andalso Bytes >= 0 andalso is_list(Opts) -> random(Bytes, maps:from_list(Opts)). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- -compile({inline, [{encode_char, 1}]}). %% @private encode_char(V) -> jose_base:encode_char_as_tuple_element_call(?INT_TO_B64URL(V)). -compile({inline, [{encode_pair, 1}]}). %% @private encode_pair(V) -> jose_base:encode_pair_as_tuple_element_call(?INT_TO_B64URL(V), sensitive). potatosalad-erlang-jose-30ac019/codegen/jose_base.hrl0000777000232200023220000000000015267717064027501 2../include/jose_base.hrlustar debalancedebalancepotatosalad-erlang-jose-30ac019/codegen/codegen.sh0000775000232200023220000000076015267717064022514 0ustar debalancedebalance#!/usr/bin/env bash set -eo pipefail set -x erlc +debug_info jose_base.erl erlc -pa . +debug_info jose_base64.erl ../erlfmt --write --require-pragma --print-width=132 jose_base64.encode_char.erl ../erlfmt --write --require-pragma --print-width=132 jose_base64.encode_pair.erl erlc -pa . +debug_info jose_base64url.erl ../erlfmt --write --require-pragma --print-width=132 jose_base64url.encode_char.erl ../erlfmt --write --require-pragma --print-width=132 jose_base64url.encode_pair.erl exit 0 potatosalad-erlang-jose-30ac019/codegen/jose_base64.erl0000664000232200023220000001773615267717064023374 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc RFC 4648, Section 4: https://tools.ietf.org/html/rfc4648#section-4 %%% %%% @end %%% Created : 11 May 2017 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_base64). -compile({parse_transform, jose_base}). -include("jose_base.hrl"). %% API -export([decode/1]). -export([decode/2]). -export(['decode!'/1]). -export(['decode!'/2]). -export([encode/1]). -export([encode/2]). -export([random/1]). -export([random/2]). % Macros -define(B64_TO_INT(C), case C of $A -> 16#00; $B -> 16#01; $C -> 16#02; $D -> 16#03; $E -> 16#04; $F -> 16#05; $G -> 16#06; $H -> 16#07; $I -> 16#08; $J -> 16#09; $K -> 16#0A; $L -> 16#0B; $M -> 16#0C; $N -> 16#0D; $O -> 16#0E; $P -> 16#0F; $Q -> 16#10; $R -> 16#11; $S -> 16#12; $T -> 16#13; $U -> 16#14; $V -> 16#15; $W -> 16#16; $X -> 16#17; $Y -> 16#18; $Z -> 16#19; $a -> 16#1A; $b -> 16#1B; $c -> 16#1C; $d -> 16#1D; $e -> 16#1E; $f -> 16#1F; $g -> 16#20; $h -> 16#21; $i -> 16#22; $j -> 16#23; $k -> 16#24; $l -> 16#25; $m -> 16#26; $n -> 16#27; $o -> 16#28; $p -> 16#29; $q -> 16#2A; $r -> 16#2B; $s -> 16#2C; $t -> 16#2D; $u -> 16#2E; $v -> 16#2F; $w -> 16#30; $x -> 16#31; $y -> 16#32; $z -> 16#33; $0 -> 16#34; $1 -> 16#35; $2 -> 16#36; $3 -> 16#37; $4 -> 16#38; $5 -> 16#39; $6 -> 16#3A; $7 -> 16#3B; $8 -> 16#3C; $9 -> 16#3D; $+ -> 16#3E; $/ -> 16#3F end). -define(INT_TO_B64(C), case C of 16#00 -> $A; 16#01 -> $B; 16#02 -> $C; 16#03 -> $D; 16#04 -> $E; 16#05 -> $F; 16#06 -> $G; 16#07 -> $H; 16#08 -> $I; 16#09 -> $J; 16#0A -> $K; 16#0B -> $L; 16#0C -> $M; 16#0D -> $N; 16#0E -> $O; 16#0F -> $P; 16#10 -> $Q; 16#11 -> $R; 16#12 -> $S; 16#13 -> $T; 16#14 -> $U; 16#15 -> $V; 16#16 -> $W; 16#17 -> $X; 16#18 -> $Y; 16#19 -> $Z; 16#1A -> $a; 16#1B -> $b; 16#1C -> $c; 16#1D -> $d; 16#1E -> $e; 16#1F -> $f; 16#20 -> $g; 16#21 -> $h; 16#22 -> $i; 16#23 -> $j; 16#24 -> $k; 16#25 -> $l; 16#26 -> $m; 16#27 -> $n; 16#28 -> $o; 16#29 -> $p; 16#2A -> $q; 16#2B -> $r; 16#2C -> $s; 16#2D -> $t; 16#2E -> $u; 16#2F -> $v; 16#30 -> $w; 16#31 -> $x; 16#32 -> $y; 16#33 -> $z; 16#34 -> $0; 16#35 -> $1; 16#36 -> $2; 16#37 -> $3; 16#38 -> $4; 16#39 -> $5; 16#3A -> $6; 16#3B -> $7; 16#3C -> $8; 16#3D -> $9; 16#3E -> $+; 16#3F -> $/ end). %%%=================================================================== %%% API functions %%%=================================================================== decode(Input) when ?is_iodata(Input) -> decode(Input, #{}). decode(Input, Opts) when ?is_iodata(Input) andalso is_map(Opts) -> try 'decode!'(Input, Opts) of Output when is_binary(Output) -> {ok, Output} catch _:_ -> error end; decode(Input, Opts) when ?is_iodata(Input) andalso is_list(Opts) -> decode(Input, maps:from_list(Opts)). 'decode!'(Input) when ?is_iodata(Input) -> 'decode!'(Input, #{}). 'decode!'([], #{}) -> <<>>; 'decode!'(<<>>, #{}) -> <<>>; 'decode!'(Input, Opts) when ?is_iodata(Input) andalso is_map(Opts) -> Padding = maps:get('padding', Opts, nil), Size = erlang:iolist_size(Input), Offset = case Padding of _ when (Padding == false orelse Padding == nil) andalso Size =< 4 -> 0; _ when (Padding == false orelse Padding == nil) andalso (Size rem 4) =/= 0 -> Size - (Size rem 4); _ when (Padding == false orelse Padding == nil) -> Size - 4; _ when (Padding == true orelse Padding == nil) andalso Size >= 4 -> Size - 4; _ -> erlang:error({badarg, [Input, Opts]}) end, << Head0:Offset/binary, Tail0/binary >> = ?to_binary(Input), Head = << << (?B64_TO_INT(V)):6 >> || << V >> <= Head0 >>, Tail = case Padding of false -> case Tail0 of << T0:8, T1:8 >> -> << (?B64_TO_INT(T0)):6, (?B64_TO_INT(T1) bsr 4):2 >>; << T0:8, T1:8, T2:8 >> -> << (?B64_TO_INT(T0)):6, (?B64_TO_INT(T1)):6, (?B64_TO_INT(T2) bsr 2):4 >>; << T:4/binary >> -> << << (?B64_TO_INT(V)):6 >> || << V >> <= T >>; <<>> -> <<>>; _ -> erlang:error({badarg, [Input, Opts]}) end; nil -> case Tail0 of << T0:8, T1:8, $=, $= >> -> << (?B64_TO_INT(T0)):6, (?B64_TO_INT(T1) bsr 4):2 >>; << T0:8, T1:8, T2:8, $= >> -> << (?B64_TO_INT(T0)):6, (?B64_TO_INT(T1)):6, (?B64_TO_INT(T2) bsr 2):4 >>; << T0:8, T1:8 >> -> << (?B64_TO_INT(T0)):6, (?B64_TO_INT(T1) bsr 4):2 >>; << T0:8, T1:8, T2:8 >> -> << (?B64_TO_INT(T0)):6, (?B64_TO_INT(T1)):6, (?B64_TO_INT(T2) bsr 2):4 >>; << T:4/binary >> -> << << (?B64_TO_INT(V)):6 >> || << V >> <= T >>; <<>> -> <<>> end; true -> case Tail0 of << T0:8, T1:8, $=, $= >> -> << (?B64_TO_INT(T0)):6, (?B64_TO_INT(T1) bsr 4):2 >>; << T0:8, T1:8, T2:8, $= >> -> << (?B64_TO_INT(T0)):6, (?B64_TO_INT(T1)):6, (?B64_TO_INT(T2) bsr 2):4 >>; << T:4/binary >> -> << << (?B64_TO_INT(V)):6 >> || << V >> <= T >>; <<>> -> <<>>; _ -> erlang:error({badarg, [Input, Opts]}) end end, << Head/binary, Tail/binary >>; 'decode!'(Input, Opts) when ?is_iodata(Input) andalso is_list(Opts) -> 'decode!'(Input, maps:from_list(Opts)). encode(Input) when ?is_iodata(Input) -> encode(Input, #{}). encode(Input, Opts) when ?is_iodata(Input) andalso is_map(Opts) -> Padding = maps:get('padding', Opts, true), Offset = 6 * (erlang:iolist_size(Input) div 6), << Head:Offset/binary, Tail/binary >> = ?to_binary(Input), H = << << (encode_pair(V0)):16, (encode_pair(V1)):16, (encode_pair(V2)):16, (encode_pair(V3)):16 >> || << V0:12, V1:12, V2:12, V3:12 >> <= Head >>, {T, Pad} = case Tail of << T0:12, T1:12, T2:12, T3:4 >> -> {<< (encode_pair(T0)):16, (encode_pair(T1)):16, (encode_pair(T2)):16, (encode_char(T3 bsl 2)):8 >>, << $= >>}; << T0:12, T1:12, T2:8 >> -> {<< (encode_pair(T0)):16, (encode_pair(T1)):16, (encode_pair(T2 bsl 4)):16 >>, << $=, $= >>}; << T0:12, T1:12 >> -> {<< (encode_pair(T0)):16, (encode_pair(T1)):16 >>, <<>>}; << T0:12, T1:4 >> -> {<< (encode_pair(T0)):16, (encode_char(T1 bsl 2)):8 >>, <<>>}; << T0:8 >> -> {<< (encode_pair(T0 bsl 4)):16 >>, << $=, $= >>}; <<>> -> {<<>>, <<>>} end, case Padding of true -> << H/binary, T/binary, Pad/binary >>; false -> << H/binary, T/binary >>; _ -> erlang:error({badarg, [Input, Opts]}) end; encode(Input, Opts) when ?is_iodata(Input) andalso is_list(Opts) -> encode(Input, maps:from_list(Opts)). random(Bytes) when is_integer(Bytes) andalso Bytes >= 0 -> random(Bytes, #{}). random(0, Opts) when is_map(Opts) -> <<>>; random(Bytes, Opts) when (Bytes =:= 1) andalso is_map(Opts) -> erlang:error({badarg, [Bytes, Opts]}); random(Bytes, Opts) when is_integer(Bytes) andalso Bytes > 0 andalso is_map(Opts) -> Padding = maps:get('padding', Opts, true), R = (Bytes rem 4), Size = case Padding of true when R =:= 0 -> (Bytes * 3) div 4; false when R =:= 0 orelse R =:= 2 orelse R =:= 3 -> (Bytes * 3) div 4; _ -> erlang:error({badarg, [Bytes, Opts]}) end, Binary = crypto:strong_rand_bytes(Size), encode(Binary, Opts); random(Bytes, Opts) when is_integer(Bytes) andalso Bytes >= 0 andalso is_list(Opts) -> random(Bytes, maps:from_list(Opts)). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- -compile({inline, [{encode_char, 1}]}). %% @private encode_char(V) -> jose_base:encode_char_as_tuple_element_call(?INT_TO_B64(V)). -compile({inline, [{encode_pair, 1}]}). %% @private encode_pair(V) -> jose_base:encode_pair_as_tuple_element_call(?INT_TO_B64(V), sensitive). potatosalad-erlang-jose-30ac019/codegen/clean.sh0000775000232200023220000000016315267717064022167 0ustar debalancedebalance#!/usr/bin/env bash set -eo pipefail set -x rm -f *.beam rm -f *.encode_char.erl rm -f *.encode_pair.erl exit 0 potatosalad-erlang-jose-30ac019/rebar.config0000664000232200023220000000132415267717064021424 0ustar debalancedebalance%%% Copyright (c) Andrew Bennett %%% %%% This source code is licensed under the MIT license found in the %%% LICENSE.md file in the root directory of this source tree. %%% %%% % @format {erl_opts, [ debug_info, warnings_as_errors ]}. % {deps, [ % {eqwalizer_support, % {git_subdir, "https://github.com/WhatsApp/eqwalizer.git", {branch, "main"}, "eqwalizer_support"}} % ]}. {shell, % {config, "config/sys.config"}, [{apps, [jose]}]}. {dialyzer, [ {plt_apps, all_deps}, {warnings, [ unknown ]} ]}. {xref_checks, [ undefined_function_calls, undefined_functions, locals_not_used, % exports_not_used, deprecated_function_calls, deprecated_functions ]}. potatosalad-erlang-jose-30ac019/mix.lock0000664000232200023220000001010715267717064020610 0ustar debalancedebalance%{ "dialyxir": {:hex, :dialyxir, "1.4.8", "7ef671a8aff9948b091d8c30f09467fbb16e77305cda451bce48109a0f5e021c", [:mix], [{:erlex, ">= 0.2.8", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "cbd5a851571e5dfeb32aaf2e840bfa98b7864cb3071bf2ef5d95d1276b12e072"}, "earmark_parser": {:hex, :earmark_parser, "1.4.46", "67607a0532e810c6f630a515c548d0b24949643f168cc556303bee4cf96105c7", [:mix], [], "hexpm", "9c44636e8a1c68c62f526b2dcd85d941dbbcee7ab82cf64ba06ce28bef8e89f5"}, "erlex": {:hex, :erlex, "0.2.9", "7debbbaa9f4f368b8cd648983e0f1d7963028508e9c59e9d4ed504e94ef52a55", [:mix], [], "hexpm", "8cfffc0ec7159e6d73de2ab28a588064de80f88b2798d5cbe4482cbbc200178b"}, "ex_doc": {:hex, :ex_doc, "0.40.4", "66f2e42bf588594d5a8aab31cad87f2ddad09d0da1b1a2f379340ec2c2e497cb", [:mix], [{:earmark_parser, "~> 1.4.46", [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", "6222b9e423d76584ee34df2c82a5ed72c2d53dc153f7f483ad28b378694186cc"}, "jason": {:hex, :jason, "1.4.5", "2e3a008590b0b8d7388c20293e9dcc9cf3e5d642fd2a114e4cbbb52e595d940a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b0c823996102bcd0239b3c2444eb00409b72f6a140c1950bc8b457d836b30684"}, "jsone": {:hex, :jsone, "1.9.0", "2242dc76859db74e27ae6b6209bbf4f03651b8eddb53fa8002a931d35953e9b6", [:rebar3], [], "hexpm", "6863dcf95a7fc26094eaaff0d72522d58fe22719b4758afb75a21735bc92646d"}, "jsx": {:hex, :jsx, "3.1.0", "d12516baa0bb23a59bb35dccaf02a1bd08243fcbb9efe24f2d9d056ccff71268", [:rebar3], [], "hexpm", "0c5cc8fdc11b53cc25cf65ac6705ad39e54ecc56d1c22e4adb8f5a53fb9427f3"}, "libdecaf": {:hex, :libdecaf, "3.0.0", "7d44ebe9e7be40b38f303c1d3e210d55f7c6581c8ad87a7991f3b83a2ee62361", [:rebar3], [], "hexpm", "13915b4c0fb9c04d9c8540dd8092dcc693813213aee3da159a31bb505e35e522"}, "libsodium": {:hex, :libsodium, "3.0.1", "9e80d95972ee76b7aa08bd8ebaeb04724e894323bc7565742c274f40a23099bf", [:rebar3], [], "hexpm", "9f1c12f23dc126204df1be4574dbcb6291f95e71cfa56ce781534e09a0b82878"}, "makeup": {:hex, :makeup, "1.2.3", "c3ef0ff83acf305384acba0f70bff87d8732502baa1eb3eb081d8c154fbe18f5", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "15970751b7021c2bcd7ed56a54b3def23affd3969fb7c214dffdf28e97ce6f76"}, "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [: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", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, "makeup_erlang": {:hex, :makeup_erlang, "1.1.0", "835f7e60792e08824cda445639555d7bf1bbbddb1b60b306e33cb6f6db24dc74", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "1cd6780fb1dd1a03979abaed0fe82712b0625118fd5257d3ebbf73f960c73c3c"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, "ojson": {:hex, :ojson, "1.0.0", "fd28614eadaec00a15cdb2f53f29d8717a812a508ddb80d202f2f2e2aaeabbcc", [:mix, :rebar3], [], "hexpm", "125088e64d95704194dc8b4b5095502363e0ca870b72712f61929499d3c48d00"}, "poison": {:hex, :poison, "6.0.0", "9bbe86722355e36ffb62c51a552719534257ba53f3271dacd20fbbd6621a583a", [:mix], [{:decimal, "~> 2.1", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "bb9064632b94775a3964642d6a78281c07b7be1319e0016e1643790704e739a2"}, "thoas": {:hex, :thoas, "1.2.1", "19a25f31177a17e74004d4840f66d791d4298c5738790fa2cc73731eb911f195", [:rebar3], [], "hexpm", "e38697edffd6e91bd12cea41b155115282630075c2a727e7a6b2947f5408b86a"}, } potatosalad-erlang-jose-30ac019/LICENSE.md0000664000232200023220000000214315267717064020546 0ustar debalancedebalanceThe MIT License (MIT) ===================== Copyright (c) Andrew Bennett Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. potatosalad-erlang-jose-30ac019/erlang.mk0000664000232200023220000051647015267717064020760 0ustar debalancedebalance# Copyright (c) Loïc Hoguin # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST))) export ERLANG_MK_FILENAME ERLANG_MK_VERSION = 841abb9 ERLANG_MK_WITHOUT = # Make 3.81 and 3.82 are deprecated. ifeq ($(MAKELEVEL)$(MAKE_VERSION),03.81) $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html) endif ifeq ($(MAKELEVEL)$(MAKE_VERSION),03.82) $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html) endif # Core configuration. PROJECT ?= $(notdir $(CURDIR)) PROJECT := $(strip $(PROJECT)) PROJECT_VERSION ?= rolling PROJECT_MOD ?= PROJECT_ENV ?= [] # Verbosity. V ?= 0 verbose_0 = @ verbose_2 = set -x; verbose = $(verbose_$(V)) ifeq ($V,3) SHELL := $(SHELL) -x endif gen_verbose_0 = @echo " GEN " $@; gen_verbose_2 = set -x; gen_verbose = $(gen_verbose_$(V)) gen_verbose_esc_0 = @echo " GEN " $$@; gen_verbose_esc_2 = set -x; gen_verbose_esc = $(gen_verbose_esc_$(V)) # Temporary files directory. ERLANG_MK_TMP ?= $(CURDIR)/.erlang.mk export ERLANG_MK_TMP # "erl" command. ERL = erl -noinput -boot no_dot_erlang -kernel start_distribution false +P 1024 +Q 1024 +S 1:1 # Platform detection. ifeq ($(PLATFORM),) UNAME_S := $(shell uname -s) ifeq ($(UNAME_S),Linux) PLATFORM = linux else ifeq ($(UNAME_S),Darwin) PLATFORM = darwin else ifeq ($(UNAME_S),SunOS) PLATFORM = solaris else ifeq ($(UNAME_S),GNU) PLATFORM = gnu else ifeq ($(UNAME_S),FreeBSD) PLATFORM = freebsd else ifeq ($(UNAME_S),NetBSD) PLATFORM = netbsd else ifeq ($(UNAME_S),OpenBSD) PLATFORM = openbsd else ifeq ($(UNAME_S),DragonFly) PLATFORM = dragonfly else ifeq ($(shell uname -o),Msys) PLATFORM = msys2 else $(error Unable to detect platform. Please open a ticket with the output of uname -a.) endif export PLATFORM endif # Core targets. all:: deps app rel # Noop to avoid a Make warning when there's nothing to do. rel:: $(verbose) : relup:: deps app check:: tests clean:: clean-crashdump clean-crashdump: ifneq ($(wildcard erl_crash.dump),) $(gen_verbose) rm -f erl_crash.dump endif distclean:: clean distclean-tmp $(ERLANG_MK_TMP): $(verbose) mkdir -p $(ERLANG_MK_TMP) distclean-tmp: $(gen_verbose) rm -rf $(ERLANG_MK_TMP) help:: $(verbose) printf "%s\n" \ "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \ "Copyright (c) Loïc Hoguin " \ "" \ "Usage: [V=1] $(MAKE) [target]..." \ "" \ "Core targets:" \ " all Run deps, app and rel targets in that order" \ " app Compile the project" \ " deps Fetch dependencies (if needed) and compile them" \ " fetch-deps Fetch dependencies recursively (if needed) without building the project" \ " lock Enable the dependency lock recorded in lock.mk" \ " unlock Disable the dependency lock recorded in lock.mk" \ " list-deps List dependencies recursively on stdout" \ " tree Print the name and version of BUILD_DEPS and DEPS" \ " search q=... Search for a package in the built-in index" \ " rel Build a release for this project, if applicable" \ " docs Build the documentation for this project" \ " install-docs Install the man pages for this project" \ " check Compile and run all tests and analysis for this project" \ " tests Run the tests for this project" \ " clean Delete temporary and output files from most targets" \ " distclean Delete all temporary and output files" \ " help Display this help and exit" \ " erlang-mk Update erlang.mk to the latest version" # Core functions. empty := space := $(empty) $(empty) tab := $(empty) $(empty) comma := , define newline endef define comma_list $(subst $(space),$(comma),$(strip $1)) endef define escape_dquotes $(subst ",\",$1) endef # Adding erlang.mk to make Erlang scripts who call init:get_plain_arguments() happy. define erlang $(ERL) $2 -pz $(ERLANG_MK_TMP)/rebar3/_build/prod/lib/*/ebin/ -eval "$(subst $(newline),,$(call escape_dquotes,$1))" -- erlang.mk endef ifeq ($(PLATFORM),msys2) core_native_path = $(shell cygpath -m $1) else core_native_path = $1 endif core_http_get = curl -Lf$(if $(filter-out 0,$V),,s)o $(call core_native_path,$1) $2 core_eq = $(and $(findstring $1,$2),$(findstring $2,$1)) # We skip files that contain spaces because they end up causing issues. # Files that begin with a dot are already ignored by the wildcard function. core_find = $(foreach f,$(wildcard $(1:%/=%)/*),$(if $(wildcard $f/.),$(call core_find,$f,$2),$(if $(filter $(subst *,%,$2),$f),$(if $(wildcard $f),$f)))) core_lc = $(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$1)))))))))))))))))))))))))) core_ls = $(filter-out $1,$(shell echo $1)) define core_relpath.erl Drop = fun D([A|T], [A|F]) -> D(T, F); D(T, F) -> {T, F} end, To0 = "$(call core_native_path,$1)", To = filename:split(filename:absname(To0)), From0 = "$(call core_native_path,$2)", From = filename:split(filename:absname(From0)), Rel = case {To, From} of {[H|_], [H|_]} -> {T1, F1} = Drop(To, From), case lists:duplicate(length(F1), "..") ++ T1 of [] -> "."; P -> filename:join(P) end; _ -> filename:absname(To0) end, io:format("~s", [Rel]), halt() endef core_relpath = $(shell $(call erlang,$(call core_relpath.erl,$1,$2))) define core_render printf -- '$(subst $(newline),\n,$(subst %,%%,$(subst ','\'',$(subst $(tab),$(WS),$(call $1)))))\n' > $2 endef # Automated update. ERLANG_MK_REPO ?= https://github.com/ninenines/erlang.mk ERLANG_MK_COMMIT ?= ERLANG_MK_BUILD_CONFIG ?= build.config ERLANG_MK_BUILD_DIR ?= .erlang.mk.build erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT) erlang-mk: ifdef ERLANG_MK_COMMIT $(verbose) git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR) $(verbose) cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT) else $(verbose) git clone --depth 1 $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR) endif $(verbose) if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi $(gen_verbose) $(MAKE) --no-print-directory -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))' UPGRADE=1 $(verbose) cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk $(verbose) rm -rf $(ERLANG_MK_BUILD_DIR) $(verbose) rm -rf $(ERLANG_MK_TMP) # The erlang.mk package index is bundled in the default erlang.mk build. # Search for the string "copyright" to skip to the rest of the code. # Copyright (c) Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. .PHONY: distclean-kerl KERL_INSTALL_DIR ?= $(HOME)/erlang ifeq ($(strip $(KERL)),) KERL := $(ERLANG_MK_TMP)/kerl/kerl endif KERL_DIR = $(ERLANG_MK_TMP)/kerl export KERL KERL_GIT ?= https://github.com/kerl/kerl KERL_COMMIT ?= e2e0ac5dc3dd8f49ce65e3db87f06986127b478a # 4.4.0 KERL_MAKEFLAGS ?= OTP_GIT ?= https://github.com/erlang/otp define kerl_otp_target $(KERL_INSTALL_DIR)/$1: $(KERL) $(verbose) if [ ! -d $$@ ]; then \ MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1; \ $(KERL) install $1 $(KERL_INSTALL_DIR)/$1; \ fi endef $(KERL): | $(ERLANG_MK_TMP) $(verbose) rm -rf $(KERL_DIR) $(gen_verbose) git clone -q -n -- $(KERL_GIT) $(ERLANG_MK_TMP)/kerl \ && cd $(ERLANG_MK_TMP)/kerl && git checkout -q $(KERL_COMMIT) \ && chmod +x $(KERL) \ || { rm -rf $(KERL_DIR); exit 1; } distclean:: distclean-kerl distclean-kerl: $(gen_verbose) rm -rf $(KERL_DIR) # Allow users to select which version of Erlang/OTP to use for a project. ifneq ($(strip $(LATEST_ERLANG_OTP)),) # In some environments it is necessary to filter out master. ERLANG_OTP := $(notdir $(lastword $(sort\ $(filter-out $(KERL_INSTALL_DIR)/master $(KERL_INSTALL_DIR)/OTP_R%,\ $(filter-out %-rc1 %-rc2 %-rc3,$(wildcard $(KERL_INSTALL_DIR)/*[^-native])))))) endif ERLANG_OTP ?= # Use kerl to enforce a specific Erlang/OTP version for a project. ifneq ($(strip $(ERLANG_OTP)),) export PATH := $(KERL_INSTALL_DIR)/$(ERLANG_OTP)/bin:$(PATH) SHELL := env PATH=$(PATH) $(SHELL) $(eval $(call kerl_otp_target,$(ERLANG_OTP))) # Build Erlang/OTP only if it doesn't already exist. ifeq ($(wildcard $(KERL_INSTALL_DIR)/$(ERLANG_OTP))$(BUILD_ERLANG_OTP),) $(info Building Erlang/OTP $(ERLANG_OTP)... Please wait...) $(shell $(MAKE) $(KERL_INSTALL_DIR)/$(ERLANG_OTP) ERLANG_OTP=$(ERLANG_OTP) BUILD_ERLANG_OTP=1 >&2) endif endif PACKAGES += asciideck pkg_asciideck_name = asciideck pkg_asciideck_description = Asciidoc for Erlang. pkg_asciideck_homepage = https://ninenines.eu pkg_asciideck_fetch = git pkg_asciideck_repo = https://github.com/ninenines/asciideck pkg_asciideck_commit = master PACKAGES += cowboy pkg_cowboy_name = cowboy pkg_cowboy_description = Small, fast and modular HTTP server. pkg_cowboy_homepage = https://ninenines.eu pkg_cowboy_fetch = git pkg_cowboy_repo = https://github.com/ninenines/cowboy pkg_cowboy_commit = master PACKAGES += cowlib pkg_cowlib_name = cowlib pkg_cowlib_description = Support library for manipulating Web protocols. pkg_cowlib_homepage = https://ninenines.eu pkg_cowlib_fetch = git pkg_cowlib_repo = https://github.com/ninenines/cowlib pkg_cowlib_commit = master PACKAGES += elixir pkg_elixir_name = elixir pkg_elixir_description = Elixir is a dynamic, functional language for building scalable and maintainable applications. pkg_elixir_homepage = https://elixir-lang.org pkg_elixir_fetch = git pkg_elixir_repo = https://github.com/elixir-lang/elixir pkg_elixir_commit = main PACKAGES += erlydtl pkg_erlydtl_name = erlydtl pkg_erlydtl_description = Django Template Language for Erlang. pkg_erlydtl_homepage = https://github.com/erlydtl/erlydtl pkg_erlydtl_fetch = git pkg_erlydtl_repo = https://github.com/erlydtl/erlydtl pkg_erlydtl_commit = master PACKAGES += gpb pkg_gpb_name = gpb pkg_gpb_description = A Google Protobuf implementation for Erlang pkg_gpb_homepage = https://github.com/tomas-abrahamsson/gpb pkg_gpb_fetch = git pkg_gpb_repo = https://github.com/tomas-abrahamsson/gpb pkg_gpb_commit = master PACKAGES += gun pkg_gun_name = gun pkg_gun_description = HTTP/1.1, HTTP/2, Websocket client (and more) for Erlang/OTP. pkg_gun_homepage = https://ninenines.eu pkg_gun_fetch = git pkg_gun_repo = https://github.com/ninenines/gun pkg_gun_commit = master PACKAGES += hex_core pkg_hex_core_name = hex_core pkg_hex_core_description = Reference implementation of Hex specifications pkg_hex_core_homepage = https://github.com/hexpm/hex_core pkg_hex_core_fetch = git HEX_CORE_GIT ?= https://github.com/hexpm/hex_core pkg_hex_core_repo = $(HEX_CORE_GIT) pkg_hex_core_commit = v0.21.1 PACKAGES += proper pkg_proper_name = proper pkg_proper_description = PropEr: a QuickCheck-inspired property-based testing tool for Erlang. pkg_proper_homepage = http://proper.softlab.ntua.gr pkg_proper_fetch = git pkg_proper_repo = https://github.com/manopapad/proper pkg_proper_commit = master PACKAGES += ranch pkg_ranch_name = ranch pkg_ranch_description = Socket acceptor pool for TCP protocols. pkg_ranch_homepage = https://ninenines.eu pkg_ranch_fetch = git pkg_ranch_repo = https://github.com/ninenines/ranch pkg_ranch_commit = master PACKAGES += relx pkg_relx_name = relx pkg_relx_description = Sane, simple release creation for Erlang pkg_relx_homepage = https://github.com/erlware/relx pkg_relx_fetch = git pkg_relx_repo = https://github.com/erlware/relx pkg_relx_commit = main PACKAGES += triq pkg_triq_name = triq pkg_triq_description = Trifork QuickCheck pkg_triq_homepage = https://triq.gitlab.io pkg_triq_fetch = git pkg_triq_repo = https://gitlab.com/triq/triq.git pkg_triq_commit = master # Copyright (c) Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. .PHONY: search define pkg_print $(verbose) printf "%s\n" \ $(if $(call core_eq,$1,$(pkg_$(1)_name)),,"Pkg name: $1") \ "App name: $(pkg_$(1)_name)" \ "Description: $(pkg_$(1)_description)" \ "Home page: $(pkg_$(1)_homepage)" \ "Fetch with: $(pkg_$(1)_fetch)" \ "Repository: $(pkg_$(1)_repo)" \ "Commit: $(pkg_$(1)_commit)" \ "" endef search: ifdef q $(foreach p,$(PACKAGES), \ $(if $(findstring $(call core_lc,$q),$(call core_lc,$(pkg_$(p)_name) $(pkg_$(p)_description))), \ $(call pkg_print,$p))) else $(foreach p,$(PACKAGES),$(call pkg_print,$p)) endif # Copyright (c) Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. .PHONY: distclean-deps clean-tmp-deps.log # Configuration. ifdef OTP_DEPS $(warning The variable OTP_DEPS is deprecated in favor of LOCAL_DEPS.) endif IGNORE_DEPS ?= export IGNORE_DEPS APPS_DIR ?= $(CURDIR)/apps export APPS_DIR DEPS_DIR ?= $(CURDIR)/deps export DEPS_DIR REBAR_DEPS_DIR = $(DEPS_DIR) export REBAR_DEPS_DIR # When testing Erlang.mk and updating these, make sure # to delete test/test_rebar_git before running tests again. REBAR3_GIT ?= https://github.com/erlang/rebar3 REBAR3_COMMIT ?= 183065cc3f7ec5a9d9d4936f7e1df6246178df40 # 3.28.0 CACHE_DEPS ?= 0 CACHE_DIR ?= $(if $(XDG_CACHE_HOME),$(XDG_CACHE_HOME),$(HOME)/.cache)/erlang.mk export CACHE_DIR HEX_CONFIG ?= define hex_config.erl begin Config0 = hex_core:default_config(), Config0$(HEX_CONFIG) end endef # External "early" plugins (see core/plugins.mk for regular plugins). # They both use the core_dep_plugin macro. define core_dep_plugin ifeq ($2,$(PROJECT)) -include $$(patsubst $(PROJECT)/%,%,$1) else -include $(DEPS_DIR)/$1 $(DEPS_DIR)/$1: $(DEPS_DIR)/$2 ; endif endef DEP_EARLY_PLUGINS ?= $(foreach p,$(DEP_EARLY_PLUGINS),\ $(eval $(if $(findstring /,$p),\ $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\ $(call core_dep_plugin,$p/early-plugins.mk,$p)))) # Query functions. query_fetch_method = $(if $(dep_$(1)),$(call _qfm_dep,$(word 1,$(dep_$(1)))),$(call _qfm_pkg,$1)) _qfm_dep = $(if $(dep_fetch_$(1)),$1,fail) _qfm_pkg = $(if $(pkg_$(1)_fetch),$(pkg_$(1)_fetch),fail) query_name = $(if $(dep_$(1)),$1,$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$1)) query_repo = $(call _qr,$1,$(call query_fetch_method,$1)) _qr = $(if $(query_repo_$(2)),$(call query_repo_$(2),$1),$(call query_repo_git,$1)) query_repo_default = $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)) query_repo_git = $(patsubst git://github.com/%,https://github.com/%,$(call query_repo_default,$1)) query_repo_git-subfolder = $(call query_repo_git,$1) query_repo_git-submodule = - query_repo_cp = $(call query_repo_default,$1) query_repo_ln = $(call query_repo_default,$1) query_repo_hex = https://hex.pm/packages/$(if $(word 3,$(dep_$(1))),$(word 3,$(dep_$(1))),$1) query_repo_fail = - query_version = $(call _qv,$1,$(call query_fetch_method,$1)) _qv = $(if $(query_version_$(2)),$(call query_version_$(2),$1),$(call query_version_default,$1)) query_version_default = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit))) query_version_git = $(call query_version_default,$1) query_version_git-subfolder = $(call query_version_default,$1) query_version_git-submodule = - query_version_cp = - query_version_ln = - query_version_hex = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_commit))) query_version_fail = - query_extra = $(call _qe,$1,$(call query_fetch_method,$1)) _qe = $(if $(query_extra_$(2)),$(call query_extra_$(2),$1),-) query_extra_git = - query_extra_git-subfolder = $(if $(dep_$(1)),subfolder=$(word 4,$(dep_$(1))),-) query_extra_git-submodule = - query_extra_cp = - query_extra_ln = - query_extra_hex = $(if $(dep_$(1)),package-name=$(word 3,$(dep_$(1))),-) query_extra_fail = - query_absolute_path = $(addprefix $(DEPS_DIR)/,$(call query_name,$1)) # Deprecated legacy query function. Used by RabbitMQ and its third party plugins. # Can be removed once RabbitMQ has been updated and enough time has passed. dep_name = $(call query_name,$(1)) # Application directories. LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$a),$(APPS_DIR)/$a)) # Elixir is handled specially as it must be built before all other deps # when Mix autopatching is necessary. ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call query_name,$(dep)))) # When we are calling an app directly we don't want to include it here # otherwise it'll be treated both as an apps and a top-level project. ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d))) ifdef ROOT_DIR ifndef IS_APP ALL_APPS_DIRS := $(filter-out $(APPS_DIR)/$(notdir $(CURDIR)),$(ALL_APPS_DIRS)) endif endif ifeq ($(PLATFORM),msys2) ERL_LIBS_SEP = ; else ERL_LIBS_SEP = : endif APPS_DIR_N := $(call core_native_path,$(APPS_DIR)) DEPS_DIR_N := $(call core_native_path,$(DEPS_DIR)) ifeq ($(filter $(APPS_DIR_N) $(DEPS_DIR_N),$(subst $(ERL_LIBS_SEP), ,$(ERL_LIBS))),) ifeq ($(ERL_LIBS),) ERL_LIBS = $(APPS_DIR_N)$(ERL_LIBS_SEP)$(DEPS_DIR_N) else ERL_LIBS := $(ERL_LIBS)$(ERL_LIBS_SEP)$(APPS_DIR_N)$(ERL_LIBS_SEP)$(DEPS_DIR_N) endif endif export ERL_LIBS export NO_AUTOPATCH # Elixir. # Elixir is automatically enabled in all cases except when # an Erlang project uses an Elixir dependency. In that case # $(ELIXIR) must be set explicitly. ELIXIR ?= $(if $(filter elixir,$(BUILD_DEPS) $(DEPS)),dep,$(if $(EX_FILES),system,disable)) export ELIXIR # Verbosity. dep_verbose_0 = @echo " DEP $1 ($(call query_version,$1))"; dep_verbose_2 = set -x; dep_verbose = $(dep_verbose_$(V)) # Core targets. ALL_APPS_DIRS_TO_BUILD = $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log | $(ERLANG_MK_TMP) # Create ebin directory for all apps to make sure Erlang recognizes them # as proper OTP applications when using -include_lib. This is a temporary # fix, a proper fix would be to compile apps/* in the right order. ifndef IS_APP ifneq ($(ALL_APPS_DIRS),) $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \ mkdir -p $$dep/ebin; \ done endif endif # At the toplevel: if LOCAL_DEPS is defined with at least one local app, only # compile that list of apps. Otherwise, compile everything. # Within an app: compile all LOCAL_DEPS that are (uncompiled) local apps. ifneq ($(ALL_APPS_DIRS_TO_BUILD),) $(verbose) set -e; for dep in $(ALL_APPS_DIRS_TO_BUILD); do \ if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \ :; \ else \ echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \ $(MAKE) -C $$dep $(if $(IS_TEST),test-build-app) IS_APP=1; \ fi \ done endif clean-tmp-deps.log: ifeq ($(IS_APP)$(IS_DEP),) $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log $(ERLANG_MK_TMP)/deps.log endif # Erlang.mk does not rebuild dependencies after they were compiled # once. If a developer is working on the top-level project and some # dependencies at the same time, he may want to change this behavior. # There are two solutions: # 1. Set `FULL=1` so that all dependencies are visited and # recursively recompiled if necessary. # 2. Set `FORCE_REBUILD=` to the specific list of dependencies that # should be recompiled (instead of the whole set). FORCE_REBUILD ?= ifeq ($(origin FULL),undefined) ifneq ($(strip $(force_rebuild_dep)$(FORCE_REBUILD)),) define force_rebuild_dep echo "$(FORCE_REBUILD)" | grep -qw "$$(basename "$1")" endef endif endif $(ERLANG_MK_TMP)/dep_built: $(verbose) mkdir -p $(ERLANG_MK_TMP)/dep_built ifneq ($(SKIP_DEPS),) deps:: else ALL_DEPS_DIRS_TO_BUILD = $(if $(filter-out $(DEPS_DIR)/elixir,$(ALL_DEPS_DIRS)),$(filter-out $(DEPS_DIR)/elixir,$(ALL_DEPS_DIRS)),$(ALL_DEPS_DIRS)) deps:: $(ALL_DEPS_DIRS_TO_BUILD) apps clean-tmp-deps.log | $(ERLANG_MK_TMP)/dep_built ifneq ($(ALL_DEPS_DIRS_TO_BUILD),) $(verbose) set -e; for dep in $(ALL_DEPS_DIRS_TO_BUILD); do \ if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \ :; \ else \ echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \ if [ -z "$(strip $(FULL))" ] $(if $(force_rebuild_dep),&& ! ($(call force_rebuild_dep,$$dep)),) && [ ! -L $$dep ] && [ -f $(ERLANG_MK_TMP)/dep_built/`basename $$dep` ]; then \ :; \ elif [ "$$dep" = "$(DEPS_DIR)/hut" -a "$(HUT_PATCH)" ]; then \ $(MAKE) -C $$dep app IS_DEP=1; \ if [ ! -L $$dep ] && [ -d $$dep/ebin ]; then touch $(ERLANG_MK_TMP)/dep_built/`basename $$dep`; fi; \ elif [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \ $(MAKE) -C $$dep IS_DEP=1; \ if [ ! -L $$dep ] && [ -d $$dep/ebin ]; then touch $(ERLANG_MK_TMP)/dep_built/`basename $$dep`; fi; \ else \ echo "Error: No Makefile to build dependency $$dep." >&2; \ exit 2; \ fi \ fi \ done endif endif # Deps related targets. autopatch_verbose_0 = @echo " PATCH " $(subst autopatch-,,$@) "(method: $(AUTOPATCH_METHOD))"; autopatch_verbose_2 = set -x; autopatch_verbose = $(autopatch_verbose_$(V)) define dep_autopatch_detect if [ -f $(DEPS_DIR)/$1/erlang.mk ]; then \ echo erlang.mk; \ elif [ -f $(DEPS_DIR)/$1/mix.exs -a -d $(DEPS_DIR)/$1/lib ]; then \ if [ "$(ELIXIR)" != "disable" ]; then \ echo mix; \ elif [ -f $(DEPS_DIR)/$1/rebar.lock -o -f $(DEPS_DIR)/$1/rebar.config ]; then \ echo rebar3; \ elif [ -f $(DEPS_DIR)/$1/Makefile ]; then \ echo noop; \ else \ exit 99; \ fi \ elif [ -f $(DEPS_DIR)/$1/Makefile ]; then \ if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \ echo rebar3; \ elif [ 0 != \`grep -c "include ../\w*\.mk" $(DEPS_DIR)/$1/Makefile\` ]; then \ echo rebar3; \ elif [ 0 != \`grep -ci "^[^#].*rebar" $(DEPS_DIR)/$1/Makefile\` ]; then \ echo rebar3; \ elif [ -n "\`find $(DEPS_DIR)/$1/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;\`" ]; then \ echo rebar3; \ else \ echo noop; \ fi \ elif [ ! -d $(DEPS_DIR)/$1/src/ ]; then \ echo noop; \ else \ echo rebar3; \ fi endef define dep_autopatch_for_erlang.mk rm -rf $(DEPS_DIR)/$1/ebin; \ $(call erlang,$(call dep_autopatch_appsrc.erl,$1)); \ $(call dep_autopatch_erlang_mk,$1) endef define dep_autopatch_for_rebar3 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \ mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \ rm -f $(DEPS_DIR)/$1/ebin/$1.app; \ if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \ $(call erlang,$(call dep_autopatch_appsrc_script.erl,$1)); \ fi; \ $(call erlang,$(call dep_autopatch_appsrc.erl,$1)); \ if [ -f $(DEPS_DIR)/$1/rebar -o -f $(DEPS_DIR)/$1/rebar.config -o -f $(DEPS_DIR)/$1/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \ $(call dep_autopatch_fetch_rebar); \ $(call dep_autopatch_rebar,$1); \ else \ $(call dep_autopatch_gen,$1); \ fi endef define dep_autopatch_for_mix $(call dep_autopatch_mix,$1) endef define dep_autopatch_for_noop test -f $(DEPS_DIR)/$1/Makefile || printf "noop:\n" > $(DEPS_DIR)/$1/Makefile endef define maybe_flock if command -v flock >/dev/null; then \ flock $1 sh -c "$2"; \ elif command -v lockf >/dev/null; then \ lockf -k $1 sh -c "$2"; \ else \ $2; \ fi endef # Replace "include erlang.mk" with a line that will load the parent Erlang.mk # if given. Do it for all 3 possible Makefile file names. ifeq ($(NO_AUTOPATCH_ERLANG_MK),) define dep_autopatch_erlang_mk for f in Makefile makefile GNUmakefile; do \ if [ -f $(DEPS_DIR)/$1/$$f ]; then \ sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \ fi \ done endef else define dep_autopatch_erlang_mk : endef endif define dep_autopatch_gen printf "%s\n" \ "ERLC_OPTS = +debug_info" \ "include ../../erlang.mk" > $(DEPS_DIR)/$1/Makefile endef # We use flock/lockf when available to avoid concurrency issues. define dep_autopatch_fetch_rebar $(call maybe_flock,$(ERLANG_MK_TMP)/rebar.lock,$(call dep_autopatch_fetch_rebar2)) endef define dep_autopatch_fetch_rebar2 if [ ! -x $(ERLANG_MK_TMP)/rebar3/rebar3 ]; then \ rm -rf $(ERLANG_MK_TMP)/rebar3 && \ git clone -q -n -- $(REBAR3_GIT) $(ERLANG_MK_TMP)/rebar3 && \ cd $(ERLANG_MK_TMP)/rebar3 && \ git checkout -q $(REBAR3_COMMIT) && \ ./bootstrap && \ { test -x rebar3 || cp -f _build/prod/bin/rebar3 rebar3; } && \ cd - || { rm -rf $(ERLANG_MK_TMP)/rebar3; exit 1; }; \ fi endef define dep_autopatch_rebar if [ -f $(DEPS_DIR)/$1/Makefile ]; then \ mv $(DEPS_DIR)/$1/Makefile $(DEPS_DIR)/$1/Makefile.orig.mk; \ fi; \ $(call erlang,$(call dep_autopatch_rebar.erl,$1)); \ rm -f $(DEPS_DIR)/$1/ebin/$1.app endef define dep_autopatch_rebar.erl application:load(rebar), application:set_env(rebar, log_level, debug), {module, rebar3} = c:l(rebar3), Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of {ok, Conf0} -> Conf0; _ -> [] end, {Conf, OsEnv} = fun() -> case filelib:is_file("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config.script)") of false -> {Conf1, []}; true -> Bindings0 = erl_eval:new_bindings(), Bindings1 = erl_eval:add_binding('CONFIG', Conf1, Bindings0), Bindings = erl_eval:add_binding('SCRIPT', "$(call core_native_path,$(DEPS_DIR)/$1/rebar.config.script)", Bindings1), Before = os:getenv(), {ok, Conf2} = file:script("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config.script)", Bindings), {Conf2, lists:foldl(fun(E, Acc) -> lists:delete(E, Acc) end, os:getenv(), Before)} end end(), Write = fun (Text) -> file:write_file("$(call core_native_path,$(DEPS_DIR)/$1/Makefile)", Text, [append]) end, Escape = fun (Text) -> re:replace(Text, "\\\\$$", "\$$$$", [global, {return, list}]) end, Write("IGNORE_DEPS += edown eper eunit_formatters meck node_package " "rebar_lock_deps_plugin rebar_vsn_plugin reltool_util\n"), Write("C_SRC_DIR = /path/do/not/exist\n"), Write("C_SRC_TYPE = rebar\n"), Write("DRV_CFLAGS = -fPIC\nexport DRV_CFLAGS\n"), Write(["ERLANG_ARCH = ", rebar_utils:wordsize(), "\nexport ERLANG_ARCH\n"]), ToList = fun (V) when is_atom(V) -> atom_to_list(V); (V) when is_list(V) -> "'\\"" ++ V ++ "\\"'" end, fun() -> Write("ERLC_OPTS = +debug_info\n"), case lists:keyfind(erl_opts, 1, Conf) of false -> ok; {_, ErlOpts} -> lists:foreach(fun ({d, D}) -> Write("ERLC_OPTS += -D" ++ ToList(D) ++ "=1\n"); ({d, DKey, DVal}) -> Write("ERLC_OPTS += -D" ++ ToList(DKey) ++ "=" ++ ToList(DVal) ++ "\n"); ({i, I}) -> Write(["ERLC_OPTS += -I ", I, "\n"]); ({platform_define, Regex, D}) -> case rebar_utils:is_arch(Regex) of true -> Write("ERLC_OPTS += -D" ++ ToList(D) ++ "=1\n"); false -> ok end; ({parse_transform, PT}) -> Write("ERLC_OPTS += +'{parse_transform, " ++ ToList(PT) ++ "}'\n"); (_) -> ok end, ErlOpts) end, Write("\n") end(), GetHexVsn2 = fun(N, NP) -> case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.lock)") of {ok, Lock} -> LockPkgs = case lists:keyfind("1.2.0", 1, Lock) of {_, LP} -> LP; _ -> case lists:keyfind("1.1.0", 1, Lock) of {_, LP} -> LP; _ -> false end end, if is_list(LockPkgs) -> case lists:keyfind(atom_to_binary(N), 1, LockPkgs) of {_, {pkg, _, Vsn}, _} -> {N, {hex, NP, binary_to_list(Vsn)}}; _ -> false end; true -> false end; _ -> false end end, ResolveHexVsn = fun(Name, Req) -> {ok, _} = application:ensure_all_started(ssl), {ok, _} = application:ensure_all_started(inets), Config0 = r3_hex_core:default_config(), Config = Config0$(HEX_CONFIG), Pkg = atom_to_binary(Name), ReqBin = list_to_binary(Req), maybe {ok, {200, _, #{releases := Releases}}} ?= r3_hex_repo:get_package(Config, Pkg), {ok, Match} ?= rebar_semver:parse_constraint(ReqBin), AllowPre = rebar_semver:is_prerelease_or_build(ReqBin), Best = lists:foldl(fun(Release, Acc) -> maybe true ?= maps:get(retired, Release, false) =:= false, VsnBin = maps:get(version, Release), true ?= AllowPre orelse not rebar_semver:is_prerelease_or_build(VsnBin), {ok, Parsed} ?= rebar_semver:parse_version(VsnBin), true ?= rebar_semver:match(Parsed, Match), true ?= case Acc of none -> true; {_, BestParsed} -> rebar_semver:cmp(Parsed, BestParsed) =:= gt end, {VsnBin, Parsed} else _ -> Acc end end, none, Releases), {VsnBin, _} ?= Best, binary_to_list(VsnBin) else {error, {invalid_vsn, _}} -> io:format(standard_error, "Invalid Hex requirement for ~p: ~p~n", [Name, Req]), halt(92); none -> io:format(standard_error, "No Hex release of ~p matches ~p~n", [Name, Req]), halt(93); Other -> io:format(standard_error, "Failed to get Hex package ~p: ~p~n", [Name, Other]), halt(94) end end, GetHexVsn3 = fun(N, NP, S) -> Req = string:trim(S), case rebar_semver:parse_version(list_to_binary(Req)) of {ok, _} -> {N, {hex, NP, Req}}; {error, _} -> case GetHexVsn2(N, NP) of false -> {N, {hex, NP, ResolveHexVsn(NP, Req)}}; NameSource -> NameSource end end end, ConvertCommit = fun ({branch, C}) -> C; ({ref, C}) -> C; ({tag, C}) -> C; (C) -> C end, fun() -> File = case lists:keyfind(deps, 1, Conf) of false -> []; {_, Deps} -> [begin case case Dep of N when is_atom(N) -> GetHexVsn2(N, N); {N, S} when is_atom(N), is_list(S) -> GetHexVsn3(N, N, S); {N, {pkg, NP}} when is_atom(N) -> GetHexVsn2(N, NP); {N, S, {pkg, NP}} -> GetHexVsn3(N, NP, S); {N, S} when is_tuple(S) -> {N, S}; {N, _, S} -> {N, S}; {N, _, S, _} -> {N, S}; _ -> false end of false -> ok; {Name, {git_subdir, Repo, Commit, SubDir}} -> Write(io_lib:format("DEPS += ~s\ndep_~s = git-subfolder ~s ~s ~s~n", [Name, Name, Repo, ConvertCommit(Commit), SubDir])); {Name, Source} -> {Method, Repo, Commit} = case Source of {hex, NPV, V} -> {hex, V, NPV}; {git, R} -> {git, R, master}; {M, R, C} -> {M, R, C} end, Write(io_lib:format("DEPS += ~s\ndep_~s = ~s ~s ~s~n", [Name, Name, Method, Repo, ConvertCommit(Commit)])) end end || Dep <- Deps] end end(), fun() -> case lists:keyfind(erl_first_files, 1, Conf) of false -> ok; {_, Files0} -> FixSlashes = fun(P) -> [if C =:= 92 -> $$/; true -> C end || C <- P] end, SrcDir = FixSlashes("$(call core_native_path,$(DEPS_DIR)/$1/src/)"), Files = [begin case filelib:wildcard(SrcDir ++ "**/" ++ filename:rootname(F) ++ ".*rl") of [Found|_] -> FixSlashes(Found); [] -> SrcDir ++ F end end || "src/" ++ F <- Files0], RelName = fun(Full) -> Rel = case lists:prefix(SrcDir, Full) orelse lists:prefix(string:to_lower(SrcDir), string:to_lower(Full)) of true -> lists:nthtail(length(SrcDir), Full); false -> filename:basename(Full) end, case lists:reverse(Rel) of "lre." ++ Elif -> lists:reverse(Elif); "lrx." ++ Elif -> lists:reverse(Elif); "lry." ++ Elif -> lists:reverse(Elif); Elif -> lists:reverse(Elif) end end, Names = [[" ", RelName(Full)] || Full <- Files], Write(io_lib:format("COMPILE_FIRST +=~s\n", [Names])) end end(), Write("\n\nrebar_dep: preprocess pre-deps deps pre-app app post-app\n"), Write("\npreprocess::\n"), Write("\npre-deps::\n"), Write("\npre-app::\n"), Write("\npost-app::\n"), PatchHook = fun(Cmd) -> Cmd2 = re:replace(Cmd, "^([g]?make)(.*)( -C.*)", "\\\\1\\\\3\\\\2", [{return, list}]), case Cmd2 of "make -C" ++ Cmd1 -> "$$\(MAKE) -C" ++ Escape(Cmd1); "gmake -C" ++ Cmd1 -> "$$\(MAKE) -C" ++ Escape(Cmd1); "make " ++ Cmd1 -> "$$\(MAKE) -f Makefile.orig.mk " ++ Escape(Cmd1); "gmake " ++ Cmd1 -> "$$\(MAKE) -f Makefile.orig.mk " ++ Escape(Cmd1); _ -> Escape(Cmd) end end, fun() -> case lists:keyfind(pre_hooks, 1, Conf) of false -> ok; {_, Hooks} -> [case H of {'get-deps', Cmd} -> Write("\npre-deps::\n\t" ++ PatchHook(Cmd) ++ "\n"); {compile, Cmd} -> Write("\npre-app::\n\tCC=$$\(CC) " ++ PatchHook(Cmd) ++ "\n"); {{pc, compile}, Cmd} -> Write("\npre-app::\n\tCC=$$\(CC) " ++ PatchHook(Cmd) ++ "\n"); {Regex, compile, Cmd} -> case rebar_utils:is_arch(Regex) of true -> Write("\npre-app::\n\tCC=$$\(CC) " ++ PatchHook(Cmd) ++ "\n"); false -> ok end; _ -> ok end || H <- Hooks] end end(), fun() -> case lists:keyfind(post_hooks, 1, Conf) of false -> ok; {_, Hooks} -> [case H of {compile, Cmd} -> Write("\npost-app::\n\tCC=$$\(CC) " ++ PatchHook(Cmd) ++ "\n"); {{pc, compile}, Cmd} -> Write("\npost-app::\n\tCC=$$\(CC) " ++ PatchHook(Cmd) ++ "\n"); {Regex, compile, Cmd} -> case rebar_utils:is_arch(Regex) of true -> Write("\npost-app::\n\tCC=$$\(CC) " ++ PatchHook(Cmd) ++ "\n"); false -> ok end; _ -> ok end || H <- Hooks] end end(), ShellToMk = fun(V0) -> V1 = re:replace(V0, "[$$][(]", "$$\(shell ", [global]), V = re:replace(V1, "([$$])(?![(])(\\\\w*)", "\\\\1(\\\\2)", [global]), re:replace(V, "-Werror\\\\b", "", [{return, list}, global]) end, PortSpecs = fun() -> case lists:keyfind(port_specs, 1, Conf) of false -> case filelib:is_dir("$(call core_native_path,$(DEPS_DIR)/$1/c_src)") of false -> []; true -> [{"priv/" ++ proplists:get_value(so_name, Conf, "$(1)_drv.so"), proplists:get_value(port_sources, Conf, ["c_src/*.c"]), []}] end; {_, Specs} -> lists:flatten([case S of {Output, Input} -> {ShellToMk(Output), Input, []}; {Regex, Output, Input} -> case rebar_utils:is_arch(Regex) of true -> {ShellToMk(Output), Input, []}; false -> [] end; {Regex, Output, Input, [{env, Env}]} -> case rebar_utils:is_arch(Regex) of true -> {ShellToMk(Output), Input, Env}; false -> [] end end || S <- Specs]) end end(), PortSpecWrite = fun (Text) -> file:write_file("$(call core_native_path,$(DEPS_DIR)/$1/c_src/Makefile.erlang.mk)", Text, [append]) end, case PortSpecs of [] -> ok; _ -> Write("\npre-app::\n\t@$$\(MAKE) --no-print-directory -f c_src/Makefile.erlang.mk\n"), PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n", [code:root_dir(), erlang:system_info(version), filename:join(code:lib_dir(erl_interface), "include")])), PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lei\n", [filename:join(code:lib_dir(erl_interface), "lib")])), [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv], FilterEnv = fun(Env) -> lists:flatten([case E of {_, _} -> E; {Regex, K, V} -> case rebar_utils:is_arch(Regex) of true -> {K, V}; false -> [] end end || E <- Env]) end, MergeEnv = fun(Env) -> lists:foldl(fun ({K, V}, Acc) -> case lists:keyfind(K, 1, Acc) of false -> [{K, rebar_utils:expand_env_variable(V, K, "")}|Acc]; {_, V0} -> [{K, rebar_utils:expand_env_variable(V, K, V0)}|Acc] end end, [], Env) end, PortEnv = case lists:keyfind(port_env, 1, Conf) of false -> []; {_, PortEnv0} -> FilterEnv(PortEnv0) end, PortSpec = fun ({Output, Input0, Env}) -> filelib:ensure_dir("$(call core_native_path,$(DEPS_DIR)/$1/)" ++ Output), Input = [[" ", I] || I <- Input0], PortSpecWrite([ [["\n", K, " = ", ShellToMk(V)] || {K, V} <- lists:reverse(MergeEnv(PortEnv))], case $(PLATFORM) of darwin -> "\n\nLDFLAGS += -flat_namespace -undefined suppress"; _ -> "" end, "\n\nall:: ", Output, "\n\t@:\n\n", "%.o: %.c\n\t$$\(CC) -c -o $$\@ $$\< $$\(CFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n", "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n", "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n", "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n", [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))], Output, ": $$\(foreach ext,.c .C .cc .cpp,", "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n", "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(LDLIBS) $$\(EXE_LDFLAGS)", case {filename:extension(Output), $(PLATFORM)} of {[], _} -> "\n"; {".so", darwin} -> " -shared\n"; {".dylib", darwin} -> " -shared\n"; {_, darwin} -> "\n"; _ -> " -shared\n" end]) end, [PortSpec(S) || S <- PortSpecs] end, fun() -> case lists:keyfind(plugins, 1, Conf) of false -> ok; {_, Plugins0} -> Plugins = [P || P <- Plugins0, is_tuple(P)], case lists:keyfind('lfe-compile', 1, Plugins) of false -> ok; _ -> Write("\nBUILD_DEPS = lfe lfe.mk\ndep_lfe.mk = git https://github.com/ninenines/lfe.mk master\nDEP_PLUGINS = lfe.mk\n") end end end(), Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"), RunPlugin = fun(Plugin, Step) -> case erlang:function_exported(Plugin, Step, 2) of false -> ok; true -> c:cd("$(call core_native_path,$(DEPS_DIR)/$1/)"), Ret = Plugin:Step({config, "", Conf, dict:new(), dict:new(), dict:new(), dict:store(base_dir, "", dict:new())}, undefined), io:format("rebar plugin ~p step ~p ret ~p~n", [Plugin, Step, Ret]) end end, fun() -> case lists:keyfind(plugins, 1, Conf) of false -> ok; {_, Plugins0} -> Plugins = [P || P <- Plugins0, is_atom(P)], [begin case lists:keyfind(deps, 1, Conf) of false -> ok; {_, Deps} -> case lists:keyfind(P, 1, Deps) of false -> ok; _ -> Path = "$(call core_native_path,$(DEPS_DIR)/)" ++ atom_to_list(P), io:format("~s", [os:cmd("$(MAKE) -C $(call core_native_path,$(DEPS_DIR)/$1) " ++ Path)]), io:format("~s", [os:cmd("$(MAKE) -C " ++ Path ++ " IS_DEP=1")]), code:add_patha(Path ++ "/ebin") end end end || P <- Plugins], [case code:load_file(P) of {module, P} -> ok; _ -> case lists:keyfind(plugin_dir, 1, Conf) of false -> ok; {_, PluginsDir} -> ErlFile = "$(call core_native_path,$(DEPS_DIR)/$1/)" ++ PluginsDir ++ "/" ++ atom_to_list(P) ++ ".erl", {ok, P, Bin} = compile:file(ErlFile, [binary]), {module, P} = code:load_binary(P, ErlFile, Bin) end end || P <- Plugins], [RunPlugin(P, preprocess) || P <- Plugins], [RunPlugin(P, pre_compile) || P <- Plugins], [RunPlugin(P, compile) || P <- Plugins] end end(), halt() endef define dep_autopatch_appsrc_script.erl AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)", AppSrcScript = AppSrc ++ ".script", Conf1 = case file:consult(AppSrc) of {ok, Conf0} -> Conf0; {error, enoent} -> [] end, Bindings0 = erl_eval:new_bindings(), Bindings1 = erl_eval:add_binding('CONFIG', Conf1, Bindings0), Bindings = erl_eval:add_binding('SCRIPT', AppSrcScript, Bindings1), Conf = case file:script(AppSrcScript, Bindings) of {ok, [C]} -> C; {ok, C} -> C end, ok = file:write_file(AppSrc, io_lib:format("~p.~n", [Conf])), halt() endef define dep_autopatch_appsrc.erl AppSrcOut = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)", AppSrcIn = case filelib:is_regular(AppSrcOut) of false -> "$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"; true -> AppSrcOut end, case filelib:is_regular(AppSrcIn) of false -> ok; true -> {ok, [{application, $1, L0}]} = file:consult(AppSrcIn), L1 = lists:keystore(modules, 1, L0, {modules, []}), GitDescribe = fun() -> lists:droplast(os:cmd("git -C $(DEPS_DIR)/$1 describe --dirty --tags --always")) end, L2 = case lists:keyfind(vsn, 1, L1) of {_, Vsn} when Vsn =:= git; Vsn =:= semver -> lists:keyreplace(vsn, 1, L1, {vsn, GitDescribe()}); {_, {Vcs, _}} when Vcs =:= git; Vcs =:= semver -> lists:keyreplace(vsn, 1, L1, {vsn, GitDescribe()}); {_, {cmd, _}} -> lists:keyreplace(vsn, 1, L1, {vsn, "cmd"}); {_, {file, _}} -> lists:keyreplace(vsn, 1, L1, {vsn, "file"}); _ -> L1 end, L3 = case lists:keyfind(registered, 1, L2) of false -> [{registered, []}|L2]; _ -> L2 end, ok = file:write_file(AppSrcOut, io_lib:format("~p.~n", [{application, $1, L3}])), case AppSrcOut of AppSrcIn -> ok; _ -> ok = file:delete(AppSrcIn) end end, halt() endef define dep_lock_git rev=`git rev-parse HEAD` && \ printf '%s\n' \ 'ifdef ERLANG_MK_LOCK' \ "dep_$(call query_name,$1) := git $(call query_repo_git,$1) $$$$rev" \ 'dep_$(call query_name,$1)_commit := '"$$$$rev" \ 'endif' >> "$(ERLANG_MK_LOCK_FILE)" endef define dep_lock_git-subfolder rev=`git rev-parse HEAD` && \ printf '%s\n' \ 'ifdef ERLANG_MK_LOCK' \ "dep_$(call query_name,$1) := git-subfolder $(call query_repo_git-subfolder,$1) $$$$rev $(word 4,$(dep_$1))" \ 'dep_$(call query_name,$1)_commit := '"$$$$rev" \ 'endif' >> "$(ERLANG_MK_LOCK_FILE)" endef ifeq ($(CACHE_DEPS),1) define dep_cache_fetch_git mkdir -p $(CACHE_DIR)/git; \ if test -d "$(join $(CACHE_DIR)/git/,$(call query_name,$1))"; then \ cd $(join $(CACHE_DIR)/git/,$(call query_name,$1)); \ if ! git checkout -q $(call query_version,$1); then \ git remote set-url origin $(call query_repo_git,$1) && \ git pull --all && \ git cat-file -e $(call query_version_git,$1) 2>/dev/null; \ fi; \ else \ git clone -q -n -- $(call query_repo_git,$1) $(join $(CACHE_DIR)/git/,$(call query_name,$1)); \ fi; \ git clone -q --single-branch -- $(join $(CACHE_DIR)/git/,$(call query_name,$1)) $2 \ && cd $2 && git checkout -q $(call query_version_git,$1) \ || { rm -rf $2; exit 1; } endef define dep_fetch_git $(call dep_cache_fetch_git,$1,$(DEPS_DIR)/$(call query_name,$1)) && \ $(call dep_lock_git,$1) endef define dep_fetch_git-subfolder mkdir -p $(ERLANG_MK_TMP)/git-subfolder; \ $(call dep_cache_fetch_git,$1,$(ERLANG_MK_TMP)/git-subfolder/$(call query_name,$1)) && \ ln -s $(ERLANG_MK_TMP)/git-subfolder/$(call query_name,$1)/$(word 4,$(dep_$1)) \ $(DEPS_DIR)/$(call query_name,$1) && \ $(call dep_lock_git-subfolder,$1) endef else define dep_fetch_git git clone -q -n -- $(call query_repo_git,$1) $(DEPS_DIR)/$(call query_name,$1) \ && cd $(DEPS_DIR)/$(call query_name,$1) && git checkout -q $(call query_version_git,$1) && \ $(call dep_lock_git,$1) \ || { rm -rf $(DEPS_DIR)/$(call query_name,$1); exit 1; } endef define dep_fetch_git-subfolder mkdir -p $(ERLANG_MK_TMP)/git-subfolder && \ git clone -q -n -- $(call query_repo_git-subfolder,$1) \ $(ERLANG_MK_TMP)/git-subfolder/$(call query_name,$1) && \ cd $(ERLANG_MK_TMP)/git-subfolder/$(call query_name,$1) && \ git checkout -q $(call query_version_git-subfolder,$1) && \ ln -s $(ERLANG_MK_TMP)/git-subfolder/$(call query_name,$1)/$(word 4,$(dep_$1)) \ $(DEPS_DIR)/$(call query_name,$1) && \ $(call dep_lock_git-subfolder,$1) \ || { rm -rf $(ERLANG_MK_TMP)/git-subfolder/$(call query_name,$1) \ $(DEPS_DIR)/$(call query_name,$1); exit 1; } endef endif define dep_fetch_git-submodule git submodule update --init -- $(DEPS_DIR)/$1; endef define dep_fetch_cp cp -R $(call query_repo_cp,$1) $(DEPS_DIR)/$(call query_name,$1); endef define dep_fetch_ln ln -s $(call query_repo_ln,$1) $(DEPS_DIR)/$(call query_name,$1); endef NATIVE_ERLANG_MK_TMP = $(eval NATIVE_ERLANG_MK_TMP := $$(call core_native_path,$(ERLANG_MK_TMP)))$(NATIVE_ERLANG_MK_TMP) NATIVE_CACHE_DIR = $(eval NATIVE_CACHE_DIR := $$(call core_native_path,$(CACHE_DIR)))$(NATIVE_CACHE_DIR) define hex_get_tarball.erl {ok, _} = application:ensure_all_started(ssl), {ok, _} = application:ensure_all_started(inets), Config = $(hex_config.erl), case hex_repo:get_tarball(Config, <<"$1">>, <<"$(strip $2)">>) of {ok, {200, _, Tarball}} -> ok = file:write_file("$3", Tarball), halt(0); {ok, {Status, _, Errors}} -> io:format("Error ~b: ~0p~n", [Status, Errors]), halt(79); Error -> io:format(standard_error, "Error: ~0p~n", [Error]), halt(79) end endef define hex_check_tarball.erl case file:read_file("$4") of {ok, Bin} -> Sum = binary:encode_hex(crypto:hash(sha256, Bin), lowercase), Pkg = case "$3" of "" -> <<"$1">>; _ -> <<"$3">> end, IsPre = fun(V) -> case string:split(V, <<"-">>) of [_, _] -> true; _ -> false end end, Parts = fun(V) -> [Core | _] = string:split(V, <<"-">>), [binary_to_integer(N) || N <- string:split(Core, <<".">>, all)] end, Norm = fun(Ps) -> lists:reverse(lists:dropwhile(fun(N) -> N =:= 0 end, lists:reverse(Ps))) end, WarnNewer = fun(Releases, Using) -> Stable = [maps:get(version, R) || R <- Releases, not maps:is_key(retired, R), not IsPre(maps:get(version, R))], case Stable of [] -> ok; _ -> Latest = lists:foldl(fun(V, Best) -> case Norm(Parts(V)) > Norm(Parts(Best)) of true -> V; false -> Best end end, hd(Stable), tl(Stable)), Newer = Norm(Parts(Latest)) > Norm(Parts(Using)) orelse (Norm(Parts(Latest)) =:= Norm(Parts(Using)) andalso IsPre(Using)), case Newer of true -> io:format(standard_error, "Warning: newer version ~s of ~s is available (using ~s).~n", [Latest, Pkg, Using]); false -> ok end end end, case <<"$5">> of <<>> -> {ok, _} = application:ensure_all_started(ssl), {ok, _} = application:ensure_all_started(inets), Config = $(hex_config.erl), LockSum = case hex_repo:get_package(Config, Pkg) of {ok, {200, _, #{releases := Releases}}} -> case [R || R <- Releases, maps:get(version, R) =:= <<"$(strip $2)">>] of [Release] -> case maps:get(outer_checksum, Release, <<>>) of <<>> -> io:format(standard_error, "Warning: no outer checksum for ~s ~s.~n", [Pkg, <<"$(strip $2)">>]), WarnNewer(Releases, <<"$(strip $2)">>), Sum; Outer -> case binary:encode_hex(Outer, lowercase) of Sum -> WarnNewer(Releases, <<"$(strip $2)">>), Sum; _ -> io:format(standard_error, "Error: checksum mismatch for ~s.~n", ["$1"]), halt(101) end end; _ -> io:format(standard_error, "Error: no Hex release ~s ~s.~n", [Pkg, <<"$(strip $2)">>]), halt(103) end; {ok, {Status, _, Errors}} -> io:format(standard_error, "Error ~b: ~0p~n", [Status, Errors]), halt(103); Error -> io:format(standard_error, "Error: ~0p~n", [Error]), halt(103) end, DepLine = case "$3" of "" -> io_lib:format("dep_~s := hex ~s", ["$1", "$2"]); _ -> io_lib:format("dep_~s := hex ~s ~s", ["$1", "$2", "$3"]) end, Block = iolist_to_binary([ "ifdef ERLANG_MK_LOCK\n", DepLine, "\n", io_lib:format("dep_~s_commit := ~s\n", ["$1", "$2"]), io_lib:format("dep_~s_checksum := ~s\n", ["$1", LockSum]), "endif\n" ]), ok = file:write_file("$6", Block, [append]), halt(0); Sum -> try true = "$7" =/= "cached", {ok, _} = application:ensure_all_started(ssl), {ok, _} = application:ensure_all_started(inets), Config = $(hex_config.erl), {ok, {200, _, #{releases := Releases}}} = hex_repo:get_package(Config, Pkg), WarnNewer(Releases, <<"$(strip $2)">>) catch _:_ -> ok end, halt(0); _ -> io:format(standard_error, "Error: checksum mismatch for ~s.~n", ["$1"]), halt(101) end; _ -> halt(102) end endef define hex_unpack_tarball.erl case hex_tarball:unpack({file, "$1"}, "$2") of {ok, _} -> halt(0); {error, Reason} -> io:format(standard_error, "Error: ~s~n", [hex_tarball:format_error(Reason)]), halt(1) end endef ifeq ($(CACHE_DEPS),1) # Hex only has a package version. No need to look in the Erlang.mk packages. define dep_fetch_hex mkdir -p $(CACHE_DIR)/hex; \ $(eval hex_pkg_name := $(if $(word 3,$(dep_$1)),$(word 3,$(dep_$1)),$1)) \ $(eval hex_tar_name := $(hex_pkg_name)-$(strip $(call query_version_hex,$1)).tar) \ $(if $(wildcard $(CACHE_DIR)/hex/$(hex_tar_name)),,\ $(call erlang,$(call hex_get_tarball.erl,$(hex_pkg_name),$(call query_version_hex,$1),$(NATIVE_CACHE_DIR)/hex/$(hex_tar_name))) &&) \ $(call erlang,$(call hex_check_tarball.erl,$1,$(call query_version_hex,$1),$(word 3,$(dep_$1)),$(NATIVE_CACHE_DIR)/hex/$(hex_tar_name),$(dep_$1_checksum),$(call core_native_path,$(ERLANG_MK_LOCK_FILE)),$(if $(wildcard $(CACHE_DIR)/hex/$(hex_tar_name)),cached))) && \ { mkdir -p $(DEPS_DIR)/$1 && \ $(call erlang,$(call hex_unpack_tarball.erl,$(NATIVE_CACHE_DIR)/hex/$(hex_tar_name),$(call core_native_path,$(DEPS_DIR)/$1))) && \ find $(DEPS_DIR)/$1 \( -type f -o -type d \) -exec chmod go-w {} + \ || { rm -rf $(DEPS_DIR)/$1; exit 1; }; } endef else # Hex only has a package version. No need to look in the Erlang.mk packages. define dep_fetch_hex mkdir -p $(ERLANG_MK_TMP)/hex; \ $(eval hex_pkg_name := $(if $(word 3,$(dep_$1)),$(word 3,$(dep_$1)),$1)) \ $(call erlang,$(call hex_get_tarball.erl,$(hex_pkg_name),$(call query_version_hex,$1),$(NATIVE_ERLANG_MK_TMP)/hex/$1.tar)) && \ $(call erlang,$(call hex_check_tarball.erl,$1,$(call query_version_hex,$1),$(word 3,$(dep_$1)),$(NATIVE_ERLANG_MK_TMP)/hex/$1.tar,$(dep_$1_checksum),$(call core_native_path,$(ERLANG_MK_LOCK_FILE)))) && \ { mkdir -p $(DEPS_DIR)/$1 && \ $(call erlang,$(call hex_unpack_tarball.erl,$(NATIVE_ERLANG_MK_TMP)/hex/$1.tar,$(call core_native_path,$(DEPS_DIR)/$1))) && \ find $(DEPS_DIR)/$1 \( -type f -o -type d \) -exec chmod go-w {} + \ || { rm -rf $(DEPS_DIR)/$1; exit 1; }; } endef endif define dep_fetch_fail echo "Error: Unknown or invalid dependency: $1." >&2; \ exit 78; endef define dep_target $(DEPS_DIR)/$(call query_name,$1): $(if $(filter elixir,$(BUILD_DEPS) $(DEPS)),$(if $(filter-out elixir,$1),$(ERLANG_MK_TMP)/dep_built/elixir)) $(if $(filter hex,$(call query_fetch_method,$1)),$(if $(wildcard $(DEPS_DIR)/$(call query_name,$1)),,$(ERLANG_MK_TMP)/dep_built/hex_core)) | $(ERLANG_MK_TMP) $(eval DEP_NAME := $(call query_name,$1)) $(eval DEP_STR := $(if $(filter $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))")) $(verbose) rm -f $(ERLANG_MK_TMP)/dep_built/$(DEP_NAME) $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \ echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \ exit 17; \ fi $(verbose) mkdir -p $(DEPS_DIR) $(dep_verbose) $(call dep_fetch_$(strip $(call query_fetch_method,$1)),$1) $(verbose) if [ -f $(DEPS_DIR)/$1/configure.ac -o -f $(DEPS_DIR)/$1/configure.in ] \ && [ ! -f $(DEPS_DIR)/$1/configure ]; then \ echo " AUTO " $(DEP_STR); \ cd $(DEPS_DIR)/$1 && autoreconf -Wall -vif -I m4; \ fi - $(verbose) if [ -f $(DEPS_DIR)/$(DEP_NAME)/configure ]; then \ echo " CONF " $(DEP_STR); \ cd $(DEPS_DIR)/$(DEP_NAME) && ./configure; \ fi ifeq ($(filter $1,$(NO_AUTOPATCH)),) $(verbose) AUTOPATCH_METHOD=`$(call dep_autopatch_detect,$1)`; \ if [ $$$$? -eq 99 ]; then \ echo "Elixir is currently disabled. Please set 'ELIXIR = system' in the Makefile to enable"; \ exit 99; \ fi; \ $$(MAKE) --no-print-directory autopatch-$(DEP_NAME) AUTOPATCH_METHOD=$$$$AUTOPATCH_METHOD endif .PHONY: autopatch-$(call query_name,$1) ifeq ($1,elixir) autopatch-elixir:: $$(verbose) mkdir -p $(DEPS_DIR)/elixir/lib/elixir/ebin $$(verbose) ln -s lib/elixir/ebin $(DEPS_DIR)/elixir/ else autopatch-$(call query_name,$1):: $$(autopatch_verbose) $$(call dep_autopatch_for_$(AUTOPATCH_METHOD),$(call query_name,$1)) endif endef ifeq ($(origin ERLANG_MK_LOCK_FILE),undefined) ERLANG_MK_LOCK_FILE := $(CURDIR)/lock.mk endif export ERLANG_MK_LOCK_FILE -include $(ERLANG_MK_LOCK_FILE) ifeq ($(IS_APP)$(IS_DEP),) .PHONY: lock unlock lock: fetch-deps $(verbose) if [ ! -f "$(ERLANG_MK_LOCK_FILE)" ]; then \ echo "Error: lock.mk was not found. Fetch dependencies before locking." >&2; \ exit 97; \ fi $(verbose) grep -qx 'ERLANG_MK_LOCK := 1' "$(ERLANG_MK_LOCK_FILE)" || { \ printf '%s\n\n' 'ERLANG_MK_LOCK := 1' | cat - "$(ERLANG_MK_LOCK_FILE)" > "$(ERLANG_MK_LOCK_FILE).tmp" && mv "$(ERLANG_MK_LOCK_FILE).tmp" "$(ERLANG_MK_LOCK_FILE)"; \ } unlock: $(verbose) if [ -f "$(ERLANG_MK_LOCK_FILE)" ]; then \ grep -vx 'ERLANG_MK_LOCK := 1' "$(ERLANG_MK_LOCK_FILE)" | sed '1{/^$$/d;}' > "$(ERLANG_MK_LOCK_FILE).tmp" && mv "$(ERLANG_MK_LOCK_FILE).tmp" "$(ERLANG_MK_LOCK_FILE)"; \ fi endif # We automatically depend on hex_core when the project isn't already. $(if $(filter hex_core,$(DEPS) $(BUILD_DEPS) $(DOC_DEPS) $(REL_DEPS) $(TEST_DEPS)),,\ $(eval $(call dep_target,hex_core))) $(ERLANG_MK_TMP)/dep_built/hex_core: | $(ERLANG_MK_TMP) $(verbose) $(call maybe_flock,$(ERLANG_MK_TMP)/hex_core.lock,\ if [ ! -e $(ERLANG_MK_TMP)/dep_built/hex_core ]; then \ $(MAKE) $(DEPS_DIR)/hex_core; \ $(MAKE) -C $(DEPS_DIR)/hex_core IS_DEP=1; \ mkdir -p $(ERLANG_MK_TMP)/dep_built; \ touch $(ERLANG_MK_TMP)/dep_built/hex_core; \ fi) $(ERLANG_MK_TMP)/dep_built/elixir: | $(ERLANG_MK_TMP) $(verbose) $(call maybe_flock,$(ERLANG_MK_TMP)/elixir.lock,\ if [ ! -e $(ERLANG_MK_TMP)/dep_built/elixir ]; then \ $(MAKE) $(DEPS_DIR)/elixir; \ $(MAKE) -C $(DEPS_DIR)/elixir; \ mkdir -p $(ERLANG_MK_TMP)/dep_built; \ touch $(ERLANG_MK_TMP)/dep_built/elixir; \ fi) $(foreach dep,$(BUILD_DEPS) $(DEPS),$(eval $(call dep_target,$(dep)))) ifndef IS_APP clean:: clean-apps clean-apps: $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \ $(MAKE) -C $$dep clean IS_APP=1; \ done distclean:: distclean-apps distclean-apps: $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \ $(MAKE) -C $$dep distclean IS_APP=1; \ done endif ifndef SKIP_DEPS distclean:: distclean-deps distclean-deps: $(gen_verbose) rm -rf $(DEPS_DIR) endif ifeq ($(CACHE_DEPS),1) cacheclean:: cacheclean-git cacheclean-hex cacheclean-git: $(gen_verbose) rm -rf $(CACHE_DIR)/git cacheclean-hex: $(gen_verbose) rm -rf $(CACHE_DIR)/hex endif # Forward-declare variables used in core/deps-tools.mk. This is required # in case plugins use them. ERLANG_MK_RECURSIVE_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-deps-list.log ERLANG_MK_RECURSIVE_DOC_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-doc-deps-list.log ERLANG_MK_RECURSIVE_REL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-rel-deps-list.log ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log ERLANG_MK_QUERY_DEPS_FILE = $(ERLANG_MK_TMP)/query-deps.log ERLANG_MK_QUERY_DOC_DEPS_FILE = $(ERLANG_MK_TMP)/query-doc-deps.log ERLANG_MK_QUERY_REL_DEPS_FILE = $(ERLANG_MK_TMP)/query-rel-deps.log ERLANG_MK_QUERY_TEST_DEPS_FILE = $(ERLANG_MK_TMP)/query-test-deps.log ERLANG_MK_QUERY_SHELL_DEPS_FILE = $(ERLANG_MK_TMP)/query-shell-deps.log # Copyright (c) Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. .PHONY: beam-cache-restore-app beam-cache-restore-test clean-beam-cache distclean-beam-cache BEAM_CACHE_DIR ?= $(ERLANG_MK_TMP)/beam-cache PROJECT_BEAM_CACHE_DIR = $(BEAM_CACHE_DIR)/$(PROJECT) clean:: clean-beam-cache clean-beam-cache: $(verbose) rm -rf $(PROJECT_BEAM_CACHE_DIR) distclean:: distclean-beam-cache $(PROJECT_BEAM_CACHE_DIR): $(verbose) mkdir -p $(PROJECT_BEAM_CACHE_DIR) distclean-beam-cache: $(gen_verbose) rm -rf $(BEAM_CACHE_DIR) beam-cache-restore-app: | $(PROJECT_BEAM_CACHE_DIR) $(verbose) rm -rf $(PROJECT_BEAM_CACHE_DIR)/ebin-test ifneq ($(wildcard ebin/),) $(verbose) mv ebin/ $(PROJECT_BEAM_CACHE_DIR)/ebin-test endif ifneq ($(wildcard $(PROJECT_BEAM_CACHE_DIR)/ebin-app),) $(gen_verbose) mv $(PROJECT_BEAM_CACHE_DIR)/ebin-app ebin/ else $(verbose) $(MAKE) --no-print-directory clean-app endif $(verbose) rm $(ERLANG_MK_TMP)/$(PROJECT).test beam-cache-restore-test: | $(PROJECT_BEAM_CACHE_DIR) $(verbose) rm -rf $(PROJECT_BEAM_CACHE_DIR)/ebin-app ifneq ($(wildcard ebin/),) $(verbose) mv ebin/ $(PROJECT_BEAM_CACHE_DIR)/ebin-app endif ifneq ($(wildcard $(PROJECT_BEAM_CACHE_DIR)/ebin-test),) $(gen_verbose) mv $(PROJECT_BEAM_CACHE_DIR)/ebin-test ebin/ else $(verbose) $(MAKE) --no-print-directory clean-app endif # Copyright (c) Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. .PHONY: clean-app # Configuration. ERLC_OPTS ?= -Werror +debug_info +warn_export_vars +warn_shadow_vars \ +warn_obsolete_guard # +bin_opt_info +warn_export_all +warn_missing_spec COMPILE_FIRST ?= COMPILE_FIRST_PATHS = $(addprefix src/,$(addsuffix .erl,$(COMPILE_FIRST))) ERLC_EXCLUDE ?= ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE))) # One Make target per Erlang source when set to 1. Any other value # keeps a single erlc invocation. Set before including erlang.mk. ERLC_PER_FILE ?= ERLC_ASN1_OPTS ?= ERLC_MIB_OPTS ?= COMPILE_MIB_FIRST ?= COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST))) # Verbosity. app_verbose_0 = @echo " APP " $(PROJECT); app_verbose_2 = set -x; app_verbose = $(app_verbose_$(V)) appsrc_verbose_0 = @echo " APP " $(PROJECT).app.src; appsrc_verbose_2 = set -x; appsrc_verbose = $(appsrc_verbose_$(V)) makedep_verbose_0 = @echo " DEPEND" $(PROJECT).d; makedep_verbose_2 = set -x; makedep_verbose = $(makedep_verbose_$(V)) erlc_verbose_0 = @echo " ERLC " $(filter-out $(patsubst %,%.erl,$(ERLC_EXCLUDE)),\ $(filter %.erl %.core,$(?F))); erlc_verbose_2 = set -x; erlc_verbose = $(erlc_verbose_$(V)) erlc_verbose_per_file_0 = @echo " ERLC " $(notdir $<); erlc_verbose_per_file_2 = set -x; erlc_verbose_per_file = $(erlc_verbose_per_file_$(V)) xyrl_verbose_0 = @echo " XYRL " $(filter %.xrl %.yrl,$(?F)); xyrl_verbose_all_0 = @echo " XYRL " $(notdir $(XRL_FILES) $(YRL_FILES)); xyrl_verbose_2 = set -x; xyrl_verbose = $(xyrl_verbose_$(V)) xyrl_verbose_all = $(xyrl_verbose_all_$(V)) asn1_verbose_0 = @echo " ASN1 " $(notdir $1); asn1_verbose_2 = set -x; asn1_verbose = $(asn1_verbose_$(V)) mib_verbose_0 = @echo " MIB " $(filter %.bin %.mib,$(?F)); mib_verbose_all_0 = @echo " MIB " $(notdir $(MIB_FILES)); mib_verbose_2 = set -x; mib_verbose = $(mib_verbose_$(V)) mib_verbose_all = $(mib_verbose_all_$(V)) ifneq ($(wildcard src/)$(wildcard lib/),) # Targets. app:: $(if $(wildcard $(ERLANG_MK_TMP)/$(PROJECT).test),beam-cache-restore-app) deps $(verbose) $(MAKE) --no-print-directory $(PROJECT).d $(verbose) $(MAKE) --no-print-directory app-build PROJECT_MOD := $(if $(PROJECT_MOD),$(PROJECT_MOD),$(if $(wildcard src/$(PROJECT)_app.erl),$(PROJECT)_app)) define app_file {application, '$(PROJECT)', [ {description, "$(PROJECT_DESCRIPTION)"}, {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP), {id$(comma)$(space)"$1"}$(comma)) {modules, [$(call comma_list,$2)]}, {registered, [$(if $(PROJECT_MOD),$(call comma_list,$(if $(filter $(PROJECT_MOD),$(PROJECT)_app),$(PROJECT)_sup) $(PROJECT_REGISTERED)))]}, {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(OPTIONAL_DEPS) $(foreach dep,$(DEPS),$(call query_name,$(dep))))]}, {optional_applications, [$(call comma_list,$(OPTIONAL_DEPS))]},$(if $(PROJECT_MOD), {mod$(comma)$(space){$(patsubst %,'%',$(PROJECT_MOD))$(comma)$(space)[]}}$(comma)) {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),) ]}. endef app-build: ebin/$(PROJECT).app $(verbose) : # Source files. ALL_SRC_FILES := $(sort $(call core_find,src/,*)) ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES)) CORE_FILES := $(filter %.core,$(ALL_SRC_FILES)) ALL_LIB_FILES := $(sort $(call core_find,lib/,*)) EX_FILES := $(filter-out lib/mix/%,$(filter %.ex,$(ALL_SRC_FILES) $(ALL_LIB_FILES))) HEEX_FILES := $(filter-out lib/mix/%,$(filter %.heex,$(ALL_SRC_FILES) $(ALL_LIB_FILES))) # Top-level projects rebuild generated files when a Makefile changes. # Dependencies and applications under apps/ do not. ifeq ($(IS_APP)$(IS_DEP),) MAKEFILE_CHANGE = $(ERLANG_MK_TMP)/last-makefile-change else MAKEFILE_CHANGE = endif # ASN.1 files. ifneq ($(wildcard asn1/),) ASN1_FILES = $(sort $(call core_find,asn1/,*.asn1)) ERL_FILES += $(addprefix src/,$(patsubst %.asn1,%.erl,$(notdir $(ASN1_FILES)))) define compile_asn1 $(verbose) mkdir -p include/ $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $1 $(verbose) mv asn1/*.erl src/ -$(verbose) mv asn1/*.hrl include/ $(verbose) mv asn1/*.asn1db include/ endef $(PROJECT).d:: $(ASN1_FILES) $(MAKEFILE_CHANGE) $(if $(filter $(MAKEFILE_CHANGE),$?),$(if $(strip $(ASN1_FILES)),$(call compile_asn1,$(ASN1_FILES))),$(if $(strip $(filter %.asn1,$?)),$(call compile_asn1,$(filter %.asn1,$?)))) endif # SNMP MIB files. ifneq ($(wildcard mibs/),) MIB_FILES = $(sort $(call core_find,mibs/,*.mib)) define compile_mib $(1) erlc -v $(ERLC_MIB_OPTS) -o priv/mibs/ -I priv/mibs/ $(2) $(1) erlc -o include/ -- $(addprefix priv/mibs/,$(patsubst %.mib,%.bin,$(notdir $(2)))) endef $(PROJECT).d:: $(COMPILE_MIB_FIRST_PATHS) $(MIB_FILES) $(MAKEFILE_CHANGE) $(verbose) mkdir -p include/ priv/mibs/ $(if $(filter $(MAKEFILE_CHANGE),$?),\ $(if $(strip $(COMPILE_MIB_FIRST_PATHS) $(MIB_FILES)),\ $(call compile_mib,$(mib_verbose_all),$(COMPILE_MIB_FIRST_PATHS) $(filter-out $(COMPILE_MIB_FIRST_PATHS),$(MIB_FILES)))),\ $(if $(strip $(filter %.mib,$?)),\ $(call compile_mib,$(mib_verbose),$(filter %.mib,$?)))) endif # Leex and Yecc files. XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES)) XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES)))) ERL_FILES += $(XRL_ERL_FILES) YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES)) YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES)))) ERL_FILES += $(YRL_ERL_FILES) $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES) $(MAKEFILE_CHANGE) $(if $(filter $(MAKEFILE_CHANGE),$?),$(if $(strip $(XRL_FILES) $(YRL_FILES)),$(xyrl_verbose_all) erlc -v -o src/ $(YRL_ERLC_OPTS) $(XRL_FILES) $(YRL_FILES)),$(if $(strip $(filter %.xrl %.yrl,$?)),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $(filter %.xrl %.yrl,$?))) # Erlang and Core Erlang files. define makedep.erl E = ets:new(makedep, [bag]), G = digraph:new([acyclic]), ErlFiles = lists:usort(string:tokens("$(ERL_FILES)", " ")), DepsDir = "$(call core_native_path,$(DEPS_DIR))", AppsDir = "$(call core_native_path,$(APPS_DIR))", DepsDirsSrc = "$(if $(wildcard $(DEPS_DIR)/*/src), $(call core_native_path,$(wildcard $(DEPS_DIR)/*/src)))", DepsDirsInc = "$(if $(wildcard $(DEPS_DIR)/*/include), $(call core_native_path,$(wildcard $(DEPS_DIR)/*/include)))", AppsDirsSrc = "$(if $(wildcard $(APPS_DIR)/*/src), $(call core_native_path,$(wildcard $(APPS_DIR)/*/src)))", AppsDirsInc = "$(if $(wildcard $(APPS_DIR)/*/include), $(call core_native_path,$(wildcard $(APPS_DIR)/*/include)))", DepsDirs = lists:usort(string:tokens(DepsDirsSrc++DepsDirsInc, " ")), AppsDirs = lists:usort(string:tokens(AppsDirsSrc++AppsDirsInc, " ")), Modules = [{list_to_atom(filename:basename(F, ".erl")), F} || F <- ErlFiles], Add = fun (Mod, Dep) -> case lists:keyfind(Dep, 1, Modules) of false -> ok; {_, DepFile} -> {_, ModFile} = lists:keyfind(Mod, 1, Modules), ets:insert(E, {ModFile, DepFile}), digraph:add_vertex(G, Mod), digraph:add_vertex(G, Dep), digraph:add_edge(G, Mod, Dep) end end, AddHd = fun (F, Mod, DepFile) -> case file:open(DepFile, [read]) of {error, enoent} -> ok; {ok, Fd} -> {_, ModFile} = lists:keyfind(Mod, 1, Modules), case ets:match(E, {ModFile, DepFile}) of [] -> ets:insert(E, {ModFile, DepFile}), F(F, Fd, Mod,0); _ -> ok end end end, SearchHrl = fun F(_Hrl, []) -> {error,enoent}; F(Hrl, [Dir|Dirs]) -> HrlF = filename:join([Dir,Hrl]), case filelib:is_file(HrlF) of true -> {ok, HrlF}; false -> F(Hrl,Dirs) end end, Attr = fun (_F, Mod, behavior, Dep) -> Add(Mod, Dep); (_F, Mod, behaviour, Dep) -> Add(Mod, Dep); (_F, Mod, compile, {parse_transform, Dep}) -> Add(Mod, Dep); (_F, Mod, compile, Opts) when is_list(Opts) -> case proplists:get_value(parse_transform, Opts) of undefined -> ok; Dep -> Add(Mod, Dep) end; (F, Mod, include, Hrl) -> case SearchHrl(Hrl, ["src", "include",AppsDir,DepsDir]++AppsDirs++DepsDirs) of {ok, FoundHrl} -> AddHd(F, Mod, FoundHrl); {error, _} -> false end; (F, Mod, include_lib, Hrl) -> case SearchHrl(Hrl, ["src", "include",AppsDir,DepsDir]++AppsDirs++DepsDirs) of {ok, FoundHrl} -> AddHd(F, Mod, FoundHrl); {error, _} -> false end; (F, Mod, import, {Imp, _}) -> IsFile = case lists:keyfind(Imp, 1, Modules) of false -> false; {_, FilePath} -> filelib:is_file(FilePath) end, case IsFile of false -> ok; true -> Add(Mod, Imp) end; (_, _, _, _) -> ok end, MakeDepend = fun (F, Fd, Mod, StartLocation) -> case io:parse_erl_form(Fd, undefined, StartLocation) of {ok, AbsData, EndLocation} -> case AbsData of {attribute, _, Key, Value} -> Attr(F, Mod, Key, Value), F(F, Fd, Mod, EndLocation); _ -> F(F, Fd, Mod, EndLocation) end; {eof, _ } -> file:close(Fd); {error, ErrorDescription } -> file:close(Fd); {error, ErrorInfo, ErrorLocation} -> F(F, Fd, Mod, ErrorLocation) end, ok end, [begin Mod = list_to_atom(filename:basename(F, ".erl")), case file:open(F, [read]) of {ok, Fd} -> MakeDepend(MakeDepend, Fd, Mod,0); {error, enoent} -> ok end end || F <- ErlFiles], Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))), CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)], TargetPath = fun(Target) -> case lists:keyfind(Target, 1, Modules) of false -> ""; {_, DepFile} -> DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")), string:join(DirSubname ++ [atom_to_list(Target)], "/") end end, Exclude = lists:usort(string:tokens("$(ERLC_EXCLUDE_PATHS)", " ")), PerFile = "$(ERLC_PER_FILE)" =:= "1", Beam = fun(File) -> "ebin/" ++ filename:basename(File, ".erl") ++ ".beam" end, Prereq = fun(Dep) -> case lists:member(Dep, Exclude) of true -> Dep; false -> case lists:keyfind(Dep, 2, Modules) of false -> Dep; _ -> Beam(Dep) end end end, Outside = lists:usort([D || {_, Deps} <- Depend, D <- Deps, not lists:prefix("include/", D), not lists:suffix(".erl", D)]), Output0 = [ "# Generated by Erlang.mk. Edit at your own risk!\n\n", case PerFile of false -> [[[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend], "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"]; true -> ["ERLC_OUTSIDE_HRL =", [[" ", D] || D <- Outside], "\n", [[Beam(F), ":", [[" ", Prereq(D)] || D <- Deps], "\n"] || {F, Deps} <- Depend]] end ], Output = case file:native_name_encoding() of utf8 -> unicode:characters_to_binary(Output0); latin1 -> Output0 end, ok = file:write_file("$1", Output), halt() endef include $(wildcard $(PROJECT).d) ifeq ($(if $(NO_MAKEDEP),$(wildcard $(PROJECT).d),),) $(PROJECT).d:: $(ERL_FILES) $(EX_FILES) $(call core_find,include/,*.hrl) $(ERLC_OUTSIDE_HRL) $(filter-out $(PROJECT).d,$(MAKEFILE_LIST)) # Rebuild everything when the .d file does not exist. # We touch $@ to make sure the command doesn't fail in empty projects. # The file will be generated with content immediately after. $(verbose) if ! test -e $@; then \ touch $@ $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \ fi $(makedep_verbose) $(call erlang,$(call makedep.erl,$@)) endif ifeq ($(IS_APP)$(IS_DEP),) # Rebuild generated files when a Makefile changes. $(PROJECT).d is # excluded so the stamp and the dependency file do not depend on each other. $(ERLANG_MK_TMP)/last-makefile-change: $(filter-out $(PROJECT).d,$(MAKEFILE_LIST)) | $(ERLANG_MK_TMP) $(verbose) touch $@ endif $(PROJECT).d:: $(verbose) : ifeq ($(ERLC_PER_FILE),1) ERL_BEAM_FILES = $(addprefix ebin/,$(patsubst %.erl,%.beam,$(notdir $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES))))) CORE_BEAM_FILES = $(addprefix ebin/,$(patsubst %.core,%.beam,$(notdir $(filter-out $(ERLC_EXCLUDE_PATHS),$(CORE_FILES))))) COMPILE_FIRST_BEAMS = $(addprefix ebin/,$(patsubst %.erl,%.beam,$(notdir $(COMPILE_FIRST_PATHS)))) define compile_erl_target ebin/$(notdir $(basename $1)).beam: $1 $(if $(filter ebin/$(notdir $(basename $1)).beam,$(COMPILE_FIRST_BEAMS)),,$(COMPILE_FIRST_BEAMS)) $(MAKEFILE_CHANGE) | ebin/ $$(erlc_verbose_per_file) ERLC_USE_SERVER=true erlc -v $$(if $$(IS_DEP),$$(filter-out -Werror,$$(ERLC_OPTS)),$$(ERLC_OPTS)) -o ebin/ \ -pa ebin/ -I include/ $$< endef $(foreach f,$(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES)),$(eval $(call compile_erl_target,$f))) endif ebin/$(PROJECT).app:: ebin/ ebin/: $(verbose) mkdir -p ebin/ define compile_erl $(erlc_verbose) erlc -v $(if $(IS_DEP),$(filter-out -Werror,$(ERLC_OPTS)),$(ERLC_OPTS)) -o ebin/ \ -pa ebin/ -I include/ $(filter-out $(ERLC_EXCLUDE_PATHS),$(COMPILE_FIRST_PATHS) $1) endef define validate_app_file case file:consult("ebin/$(PROJECT).app") of {ok, _} -> halt(); _ -> halt(1) end endef ebin/$(PROJECT).app:: $(if $(filter 1,$(ERLC_PER_FILE)),$(ERL_BEAM_FILES) $(CORE_BEAM_FILES),$(ERL_FILES) $(CORE_FILES)) $(wildcard src/$(PROJECT).app.src) $(EX_FILES) $(HEEX_FILES) $(MAKEFILE_CHANGE) ifneq ($(ERLC_PER_FILE),1) $(eval FILES_TO_COMPILE := $(filter-out $(EX_FILES) $(HEEX_FILES) src/$(PROJECT).app.src,$?)) $(if $(filter $(MAKEFILE_CHANGE),$?),$(call compile_erl,$(filter-out $(EX_FILES) src/$(PROJECT).app.src,$(ERL_FILES) $(CORE_FILES))),$(if $(strip $(FILES_TO_COMPILE)),$(call compile_erl,$(FILES_TO_COMPILE)))) endif $(if $(filter $(ELIXIR),disable),,$(if $(strip $(EX_FILES)),$(if $(filter $(MAKEFILE_CHANGE) $(EX_FILES) $(HEEX_FILES),$?),$(elixirc_verbose) $(eval MODULES := $(shell $(call erlang,$(call compile_ex.erl,$(EX_FILES)),-pa $(CURDIR)/ebin))),$(eval MODULES := $(patsubst %,'%',$(notdir $(basename $(wildcard ebin/Elixir.*.beam)))))))) $(eval ELIXIR_COMP_FAILED := $(if $(filter _ERROR_,$(firstword $(MODULES))),true,false)) # Older git versions do not have the --first-parent flag. Do without in that case. $(verbose) if $(ELIXIR_COMP_FAILED); then exit 1; fi $(eval GITDESCRIBE := $(shell git describe --dirty --abbrev=7 --tags --always --first-parent 2>/dev/null \ || git describe --dirty --abbrev=7 --tags --always 2>/dev/null || true)) $(eval MODULES := $(sort $(MODULES) $(patsubst %,'%',$(notdir $(basename \ $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES))))))) ifeq ($(wildcard src/$(PROJECT).app.src),) $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \ > ebin/$(PROJECT).app $(verbose) if ! $(call erlang,$(call validate_app_file)); then \ echo "The .app file produced is invalid. Please verify the value of PROJECT_ENV." >&2; \ exit 1; \ fi else $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \ echo "Empty modules entry not found in $(PROJECT).app.src. Please consult the erlang.mk documentation for instructions." >&2; \ exit 1; \ fi $(appsrc_verbose) cat src/$(PROJECT).app.src \ | sed "s/{[[:space:]]*modules[[:space:]]*,[[:space:]]*\[\]}/{modules, \[$(call comma_list,$(MODULES))\]}/" \ | sed "s/{id,[[:space:]]*\"git\"}/{id, \"$(subst /,\/,$(GITDESCRIBE))\"}/" \ > ebin/$(PROJECT).app endif ifneq ($(wildcard src/$(PROJECT).appup),) $(verbose) cp src/$(PROJECT).appup ebin/ endif clean:: clean-app clean-app: $(gen_verbose) rm -rf $(PROJECT).d ebin priv/mibs $(XRL_ERL_FILES) $(YRL_ERL_FILES) \ $(addprefix include/,$(patsubst %.mib,%.hrl,$(notdir $(MIB_FILES)))) \ $(addprefix include/,$(patsubst %.asn1,%.hrl,$(notdir $(ASN1_FILES)))) \ $(addprefix include/,$(patsubst %.asn1,%.asn1db,$(notdir $(ASN1_FILES)))) \ $(addprefix src/,$(patsubst %.asn1,%.erl,$(notdir $(ASN1_FILES)))) endif # Copyright (c) 2024, Tyler Hughes # Copyright (c) Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. ifeq ($(ELIXIR),system) # We expect 'elixir' to be on the path. ELIXIR_BIN ?= $(shell readlink -f `which elixir`) ELIXIR_LIBS ?= $(abspath $(dir $(ELIXIR_BIN))/../lib) # Fallback in case 'elixir' is a shim. ifeq ($(wildcard $(ELIXIR_LIBS)/elixir/),) ELIXIR_LIBS = $(abspath $(shell elixir -e 'IO.puts(:code.lib_dir(:elixir))')/../) endif ELIXIR_LIBS := $(ELIXIR_LIBS) export ELIXIR_LIBS ERL_LIBS := $(ERL_LIBS)$(ERL_LIBS_SEP)$(call core_native_path,$(ELIXIR_LIBS)) else ifeq ($(ELIXIR),dep) ERL_LIBS := $(ERL_LIBS)$(ERL_LIBS_SEP)$(call core_native_path,$(DEPS_DIR)/elixir/lib/) endif endif elixirc_verbose_0 = @echo " EXC $(words $(EX_FILES)) files"; elixirc_verbose_2 = set -x; elixirc_verbose = $(elixirc_verbose_$(V)) define hex_version_resolver.erl HexVersionResolve = fun(Name, Req) -> application:ensure_all_started(ssl), application:ensure_all_started(inets), Config = $(hex_config.erl), Ver = list_to_atom("Elixir.Version"), Match = list_to_atom("match?"), Compare = list_to_atom("compare"), ParseReq = list_to_atom("parse_requirement!"), ParseVer = list_to_atom("parse!"), maybe {ok, {200, _, Package}} ?= hex_repo:get_package(Config, atom_to_binary(Name)), #{releases := List} = Package, ParsedReq = Ver:ParseReq(Req), AllowPre = lists:any(fun ({_, _, _, Pre, Build}) -> Pre =/= [] orelse Build =/= []; (_) -> false end, maps:get(lexed, ParsedReq)), Best = lists:foldl(fun(Release, Acc) -> maybe true ?= maps:get(retired, Release, false) =:= false, Vsn = maps:get(version, Release), Parsed = Ver:ParseVer(Vsn), Pre = maps:get(pre, Parsed), Build = maps:get(build, Parsed), true ?= AllowPre orelse ((Pre =:= []) andalso (Build =:= nil orelse Build =:= [])), true ?= Ver:Match(Vsn, Req), true ?= case Acc of none -> true; {ok, BestVsn} -> Ver:Compare(Vsn, BestVsn) =:= gt end, {ok, Vsn} else _ -> Acc end end, none, List), {ok, Vsn} ?= Best else {ok, {Status, _, Errors}} -> {error, Status, Errors}; {error, Reason} -> {error, Reason}; none -> {error, nomatch} end end, HexVersion = fun(Name, Req) -> case HexVersionResolve(Name, Req) of {ok, Vsn} -> Vsn; {error, Status, Errors} -> io:format(standard_error, "Error ~b: ~0p~n", [Status, Errors]), halt(77); {error, Reason} -> io:format(standard_error, "Error: ~0p~n", [Reason]), halt(95) end end, HexVersionResolveAndPrint = fun(Name, Req) -> io:format("~s", [HexVersion(Name, Req)]), halt(0) end endef define dep_autopatch_mix.erl $(call hex_version_resolver.erl), {ok, _} = application:ensure_all_started(elixir), {ok, _} = application:ensure_all_started(mix), MixFile = <<"$(call core_native_path,$(DEPS_DIR)/$1/mix.exs)">>, {ok, OldCwd} = file:get_cwd(), ok = file:set_cwd("$(call core_native_path,$(DEPS_DIR)/$1)"), {Mod, Bin} = case elixir_compiler:file(MixFile, fun(_File, _LexerPid) -> ok end) of [{T = {_, _}, _CheckerPid}] -> T; [T = {_, _}] -> T end, {module, Mod} = code:load_binary(Mod, binary_to_list(MixFile), Bin), Project = Mod:project(), Application = try Mod:application() catch error:undef -> [] end, ok = file:set_cwd(OldCwd), StartMod = case lists:keyfind(mod, 1, Application) of {mod, {StartMod0, _StartArgs}} -> atom_to_list(StartMod0); _ -> "" end, Write = fun (Text) -> file:write_file("$(call core_native_path,$(DEPS_DIR)/$1/Makefile)", Text, [append]) end, Write([ "PROJECT = ", atom_to_list(proplists:get_value(app, Project)), "\n" "PROJECT_DESCRIPTION = ", proplists:get_value(description, Project, ""), "\n" "PROJECT_VERSION = ", proplists:get_value(version, Project, ""), "\n" "PROJECT_MOD = ", StartMod, "\n" "define PROJECT_ENV\n", io_lib:format("~p", [proplists:get_value(env, Application, [])]), "\n" "endef\n\n"]), ExtraApps = lists:usort([eex, elixir, logger, mix] ++ proplists:get_value(extra_applications, Application, [])), Write(["LOCAL_DEPS += ", lists:join(" ", [atom_to_list(App) || App <- ExtraApps]), "\n\n"]), Deps = proplists:get_value(deps, Project, []) -- [elixir_make], IsRequiredProdDep = fun(Opts) -> (proplists:get_value(optional, Opts) =/= true) andalso case proplists:get_value(only, Opts, prod) of prod -> true; L when is_list(L) -> lists:member(prod, L); _ -> false end end, lists:foreach(fun ({Name, Req}) when is_binary(Req) -> Vsn = HexVersion(Name, Req), Write(["DEPS += ", atom_to_list(Name), "\n"]), Write(["dep_", atom_to_list(Name), " = hex ", Vsn, " ", atom_to_list(Name), "\n"]); ({Name, Opts}) when is_list(Opts) -> Path = proplists:get_value(path, Opts), case IsRequiredProdDep(Opts) of true when Path =/= undefined -> Write(["DEPS += ", atom_to_list(Name), "\n"]), Write(["dep_", atom_to_list(Name), " = ln ", Path, "\n"]); true when Path =:= undefined -> Write(["DEPS += ", atom_to_list(Name), "\n"]), io:format(standard_error, "Warning: No version given for ~p.", [Name]); false -> ok end; ({Name, Req, Opts}) -> case IsRequiredProdDep(Opts) of true -> Vsn = HexVersion(Name, Req), Write(["DEPS += ", atom_to_list(Name), "\n"]), Write(["dep_", atom_to_list(Name), " = hex ", Vsn, " ", atom_to_list(Name), "\n"]); false -> ok end; (_) -> ok end, Deps), case lists:member(elixir_make, proplists:get_value(compilers, Project, [])) of false -> ok; true -> Write("# https://hexdocs.pm/elixir_make/Mix.Tasks.Compile.ElixirMake.html\n"), MakeVal = fun(Key, Proplist, DefaultVal, DefaultReplacement) -> case proplists:get_value(Key, Proplist, DefaultVal) of DefaultVal -> DefaultReplacement; Value -> Value end end, MakeMakefile = binary_to_list(MakeVal(make_makefile, Project, default, <<"Makefile">>)), MakeExe = MakeVal(make_executable, Project, default, "$$\(MAKE)"), MakeCwd = MakeVal(make_cwd, Project, undefined, <<".">>), MakeTargets = MakeVal(make_targets, Project, [], []), MakeArgs = MakeVal(make_args, Project, undefined, []), case file:rename("$(call core_native_path,$(DEPS_DIR)/$1)/" ++ MakeMakefile, "$(call core_native_path,$(DEPS_DIR)/$1)/elixir_make.mk") of ok -> ok; Err = {error, _} -> io:format(standard_error, "Failed to copy Makefile with error ~p~n", [Err]), halt(90) end, Write(io_lib:format( "export ERTS_INCLUDE_DIR := ~s/erts-~s/include\n" "export ERL_EI_INCLUDE_DIR := ~s\n" "export ERL_EI_LIBDIR := ~s\n" "export MIX_APP_PATH := $(DEPS_DIR)/$1\n" "C_SRC_DIR := $(DEPS_DIR)/$1/elixir_make.disabled_c_src\n\n", [code:root_dir(), erlang:system_info(version), filename:join(code:lib_dir(erl_interface), "include"), filename:join(code:lib_dir(erl_interface), "lib")])), Write(["app::\n" "\t", MakeExe, " -C ", MakeCwd, " -f $(DEPS_DIR)/$1/elixir_make.mk", " ", lists:join(" ", MakeTargets), " ", lists:join(" ", MakeArgs), "\n\n"]), case MakeVal(make_clean, Project, nil, undefined) of undefined -> ok; Clean -> Write(["clean::\n\t", Clean, "\n\n"]) end end, Write("ERLC_OPTS = +debug_info\n\n"), Write("include $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"), halt() endef define dep_autopatch_mix sed 's|\(defmodule.*do\)|\1\n try do\n Code.compiler_options(on_undefined_variable: :warn)\n rescue _ -> :ok\n end\n|g' $(DEPS_DIR)/$(1)/mix.exs > $(DEPS_DIR)/$(1)/mix.exs.new; \ mv $(DEPS_DIR)/$(1)/mix.exs.new $(DEPS_DIR)/$(1)/mix.exs; \ $(MAKE) $(ERLANG_MK_TMP)/dep_built/hex_core; \ MIX_ENV="$(if $(MIX_ENV),$(strip $(MIX_ENV)),prod)" \ $(call erlang,$(call dep_autopatch_mix.erl,$1)) endef # We change the group leader so the Elixir io:format output # isn't captured as we need to either print the modules on # success, or print _ERROR_ on failure. define compile_ex.erl {ok, _} = application:ensure_all_started(elixir), {ok, _} = application:ensure_all_started(mix), $(foreach dep,$(LOCAL_DEPS),_ = application:load($(dep)),) ModCode = list_to_atom("Elixir.Code"), ModCode:put_compiler_option(ignore_module_conflict, true), ModComp = list_to_atom("Elixir.Kernel.ParallelCompiler"), ModMixProject = list_to_atom("Elixir.Mix.Project"), erlang:group_leader(whereis(standard_error), self()), ModMixProject:in_project($(PROJECT), ".", [], fun(_MixFile) -> case ModComp:compile_to_path([$(call comma_list,$(patsubst %,<<"%">>,$1))], <<"ebin/">>) of {ok, Modules, _} -> lists:foreach(fun(E) -> io:format(user, "~p ", [E]) end, Modules), halt(0); {error, _ErroredModules, _WarnedModules} -> io:format(user, "_ERROR_", []), halt(1) end end) endef # Copyright (c) Loïc Hoguin # Copyright (c) 2015, Viktor Söderqvist # This file is part of erlang.mk and subject to the terms of the ISC License. .PHONY: docs-deps # Configuration. ALL_DOC_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(DOC_DEPS)) # Targets. $(foreach dep,$(DOC_DEPS),$(eval $(call dep_target,$(dep)))) ifneq ($(SKIP_DEPS),) doc-deps: else doc-deps: $(ALL_DOC_DEPS_DIRS) $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done endif # Copyright (c) Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. .PHONY: rel-deps # Configuration. ALL_REL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(REL_DEPS)) # Targets. $(foreach dep,$(REL_DEPS),$(eval $(call dep_target,$(dep)))) ifneq ($(SKIP_DEPS),) rel-deps: else rel-deps: $(ALL_REL_DEPS_DIRS) $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done endif # Copyright (c) Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. .PHONY: test-deps test-dir test-build clean-test-dir # Configuration. TEST_DIR ?= $(CURDIR)/test ALL_TEST_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(TEST_DEPS)) TEST_ERLC_OPTS ?= +debug_info +warn_export_vars +warn_shadow_vars +warn_obsolete_guard TEST_ERLC_OPTS += -DTEST=1 # Targets. $(foreach dep,$(TEST_DEPS),$(eval $(call dep_target,$(dep)))) ifneq ($(SKIP_DEPS),) test-deps: else test-deps: deps | $(ERLANG_MK_TMP)/dep_built ifneq ($(ALL_TEST_DEPS_DIRS),) $(verbose) missing=; for dep in $(ALL_TEST_DEPS_DIRS); do \ [ -e $$dep ] || missing="$$missing $$dep"; \ done; \ if [ -n "$$missing" ]; then $(MAKE) --no-print-directory $$missing; fi $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do \ if [ -z "$(strip $(FULL))" ] && [ ! -L $$dep ] && [ -f $(ERLANG_MK_TMP)/dep_built/`basename $$dep` ]; then \ :; \ else \ $(MAKE) -C $$dep IS_DEP=1; \ if [ ! -L $$dep ] && [ -d $$dep/ebin ]; then touch $(ERLANG_MK_TMP)/dep_built/`basename $$dep`; fi; \ fi \ done endif endif ifneq ($(wildcard $(TEST_DIR)),) test-dir: $(ERLANG_MK_TMP)/$(PROJECT).last-testdir-build @: test_erlc_verbose_0 = @echo " ERLC " $(filter-out $(patsubst %,%.erl,$(ERLC_EXCLUDE)),\ $(filter %.erl %.core,$(notdir $(FILES_TO_COMPILE)))); test_erlc_verbose_2 = set -x; test_erlc_verbose = $(test_erlc_verbose_$(V)) define compile_test_erl $(test_erlc_verbose) erlc -v $(TEST_ERLC_OPTS) -o $(TEST_DIR) \ -pa ebin/ -I include/ $1 endef ERL_TEST_FILES = $(call core_find,$(TEST_DIR)/,*.erl) $(ERLANG_MK_TMP)/$(PROJECT).last-testdir-build: $(ERL_TEST_FILES) $(MAKEFILE_LIST) # When we have to recompile files in src/ the .d file always gets rebuilt. # Therefore we want to ignore it when rebuilding test files. $(eval FILES_TO_COMPILE := $(if $(filter $(filter-out $(PROJECT).d,$(MAKEFILE_LIST)),$?),$(filter $(ERL_TEST_FILES),$^),$(filter $(ERL_TEST_FILES),$?))) $(if $(strip $(FILES_TO_COMPILE)),$(call compile_test_erl,$(FILES_TO_COMPILE)) && touch $@) endif test-build:: IS_TEST=1 test-build:: ERLC_OPTS=$(TEST_ERLC_OPTS) test-build:: $(if $(wildcard src),$(if $(wildcard $(ERLANG_MK_TMP)/$(PROJECT).test),,beam-cache-restore-test)) $(if $(IS_APP),,deps test-deps) # We already compiled everything when IS_APP=1. ifndef IS_APP ifneq ($(wildcard src),) $(verbose) $(MAKE) --no-print-directory $(PROJECT).d ERLC_OPTS="$(call escape_dquotes,$(TEST_ERLC_OPTS))" $(verbose) $(MAKE) --no-print-directory app-build ERLC_OPTS="$(call escape_dquotes,$(TEST_ERLC_OPTS))" $(gen_verbose) touch $(ERLANG_MK_TMP)/$(PROJECT).test endif ifneq ($(wildcard $(TEST_DIR)),) $(verbose) $(MAKE) --no-print-directory test-dir ERLC_OPTS="$(call escape_dquotes,$(TEST_ERLC_OPTS))" endif endif # Roughly the same as test-build, but when IS_APP=1. # We only care about compiling the current application. ifdef IS_APP test-build-app:: ERLC_OPTS=$(TEST_ERLC_OPTS) test-build-app:: deps test-deps ifneq ($(wildcard src),) $(verbose) $(MAKE) --no-print-directory $(PROJECT).d ERLC_OPTS="$(call escape_dquotes,$(TEST_ERLC_OPTS))" $(verbose) $(MAKE) --no-print-directory app-build ERLC_OPTS="$(call escape_dquotes,$(TEST_ERLC_OPTS))" $(gen_verbose) touch $(ERLANG_MK_TMP)/$(PROJECT).test endif ifneq ($(wildcard $(TEST_DIR)),) $(verbose) $(MAKE) --no-print-directory test-dir ERLC_OPTS="$(call escape_dquotes,$(TEST_ERLC_OPTS))" endif endif clean:: clean-test-dir clean-test-dir: ifneq ($(wildcard $(TEST_DIR)/*.beam),) $(gen_verbose) rm -f $(TEST_DIR)/*.beam $(ERLANG_MK_TMP)/$(PROJECT).last-testdir-build endif # Copyright (c) Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. .PHONY: rebar.config compat_ref = {$(shell (git -C $(DEPS_DIR)/$1 show-ref -q --verify "refs/heads/$2" && echo branch) || (git -C $(DEPS_DIR)/$1 show-ref -q --verify "refs/tags/$2" && echo tag) || echo ref),"$2"} # We strip out -Werror because we don't want to fail due to # warnings when used as a dependency. compat_prepare_erlc_opts = $(shell echo "$1" | sed 's/, */,/g') define compat_convert_erlc_opts $(if $(filter-out -Werror,$1),\ $(if $(findstring +,$1),\ $(shell echo $1 | cut -b 2-))) endef define compat_erlc_opts_to_list [$(call comma_list,$(foreach o,$(call compat_prepare_erlc_opts,$1),$(call compat_convert_erlc_opts,$o)))] endef define compat_rebar_config {deps, [ $(call comma_list,$(foreach d,$(DEPS),\ $(if $(filter hex,$(call query_fetch_method,$d)),\ {$(call query_name,$d)$(comma)"$(call query_version_hex,$d)"},\ {$(call query_name,$d)$(comma)".*"$(comma){git,"$(call query_repo,$d)"$(comma)$(call compat_ref,$(call query_name,$d),$(call query_version,$d))}}))) ]}. {erl_opts, $(call compat_erlc_opts_to_list,$(ERLC_OPTS))}. endef rebar.config: deps $(gen_verbose) $(call core_render,compat_rebar_config,rebar.config) define tpl_application.app.src {application, project_name, [ {description, ""}, {vsn, "0.1.0"}, {id, "git"}, {modules, []}, {registered, []}, {applications, [ kernel, stdlib ]}, {mod, {project_name_app, []}}, {env, []} ]}. endef define tpl_application -module(project_name_app). -behaviour(application). -export([start/2]). -export([stop/1]). start(_Type, _Args) -> project_name_sup:start_link(). stop(_State) -> ok. endef define tpl_apps_Makefile PROJECT = project_name PROJECT_DESCRIPTION = New project PROJECT_VERSION = 0.1.0 template_sp # Make sure we know where the applications are located. ROOT_DIR ?= rel_root_dir APPS_DIR ?= .. DEPS_DIR ?= rel_deps_dir include rel_root_dir/erlang.mk endef define tpl_cowboy_http_h -module(template_name). -behaviour(cowboy_http_handler). -export([init/3]). -export([handle/2]). -export([terminate/3]). -record(state, { }). init(_, Req, _Opts) -> {ok, Req, #state{}}. handle(Req, State=#state{}) -> {ok, Req2} = cowboy_req:reply(200, Req), {ok, Req2, State}. terminate(_Reason, _Req, _State) -> ok. endef define tpl_cowboy_loop_h -module(template_name). -behaviour(cowboy_loop_handler). -export([init/3]). -export([info/3]). -export([terminate/3]). -record(state, { }). init(_, Req, _Opts) -> {loop, Req, #state{}, 5000, hibernate}. info(_Info, Req, State) -> {loop, Req, State, hibernate}. terminate(_Reason, _Req, _State) -> ok. endef define tpl_cowboy_rest_h -module(template_name). -export([init/3]). -export([content_types_provided/2]). -export([get_html/2]). init(_, _Req, _Opts) -> {upgrade, protocol, cowboy_rest}. content_types_provided(Req, State) -> {[{{<<"text">>, <<"html">>, '*'}, get_html}], Req, State}. get_html(Req, State) -> {<<"This is REST!">>, Req, State}. endef define tpl_cowboy_websocket_h -module(template_name). -behaviour(cowboy_websocket_handler). -export([init/3]). -export([websocket_init/3]). -export([websocket_handle/3]). -export([websocket_info/3]). -export([websocket_terminate/3]). -record(state, { }). init(_, _, _) -> {upgrade, protocol, cowboy_websocket}. websocket_init(_, Req, _Opts) -> Req2 = cowboy_req:compact(Req), {ok, Req2, #state{}}. websocket_handle({text, Data}, Req, State) -> {reply, {text, Data}, Req, State}; websocket_handle({binary, Data}, Req, State) -> {reply, {binary, Data}, Req, State}; websocket_handle(_Frame, Req, State) -> {ok, Req, State}. websocket_info(_Info, Req, State) -> {ok, Req, State}. websocket_terminate(_Reason, _Req, _State) -> ok. endef define tpl_gen_server -module(template_name). -behaviour(gen_server). %% API. -export([start_link/0]). %% gen_server. -export([init/1]). -export([handle_call/3]). -export([handle_cast/2]). -export([handle_info/2]). -export([terminate/2]). -export([code_change/3]). -record(state, { }). %% API. -spec start_link() -> {ok, pid()}. start_link() -> gen_server:start_link(?MODULE, [], []). %% gen_server. init([]) -> {ok, #state{}}. handle_call(_Request, _From, State) -> {reply, ignored, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. endef define tpl_gen_statem -module(template_name). -behaviour(gen_statem). %% API. -export([start_link/0]). %% gen_statem. -export([callback_mode/0]). -export([init/1]). -export([state_name/3]). -export([handle_event/4]). -export([terminate/3]). -export([code_change/4]). -record(state, { }). %% API. -spec start_link() -> {ok, pid()}. start_link() -> gen_statem:start_link(?MODULE, [], []). %% gen_statem. callback_mode() -> state_functions. init([]) -> {ok, state_name, #state{}}. state_name(_EventType, _EventData, StateData) -> {next_state, state_name, StateData}. handle_event(_EventType, _EventData, StateName, StateData) -> {next_state, StateName, StateData}. terminate(_Reason, _StateName, _StateData) -> ok. code_change(_OldVsn, StateName, StateData, _Extra) -> {ok, StateName, StateData}. endef define tpl_library.app.src {application, project_name, [ {description, ""}, {vsn, "0.1.0"}, {id, "git"}, {modules, []}, {registered, []}, {applications, [ kernel, stdlib ]} ]}. endef define tpl_module -module(template_name). -export([]). endef define tpl_ranch_protocol -module(template_name). -behaviour(ranch_protocol). -export([start_link/4]). -export([init/4]). -type opts() :: []. -export_type([opts/0]). -record(state, { socket :: inet:socket(), transport :: module() }). start_link(Ref, Socket, Transport, Opts) -> Pid = spawn_link(?MODULE, init, [Ref, Socket, Transport, Opts]), {ok, Pid}. -spec init(ranch:ref(), inet:socket(), module(), opts()) -> ok. init(Ref, Socket, Transport, _Opts) -> ok = ranch:accept_ack(Ref), loop(#state{socket=Socket, transport=Transport}). loop(State) -> loop(State). endef define tpl_relx.config {release, {project_name_release, "1"}, [project_name, sasl, runtime_tools]}. {dev_mode, false}. {include_erts, true}. {extended_start_script, true}. {sys_config, "config/sys.config"}. {vm_args, "config/vm.args"}. endef define tpl_supervisor -module(template_name). -behaviour(supervisor). -export([start_link/0]). -export([init/1]). start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). init([]) -> Procs = [], {ok, {{one_for_one, 1, 5}, Procs}}. endef define tpl_sys.config [ ]. endef define tpl_top_Makefile PROJECT = project_name PROJECT_DESCRIPTION = New project PROJECT_VERSION = 0.1.0 template_sp include erlang.mk endef define tpl_vm.args -name project_name@127.0.0.1 -setcookie project_name -kernel inet_dist_use_interface {127,0,0,1} -env ERL_EPMD_ADDRESS 127.0.0.1 -heart endef # Copyright (c) Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck) .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual # Core targets. docs:: asciidoc distclean:: distclean-asciidoc-guide distclean-asciidoc-manual # Plugin-specific targets. asciidoc: asciidoc-guide asciidoc-manual # User guide. ifeq ($(wildcard doc/src/guide/book.asciidoc),) asciidoc-guide: else asciidoc-guide: distclean-asciidoc-guide doc-deps a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/ distclean-asciidoc-guide: $(gen_verbose) rm -rf doc/html/ doc/guide.pdf endif # Man pages. ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc) ifeq ($(ASCIIDOC_MANUAL_FILES),) asciidoc-manual: else # Configuration. MAN_INSTALL_PATH ?= /usr/local/share/man MAN_SECTIONS ?= 3 7 MAN_PROJECT ?= $(shell echo $(PROJECT) | awk '{ print toupper(substr($$0,1,1)) substr($$0,2) }') MAN_VERSION ?= $(PROJECT_VERSION) # Plugin-specific targets. define asciidoc2man.erl try [begin io:format(" ADOC ~s~n", [F]), ok = asciideck:to_manpage(asciideck:parse_file(F), #{ compress => gzip, outdir => filename:dirname(F), extra2 => "$(MAN_PROJECT) $(MAN_VERSION)", extra3 => "$(MAN_PROJECT) Function Reference" }) end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]], halt(0) catch C:E$(if $V,:S) -> io:format("Exception: ~p:~p~n$(if $V,Stacktrace: ~p~n)", [C, E$(if $V,$(comma) S)]), halt(1) end. endef asciidoc-manual:: doc-deps asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES) $(gen_verbose) $(call erlang,$(call asciidoc2man.erl,$?)) $(verbose) $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;) install-docs:: install-asciidoc install-asciidoc: asciidoc-manual $(foreach s,$(MAN_SECTIONS),\ mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \ install -g `id -g` -o `id -u` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;) distclean-asciidoc-manual: $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS)) endif endif # Copyright (c) Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates # Core targets. help:: $(verbose) printf "%s\n" "" \ "Bootstrap targets:" \ " bootstrap Generate a skeleton of an OTP application" \ " bootstrap-lib Generate a skeleton of an OTP library" \ " bootstrap-rel Generate the files needed to build a release" \ " new-app in=NAME Create a new local OTP application NAME" \ " new-lib in=NAME Create a new local OTP library NAME" \ " new t=TPL n=NAME Generate a module NAME based on the template TPL" \ " new t=T n=N in=APP Generate a module NAME based on the template TPL in APP" \ " list-templates List available templates" # Plugin-specific targets. ifndef WS ifdef SP WS = $(subst a,,a $(wordlist 1,$(SP),a a a a a a a a a a a a a a a a a a a a)) else WS = $(tab) endif endif ifdef SP define template_sp # By default templates indent with a single tab per indentation # level. Set this variable to the number of spaces you prefer: SP = $(SP) endef else template_sp = endif subst_template = $(subst rel_root_dir,$(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app),$(subst rel_deps_dir,$(call core_relpath,$(DEPS_DIR),$(APPS_DIR)/app),$(subst template_sp,$(template_sp),$(subst project_name,$p,$(subst template_name,$n,$1))))) define core_render_template $(eval define _tpl_$(1)$(newline)$(call subst_template,$(tpl_$(1)))$(newline)endef) $(verbose) $(call core_render,_tpl_$(1),$2) endef bootstrap: ifneq ($(wildcard Makefile),) $(error Error: Makefile already exists) endif ifneq ($(wildcard src/),) $(error Error: src/ directory already exists) endif $(eval p := $(PROJECT)) $(if $(shell echo $p | LC_ALL=C grep -x "[a-z0-9_]*"),,\ $(error Error: Invalid characters in the application name)) $(eval n := $(PROJECT)_sup) $(verbose) $(call core_render_template,top_Makefile,Makefile) $(verbose) mkdir src/ ifdef LEGACY $(verbose) $(call core_render_template,application.app.src,src/$(PROJECT).app.src) endif $(verbose) $(call core_render_template,application,src/$(PROJECT)_app.erl) $(verbose) $(call core_render_template,supervisor,src/$(PROJECT)_sup.erl) bootstrap-lib: ifneq ($(wildcard Makefile),) $(error Error: Makefile already exists) endif ifneq ($(wildcard src/),) $(error Error: src/ directory already exists) endif $(eval p := $(PROJECT)) $(if $(shell echo $p | LC_ALL=C grep -x "[a-z0-9_]*"),,\ $(error Error: Invalid characters in the application name)) $(verbose) $(call core_render_template,top_Makefile,Makefile) $(verbose) mkdir src/ ifdef LEGACY $(verbose) $(call core_render_template,library.app.src,src/$(PROJECT).app.src) endif bootstrap-rel: ifneq ($(wildcard relx.config),) $(error Error: relx.config already exists) endif ifneq ($(wildcard config/),) $(error Error: config/ directory already exists) endif $(eval p := $(PROJECT)) $(verbose) $(call core_render_template,relx.config,relx.config) $(verbose) mkdir config/ $(verbose) $(call core_render_template,sys.config,config/sys.config) $(verbose) $(call core_render_template,vm.args,config/vm.args) $(verbose) awk '/^include erlang.mk/ && !ins {print "REL_DEPS += relx";ins=1};{print}' Makefile > Makefile.bak $(verbose) mv Makefile.bak Makefile new-app: ifndef in $(error Usage: $(MAKE) new-app in=APP) endif ifneq ($(wildcard $(APPS_DIR)/$in),) $(error Error: Application $in already exists) endif $(eval p := $(in)) $(if $(shell echo $p | LC_ALL=C grep -x "[a-z0-9_]*"),,\ $(error Error: Invalid characters in the application name)) $(eval n := $(in)_sup) $(verbose) mkdir -p $(APPS_DIR)/$p/src/ $(verbose) $(call core_render_template,apps_Makefile,$(APPS_DIR)/$p/Makefile) ifdef LEGACY $(verbose) $(call core_render_template,application.app.src,$(APPS_DIR)/$p/src/$p.app.src) endif $(verbose) $(call core_render_template,application,$(APPS_DIR)/$p/src/$p_app.erl) $(verbose) $(call core_render_template,supervisor,$(APPS_DIR)/$p/src/$p_sup.erl) new-lib: ifndef in $(error Usage: $(MAKE) new-lib in=APP) endif ifneq ($(wildcard $(APPS_DIR)/$in),) $(error Error: Application $in already exists) endif $(eval p := $(in)) $(if $(shell echo $p | LC_ALL=C grep -x "[a-z0-9_]*"),,\ $(error Error: Invalid characters in the application name)) $(verbose) mkdir -p $(APPS_DIR)/$p/src/ $(verbose) $(call core_render_template,apps_Makefile,$(APPS_DIR)/$p/Makefile) ifdef LEGACY $(verbose) $(call core_render_template,library.app.src,$(APPS_DIR)/$p/src/$p.app.src) endif # These are not necessary because we don't expose those as "normal" templates. BOOTSTRAP_TEMPLATES = apps_Makefile top_Makefile \ application.app.src library.app.src application \ relx.config sys.config vm.args # Templates may override the path they will be written to when using 'new'. # Only special template paths must be listed. Default is src/template_name.erl # Substitution is also applied to the paths. Examples: # #tplp_top_Makefile = Makefile #tplp_application.app.src = src/project_name.app.src #tplp_application = src/project_name_app.erl #tplp_relx.config = relx.config # Erlang.mk bundles its own templates at build time into the erlang.mk file. new: $(if $(t),,$(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])) $(if $(n),,$(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])) $(if $(tpl_$(t)),,$(error Error: $t template does not exist; try $(Make) list-templates)) $(eval dest := $(if $(in),$(APPS_DIR)/$(in)/)$(call subst_template,$(if $(tplp_$(t)),$(tplp_$(t)),src/template_name.erl))) $(if $(wildcard $(dir $(dest))),,$(error Error: $(dir $(dest)) directory does not exist)) $(if $(wildcard $(dest)),$(error Error: The file $(dest) already exists)) $(eval p := $(PROJECT)) $(call core_render_template,$(t),$(dest)) list-templates: $(verbose) @echo Available templates: $(verbose) printf " %s\n" $(sort $(filter-out $(BOOTSTRAP_TEMPLATES),$(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))) # Copyright (c) Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. .PHONY: clean-c_src distclean-c_src-env # Configuration. C_SRC_DIR ?= $(CURDIR)/c_src C_SRC_ENV ?= $(C_SRC_DIR)/env.mk C_SRC_OUTPUT ?= $(CURDIR)/priv/$(PROJECT) C_SRC_TYPE ?= shared # System type and C compiler/flags. ifeq ($(PLATFORM),msys2) C_SRC_OUTPUT_EXECUTABLE_EXTENSION ?= .exe C_SRC_OUTPUT_SHARED_EXTENSION ?= .dll C_SRC_OUTPUT_STATIC_EXTENSION ?= .lib else C_SRC_OUTPUT_EXECUTABLE_EXTENSION ?= C_SRC_OUTPUT_SHARED_EXTENSION ?= .so C_SRC_OUTPUT_STATIC_EXTENSION ?= .a endif ifeq ($(C_SRC_TYPE),shared) C_SRC_OUTPUT_FILE = $(C_SRC_OUTPUT)$(C_SRC_OUTPUT_SHARED_EXTENSION) else ifeq ($(C_SRC_TYPE),static) C_SRC_OUTPUT_FILE = $(C_SRC_OUTPUT)$(C_SRC_OUTPUT_STATIC_EXTENSION) else C_SRC_OUTPUT_FILE = $(C_SRC_OUTPUT)$(C_SRC_OUTPUT_EXECUTABLE_EXTENSION) endif RANLIB ?= ranlib ARFLAGS ?= cr ifeq ($(PLATFORM),msys2) # We hardcode the compiler used on MSYS2. The default CC=cc does # not produce working code. The "gcc" MSYS2 package also doesn't. CC = /mingw64/bin/gcc export CC CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes CXXFLAGS ?= -O3 -finline-functions -Wall else ifeq ($(PLATFORM),darwin) CC ?= cc CFLAGS ?= -O3 -std=c99 -Wall -Wmissing-prototypes CXXFLAGS ?= -O3 -Wall LDFLAGS ?= -flat_namespace -undefined suppress else ifeq ($(PLATFORM),freebsd) CC ?= cc CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes CXXFLAGS ?= -O3 -finline-functions -Wall else ifeq ($(PLATFORM),linux) CC ?= gcc CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes CXXFLAGS ?= -O3 -finline-functions -Wall endif ifneq ($(PLATFORM),msys2) CFLAGS += -fPIC CXXFLAGS += -fPIC endif ifeq ($(C_SRC_TYPE),static) CFLAGS += -DSTATIC_ERLANG_NIF=1 CXXFLAGS += -DSTATIC_ERLANG_NIF=1 endif CFLAGS += -I"$(ERTS_INCLUDE_DIR)" -I"$(ERL_INTERFACE_INCLUDE_DIR)" CXXFLAGS += -I"$(ERTS_INCLUDE_DIR)" -I"$(ERL_INTERFACE_INCLUDE_DIR)" LDLIBS += -L"$(ERL_INTERFACE_LIB_DIR)" -lei # Verbosity. c_verbose_0 = @echo " C " $(filter-out $(notdir $(MAKEFILE_LIST) $(C_SRC_ENV)),$(^F)); c_verbose = $(c_verbose_$(V)) cpp_verbose_0 = @echo " CPP " $(filter-out $(notdir $(MAKEFILE_LIST) $(C_SRC_ENV)),$(^F)); cpp_verbose = $(cpp_verbose_$(V)) link_verbose_0 = @echo " LD " $(@F); link_verbose = $(link_verbose_$(V)) ar_verbose_0 = @echo " AR " $(@F); ar_verbose = $(ar_verbose_$(V)) ranlib_verbose_0 = @echo " RANLIB" $(@F); ranlib_verbose = $(ranlib_verbose_$(V)) # Targets. ifeq ($(wildcard $(C_SRC_DIR)),) else ifneq ($(wildcard $(C_SRC_DIR)/Makefile),) app:: app-c_src test-build:: app-c_src app-c_src: $(MAKE) -C $(C_SRC_DIR) clean:: $(MAKE) -C $(C_SRC_DIR) clean else ifeq ($(SOURCES),) SOURCES := $(sort $(foreach pat,*.c *.C *.cc *.cpp,$(call core_find,$(C_SRC_DIR)/,$(pat)))) endif OBJECTS = $(addsuffix .o, $(basename $(SOURCES))) COMPILE_C = $(c_verbose) $(CC) $(CFLAGS) $(CPPFLAGS) -c COMPILE_CPP = $(cpp_verbose) $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c app:: $(C_SRC_ENV) $(C_SRC_OUTPUT_FILE) test-build:: $(C_SRC_ENV) $(C_SRC_OUTPUT_FILE) ifneq ($(C_SRC_TYPE),static) $(C_SRC_OUTPUT_FILE): $(OBJECTS) $(verbose) mkdir -p $(dir $@) $(link_verbose) $(CC) $(OBJECTS) \ $(LDFLAGS) $(if $(filter $(C_SRC_TYPE),shared),-shared) $(LDLIBS) \ -o $(C_SRC_OUTPUT_FILE) else $(C_SRC_OUTPUT_FILE): $(OBJECTS) $(verbose) mkdir -p $(dir $@) $(ar_verbose) $(AR) $(ARFLAGS) $(C_SRC_OUTPUT_FILE) $(OBJECTS) $(ranlib_verbose) $(RANLIB) $(C_SRC_OUTPUT_FILE) endif $(OBJECTS): $(MAKEFILE_LIST) $(C_SRC_ENV) %.o: %.c $(COMPILE_C) $(OUTPUT_OPTION) $< %.o: %.cc $(COMPILE_CPP) $(OUTPUT_OPTION) $< %.o: %.C $(COMPILE_CPP) $(OUTPUT_OPTION) $< %.o: %.cpp $(COMPILE_CPP) $(OUTPUT_OPTION) $< clean:: clean-c_src clean-c_src: $(gen_verbose) rm -f $(C_SRC_OUTPUT_FILE) $(OBJECTS) endif ifneq ($(wildcard $(C_SRC_DIR)),) ERL_ERTS_DIR = $(shell $(ERL) -eval 'io:format("~s~n", [code:lib_dir(erts)]), halt().') $(C_SRC_ENV): $(verbose) $(ERL) -eval "file:write_file(\"$(call core_native_path,$(C_SRC_ENV))\", \ io_lib:format( \ \"# Generated by Erlang.mk. Edit at your own risk!~n~n\" \ \"ERTS_INCLUDE_DIR ?= ~s/erts-~s/include/~n\" \ \"ERL_INTERFACE_INCLUDE_DIR ?= ~s~n\" \ \"ERL_INTERFACE_LIB_DIR ?= ~s~n\" \ \"ERTS_DIR ?= $(ERL_ERTS_DIR)~n\", \ [code:root_dir(), erlang:system_info(version), \ filename:join(code:lib_dir(erl_interface), \"include\"), \ filename:join(code:lib_dir(erl_interface), \"lib\")])), \ halt()." distclean:: distclean-c_src-env distclean-c_src-env: $(gen_verbose) rm -f $(C_SRC_ENV) -include $(C_SRC_ENV) ifneq ($(ERL_ERTS_DIR),$(ERTS_DIR)) $(shell rm -f $(C_SRC_ENV)) endif endif # Templates. define bs_c_nif #include "erl_nif.h" static int loads = 0; static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) { /* Initialize private data. */ *priv_data = NULL; loads++; return 0; } static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info) { /* Convert the private data to the new version. */ *priv_data = *old_priv_data; loads++; return 0; } static void unload(ErlNifEnv* env, void* priv_data) { if (loads == 1) { /* Destroy the private data. */ } loads--; } static ERL_NIF_TERM hello(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { if (enif_is_atom(env, argv[0])) { return enif_make_tuple2(env, enif_make_atom(env, "hello"), argv[0]); } return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_atom(env, "badarg")); } static ErlNifFunc nif_funcs[] = { {"hello", 1, hello} }; ERL_NIF_INIT($n, nif_funcs, load, NULL, upgrade, unload) endef define bs_erl_nif -module($n). -export([hello/1]). -on_load(on_load/0). on_load() -> PrivDir = case code:priv_dir(?MODULE) of {error, _} -> AppPath = filename:dirname(filename:dirname(code:which(?MODULE))), filename:join(AppPath, "priv"); Path -> Path end, erlang:load_nif(filename:join(PrivDir, atom_to_list(?MODULE)), 0). hello(_) -> erlang:nif_error({not_loaded, ?MODULE}). endef new-nif: ifneq ($(wildcard $(C_SRC_DIR)/$n.c),) $(error Error: $(C_SRC_DIR)/$n.c already exists) endif ifneq ($(wildcard src/$n.erl),) $(error Error: src/$n.erl already exists) endif ifndef n $(error Usage: $(MAKE) new-nif n=NAME [in=APP]) endif ifdef in $(verbose) $(MAKE) -C $(APPS_DIR)/$(in)/ new-nif n=$n in= else $(verbose) mkdir -p $(C_SRC_DIR) src/ $(verbose) $(call core_render,bs_c_nif,$(C_SRC_DIR)/$n.c) $(verbose) $(call core_render,bs_erl_nif,src/$n.erl) endif # Copyright (c) Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. .PHONY: ci ci-prepare ci-setup CI_OTP ?= ifeq ($(strip $(CI_OTP)),) ci:: else ci:: $(addprefix ci-,$(CI_OTP)) ci-prepare: $(addprefix ci-prepare-,$(CI_OTP)) ci-setup:: $(verbose) : ci-extra:: $(verbose) : ci_verbose_0 = @echo " CI " $1; ci_verbose = $(ci_verbose_$(V)) define ci_target ci-prepare-$1: $(KERL_INSTALL_DIR)/$2 $(verbose) : ci-$1: ci-prepare-$1 $(verbose) $(MAKE) --no-print-directory clean $(ci_verbose) \ PATH="$(KERL_INSTALL_DIR)/$2/bin:$(PATH)" \ CI_OTP_RELEASE="$1" \ CT_OPTS="-label $1" \ CI_VM="$3" \ $(MAKE) ci-setup tests $(verbose) $(MAKE) --no-print-directory ci-extra endef $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp))) $(foreach otp,$(filter-out $(ERLANG_OTP),$(CI_OTP)),$(eval $(call kerl_otp_target,$(otp)))) help:: $(verbose) printf "%s\n" "" \ "Continuous Integration targets:" \ " ci Run '$(MAKE) tests' on all configured Erlang versions." \ "" \ "The CI_OTP variable must be defined with the Erlang versions" \ "that must be tested. For example: CI_OTP = OTP-27.3.4 OTP-28.3 OTP-29.0" endif # Copyright (c) Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. ifdef CONCUERROR_TESTS .PHONY: concuerror distclean-concuerror # Configuration CONCUERROR_LOGS_DIR ?= $(CURDIR)/logs CONCUERROR_OPTS ?= CONCUERROR_GIT ?= https://github.com/parapluu/Concuerror CONCUERROR_COMMIT ?= 0cbd2fc2acd7e1e01dc92c41c303f7f37b13dcca # master @ 2026-06-24 # Core targets. check:: concuerror ifndef KEEP_LOGS distclean:: distclean-concuerror endif # Plugin-specific targets. $(ERLANG_MK_TMP)/Concuerror/bin/concuerror: | $(ERLANG_MK_TMP) $(verbose) rm -rf $(ERLANG_MK_TMP)/Concuerror $(verbose) git clone -q -n -- $(CONCUERROR_GIT) $(ERLANG_MK_TMP)/Concuerror \ && cd $(ERLANG_MK_TMP)/Concuerror && git checkout -q $(CONCUERROR_COMMIT) \ || { rm -rf $(ERLANG_MK_TMP)/Concuerror; exit 1; } $(verbose) $(call dep_autopatch_fetch_rebar) \ && test -x $(ERLANG_MK_TMP)/rebar3/rebar3 \ || { rm -rf $(ERLANG_MK_TMP)/Concuerror; exit 1; } $(verbose) $(MAKE) -C $(ERLANG_MK_TMP)/Concuerror REBAR=$(ERLANG_MK_TMP)/rebar3/rebar3 \ || { rm -rf $(ERLANG_MK_TMP)/Concuerror; exit 1; } $(CONCUERROR_LOGS_DIR): $(verbose) mkdir -p $(CONCUERROR_LOGS_DIR) define concuerror_html_report Concuerror HTML report

Concuerror HTML report

Generated on $(concuerror_date)

    $(foreach t,$(concuerror_targets),
  • $(t)
  • )
endef concuerror: $(addprefix concuerror-,$(subst :,-,$(CONCUERROR_TESTS))) $(eval concuerror_date := $(shell date)) $(eval concuerror_targets := $^) $(verbose) $(call core_render,concuerror_html_report,$(CONCUERROR_LOGS_DIR)/concuerror.html) define concuerror_target .PHONY: concuerror-$1-$2 concuerror-$1-$2: test-build | $(ERLANG_MK_TMP)/Concuerror/bin/concuerror $(CONCUERROR_LOGS_DIR) $(ERLANG_MK_TMP)/Concuerror/bin/concuerror \ --pa $(CURDIR)/ebin --pa $(TEST_DIR) \ -o $(CONCUERROR_LOGS_DIR)/concuerror-$1-$2.txt \ $$(CONCUERROR_OPTS) -m $1 -t $2 endef $(foreach test,$(CONCUERROR_TESTS),$(eval $(call concuerror_target,$(firstword $(subst :, ,$(test))),$(lastword $(subst :, ,$(test)))))) distclean-concuerror: $(gen_verbose) rm -rf $(CONCUERROR_LOGS_DIR) endif # Copyright (c) Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. .PHONY: ct apps-ct distclean-ct # Configuration. CT_OPTS ?= ifneq ($(wildcard $(TEST_DIR)),) ifndef CT_SUITES CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl)))) endif endif CT_SUITES ?= CT_LOGS_DIR ?= $(CURDIR)/logs # Core targets. tests:: ct ifndef KEEP_LOGS distclean:: distclean-ct endif help:: $(verbose) printf "%s\n" "" \ "Common_test targets:" \ " ct Run all the common_test suites for this project" \ "" \ "All your common_test suites have their associated targets." \ "A suite named http_SUITE can be ran using the ct-http target." \ "Pass t=group or t=group:case to select tests inside a suite." # Plugin-specific targets. CT_RUN = ct_run \ -no_auto_compile \ -noinput \ -pa $(CURDIR)/ebin $(TEST_DIR) \ -dir $(TEST_DIR) \ -logdir $(CT_LOGS_DIR) ifeq ($(CT_SUITES),) ct: $(if $(IS_APP)$(ROOT_DIR),,apps-ct) else # We do not run tests if we are in an apps/* with no test directory. ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1) ct: test-build $(if $(IS_APP)$(ROOT_DIR),,apps-ct) $(verbose) mkdir -p $(CT_LOGS_DIR) $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS) endif endif ifneq ($(ALL_APPS_DIRS),) define ct_app_target apps-ct-$1: test-build $$(MAKE) -C $1 ct IS_APP=1 endef $(foreach app,$(ALL_APPS_DIRS),$(eval $(call ct_app_target,$(app)))) apps-ct: $(addprefix apps-ct-,$(ALL_APPS_DIRS)) endif ifdef t ifeq (,$(findstring :,$t)) CT_EXTRA = -group $t else t_words = $(subst :, ,$t) CT_EXTRA = -group $(firstword $(t_words)) -case $(lastword $(t_words)) endif else ifdef c CT_EXTRA = -case $c else CT_EXTRA = endif endif define ct_suite_target ct-$1: test-build $$(verbose) mkdir -p $$(CT_LOGS_DIR) $$(gen_verbose_esc) $$(CT_RUN) -sname ct_$$(PROJECT) -suite $$(addsuffix _SUITE,$1) $$(CT_EXTRA) $$(CT_OPTS) endef $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test)))) distclean-ct: $(gen_verbose) rm -rf $(CT_LOGS_DIR) # Copyright (c) Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. .PHONY: plt distclean-plt dialyze # Configuration. DIALYZER_PLT ?= $(CURDIR)/.$(PROJECT).plt export DIALYZER_PLT PLT_APPS ?= DIALYZER_DIRS ?= --src -r $(wildcard src) $(ALL_APPS_DIRS) DIALYZER_OPTS ?= -Werror_handling -Wunmatched_returns # -Wunderspecs DIALYZER_PLT_OPTS ?= # Core targets. check:: dialyze distclean:: distclean-plt help:: $(verbose) printf "%s\n" "" \ "Dialyzer targets:" \ " plt Build a PLT file for this project" \ " dialyze Analyze the project using Dialyzer" # Plugin-specific targets. define filter_opts.erl Opts = init:get_plain_arguments(), {Filtered, _} = lists:foldl(fun (O, {Os, true}) -> {[O|Os], false}; (O = "-D", {Os, _}) -> {[O|Os], true}; (O = [\\$$-, \\$$D, _ | _], {Os, _}) -> {[O|Os], false}; (O = "-I", {Os, _}) -> {[O|Os], true}; (O = [\\$$-, \\$$I, _ | _], {Os, _}) -> {[O|Os], false}; (O = "-pa", {Os, _}) -> {[O|Os], true}; (_, Acc) -> Acc end, {[], false}, Opts), io:format("~s~n", [string:join(lists:reverse(Filtered), " ")]), halt(). endef # DIALYZER_PLT is a variable understood directly by Dialyzer. # # We append the path to erts at the end of the PLT. This works # because the PLT file is in the external term format and the # function binary_to_term/1 ignores any trailing data. $(DIALYZER_PLT): deps app $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \ while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log)) $(verbose) dialyzer --build_plt $(DIALYZER_PLT_OPTS) --apps \ erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG) || test $$? -eq 2 $(verbose) $(ERL) -eval 'io:format("~n~s~n", [code:lib_dir(erts)]), halt().' >> $@ plt: $(DIALYZER_PLT) distclean-plt: $(gen_verbose) rm -f $(DIALYZER_PLT) ifneq ($(wildcard $(DIALYZER_PLT)),) dialyze: $(if $(filter --src,$(DIALYZER_DIRS)),,deps app) $(verbose) if ! tail -n1 $(DIALYZER_PLT) | \ grep -q "^`$(ERL) -eval 'io:format("~s", [code:lib_dir(erts)]), halt().'`$$"; then \ rm $(DIALYZER_PLT); \ $(MAKE) plt; \ fi else dialyze: $(DIALYZER_PLT) endif $(verbose) dialyzer `$(ERL) \ -eval "$(subst $(newline),,$(call escape_dquotes,$(call filter_opts.erl)))" \ -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS) $(if $(wildcard ebin/),-pa ebin/) # Copyright (c) Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. .PHONY: distclean-edoc edoc # Configuration. EDOC_OPTS ?= EDOC_SRC_DIRS ?= EDOC_OUTPUT ?= doc define edoc.erl SrcPaths = lists:foldl(fun(P, Acc) -> filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ lists:filter(fun(D) -> filelib:is_dir(D) end, filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}/**")) ++ Acc end, [], [$(call comma_list,$(patsubst %,'%',$(call core_native_path,$(EDOC_SRC_DIRS))))]), DefaultOpts = [{dir, "$(EDOC_OUTPUT)"}, {source_path, SrcPaths}, {subpackages, false}], edoc:application($(1), ".", [$(2)] ++ DefaultOpts), halt(0). endef # Core targets. ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),) docs:: edoc endif distclean:: distclean-edoc # Plugin-specific targets. edoc: distclean-edoc doc-deps $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS))) distclean-edoc: $(gen_verbose) rm -f $(EDOC_OUTPUT)/*.css $(EDOC_OUTPUT)/*.html $(EDOC_OUTPUT)/*.png $(EDOC_OUTPUT)/edoc-info # Copyright (c) Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. # Configuration. DTL_FULL_PATH ?= DTL_PATH ?= templates/ DTL_PREFIX ?= DTL_SUFFIX ?= _dtl DTL_OPTS ?= # Verbosity. dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F)); dtl_verbose_all_0 = @echo " DTL " $(notdir $(DTL_FILES)); dtl_verbose = $(dtl_verbose_$(V)) dtl_verbose_all = $(dtl_verbose_all_$(V)) # Core targets. DTL_PATH := $(abspath $(DTL_PATH)) DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl)) ifneq ($(DTL_FILES),) DTL_NAMES = $(addprefix $(DTL_PREFIX),$(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(subst %,\%,$(DTL_PATH))/%.dtl=%))) DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES))) BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES))) ifneq ($(words $(DTL_FILES)),0) # Rebuild templates when a Makefile changes, without touching them. # $(PROJECT).d is excluded to avoid a circular dependency. $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(filter-out $(PROJECT).d,$(MAKEFILE_LIST)) | $(ERLANG_MK_TMP) $(verbose) touch $@ endif # Every template is compiled. Without force_recompile, ErlyDTL rewrites a # beam only when that file's checksum or a stored include/extends checksum # changed. A Makefile change still passes force_recompile. define erlydtl_compile.erl All = string:tokens("$(call core_native_path,$(DTL_FILES))", " "), Extra = case "$1" of "force_recompile" -> [force_recompile]; _ -> [] end, [begin Module0 = case "$(strip $(DTL_FULL_PATH))" of "" -> filename:basename(F, ".dtl"); _ -> "$(call core_native_path,$(DTL_PATH))/" ++ F2 = filename:rootname(F, ".dtl"), re:replace(F2, "/", "_", [{return, list}, global]) end, Module = list_to_atom("$(DTL_PREFIX)" ++ string:to_lower(Module0) ++ "$(DTL_SUFFIX)"), case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ Extra ++ [{out_dir, "ebin/"}, return_errors]) of ok -> ok; {ok, _} -> ok; {error, Errors, Warnings} -> io:format("Errors: ~p~nWarnings: ~p~n", [Errors, Warnings]), halt(91) end end || F <- All], halt(). endef # Refresh the .app file. The makefile stamp is built after the # application file on the first run; without this it stays newer # and every template is compiled again on the next make. ebin/$(PROJECT).app:: $(DTL_FILES) $(ERLANG_MK_TMP)/last-makefile-change-erlydtl | ebin/ $(if $(filter $(ERLANG_MK_TMP)/last-makefile-change-erlydtl,$?),\ $(dtl_verbose_all) $(call erlang,$(call erlydtl_compile.erl,force_recompile),-pa ebin/),\ $(if $(strip $(filter %.dtl,$?)),$(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl),-pa ebin/))) $(verbose) touch $@ endif # Copyright (c) Loïc Hoguin # Copyright (c) 2014, Dave Cottlehuber # This file is part of erlang.mk and subject to the terms of the ISC License. .PHONY: distclean-escript escript escript-zip # Configuration. ESCRIPT_NAME ?= $(PROJECT) ESCRIPT_FILE ?= $(ESCRIPT_NAME) ESCRIPT_SHEBANG ?= /usr/bin/env escript ESCRIPT_COMMENT ?= This is an -*- erlang -*- file ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME) ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null) ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip # Core targets. distclean:: distclean-escript help:: $(verbose) printf "%s\n" "" \ "Escript targets:" \ " escript Build an executable escript archive" \ # Plugin-specific targets. ALL_ESCRIPT_DEPS_DIRS = $(LOCAL_DEPS_DIRS) $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(DEPS)),$(call query_name,$(dep)))) ESCRIPT_RUNTIME_DEPS_FILE ?= $(ERLANG_MK_TMP)/escript-deps.log escript-list-runtime-deps: ifeq ($(IS_DEP),) $(verbose) rm -f $(ESCRIPT_RUNTIME_DEPS_FILE) endif $(verbose) touch $(ESCRIPT_RUNTIME_DEPS_FILE) $(verbose) set -e; for dep in $(ALL_ESCRIPT_DEPS_DIRS) ; do \ if ! grep -qs ^$$dep$$ $(ESCRIPT_RUNTIME_DEPS_FILE); then \ echo $$dep >> $(ESCRIPT_RUNTIME_DEPS_FILE); \ if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \ $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \ $(MAKE) -C $$dep escript-list-runtime-deps \ IS_DEP=1 \ ESCRIPT_RUNTIME_DEPS_FILE=$(ESCRIPT_RUNTIME_DEPS_FILE); \ fi \ fi \ done ifeq ($(IS_DEP),) $(verbose) sort < $(ESCRIPT_RUNTIME_DEPS_FILE) | uniq > $(ESCRIPT_RUNTIME_DEPS_FILE).sorted $(verbose) mv $(ESCRIPT_RUNTIME_DEPS_FILE).sorted $(ESCRIPT_RUNTIME_DEPS_FILE) endif escript-prepare: deps app $(MAKE) escript-list-runtime-deps escript-zip:: escript-prepare $(verbose) mkdir -p $(dir $(abspath $(ESCRIPT_ZIP_FILE))) $(verbose) rm -f $(abspath $(ESCRIPT_ZIP_FILE)) $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(abspath $(ESCRIPT_ZIP_FILE)) $(notdir $(CURDIR))/ebin/* ifneq ($(DEPS),) $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(abspath $(ESCRIPT_ZIP_FILE)) \ $(subst $(DEPS_DIR)/,,$(addsuffix /*,$(wildcard \ $(addsuffix /ebin,$(shell cat $(ESCRIPT_RUNTIME_DEPS_FILE)))))) endif escript:: escript-zip $(gen_verbose) printf "%s\n" \ "#!$(ESCRIPT_SHEBANG)" \ "%% $(ESCRIPT_COMMENT)" \ "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE) $(verbose) cat $(abspath $(ESCRIPT_ZIP_FILE)) >> $(ESCRIPT_FILE) $(verbose) chmod +x $(ESCRIPT_FILE) distclean-escript: $(gen_verbose) rm -f $(ESCRIPT_FILE) $(abspath $(ESCRIPT_ZIP_FILE)) # Copyright (c) Loïc Hoguin # Copyright (c) 2014, Enrique Fernandez # This file is contributed to erlang.mk and subject to the terms of the ISC License. .PHONY: eunit apps-eunit # Eunit can be disabled by setting this to any other value. EUNIT ?= system ifeq ($(EUNIT),system) # Configuration EUNIT_OPTS ?= EUNIT_ERL_OPTS ?= EUNIT_TEST_SPEC ?= $1 # Core targets. tests:: eunit help:: $(verbose) printf "%s\n" "" \ "EUnit targets:" \ " eunit Run all the EUnit tests for this project" \ "" \ "Pass t=mod or t=mod:case to run one module or test." # Plugin-specific targets. define eunit.erl $(call cover.erl) CoverSetup(), case eunit:test($(call EUNIT_TEST_SPEC,$1), [$(EUNIT_OPTS)]) of ok -> ok; error -> halt(2) end, CoverExport("$(call core_native_path,$(COVER_DATA_DIR))/eunit.coverdata"), halt() endef EUNIT_ERL_OPTS += -pa $(TEST_DIR) $(CURDIR)/ebin ifdef t ifeq (,$(findstring :,$(t))) eunit: test-build cover-data-dir $(gen_verbose) $(call erlang,$(call eunit.erl,['$(t)']),$(EUNIT_ERL_OPTS)) else eunit: test-build cover-data-dir $(gen_verbose) $(call erlang,$(call eunit.erl,fun $(t)/0),$(EUNIT_ERL_OPTS)) endif else EUNIT_EBIN_MODS = $(notdir $(basename $(ERL_FILES) $(BEAM_FILES))) EUNIT_TEST_MODS = $(notdir $(basename $(call core_find,$(TEST_DIR)/,*.erl))) EUNIT_MODS = $(foreach mod,$(EUNIT_EBIN_MODS) $(filter-out \ $(patsubst %,%_tests,$(EUNIT_EBIN_MODS)),$(EUNIT_TEST_MODS)),'$(mod)') eunit: test-build $(if $(IS_APP)$(ROOT_DIR),,apps-eunit) cover-data-dir ifneq ($(wildcard src/ $(TEST_DIR)),) $(gen_verbose) $(call erlang,$(call eunit.erl,[$(call comma_list,$(EUNIT_MODS))]),$(EUNIT_ERL_OPTS)) endif ifneq ($(ALL_APPS_DIRS),) apps-eunit: test-build $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \ [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \ exit $$eunit_retcode endif endif endif # Copyright (c) Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. HEX_TARBALL_EXTRA_METADATA ?= HEX_TARBALL_FILES ?= \ $(wildcard early-plugins.mk) \ $(wildcard ebin/$(PROJECT).app) \ $(wildcard ebin/$(PROJECT).appup) \ $(wildcard $(notdir $(ERLANG_MK_FILENAME))) \ $(sort $(call core_find,include/,*.hrl)) \ $(wildcard LICENSE*) \ $(wildcard Makefile) \ $(wildcard plugins.mk) \ $(sort $(call core_find,priv/,*)) \ $(wildcard README*) \ $(wildcard rebar.config) \ $(sort $(if $(LEGACY),$(filter-out src/$(PROJECT).app.src,$(call core_find,src/,*)),$(call core_find,src/,*))) HEX_TARBALL_OUTPUT_FILE ?= $(ERLANG_MK_TMP)/$(PROJECT).tar # In order to build the requirements metadata we look into DEPS. # We do not require that the project use Hex dependencies, however # Hex.pm does require that the package name and version numbers # correspond to a real Hex package. define hex_tarball_create.erl Files0 = [$(call comma_list,$(patsubst %,"%",$(HEX_TARBALL_FILES)))], Requirements0 = #{ $(foreach d,$(DEPS), <<"$(if $(subst hex,,$(call query_fetch_method,$d)),$d,$(if $(word 3,$(dep_$d)),$(word 3,$(dep_$d)),$d))">> => #{ <<"app">> => <<"$d">>, <<"optional">> => false, <<"requirement">> => <<"$(if $(hex_req_$d),$(strip $(hex_req_$d)),$(call query_version,$d))">> },) $(if $(DEPS),dummy => dummy) }, Requirements = maps:remove(dummy, Requirements0), Metadata0 = #{ app => <<"$(strip $(PROJECT))">>, build_tools => [<<"make">>$(if $(or $(wildcard rebar.config),$(if $(strip $(DEPS)),,true)),$(comma) <<"rebar3">>)], description => <<"$(strip $(PROJECT_DESCRIPTION))">>, files => [unicode:characters_to_binary(F) || F <- Files0], name => <<"$(strip $(PROJECT))">>, requirements => Requirements, version => <<"$(strip $(PROJECT_VERSION))">> }, Metadata = Metadata0$(HEX_TARBALL_EXTRA_METADATA), Files = [case file:read_file(F) of {ok, Bin} -> {F, Bin}; {error, Reason} -> io:format("Error trying to open file ~0p: ~0p~n", [F, Reason]), halt(82) end || F <- Files0], case hex_tarball:create(Metadata, Files) of {ok, #{tarball := Tarball}} -> ok = file:write_file("$(strip $(HEX_TARBALL_OUTPUT_FILE))", Tarball), halt(0); {error, Reason} -> io:format("Error ~0p~n", [Reason]), halt(83) end endef hex_tar_verbose_0 = @echo " TAR $(notdir $(ERLANG_MK_TMP))/$(@F)"; hex_tar_verbose_2 = set -x; hex_tar_verbose = $(hex_tar_verbose_$(V)) $(HEX_TARBALL_OUTPUT_FILE): $(ERLANG_MK_TMP)/dep_built/hex_core app $(hex_tar_verbose) $(call erlang,$(call hex_tarball_create.erl)) hex-tarball-create: $(HEX_TARBALL_OUTPUT_FILE) define hex_release_publish_summary.erl {ok, Tarball} = erl_tar:open("$(strip $(HEX_TARBALL_OUTPUT_FILE))", [read]), ok = erl_tar:extract(Tarball, [{cwd, "$(ERLANG_MK_TMP)"}, {files, ["metadata.config"]}]), {ok, Metadata} = file:consult("$(ERLANG_MK_TMP)/metadata.config"), #{ <<"name">> := Name, <<"version">> := Version, <<"files">> := Files, <<"requirements">> := Deps } = maps:from_list(Metadata), io:format("Publishing ~s ~s~n Dependencies:~n", [Name, Version]), case Deps of [] -> io:format(" (none)~n"); _ -> [begin #{<<"app">> := DA, <<"requirement">> := DR} = maps:from_list(D), io:format(" ~s ~s~n", [DA, DR]) end || {_, D} <- Deps] end, io:format(" Included files:~n"), [io:format(" ~s~n", [F]) || F <- Files], io:format("You may also review the contents of the tarball file.~n" "Please enter your secret key to proceed.~n"), halt(0) endef define hex_api_key.erl case os:getenv("HEX_SECRET") of false -> <<>>; Secret -> list_to_binary(string:trim(Secret)) end endef define hex_release_publish.erl {ok, _} = application:ensure_all_started(ssl), {ok, _} = application:ensure_all_started(inets), Config = $(hex_config.erl), ConfigF = Config#{api_key => $(hex_api_key.erl)}, {ok, Tarball} = file:read_file("$(strip $(HEX_TARBALL_OUTPUT_FILE))"), case hex_api_release:publish(ConfigF, Tarball, [{replace, $1}]) of {ok, {200, _, #{}}} -> io:format("Release replaced~n"), halt(0); {ok, {201, _, #{}}} -> io:format("Release published~n"), halt(0); {ok, {Status, _, Errors}} -> io:format("Error ~b: ~0p~n", [Status, Errors]), halt(84) end endef hex-release-tarball: $(ERLANG_MK_TMP)/dep_built/hex_core $(HEX_TARBALL_OUTPUT_FILE) $(verbose) $(call erlang,$(call hex_release_publish_summary.erl)) hex-release-publish: $(ERLANG_MK_TMP)/dep_built/hex_core hex-release-tarball $(if $(HEX_SECRET),,$(eval HEX_SECRET := $(shell stty -echo; read -p "Secret: " secret; stty echo; echo $$secret) $(info ))) $(eval export HEX_SECRET) $(gen_verbose) $(call erlang,$(call hex_release_publish.erl,false)) hex-release-replace: $(ERLANG_MK_TMP)/dep_built/hex_core hex-release-tarball $(if $(HEX_SECRET),,$(eval HEX_SECRET := $(shell stty -echo; read -p "Secret: " secret; stty echo; echo $$secret) $(info ))) $(eval export HEX_SECRET) $(gen_verbose) $(call erlang,$(call hex_release_publish.erl,true)) define hex_release_delete.erl {ok, _} = application:ensure_all_started(ssl), {ok, _} = application:ensure_all_started(inets), Config = $(hex_config.erl), ConfigF = Config#{api_key => $(hex_api_key.erl)}, case hex_api_release:delete(ConfigF, <<"$(strip $(PROJECT))">>, <<"$(strip $(PROJECT_VERSION))">>) of {ok, {204, _, _}} -> io:format("Release $(strip $(PROJECT_VERSION)) deleted~n"), halt(0); {ok, {Status, _, Errors}} -> io:format("Error ~b: ~0p~n", [Status, Errors]), halt(85) end endef hex-release-delete: $(ERLANG_MK_TMP)/dep_built/hex_core $(if $(HEX_SECRET),,$(eval HEX_SECRET := $(shell stty -echo; read -p "Secret: " secret; stty echo; echo $$secret) $(info ))) $(eval export HEX_SECRET) $(gen_verbose) $(call erlang,$(call hex_release_delete.erl)) define hex_release_retire.erl {ok, _} = application:ensure_all_started(ssl), {ok, _} = application:ensure_all_started(inets), Config = $(hex_config.erl), ConfigF = Config#{api_key => $(hex_api_key.erl)}, Params = #{<<"reason">> => <<"$(strip $2)">>, <<"message">> => <<"$(strip $3)">>}, case hex_api_release:retire(ConfigF, <<"$(strip $(PROJECT))">>, <<"$(strip $1)">>, Params) of {ok, {204, _, _}} -> io:format("Release $(strip $1) has been retired~n"), halt(0); {ok, {Status, _, Errors}} -> io:format("Error ~b: ~0p~n", [Status, Errors]), halt(86) end endef hex-release-retire: $(ERLANG_MK_TMP)/dep_built/hex_core $(if $(HEX_SECRET),,$(eval HEX_SECRET := $(shell stty -echo; read -p "Secret: " secret; stty echo; echo $$secret) $(info ))) $(eval export HEX_SECRET) $(gen_verbose) $(call erlang,$(call hex_release_retire.erl,\ $(if $(HEX_VERSION),$(HEX_VERSION),$(PROJECT_VERSION)),\ $(if $(HEX_REASON),$(HEX_REASON),invalid),\ $(HEX_MESSAGE))) define hex_release_unretire.erl {ok, _} = application:ensure_all_started(ssl), {ok, _} = application:ensure_all_started(inets), Config = $(hex_config.erl), ConfigF = Config#{api_key => $(hex_api_key.erl)}, case hex_api_release:unretire(ConfigF, <<"$(strip $(PROJECT))">>, <<"$(strip $1)">>) of {ok, {204, _, _}} -> io:format("Release $(strip $1) is not retired anymore~n"), halt(0); {ok, {Status, _, Errors}} -> io:format("Error ~b: ~0p~n", [Status, Errors]), halt(87) end endef hex-release-unretire: $(ERLANG_MK_TMP)/dep_built/hex_core $(if $(HEX_SECRET),,$(eval HEX_SECRET := $(shell stty -echo; read -p "Secret: " secret; stty echo; echo $$secret) $(info ))) $(eval export HEX_SECRET) $(gen_verbose) $(call erlang,$(call hex_release_unretire.erl,\ $(if $(HEX_VERSION),$(HEX_VERSION),$(PROJECT_VERSION)))) HEX_DOCS_DOC_DIR ?= doc/ HEX_DOCS_TARBALL_FILES ?= $(sort $(call core_find,$(HEX_DOCS_DOC_DIR),*)) HEX_DOCS_TARBALL_OUTPUT_FILE ?= $(ERLANG_MK_TMP)/$(PROJECT)-docs.tar.gz $(HEX_DOCS_TARBALL_OUTPUT_FILE): $(ERLANG_MK_TMP)/dep_built/hex_core app docs $(hex_tar_verbose) tar czf $(HEX_DOCS_TARBALL_OUTPUT_FILE) -C $(HEX_DOCS_DOC_DIR) \ $(HEX_DOCS_TARBALL_FILES:$(HEX_DOCS_DOC_DIR)%=%) hex-docs-tarball-create: $(HEX_DOCS_TARBALL_OUTPUT_FILE) define hex_docs_publish.erl {ok, _} = application:ensure_all_started(ssl), {ok, _} = application:ensure_all_started(inets), Config = $(hex_config.erl), ConfigF = Config#{api_key => $(hex_api_key.erl)}, {ok, Tarball} = file:read_file("$(strip $(HEX_DOCS_TARBALL_OUTPUT_FILE))"), case hex_api:post(ConfigF, ["packages", "$(strip $(PROJECT))", "releases", "$(strip $(PROJECT_VERSION))", "docs"], {"application/octet-stream", Tarball}) of {ok, {Status, _, _}} when Status >= 200, Status < 300 -> io:format("Docs published~n"), halt(0); {ok, {Status, _, Errors}} -> io:format("Error ~b: ~0p~n", [Status, Errors]), halt(88) end endef hex-docs-publish: $(ERLANG_MK_TMP)/dep_built/hex_core hex-docs-tarball-create $(if $(HEX_SECRET),,$(eval HEX_SECRET := $(shell stty -echo; read -p "Secret: " secret; stty echo; echo $$secret) $(info ))) $(eval export HEX_SECRET) $(gen_verbose) $(call erlang,$(call hex_docs_publish.erl)) define hex_docs_delete.erl {ok, _} = application:ensure_all_started(ssl), {ok, _} = application:ensure_all_started(inets), Config = $(hex_config.erl), ConfigF = Config#{api_key => $(hex_api_key.erl)}, case hex_api:delete(ConfigF, ["packages", "$(strip $(PROJECT))", "releases", "$(strip $1)", "docs"]) of {ok, {Status, _, _}} when Status >= 200, Status < 300 -> io:format("Docs removed~n"), halt(0); {ok, {Status, _, Errors}} -> io:format("Error ~b: ~0p~n", [Status, Errors]), halt(89) end endef hex-docs-delete: $(ERLANG_MK_TMP)/dep_built/hex_core $(if $(HEX_SECRET),,$(eval HEX_SECRET := $(shell stty -echo; read -p "Secret: " secret; stty echo; echo $$secret) $(info ))) $(eval export HEX_SECRET) $(gen_verbose) $(call erlang,$(call hex_docs_delete.erl,\ $(if $(HEX_VERSION),$(HEX_VERSION),$(PROJECT_VERSION)))) # Copyright (c) Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. help:: $(verbose) printf "%s\n" "" \ "PropEr targets:" \ " proper Run PropEr properties for this project" \ "" \ "Requires proper in DEPS or TEST_DEPS." \ "Pass t=mod or t=mod:prop to run one module or property." ifeq ($(filter proper,$(DEPS) $(TEST_DEPS)),proper) .PHONY: proper # Targets. tests:: proper define proper_check.erl $(call cover.erl) code:add_pathsa([ "$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)", "$(call core_native_path,$(TEST_DIR))"]), Module = fun(M) -> [true] =:= lists:usort([ case atom_to_list(F) of "prop_" ++ _ -> io:format("Testing ~p:~p/0~n", [M, F]), proper:quickcheck(M:F(), nocolors); _ -> true end || {F, 0} <- M:module_info(exports)]) end, try begin CoverSetup(), Res = case $(1) of all -> [true] =:= lists:usort([Module(M) || M <- [$(call comma_list,$(3))]]); module -> Module($(2)); function -> proper:quickcheck($(2), nocolors) end, CoverExport("$(COVER_DATA_DIR)/proper.coverdata"), Res end of true -> halt(0); _ -> halt(1) catch error:undef$(if $V,:Stacktrace) -> io:format("Undefined property or module?~n$(if $V,~p~n)", [$(if $V,Stacktrace)]), halt(0) end. endef ifdef t ifeq (,$(findstring :,$(t))) proper: test-build cover-data-dir $(verbose) $(call erlang,$(call proper_check.erl,module,$(t))) else proper: test-build cover-data-dir $(verbose) echo Testing $(t)/0 $(verbose) $(call erlang,$(call proper_check.erl,function,$(t)())) endif else proper: test-build cover-data-dir $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \ $(wildcard ebin/*.beam) $(call core_find,$(TEST_DIR)/,*.beam)))))) $(gen_verbose) $(call erlang,$(call proper_check.erl,all,undefined,$(MODULES))) endif endif # Copyright (c) Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. # Verbosity. proto_verbose_0 = @echo " PROTO " $(filter %.proto,$(?F)); proto_verbose_all_0 = @echo " PROTO " $(notdir $(PROTO_FILES)); proto_verbose = $(proto_verbose_$(V)) proto_verbose_all = $(proto_verbose_all_$(V)) # Core targets. ifneq ($(wildcard src/),) ifneq ($(filter gpb protobuffs,$(BUILD_DEPS) $(DEPS)),) PROTO_FILES := $(filter %.proto,$(ALL_SRC_FILES)) ERL_FILES += $(addprefix src/,$(patsubst %.proto,%_pb.erl,$(notdir $(PROTO_FILES)))) ifeq ($(filter gpb,$(BUILD_DEPS) $(DEPS)),) define compile_proto.erl [begin protobuffs_compile:generate_source(F, [ {output_include_dir, "./include"}, {output_src_dir, "./src"}]) end || F <- string:tokens("$1", " ")], halt(). endef else define compile_proto.erl [begin gpb_compile:file(F, [ $(foreach i,$(sort $(dir $(PROTO_FILES))),{i$(comma) "$i"}$(comma)) {include_as_lib, true}, {module_name_suffix, "_pb"}, {o_hrl, "./include"}, {o_erl, "./src"}, {use_packages, true} ]) end || F <- string:tokens("$1", " ")], halt(). endef endif ifneq ($(PROTO_FILES),) $(PROJECT).d:: $(PROTO_FILES) $(MAKEFILE_CHANGE) $(verbose) mkdir -p ebin/ include/ $(if $(filter $(MAKEFILE_CHANGE),$?),\ $(proto_verbose_all) $(call erlang,$(call compile_proto.erl,$(PROTO_FILES))),\ $(if $(strip $(filter %.proto,$?)),$(proto_verbose) $(call erlang,$(call compile_proto.erl,$(filter %.proto,$?))))) endif endif endif # Copyright (c) Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. ifeq ($(filter relx,$(BUILD_DEPS) $(DEPS) $(REL_DEPS)),relx) .PHONY: relx-rel relx-relup distclean-relx-rel run # Configuration. RELX_CONFIG ?= $(CURDIR)/relx.config RELX_CONFIG_SCRIPT ?= $(CURDIR)/relx.config.script RELX_OUTPUT_DIR ?= _rel RELX_REL_EXT ?= RELX_TAR ?= 1 ifdef SFX RELX_TAR = 1 endif # Core targets. ifeq ($(IS_DEP),) ifneq ($(wildcard $(RELX_CONFIG))$(wildcard $(RELX_CONFIG_SCRIPT)),) rel:: relx-rel relup:: relx-relup endif endif distclean:: distclean-relx-rel # Plugin-specific targets. define relx_get_config.erl (fun() -> Config0 = case file:consult("$(call core_native_path,$(RELX_CONFIG))") of {ok, Terms} -> Terms; {error, _} -> [] end, case filelib:is_file("$(call core_native_path,$(RELX_CONFIG_SCRIPT))") of true -> Bindings = erl_eval:add_binding('CONFIG', Config0, erl_eval:new_bindings()), {ok, Config1} = file:script("$(call core_native_path,$(RELX_CONFIG_SCRIPT))", Bindings), Config1; false -> Config0 end end)() endef define relx_release.erl Config = $(call relx_get_config.erl), {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config), Vsn = case Vsn0 of {cmd, Cmd} -> os:cmd(Cmd); semver -> ""; {semver, _} -> ""; {git, short} -> string:trim(os:cmd("git rev-parse --short HEAD"), both, "\n"); {git, long} -> string:trim(os:cmd("git rev-parse HEAD"), both, "\n"); VsnStr -> Vsn0 end, {ok, _} = relx:build_release(#{name => Name, vsn => Vsn}, Config ++ [{output_dir, "$(RELX_OUTPUT_DIR)"}]), halt(0). endef define relx_tar.erl Config = $(call relx_get_config.erl), {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config), Vsn = case Vsn0 of {cmd, Cmd} -> os:cmd(Cmd); semver -> ""; {semver, _} -> ""; {git, short} -> string:trim(os:cmd("git rev-parse --short HEAD"), both, "\n"); {git, long} -> string:trim(os:cmd("git rev-parse HEAD"), both, "\n"); VsnStr -> Vsn0 end, {ok, _} = relx:build_tar(#{name => Name, vsn => Vsn}, Config ++ [{output_dir, "$(RELX_OUTPUT_DIR)"}]), halt(0). endef define relx_relup.erl Config = $(call relx_get_config.erl), {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config), Vsn = case Vsn0 of {cmd, Cmd} -> os:cmd(Cmd); semver -> ""; {semver, _} -> ""; {git, short} -> string:trim(os:cmd("git rev-parse --short HEAD"), both, "\n"); {git, long} -> string:trim(os:cmd("git rev-parse HEAD"), both, "\n"); VsnStr -> Vsn0 end, {ok, _} = relx:build_relup(Name, Vsn, undefined, Config ++ [{output_dir, "$(RELX_OUTPUT_DIR)"}]), halt(0). endef relx-rel: rel-deps app # OTP finds this application only when the directory is named # $(PROJECT) or $(PROJECT)-. $(verbose) dir="$(notdir $(CURDIR))"; \ case "$$dir" in \ "$(PROJECT)"|"$(PROJECT)"-*) ;; \ *) \ printf '%s\n' "Error: application $(PROJECT) was not found. The project directory is named '$$dir'; OTP requires it to be named '$(PROJECT)' or '$(PROJECT)-' so the application can be found on the code path." >&2; \ exit 1 ;; \ esac $(call erlang,$(call relx_release.erl),-pa ebin/) $(verbose) $(MAKE) relx-post-rel $(if $(filter-out 0,$(RELX_TAR)),$(call erlang,$(call relx_tar.erl),-pa ebin/)) relx-relup: rel-deps app $(call erlang,$(call relx_release.erl),-pa ebin/) $(MAKE) relx-post-rel $(call erlang,$(call relx_relup.erl),-pa ebin/) $(if $(filter-out 0,$(RELX_TAR)),$(call erlang,$(call relx_tar.erl),-pa ebin/)) distclean-relx-rel: $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR) # Default hooks. relx-post-rel:: $(verbose) : # Run target. ifeq ($(wildcard $(RELX_CONFIG))$(wildcard $(RELX_CONFIG_SCRIPT)),) run:: else define get_relx_release.erl Config = $(call relx_get_config.erl), {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config), Vsn = case Vsn0 of {cmd, Cmd} -> os:cmd(Cmd); semver -> ""; {semver, _} -> ""; {git, short} -> string:trim(os:cmd("git rev-parse --short HEAD"), both, "\n"); {git, long} -> string:trim(os:cmd("git rev-parse HEAD"), both, "\n"); VsnStr -> Vsn0 end, Extended = case lists:keyfind(extended_start_script, 1, Config) of {_, true} -> "1"; _ -> "" end, io:format("~s ~s ~s", [Name, Vsn, Extended]), halt(0). endef RELX_REL := $(shell $(call erlang,$(get_relx_release.erl))) RELX_REL_NAME := $(word 1,$(RELX_REL)) RELX_REL_VSN := $(word 2,$(RELX_REL)) RELX_REL_CMD := $(if $(word 3,$(RELX_REL)),console) ifeq ($(PLATFORM),msys2) RELX_REL_EXT := .cmd endif run:: RELX_TAR := 0 run:: all $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) $(RELX_REL_CMD) ifdef RELOAD rel:: $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) ping $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) \ eval "io:format(\"reloaded ~p~n\", [c:lm()])." endif help:: $(verbose) printf "%s\n" "" \ "Relx targets:" \ " run Compile the project, build the release and run it" endif endif # Copyright (c) Loïc Hoguin # Copyright (c) 2014, M Robert Martin # This file is contributed to erlang.mk and subject to the terms of the ISC License. .PHONY: shell # Configuration. SHELL_ERL ?= erl SHELL_PATHS ?= $(CURDIR)/ebin $(TEST_DIR) SHELL_OPTS ?= ALL_SHELL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(SHELL_DEPS)) # Core targets help:: $(verbose) printf "%s\n" "" \ "Shell targets:" \ " shell Run an erlang shell with SHELL_OPTS or reasonable default" \ " Set RELOAD=1 to reload code automatically." # Plugin-specific targets. $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep)))) ifneq ($(SKIP_DEPS),) build-shell-deps: else build-shell-deps: $(ALL_SHELL_DEPS_DIRS) | $(ERLANG_MK_TMP)/dep_built $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do \ if [ -z "$(strip $(FULL))" ] && [ ! -L $$dep ] && [ -f $(ERLANG_MK_TMP)/dep_built/`basename $$dep` ]; then \ :; \ else \ $(MAKE) -C $$dep IS_DEP=1; \ if [ ! -L $$dep ] && [ -d $$dep/ebin ]; then touch $(ERLANG_MK_TMP)/dep_built/`basename $$dep`; fi; \ fi \ done endif ifdef RELOAD define shell_reload.erl spawn(fun F() -> case c:lm() of [] -> ok; Reloaded -> io:format("reloaded ~p~n", [Reloaded]) end, receive after 5000 -> F() end end). endef SHELL_RELOAD_OPTS = -eval "$(subst $(newline),,$(call escape_dquotes,$(call shell_reload.erl)))" else SHELL_RELOAD_OPTS = endif shell:: build-shell-deps $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_RELOAD_OPTS) $(SHELL_OPTS) # Copyright 2017, Stanislaw Klekot # This file is part of erlang.mk and subject to the terms of the ISC License. .PHONY: distclean-sphinx sphinx # Configuration. SPHINX_BUILD ?= sphinx-build SPHINX_SOURCE ?= doc SPHINX_CONFDIR ?= SPHINX_FORMATS ?= html SPHINX_DOCTREES ?= $(ERLANG_MK_TMP)/sphinx.doctrees SPHINX_OPTS ?= #sphinx_html_opts = #sphinx_html_output = html #sphinx_man_opts = #sphinx_man_output = man #sphinx_latex_opts = #sphinx_latex_output = latex # Helpers. sphinx_build_0 = @echo " SPHINX" $1; $(SPHINX_BUILD) -N -q sphinx_build_1 = $(SPHINX_BUILD) -N sphinx_build_2 = set -x; $(SPHINX_BUILD) sphinx_build = $(sphinx_build_$(V)) define sphinx.build $(call sphinx_build,$1) -b $1 -d $(SPHINX_DOCTREES) $(if $(SPHINX_CONFDIR),-c $(SPHINX_CONFDIR)) $(SPHINX_OPTS) $(sphinx_$1_opts) -- $(SPHINX_SOURCE) $(call sphinx.output,$1) endef define sphinx.output $(if $(sphinx_$1_output),$(sphinx_$1_output),$1) endef # Targets. ifneq ($(wildcard $(if $(SPHINX_CONFDIR),$(SPHINX_CONFDIR),$(SPHINX_SOURCE))/conf.py),) docs:: sphinx distclean:: distclean-sphinx endif help:: $(verbose) printf "%s\n" "" \ "Sphinx targets:" \ " sphinx Generate Sphinx documentation." \ "" \ "ReST sources and 'conf.py' file are expected in directory pointed by" \ "SPHINX_SOURCE ('doc' by default). SPHINX_FORMATS lists formats to build (only" \ "'html' format is generated by default); target directory can be specified by" \ 'setting sphinx_$${format}_output, for example: sphinx_html_output = output/html' \ "Additional Sphinx options can be set in SPHINX_OPTS." # Plugin-specific targets. sphinx: $(foreach F,$(SPHINX_FORMATS),$(call sphinx.build,$F)) distclean-sphinx: $(gen_verbose) rm -rf $(filter-out $(SPHINX_SOURCE),$(foreach F,$(SPHINX_FORMATS),$(call sphinx.output,$F))) # Copyright (c) 2017, Jean-Sébastien Pédron # This file is contributed to erlang.mk and subject to the terms of the ISC License. .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS show-ERL_LIBS: @echo $(ERL_LIBS) show-ERLC_OPTS: @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";) show-TEST_ERLC_OPTS: @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";) # Copyright (c) Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. help:: $(verbose) printf "%s\n" "" \ "Triq targets:" \ " triq Run Triq properties for this project" \ "" \ "Requires triq in DEPS or TEST_DEPS." \ "Pass t=mod or t=mod:prop to run one module or property." ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq) .PHONY: triq # Targets. tests:: triq define triq_check.erl $(call cover.erl) code:add_pathsa([ "$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)", "$(call core_native_path,$(TEST_DIR))"]), try begin CoverSetup(), Res = case $(1) of all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]); module -> triq:check($(2)); function -> triq:check($(2)) end, CoverExport("$(COVER_DATA_DIR)/triq.coverdata"), Res end of true -> halt(0); _ -> halt(1) catch error:undef$(if $V,:Stacktrace) -> io:format("Undefined property or module?~n$(if $V,~p~n)", [$(if $V,Stacktrace)]), halt(0) end. endef ifdef t ifeq (,$(findstring :,$(t))) triq: test-build cover-data-dir $(verbose) $(call erlang,$(call triq_check.erl,module,$(t))) else triq: test-build cover-data-dir $(verbose) echo Testing $(t)/0 $(verbose) $(call erlang,$(call triq_check.erl,function,$(t)())) endif else triq: test-build cover-data-dir $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \ $(wildcard ebin/*.beam) $(call core_find,$(TEST_DIR)/,*.beam)))))) $(gen_verbose) $(call erlang,$(call triq_check.erl,all,undefined,$(MODULES))) endif endif # Copyright (c) Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. .PHONY: xref # Configuration. # We do not use locals_not_used or deprecated_function_calls # because the compiler will error out by default in those # cases with Erlang.mk. Deprecated functions may make sense # in some cases but few libraries define them. We do not # use exports_not_used by default because it hinders more # than it helps library projects such as Cowboy. Finally, # undefined_functions provides little that undefined_function_calls # doesn't already provide, so it's not enabled by default. XREF_CHECKS ?= [undefined_function_calls] # Instead of predefined checks a query can be evaluated # using the Xref DSL. The $q variable is used in that case. # The scope is a list of keywords that correspond to # application directories, being essentially an easy way # to configure which applications to analyze. With: # # - app: . # - apps: $(ALL_APPS_DIRS) # - deps: $(ALL_DEPS_DIRS) # - otp: Built-in Erlang/OTP applications. # # The default is conservative (app) and will not be # appropriate for all types of queries (for example # application_call requires adding all applications # that might be called or they will not be found). XREF_SCOPE ?= app # apps deps otp # If the above is not enough, additional application # directories can be configured. XREF_EXTRA_APP_DIRS ?= # As well as additional non-application directories. XREF_EXTRA_DIRS ?= # Erlang.mk supports -ignore_xref([...]) with forms # {M, F, A} | {F, A} | M, the latter ignoring whole # modules. Ignores can also be provided project-wide. XREF_IGNORE ?= [] # All callbacks may be ignored. Erlang.mk will ignore # them automatically for exports_not_used (unless it # is explicitly disabled by the user). XREF_IGNORE_CALLBACKS ?= # Core targets. help:: $(verbose) printf '%s\n' '' \ 'Xref targets:' \ ' xref Analyze the project using Xref' \ ' xref q=QUERY Evaluate an Xref query' # Plugin-specific targets. define xref.erl {ok, Xref} = xref:start([]), Scope = [$(call comma_list,$(XREF_SCOPE))], AppDirs0 = [$(call comma_list,$(foreach d,$(XREF_EXTRA_APP_DIRS),"$d"))], AppDirs1 = case lists:member(otp, Scope) of false -> AppDirs0; true -> RootDir = code:root_dir(), AppDirs0 ++ [filename:dirname(P) || P <- code:get_path(), lists:prefix(RootDir, P)] end, AppDirs2 = case lists:member(deps, Scope) of false -> AppDirs1; true -> [$(call comma_list,$(foreach d,$(ALL_DEPS_DIRS),"$d"))] ++ AppDirs1 end, AppDirs3 = case lists:member(apps, Scope) of false -> AppDirs2; true -> [$(call comma_list,$(foreach d,$(ALL_APPS_DIRS),"$d"))] ++ AppDirs2 end, AppDirs = case lists:member(app, Scope) of false -> AppDirs3; true -> ["../$(notdir $(CURDIR))"|AppDirs3] end, [{ok, _} = xref:add_application(Xref, AppDir, [{builtins, true}]) || AppDir <- AppDirs], ExtraDirs = [$(call comma_list,$(foreach d,$(XREF_EXTRA_DIRS),"$d"))], [{ok, _} = xref:add_directory(Xref, ExtraDir, [{builtins, true}]) || ExtraDir <- ExtraDirs], ok = xref:set_library_path(Xref, code:get_path() -- (["ebin", "."] ++ AppDirs ++ ExtraDirs)), Checks = case {$1, is_list($2)} of {check, true} -> $2; {check, false} -> [$2]; {query, _} -> [$2] end, FinalRes = [begin IsInformational = case $1 of query -> true; check -> is_tuple(Check) andalso lists:member(element(1, Check), [call, use, module_call, module_use, application_call, application_use]) end, {ok, Res0} = case $1 of check -> xref:analyze(Xref, Check); query -> xref:q(Xref, Check) end, Res = case IsInformational of true -> Res0; false -> lists:filter(fun(R) -> {Mod, InMFA, MFA} = case R of {InMFA0 = {M, _, _}, MFA0} -> {M, InMFA0, MFA0}; {M, _, _} -> {M, R, R} end, Attrs = try Mod:module_info(attributes) catch error:undef -> [] end, InlineIgnores = lists:flatten([ [case V of M when is_atom(M) -> {M, '_', '_'}; {F, A} -> {Mod, F, A}; _ -> V end || V <- Values] || {ignore_xref, Values} <- Attrs]), BuiltinIgnores = [ {eunit_test, wrapper_test_exported_, 0} ], DoCallbackIgnores = case {Check, "$(strip $(XREF_IGNORE_CALLBACKS))"} of {exports_not_used, ""} -> true; {_, "0"} -> false; _ -> true end, CallbackIgnores = case DoCallbackIgnores of false -> []; true -> Behaviors = lists:flatten([ [BL || {behavior, BL} <- Attrs], [BL || {behaviour, BL} <- Attrs] ]), [{Mod, CF, CA} || B <- Behaviors, {CF, CA} <- B:behaviour_info(callbacks)] end, WideIgnores = if is_list($(XREF_IGNORE)) -> [if is_atom(I) -> {I, '_', '_'}; true -> I end || I <- $(XREF_IGNORE)]; true -> [$(XREF_IGNORE)] end, Ignores = InlineIgnores ++ BuiltinIgnores ++ CallbackIgnores ++ WideIgnores, not (lists:member(InMFA, Ignores) orelse lists:member(MFA, Ignores) orelse lists:member({Mod, '_', '_'}, Ignores)) end, Res0) end, case Res of [] -> ok; _ when IsInformational -> case Check of {call, {CM, CF, CA}} -> io:format("Functions that ~s:~s/~b calls:~n", [CM, CF, CA]); {use, {CM, CF, CA}} -> io:format("Function ~s:~s/~b is called by:~n", [CM, CF, CA]); {module_call, CMod} -> io:format("Modules that ~s calls:~n", [CMod]); {module_use, CMod} -> io:format("Module ~s is used by:~n", [CMod]); {application_call, CApp} -> io:format("Applications that ~s calls:~n", [CApp]); {application_use, CApp} -> io:format("Application ~s is used by:~n", [CApp]); _ when $1 =:= query -> io:format("Query ~s returned:~n", [Check]) end, [case R of {{InM, InF, InA}, {M, F, A}} -> io:format("- ~s:~s/~b called by ~s:~s/~b~n", [M, F, A, InM, InF, InA]); {M, F, A} -> io:format("- ~s:~s/~b~n", [M, F, A]); ModOrApp -> io:format("- ~s~n", [ModOrApp]) end || R <- Res], ok; _ -> [case {Check, R} of {undefined_function_calls, {{InM, InF, InA}, {M, F, A}}} -> io:format("Undefined function ~s:~s/~b called by ~s:~s/~b~n", [M, F, A, InM, InF, InA]); {undefined_functions, {M, F, A}} -> io:format("Undefined function ~s:~s/~b~n", [M, F, A]); {locals_not_used, {M, F, A}} -> io:format("Unused local function ~s:~s/~b~n", [M, F, A]); {exports_not_used, {M, F, A}} -> io:format("Unused exported function ~s:~s/~b~n", [M, F, A]); {deprecated_function_calls, {{InM, InF, InA}, {M, F, A}}} -> io:format("Deprecated function ~s:~s/~b called by ~s:~s/~b~n", [M, F, A, InM, InF, InA]); {deprecated_functions, {M, F, A}} -> io:format("Deprecated function ~s:~s/~b~n", [M, F, A]); _ -> io:format("~p: ~p~n", [Check, R]) end || R <- Res], error end end || Check <- Checks], stopped = xref:stop(Xref), case lists:usort(FinalRes) of [ok] -> halt(0); _ -> halt(1) end endef xref: deps app ifdef q $(verbose) $(call erlang,$(call xref.erl,query,"$q"),-pa ebin/) else $(verbose) $(call erlang,$(call xref.erl,check,$(XREF_CHECKS)),-pa ebin/) endif # Copyright (c) Loïc Hoguin # Copyright (c) 2015, Viktor Söderqvist # This file is part of erlang.mk and subject to the terms of the ISC License. COVER_REPORT_DIR ?= cover COVER_DATA_DIR ?= $(COVER_REPORT_DIR) ifdef COVER COVER_APPS ?= $(notdir $(ALL_APPS_DIRS)) COVER_DEPS ?= COVER_EXCLUDE_MODS ?= endif # Code coverage for Common Test. ifdef COVER ifdef CT_RUN ifneq ($(wildcard $(TEST_DIR)),) test-build:: $(TEST_DIR)/ct.cover.spec $(TEST_DIR)/ct.cover.spec: cover-data-dir $(gen_verbose) printf "%s\n" \ "{incl_app, '$(PROJECT)', details}." \ "{incl_dirs, '$(PROJECT)', [\"$(call core_native_path,$(CURDIR)/ebin)\" \ $(foreach a,$(COVER_APPS),$(comma) \"$(call core_native_path,$(APPS_DIR)/$a/ebin)\") \ $(foreach d,$(COVER_DEPS),$(comma) \"$(call core_native_path,$(DEPS_DIR)/$d/ebin)\")]}." \ '{export,"$(call core_native_path,$(abspath $(COVER_DATA_DIR))/ct.coverdata)"}.' \ "{excl_mods, '$(PROJECT)', [$(call comma_list,$(COVER_EXCLUDE_MODS))]}." > $@ CT_RUN += -cover $(TEST_DIR)/ct.cover.spec endif endif endif # Code coverage for other tools. ifdef COVER define cover.erl CoverSetup = fun() -> Dirs = ["$(call core_native_path,$(CURDIR)/ebin)" $(foreach a,$(COVER_APPS),$(comma) "$(call core_native_path,$(APPS_DIR)/$a/ebin)") $(foreach d,$(COVER_DEPS),$(comma) "$(call core_native_path,$(DEPS_DIR)/$d/ebin)")], Excludes = [$(call comma_list,$(foreach e,$(COVER_EXCLUDE_MODS),"$e"))], [case file:list_dir(Dir) of {error, enotdir} -> false; {error, _} -> halt(2); {ok, Files} -> BeamFiles = [filename:join(Dir, File) || File <- Files, not lists:member(filename:basename(File, ".beam"), Excludes), filename:extension(File) =:= ".beam"], case cover:compile_beam(BeamFiles) of {error, _} -> halt(1); _ -> true end end || Dir <- Dirs] end, CoverExport = fun(Filename) -> cover:export(Filename) end, endef else define cover.erl CoverSetup = fun() -> ok end, CoverExport = fun(_) -> ok end, endef endif # Core targets ifdef COVER ifneq ($(COVER_REPORT_DIR),) tests:: $(verbose) $(MAKE) --no-print-directory cover-report endif cover-data-dir: | $(COVER_DATA_DIR) $(COVER_DATA_DIR): $(verbose) mkdir -p $(COVER_DATA_DIR) else cover-data-dir: endif clean:: coverdata-clean ifneq ($(COVER_REPORT_DIR),) distclean:: cover-report-clean endif help:: $(verbose) printf "%s\n" "" \ "Cover targets:" \ " cover-report Generate a HTML coverage report from previously collected" \ " cover data." \ " all.coverdata Merge all coverdata files into all.coverdata." \ "" \ "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \ "target tests additionally generates a HTML coverage report from the combined" \ "coverdata files from each of these testing tools. HTML reports can be disabled" \ "by setting COVER_REPORT_DIR to empty." # Plugin specific targets COVERDATA = $(filter-out $(COVER_DATA_DIR)/all.coverdata,$(wildcard $(COVER_DATA_DIR)/*.coverdata)) .PHONY: coverdata-clean coverdata-clean: $(gen_verbose) rm -f $(COVER_DATA_DIR)/*.coverdata $(TEST_DIR)/ct.cover.spec # Merge all coverdata files into one. define cover_export.erl $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) cover:export("$(COVER_DATA_DIR)/$@"), halt(0). endef all.coverdata: $(COVERDATA) cover-data-dir $(gen_verbose) $(call erlang,$(cover_export.erl)) # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to # empty if you want the coverdata files but not the HTML report. ifneq ($(COVER_REPORT_DIR),) .PHONY: cover-report-clean cover-report cover-report-clean: $(gen_verbose) rm -rf $(COVER_REPORT_DIR) ifneq ($(COVER_REPORT_DIR),$(COVER_DATA_DIR)) $(if $(shell ls -A $(COVER_DATA_DIR)/),,$(verbose) rmdir $(COVER_DATA_DIR)) endif ifeq ($(COVERDATA),) cover-report: else # Modules which include eunit.hrl always contain one line without coverage # because eunit defines test/0 which is never called. We compensate for this. EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \ grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \ | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq)) define cover_report.erl $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) Ms0 = cover:imported_modules(), Exclude = [$(call comma_list,$(COVER_EXCLUDE_MODS))], Ms = [M || M <- Ms0, not lists:member(M, Exclude)], [cover:analyse_to_file(M, "$(COVER_REPORT_DIR)/" ++ atom_to_list(M) ++ ".COVER.html", [html]) || M <- Ms], Report = [begin {result, [R], []} = cover:analyse([M], module), R end || M <- Ms], EunitHrlMods = [$(EUNIT_HRL_MODS)], Report1 = [{M, {Y, case lists:member(M, EunitHrlMods) of true -> N - 1; false -> N end}} || {M, {Y, N}} <- Report], TotalY = lists:sum([Y || {_, {Y, _}} <- Report1]), TotalN = lists:sum([N || {_, {_, N}} <- Report1]), Perc = fun(Y, N) -> case Y + N of 0 -> 100; S -> round(100 * Y / S) end end, TotalPerc = Perc(TotalY, TotalN), {ok, F} = file:open("$(COVER_REPORT_DIR)/index.html", [write]), io:format(F, "~n" "~n" "Coverage report~n" "~n", []), io:format(F, "

Coverage

~n

Total: ~p%

~n", [TotalPerc]), io:format(F, "~n", []), [io:format(F, "" "~n", [M, M, Perc(Y, N)]) || {M, {Y, N}} <- Report1], How = "$(subst $(space),$(comma)$(space),$(basename $(COVERDATA)))", Date = "$(shell date -u "+%Y-%m-%dT%H:%M:%SZ")", io:format(F, "
ModuleCoverage
~p~p%
~n" "

Generated using ~s and erlang.mk on ~s.

~n" "", [How, Date]), halt(). endef cover-report: $(verbose) mkdir -p $(COVER_REPORT_DIR) $(gen_verbose) $(call erlang,$(cover_report.erl)) endif endif # ifneq ($(COVER_REPORT_DIR),) # Copyright (c) Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. .PHONY: sfx ifdef RELX_REL ifdef SFX # Configuration. SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run # Core targets. rel:: sfx # Plugin-specific targets. define sfx_stub #!/bin/sh TMPDIR=`mktemp -d` ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0` FILENAME=$$(basename $$0) REL=$${FILENAME%.*} tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR $$TMPDIR/bin/$$REL console RET=$$? rm -rf $$TMPDIR exit $$RET __ARCHIVE_BELOW__ endef sfx: $(verbose) $(call core_render,sfx_stub,$(SFX_OUTPUT_FILE)) $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE) $(verbose) chmod +x $(SFX_OUTPUT_FILE) endif endif # Copyright (c) Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. # External plugins. DEP_PLUGINS ?= $(foreach p,$(DEP_PLUGINS),\ $(eval $(if $(findstring /,$p),\ $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\ $(call core_dep_plugin,$p/plugins.mk,$p)))) help:: help-plugins help-plugins:: $(verbose) : # Copyright (c) Loïc Hoguin # Copyright (c) 2015-2016, Jean-Sébastien Pédron # This file is part of erlang.mk and subject to the terms of the ISC License. # Fetch dependencies recursively (without building them). .PHONY: fetch-deps fetch-doc-deps fetch-rel-deps fetch-test-deps \ fetch-shell-deps .PHONY: $(ERLANG_MK_RECURSIVE_DEPS_LIST) \ $(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST) \ $(ERLANG_MK_RECURSIVE_REL_DEPS_LIST) \ $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST) \ $(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST) fetch-deps: $(ERLANG_MK_RECURSIVE_DEPS_LIST) fetch-doc-deps: $(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST) fetch-rel-deps: $(ERLANG_MK_RECURSIVE_REL_DEPS_LIST) fetch-test-deps: $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST) fetch-shell-deps: $(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST) ifneq ($(SKIP_DEPS),) $(ERLANG_MK_RECURSIVE_DEPS_LIST) \ $(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST) \ $(ERLANG_MK_RECURSIVE_REL_DEPS_LIST) \ $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST) \ $(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST): $(verbose) :> $@ else # By default, we fetch "normal" dependencies. They are also included no # matter the type of requested dependencies. # # $(ALL_DEPS_DIRS) includes $(BUILD_DEPS). $(ERLANG_MK_RECURSIVE_DEPS_LIST): $(LOCAL_DEPS_DIRS) $(ALL_DEPS_DIRS) $(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST): $(LOCAL_DEPS_DIRS) $(ALL_DEPS_DIRS) $(ALL_DOC_DEPS_DIRS) $(ERLANG_MK_RECURSIVE_REL_DEPS_LIST): $(LOCAL_DEPS_DIRS) $(ALL_DEPS_DIRS) $(ALL_REL_DEPS_DIRS) $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST): $(LOCAL_DEPS_DIRS) $(ALL_DEPS_DIRS) $(ALL_TEST_DEPS_DIRS) $(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST): $(LOCAL_DEPS_DIRS) $(ALL_DEPS_DIRS) $(ALL_SHELL_DEPS_DIRS) # Allow to use fetch-deps and $(DEP_TYPES) to fetch multiple types of # dependencies with a single target. ifneq ($(filter doc,$(DEP_TYPES)),) $(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_DOC_DEPS_DIRS) endif ifneq ($(filter rel,$(DEP_TYPES)),) $(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_REL_DEPS_DIRS) endif ifneq ($(filter test,$(DEP_TYPES)),) $(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_TEST_DEPS_DIRS) endif ifneq ($(filter shell,$(DEP_TYPES)),) $(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_SHELL_DEPS_DIRS) endif ERLANG_MK_RECURSIVE_TMP_LIST := $(abspath $(ERLANG_MK_TMP)/recursive-tmp-deps-$(shell echo $$PPID).log) $(ERLANG_MK_RECURSIVE_DEPS_LIST) \ $(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST) \ $(ERLANG_MK_RECURSIVE_REL_DEPS_LIST) \ $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST) \ $(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST): | $(ERLANG_MK_TMP) ifeq ($(IS_APP)$(IS_DEP),) $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST) endif $(verbose) touch $(ERLANG_MK_RECURSIVE_TMP_LIST) $(verbose) set -e; for dep in $^ ; do \ if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \ echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \ if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \ $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \ $(MAKE) -C $$dep fetch-deps \ IS_DEP=1 \ ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \ fi \ fi \ done ifeq ($(IS_APP)$(IS_DEP),) $(verbose) sort < $(ERLANG_MK_RECURSIVE_TMP_LIST) | \ uniq > $(ERLANG_MK_RECURSIVE_TMP_LIST).sorted $(verbose) mv $(ERLANG_MK_RECURSIVE_TMP_LIST).sorted $@ $(verbose) rm $(ERLANG_MK_RECURSIVE_TMP_LIST) endif endif # ifneq ($(SKIP_DEPS),) # List dependencies recursively. .PHONY: list-deps list-doc-deps list-rel-deps list-test-deps \ list-shell-deps list-deps: $(ERLANG_MK_RECURSIVE_DEPS_LIST) list-doc-deps: $(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST) list-rel-deps: $(ERLANG_MK_RECURSIVE_REL_DEPS_LIST) list-test-deps: $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST) list-shell-deps: $(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST) list-deps list-doc-deps list-rel-deps list-test-deps list-shell-deps: $(verbose) cat $^ # Query dependencies recursively. .PHONY: query-deps query-doc-deps query-rel-deps query-test-deps \ query-shell-deps QUERY ?= name fetch_method repo version define query_target $1: $2 clean-tmp-query.log ifeq ($(IS_APP)$(IS_DEP),) $(verbose) rm -f $4 endif $(verbose) $(foreach dep,$3,\ echo $(PROJECT): $(foreach q,$(QUERY),$(call query_$(q),$(dep))) >> $4 ;) $(if $(filter-out query-deps,$1),,\ $(verbose) set -e; for dep in $3 ; do \ if grep -qs ^$$$$dep$$$$ $(ERLANG_MK_TMP)/query.log; then \ :; \ else \ echo $$$$dep >> $(ERLANG_MK_TMP)/query.log; \ $(MAKE) -C $(DEPS_DIR)/$$$$dep $$@ QUERY="$(QUERY)" IS_DEP=1 || true; \ fi \ done) ifeq ($(IS_APP)$(IS_DEP),) $(verbose) touch $4 $(verbose) cat $4 endif endef clean-tmp-query.log: ifeq ($(IS_DEP),) $(verbose) rm -f $(ERLANG_MK_TMP)/query.log endif $(eval $(call query_target,query-deps,$(ERLANG_MK_RECURSIVE_DEPS_LIST),$(BUILD_DEPS) $(DEPS),$(ERLANG_MK_QUERY_DEPS_FILE))) $(eval $(call query_target,query-doc-deps,$(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST),$(DOC_DEPS),$(ERLANG_MK_QUERY_DOC_DEPS_FILE))) $(eval $(call query_target,query-rel-deps,$(ERLANG_MK_RECURSIVE_REL_DEPS_LIST),$(REL_DEPS),$(ERLANG_MK_QUERY_REL_DEPS_FILE))) $(eval $(call query_target,query-test-deps,$(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST),$(TEST_DEPS),$(ERLANG_MK_QUERY_TEST_DEPS_FILE))) $(eval $(call query_target,query-shell-deps,$(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST),$(SHELL_DEPS),$(ERLANG_MK_QUERY_SHELL_DEPS_FILE))) ERLANG_MK_TREE_FILE = $(ERLANG_MK_TMP)/tree.log .PHONY: tree ifeq ($(IS_APP)$(IS_DEP),) tree: $(verbose) $(MAKE) --no-print-directory query-deps QUERY="name fetch_method repo version" >/dev/null $(verbose) $(call erlang,$(call dep_tree.erl,$(call core_native_path,$(ERLANG_MK_QUERY_DEPS_FILE)),$(call core_native_path,$(ERLANG_MK_TREE_FILE)),$(PROJECT))) $(verbose) cat $(ERLANG_MK_TREE_FILE) else tree: ; endif define dep_tree.erl {ok, Bin} = file:read_file("$1"), Edges = [case string:lexemes(Line, " ") of [Parent, Name, _, _, Version] -> {string:trim(Parent, trailing, ":"), Name, Version}; _ -> io:format(standard_error, "bad query-deps line: ~ts~n", [Line]), halt(96) end || Line <- string:lexemes(Bin, "\n")], Root = unicode:characters_to_binary("$3"), Walk = fun Walk(Parent, Prefix, Seen) -> Children = [{N, V} || {P, N, V} <- Edges, P =:= Parent], Draw = fun Draw([], SeenAcc) -> {[], SeenAcc}; Draw([{Name, Version}|Rest], SeenAcc) -> {Branch, Pad} = case Rest of [] -> {"\x{2514}\x{2500}\x{2500} ", " "}; _ -> {"\x{251C}\x{2500}\x{2500} ", "\x{2502} "} end, Line = [Prefix, Branch, Name, " ", Version, "\n"], {ChildIo, Seen1} = case lists:member(Name, SeenAcc) of true -> {[], SeenAcc}; false -> Walk(Name, [Prefix, Pad], [Name|SeenAcc]) end, {RestIo, Seen2} = Draw(Rest, Seen1), {[Line, ChildIo, RestIo], Seen2} end, Draw(Children, Seen) end, {Body, _} = Walk(Root, "", [Root]), case file:write_file("$2", unicode:characters_to_binary([Root, "\n", Body])) of ok -> halt(0); {error, Reason} -> io:format(standard_error, "Error: ~0p~n", [Reason]), halt(1) end endef potatosalad-erlang-jose-30ac019/CHANGELOG.md0000664000232200023220000006533315267717064020765 0ustar debalancedebalance# Changelog ## 1.11.13 (2026-10-26) * Security Patches * [GHSA-wfpv-qxp8-x5gm](https://github.com/potatosalad/erlang-jose/security/advisories/GHSA-wfpv-qxp8-x5gm), [GHSA-wh8p-p48q-3vw2](https://github.com/potatosalad/erlang-jose/security/advisories/GHSA-wh8p-p48q-3vw2): Bound incremental JWE `zip: "DEF"` decompression to 1 MiB by default. Applications requiring larger compressed plaintexts can set `jose:zip_size_maximum/1` or the `zip_size_maximum` application environment key. * [GHSA-44hw-rpw9-mwg4](https://github.com/potatosalad/erlang-jose/security/advisories/GHSA-44hw-rpw9-mwg4): Require the canonical ECDSA signature length for ES256, ES384, ES512 and ES256K. * [GHSA-6gxw-6f7r-cjr6](https://github.com/potatosalad/erlang-jose/security/advisories/GHSA-6gxw-6f7r-cjr6): Reject AEAD tags other than 128 bits before provider dispatch, and in the AES-GCM fallback. * [GHSA-3m45-vrpj-xmrq](https://github.com/potatosalad/erlang-jose/security/advisories/GHSA-3m45-vrpj-xmrq): Bound RSA private-key prime recovery with random trials, reused modular squares and deadlines checked before expensive operations; validate recovered factors against the supplied exponents. Keys above 8192 bits or exhausting the recovery budget must supply CRT parameters. * [GHSA-fpj2-9h59-32pp](https://github.com/potatosalad/erlang-jose/security/advisories/GHSA-fpj2-9h59-32pp): Reject mismatched shared-secret key types instead of recursing indefinitely. * [GHSA-h4gx-8jq2-mfhc](https://github.com/potatosalad/erlang-jose/security/advisories/GHSA-h4gx-8jq2-mfhc): Strengthen encrypted PKCS#8 DER and PEM exports to PBKDF2-HMAC-SHA256 with 600,000 iterations and 16-byte salts. Previously exported keys remain readable. Use native PBKDF2 when available. * [GHSA-vwvr-f994-6j9j](https://github.com/potatosalad/erlang-jose/security/advisories/GHSA-vwvr-f994-6j9j): Substitute a random CEK of the required length on RSA JWE unwrap failure, then authenticate the content. Low-level RSA padding helpers still report errors; this mitigation does not guarantee constant-time RSA. Prefer RSA-OAEP or RSA-OAEP-256. * [GHSA-xvr2-gggj-h2pp](https://github.com/potatosalad/erlang-jose/security/advisories/GHSA-xvr2-gggj-h2pp): Correct the shipped `crypto_fallback` default to `false` and document the variable-time pure Erlang curve implementations. Explicitly enabled fallback remains variable-time and should not handle long-lived private keys where timing is observable. * Fixes * Return compressed JWE authentication failures without invoking decompression, including external AAD. * Preserve native Ed448/X448 selection when fallback SHAKE256 is unavailable; include X25519/X448 agreement in provider checks. * Add public API security regressions and independent legacy PKCS#8 import checks. * Treat a random base sharing a factor with the modulus as a failed RSA prime recovery trial, so inconsistent `(n, e, d)` keys are rejected instead of imported with a composite factor. * Return `<<>>` from Concat KDF for a zero key length instead of looping indefinitely, and reject negative lengths with `badarg`. * Erlang/OTP 29 compatibility: decode X25519/X448 PKCS#8 private keys, encode EC `SubjectPublicKeyInfo` DER, and replace deprecated `catch Expr` expressions. * Notes * Deployments relying on unavailable native algorithms must explicitly enable `crypto_fallback` and review the timing limitations. Native providers remain preferred. * Tested on Erlang/OTP 28 and 29. ## 1.11.12 (2025-11-20) * Fixes * Properly handle `nil` from Elixir maps when encoding with the builtin `json` Erlang/OTP module; see [#184](https://github.com/potatosalad/erlang-jose/issues/184) * Drop required Elixir version back from 1.16 -> 1.13; see [#185](https://github.com/potatosalad/erlang-jose/issues/185) ## 1.11.11 (2025-11-17) * Fixes * EC key decode/encode issues in OTP 28; see [#180](https://github.com/potatosalad/erlang-jose/pull/180), [#181](https://github.com/potatosalad/erlang-jose/pull/181), [#182](https://github.com/potatosalad/erlang-jose/pull/182), [#179](https://github.com/potatosalad/erlang-jose/pull/179) * EdDSA key issues with OTP 27.1.3+; see [#177](https://github.com/potatosalad/erlang-jose/pull/177) * Notes * This will hopefully be one of the last 1.x releases, next release will be [2.x](https://github.com/potatosalad/erlang-jose/tree/2.x) ## 1.11.10 (2024-04-17) * Fixes * Various type spec fixes; see [#165](https://github.com/potatosalad/erlang-jose/pull/165) and [#166](https://github.com/potatosalad/erlang-jose/pull/166). ## 1.11.9 (2024-04-08) * Fixes * Change Elixir dependency on `dialyxer` to only be used in `dev` and `test`, thanks to [@requestben](https://github.com/requestben) and [@dvic](https://github.com/dvic); see [#162](https://github.com/potatosalad/erlang-jose/issues/162) and [#163](https://github.com/potatosalad/erlang-jose/issues/163). * `rebar3` upgrade errors fixed; see [#160](https://github.com/potatosalad/erlang-jose/issues/160) and [#163](https://github.com/potatosalad/erlang-jose/issues/163). ## 1.11.8 (2024-04-07) * Fixes * Removes use of `dynamic()` type spec so OTP 24 and OTP 25 are still supported for now. ## 1.11.7 (2024-04-07) * Security Patches * [CVE-2023-50966](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-50966): Add `jose:pbes2_count_maximum/0`. By default, the maximum iterations are set to 10,000 and it will raise an error if `p2c` is larger than this value. * Changes * Declare Poison as an optional dependency, thanks to [@lnikkila][https://github.com/lnikkila]; see [#144](https://github.com/potatosalad/erlang-jose/pull/144). * Ensure `jiffy:encode/1` returns a binary, thanks to [@ssepml](https://github.com/ssepml); see [#145](https://github.com/potatosalad/erlang-jose/pull/145). * Various type spec additions and dialyzer/dialyxir integrations, thanks to [@whatyouhide](https://github.com/whatyouhide) and [@maennchen](https://github.com/maennchen). * Doc updates and fixes, thanks to [@aymanosman](https://github.com/aymanosman) and [@adamu](https://github.com/adamu); see [#158](https://github.com/potatosalad/erlang-jose/pull/158) and [#159](https://github.com/potatosalad/erlang-jose/pull/159). ## 1.11.6 (2023-07-18) * Fixes * Use `sha256` instead of `sha` when validating RSA signing supportability, thanks to [@pavledjo](https://github.com/pavledjo); see [#142](https://github.com/potatosalad/erlang-jose/pull/142). ## 1.11.5 (2022-12-16) * Fixes * Remove dependency on `parse_transform` for `jose_base64` and `jose_base64url`. ## 1.11.4 (2022-12-15) * Enhancements * Add support for native `crypto` operations for Ed25519 and Ed448, thanks to [@brettbeatty](https://github.com/brettbeatty); see [#123](https://github.com/potatosalad/erlang-jose/pull/123). * Add support for native `crypto` operations for ChaCha20-Poly1305 and XChaCha20-Poly1305, when available. * Add support for `libsodium` operations for XChaCha20-Poly1305, when available. * Add support for [`thoas`](https://github.com/lpil/thoas) JSON encode/decode, thanks to [@michaelklishin](https://github.com/michaelklishin); see [#126](https://github.com/potatosalad/erlang-jose/pull/126). * Add support for `ES256K` which uses the `secp256k1` curve and `RS1` signatures (see [291dbb8](https://github.com/potatosalad/erlang-jose/commit/291dbb86fb5e5c71a8395b3f68c16f31f8bd06db)). * Add support for ECDH-1PU `JOSE.JWK.box_encrypt_ecdh_1pu` and ECDH-ES `JOSE.JWK.box_encrypt_ecdh_es` and document the deprecated `JOSE.JWK.box_encrypt`. * Add support for ECDH-SS `JOSE.JWK.box_encrypt_ecdh_ss`. * Hide `kty` field when inspecting `%JOSE.JWK{}` strict, thanks to [@spencerdcarlson](https://github.com/spencerdcarlson); see [#139](https://github.com/potatosalad/erlang-jose/pull/139) * Fixes * Version mismatch causing `rebar3` to constantly try to update; see [#122](https://github.com/potatosalad/erlang-jose/pull/122). * Fix Ed25519 and Ed448 key DER/PEM encode/decode for OTP 25. * Fix Ed25519ctx, Ed25519ph, Ed448, and Ed448ph when dealing with contexts so the implementation matches [IETF RFC 8032](https://datatracker.ietf.org/doc/html/rfc8032). * Drop direct usage of `crypto:hmac/4`, thanks to [@thalesmg](https://github.com/thalesmg); see [#136](https://github.com/potatosalad/erlang-jose/pull/136) * Replace incorrect usage of `-include_lib` with `-include`, thanks to [@Richiban](https://github.com/Richiban); see [#140](https://github.com/potatosalad/erlang-jose/pull/140) * Update the CI jobs so they actually run the Elixir tests, thanks to [@moogle19](https://github.com/moogle19); see [#137](https://github.com/potatosalad/erlang-jose/pull/137) * Change the `master` branch to `main`. ## 1.11.3 (2022-08-28) - unreleased * This version was never actually released, but was referenced for a while on the `master` branch (see [43d3db4](https://github.com/potatosalad/erlang-jose/commit/43d3db467f909bbc932bd663ddcb1af93180dfd3)). ## 1.11.2 (2021-08-06) * Fixes * Add compatability with OTP 24 ## 1.11.1 (2020-12-23) * Fixes * Fix compatibility with older OTP versions * Fix AES detection on OTP 23 * Fix AES GCM bugs on OTP 23 ## 1.11.0 (2020-11-24) * Fixes * Add compatability layer to fix deprecated `crypto` functions. * Use `:extra_applications` instead of `:applications` (bumps Elixir requirement to `~> 1.4`). * Conditionally compile `Poison` modules used for testing. ## 1.10.1 (2020-01-08) * Fixes * Add PEM/DER compatibility layer for PKCS-8 incompatibilities with various versions of OTP, `crypto`, and `public_key`; see [#82](https://github.com/potatosalad/erlang-jose/issues/82) ## 1.10.0 (2020-01-03) * Enhancements * Remove [base64url](https://github.com/dvv/base64url) dependency and include embedded version. * Add support for `C20P` and `XC20P` encryption based on [draft-amringer-jose-chacha](https://tools.ietf.org/html/draft-amringer-jose-chacha-01) (ChaCha20/Poly1305 and XChaCha20/Poly1305). * Add support for ECDH-ES keywrapping for AES-GCM, ChaCha20/Poly1305, and XChaCha20/Poly1305. * Add support for PBES2 keywrapping for AES-GCM, ChaCha20/Poly1305, and XChaCha20/Poly1305. * Add support for `ECDH-1PU` encryption based on [draft-madden-jose-ecdh-1pu](https://tools.ietf.org/html/draft-madden-jose-ecdh-1pu-02). * Add support for reading/writing DER format (or PKCS8 format). * Fixes * Fix PSS salt length (thanks to [@ntrepid8](https://github.com/ntrepid8), see [#65](https://github.com/potatosalad/erlang-jose/pull/65)) * Speed up and stabilize tests on CI environment. ## 1.9.0 (2018-12-31) * Enhancements * Add support for [Jason](https://github.com/michalmuskala/jason) JSON encoding and decoding. * Add support for Poison 4.x and lexical ordering. * Use `public_key` over `cutkey` for RSA key generation if available. * Drop support for older versions of OTP (19+ now required). * Relicense library under MIT license. * Fixes * Add macro so the application compiles without warnings after `erlang:get_stacktrace/0` has been deprecated. * Extra sanity check for RSA padding modes when falling back. ## 1.8.4 (2017-05-18) * Enhancements * Add support for reading and writing PEM files for Ed25519, Ed448, X25519, and X448 keys based on [draft-ietf-curdle-pkix](https://tools.ietf.org/html/draft-ietf-curdle-pkix). * Add support for [ojson](https://github.com/potatosalad/erlang-json) adapter for encoding/decoding JSON. ## 1.8.3 (2017-03-30) * Fixes * Regression fix from 1.8.2 for OTP-17 (thanks to [@alexandrejbr](https://github.com/alexandrejbr), see [#35](https://github.com/potatosalad/erlang-jose/issues/35) and [#36](https://github.com/potatosalad/erlang-jose/pull/36)) ## 1.8.2 (2017-03-15) * Enhancements * Add support for decoding firebase certificate public keys (thanks to [@noizu](https://github.com/noizu), see [#30](https://github.com/potatosalad/erlang-jose/issues/30)) * Fixes * Fix cross-platform issues with EC signatures (specifically S and R sizes, thanks to [@alexandrejbr](https://github.com/alexandrejbr), see [#32](https://github.com/potatosalad/erlang-jose/pull/32)) * Typo in documentation for `JOSE.encode/1` (thanks to [@DaveLampton](https://github.com/DaveLampton), see [#31](https://github.com/potatosalad/erlang-jose/issues/31)) * Tests * Tested against OTP 19.3, Elixir 1.4.x, and Poison 3.x ## 1.8.1 (2017-02-02) * Fixes * Parentheses to remove ambiguity on Elixir 1.4 [#26](https://github.com/potatosalad/erlang-jose/pull/26), thanks [@alexandrubagu](https://github.com/alexandrubagu) ## 1.8.0 (2016-08-08) * Enhancements * ChaCha20/Poly1305 encryption and one-time message authentication functions are experimentally supported based on [RFC 7539](https://tools.ietf.org/html/rfc7539). * Fixes * Handling invalid token without raising Exception [#22](https://github.com/potatosalad/erlang-jose/issues/22) * `JOSE.JWT.verify` uses CPU intensively when signed is nil [#23](https://github.com/potatosalad/erlang-jose/issues/23) Examples of new functionality: ```elixir iex> # Encrypt iex> jwe = %{"alg" => "dir", "enc" => "ChaCha20/Poly1305"} iex> jwk = JOSE.JWE.generate_key(jwe) |> JOSE.JWK.to_map |> elem(1) %{"alg" => "dir", "enc" => "ChaCha20/Poly1305", "k" => "EffEuY2nbShIVtizmek8AuR7ftSuY2e8XRxGjMc8QAc", "kty" => "oct", "use" => "enc"} iex> plain_text = "message to encrypt" iex> encrypted = JOSE.JWK.block_encrypt(plain_text, jwk) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJkaXIiLCJlbmMiOiJDaGFDaGEyMC9Qb2x5MTMwNSJ9..lbsERynEgQS8CRXZ.D_kt8ChsaYWX9gL9tJlJ2n0E.y0o_TYjGlaB9sEEcA9o12A" iex> # Decrypt iex> plain_text == JOSE.JWK.block_decrypt(encrypted, jwk) |> elem(0) true iex> # Sign iex> jws = %{"alg" => "Poly1305"} iex> jwk = JOSE.JWS.generate_key(jws) |> JOSE.JWK.to_map |> elem(1) %{"alg" => "Poly1305", "k" => "2X-OZVLA41Wy7mAjqWRaZyOw8FLyL3O3_f8d16D_-tQ", "kty" => "oct", "use" => "sig"} iex> message = "message to sign" iex> signed = JOSE.JWK.sign(message, jwk) |> JOSE.JWS.compact |> elem(1) "eyJhbGciOiJQb2x5MTMwNSIsIm5vbmNlIjoicGExU1dlQzJVQzhwZlQ1NCJ9.bWVzc2FnZSB0byBzaWdu.IUI-PvN5bh_9jX-MeDtetw" iex> # Verify iex> JOSE.JWK.verify_strict(signed, ["Poly1305"], jwk) |> elem(0) true ``` ## 1.7.9 (2016-07-13) * Fixes * Fixed JSON encoding bug in `jose_json_poison_compat_encoder` for projects using Poison as the JSON encoder where Erlang loads Elixir as a dependency. ## 1.7.8 (2016-07-08) * Enhancements * Updated EdDSA tests to comply with draft 04 of [draft-ietf-jose-cfrg-curves-04](https://tools.ietf.org/html/draft-ietf-jose-cfrg-curves-04). * Fixes * Fixed compression encoding bug for `{"zip":"DEF"}` operations (thanks to [@amadden734](https://github.com/amadden734) see [potatosalad/ruby-jose#3](https://github.com/potatosalad/ruby-jose/pull/3)) ## 1.7.7 (2016-06-30) * Enhancements * Improved handling of RSA private keys in SMF (Straightforward Method) form to CRT (Chinese Remainder Theorem) form, see [#19](https://github.com/potatosalad/erlang-jose/issues/19) This is especially useful for keys produced by Java programs using the `RSAPrivateKeySpec` API as mentioned in [Section 9.3 of RFC 7517](https://tools.ietf.org/html/rfc7517#section-9.3). * Updated EdDSA operations to comply with draft 02 of [draft-ietf-jose-cfrg-curves-02](https://tools.ietf.org/html/draft-ietf-jose-cfrg-curves-02). Example RSA SMF to CRT usage: ```erlang %% The following map of an RSA secret key is in SMF (Straightforward Method) form. %% Notice that we only have d, e, and n for this secret key. JWK = jose_jwk:from(#{ <<"d">> => <<"WSAGFGM7fSyYn5NyBL0dp3kjHjQ3djjhQoOAFasoyeE">>, <<"e">> => <<"AQAB">>, <<"kty">> => <<"RSA">>, <<"n">> => <<"0PM6Aooi_KYkDA1r-S24SauFpfTRc5kiPLF3a1EhuY8">> }). %% If we convert it back to a map, it is now in CRT (Chinese Remainder Theorem) form. %% Notice that the dp, dq, p, q, and qi have been restored. element(2, jose_jwk:to_map(JWK)) =:= #{ <<"d">> => <<"WSAGFGM7fSyYn5NyBL0dp3kjHjQ3djjhQoOAFasoyeE">>, <<"dp">> => <<"G00J545ym1bqC9hnFDo3aQ">>, <<"dq">> => <<"tt0FvEZgKli6IL4rVKx3cw">>, <<"e">> => <<"AQAB">>, <<"kty">> => <<"RSA">>, <<"n">> => <<"0PM6Aooi_KYkDA1r-S24SauFpfTRc5kiPLF3a1EhuY8">>, <<"p">> => <<"9O5YQ0w6PIpDl6c6yqwyKQ">>, <<"q">> => <<"2mScgy86M3q6b301UAU09w">>, <<"qi">> => <<"Wrp0SgcGgTT5WmeuHD6Sqw">> }. ``` ## 1.7.6 (2016-06-29) * Fixes * Compatibility fixes for OTP 19 and Elixir 1.3 ## 1.7.5 (2016-05-13) * Fixes * Removed leftover development file accidentally included in last release. ## 1.7.4 (2016-05-13) * Enhancements * More detailed documentation on [key generation](https://hexdocs.pm/jose/key-generation.html). * Fixes * Replaced usage of `crypto:rand_bytes/1` with `crypto:strong_rand_bytes/1` in preparation for Elixir 1.3 and OTP 19 (thanks to [@asonge](https://github.com/asonge) for [#17](https://github.com/potatosalad/erlang-jose/pull/17)). ## 1.7.3 (2016-03-17) * Fixes * `JOSE.JWT.encrypt/2` now uses `JOSE.JWK.block_encryptor/1` properly. ## 1.7.2 (2016-03-16) * Enhancements * Better support for lists of terms. * Added merge functions: * `JOSE.JWE.merge/2` * `JOSE.JWK.merge/2` * `JOSE.JWS.merge/2` * `JOSE.JWT.merge/2` * Added signer, verifier, and block_encryptor functions: * `JOSE.JWK.signer/1` * `JOSE.JWK.verifier/1` * `JOSE.JWK.block_encryptor/1` * Support for `"alg"`, `"enc"`, and `"use"` on keys. Examples of new functionality: ```elixir iex> # Let's generate a 64 byte octet key iex> jwk = JOSE.JWK.generate_key({:oct, 64}) |> JOSE.JWK.to_map |> elem(1) %{"k" => "FXSy7PufOayusvfyKQzdxCegm7yWIMp1b0LD13v57Nq2wF_B-fcr7LDOkufDikmFFsVYWLgrA2zEB--_qqDn3g", "kty" => "oct"} iex> # Based on the key's size and type, a default signer (JWS) can be determined iex> JOSE.JWK.signer(jwk) %{"alg" => "HS512"} iex> # A list of algorithms for which this key type can be verified against can also be determined iex> JOSE.JWK.verifier(jwk) ["HS256", "HS384", "HS512"] iex> # Based on the key's size and type, a default enctypro (JWE) can be determined iex> JOSE.JWK.block_encryptor(jwk) %{"alg" => "dir", "enc" => "A256CBC-HS512"} iex> # Keys can be generated based on the signing algorithm (JWS) iex> JOSE.JWS.generate_key(%{"alg" => "HS256"}) |> JOSE.JWK.to_map |> elem(1) %{"alg" => "HS256", "k" => "UuP3Tw2xbGV5N3BGh34cJNzzC2R1zU7i4rOnF9A8nqY", "kty" => "oct", "use" => "sig"} iex> # Keys can be generated based on the encryption algorithm (JWE) iex> JOSE.JWE.generate_key(%{"alg" => "dir", "enc" => "A128GCM"}) |> JOSE.JWK.to_map |> elem(1) %{"alg" => "dir", "enc" => "A128GCM", "k" => "8WNdBjXXwg6QTwrrOnvEPw", "kty" => "oct", "use" => "enc"} iex> # Example of merging a map into an existing JWS (also works with JWE, JWK, and JWT) iex> jws = JOSE.JWS.from(%{"alg" => "HS256"}) iex> JOSE.JWS.merge(jws, %{"typ" => "JWT"}) |> JOSE.JWS.to_map |> elem(1) %{"alg" => "HS256", "typ" => "JWT"} ``` ## 1.7.1 (2016-03-08) * Enhancements * New [Edwards-curve Digital Signature Algorithm (EdDSA) version 04](https://tools.ietf.org/html/draft-irtf-cfrg-eddsa-04) is out, update test vectors and remove support for 32 byte secrets for Ed448 and Ed448ph. ## 1.7.0 (2016-03-01) * Enhancements * Add support for [libdecaf](https://github.com/potatosalad/erlang-libdecaf) NIF which provides support for; * `Ed25519` * `Ed25519ph` * `Ed448` * `Ed448ph` * `X25519` * `X448` * Fixes * Return 56 bytes instead of 57 bytes when converting between edwards448 and curve448. * EdDSA related refactoring/cleanup. ## 1.6.1 (2016-02-05) * Enhancements * Add support for NIF version of [keccakf1600](https://github.com/potatosalad/erlang-keccakf1600) library with `jose_sha3_keccakf1600_nif` (version 2 and up) for even faster SHA-3 operations. ## 1.6.0 (2016-01-20) * Enhancements * Add `Ed448` and `Ed448ph` standards from [draft-irtf-cfrg-eddsa](https://tools.ietf.org/html/draft-irtf-cfrg-eddsa). * Add support for [keccakf1600](https://github.com/potatosalad/erlang-keccakf1600) library with `jose_sha3_keccakf1600` for faster SHA-3 operations. * Many, many more tests. * Fixes * Fix pure Erlang implementation of SHA-3 algorithms. ## 1.5.2 (2016-01-19) * Enhancements * Documentation of the encryption algorithms, specifically [`JOSE.JWE`](https://hexdocs.pm/jose/JOSE.JWE.html). * Fixes * Corrected optional callbacks issue for Elixir. * More consistent behavior for ECDH related encryption and decryption. ## 1.5.1 (2016-01-16) * Fixes * Corrected formatting on some of the documentation. * Fixed optional callbacks for `jose_jwk_kty:sign/3` ## 1.5.0 (2016-01-16) * Enhancements * Support [OKP](https://tools.ietf.org/html/draft-ietf-jose-cfrg-curves) key type with the following curves: * `Ed25519` (external [libsodium](https://github.com/potatosalad/erlang-libsodium) or fallback supported) * `Ed25519ph` (external [libsodium](https://github.com/potatosalad/erlang-libsodium) or fallback supported) * `X25519` (external [libsodium](https://github.com/potatosalad/erlang-libsodium) or fallback supported) * `Ed448` (no external, no fallback) * `Ed448ph` (no external, no fallback) * `X448` (no external, but fallback supported) * Support [SHA-3](https://en.wikipedia.org/wiki/SHA-3) functions for future use with `Ed448` and `Ed448ph`. * Add `jose_jwk:shared_secret/2` for computing the shared secret between two `EC` or `OKP` keys. ## 1.4.2 (2015-11-30) * Enhancements * Support [PKCS#8](https://www.openssl.org/docs/manmaster/apps/pkcs8.html) formatted private key PEM files. See #13 * Fixes * Add missing guards in `jose_jws:sign/4` #11 * Add missing guards in `jose_jwe:block_encrypt/5` ## 1.4.1 (2015-11-18) * Enhancements * Added `JOSE.JWS.peek_signature/1` for inspecting the signature parts of a signed binary. * `JOSE.JWS.compact/1` and `JOSE.JWS.expand/1` now work with signed lists. * First pass at documenting all of the major `JOSE` modules. `JOSE.JWE` still needs more examples. Closes #7 * Fixes * Fix infinite loop on `JOSE.JWE.key_decrypt/3` when no `"enc"` has been specified. * Fix various functions on `JOSE.JWE` that would fail due to `JOSE.JWE.from_record/1` on wrong terms. ## 1.4.0 (2015-11-17) * Enhancements * Added `JOSE.unsecured_signing/0` and `JOSE.unsecured_signing/1` for disabling the `"none"` algorithm due to the [unsecured signing vulnerability](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/) and in relation to #10 * Added `JOSE.JWK.verify_strict/3`, `JOSE.JWS.verify_strict/3`, and `JOSE.JWT.verify_strict/3` for whitelisting which signing algorithms are allowed for verification. * Added `JOSE.JWT.peek_payload/1` and `JOSE.JWT.peek_protected/1` for inspecting the payload and protected parts of a signature. ## 1.3.0 (2015-09-22) * Enhancements * `oct` key management (see `JOSE.JWK.from_oct/1,2`) * Key generation functions for `EC`, `RSA`, and `oct` keys (see `JOSE.JWK.generate_key/1`) * Add `JOSE.JWK.box_encrypt/2` which generates an ephemeral private key based on the given key curve. * Add support for detecting OTP version 18 and up with optional_callbacks. * Document key generation under `examples/KEY-GENERATION.md` * jiffy and jsone JSON support * Begin documenting the Elixir API (thanks to #8) * Add support for `jose_jws:peek/1` and `jose_jwt:peek/1` * Preparations for future upstream OTP crypto changes. * Improved detection of AES CBC, ECB, and GCM support. * Improved detection of RSAES-OAEP, RSAES-PKCS1-v1_5, RSASSA-PKCS1-v1_5, and RSASSA-PSS support. * Implemented fallback RSAES-PKCS1-v1_5 and RSASSA-PKCS1-v1_5 algorithms. * Improved selection of encryptor for oct keys. * Improved algorithm support detection for jose_jwa. * Fixes * Remove "sph" from jose_jws (removed from [JWS Unencoded Payload Option](https://tools.ietf.org/html/draft-ietf-jose-jws-signing-input-options-02)). * Tests * Only run 1 in 10 for AES GCM and 1 in 5 for AES KW CAVP test vectors to speed up tests. * Additional tests for RSAES-PKCS1-v1_5 and RSASSA-PKCS1-v1_5 algorithms. ## 1.2.0 (2015-08-14) * Enhancements * Add RSA PKCS-1 algorithms to support detection. * Add support for `crypto_fallback` option to enable/disable non-native cryptographic algorithms. * Add support for `json_module` option for encoding/decoding of JSON. * Fixes * Fix AES GCM algorithm for non 96-bit IV values. * Allow RSA OAEP to specify Seed on encrypt. * Tests * NIST and EMC test vectors for AES, PKCS-1, and PKCS-5. * Concat KDF, PBKDF1, and PKCS-7 Padding informal verification. * AES Key Wrap informal verification with NIST test vectors. ## 1.1.3 (2015-08-10) * Fixes * Missed a case where jose was not starting automatically (see 1.1.2). ## 1.1.2 (2015-08-10) * Enhancements * Automatically start jose if one of the fallback algorithms is required. ## 1.1.1 (2015-08-07) * Fixes * Fix bit sizes for A128CBC-HS256, A192CBC-HS384, and A256CBC-HS512 algorithms. * Don't precompute the GHASH table (speeds up AES GCM fallback on OTP 17). * Use case statement instead of map pattern matching for block_decrypt (fixes map pattern matching bug on OTP 17). * Allow mostly empty EC keys to be converted back to JSON. * Add jose_jwk_props property test for full algorithm range of encryption and decryption. ## 1.1.0 (2015-08-06) * Enhancements * Detect supported `crypto` AES ciphers and use fallbacks when necessary. * Detect EC key mode (to support OTP 17.5). * Mostly pure Erlang implementation of AES GCM and GHASH functions. * Add `JOSE.JWA` module for Elixir. * Fixes * All tests now pass on OTP 17.5 and OTP 18. * Fallback to non-native crypto implementations for OTP 17.5. ## 1.0.1 (2015-08-05) * Dependencies * Use [`base64url`](https://hex.pm/packages/base64url) package from hex.pm ## 1.0.0 (2015-08-05) * Initial Release * Algorithm Support * JSON Web Encryption (JWE) [RFC 7516](https://tools.ietf.org/html/rfc7516) * `"alg"` [RFC 7518 Section 4](https://tools.ietf.org/html/rfc7518#section-4) * `RSA1_5` * `RSA-OAEP` * `RSA-OAEP-256` * `A128KW` * `A192KW` * `A256KW` * `dir` * `ECDH-ES` * `ECDH-ES+A128KW` * `ECDH-ES+A192KW` * `ECDH-ES+A256KW` * `A128GCMKW` * `A192GCMKW` * `A256GCMKW` * `PBES2-HS256+A128KW` * `PBES2-HS384+A192KW` * `PBES2-HS512+A256KW` * `"enc"` [RFC 7518 Section 5](https://tools.ietf.org/html/rfc7518#section-5) * `A128CBC-HS256` * `A192CBC-HS384` * `A256CBC-HS512` * `A128GCM` * `A192GCM` * `A256GCM` * `"zip"` [RFC 7518 Section 7.3](https://tools.ietf.org/html/rfc7518#section-7.3) * `DEF` * JSON Web Key (JWK) [RFC 7517](https://tools.ietf.org/html/rfc7517) * `"alg"` [RFC 7518 Section 6](https://tools.ietf.org/html/rfc7518#section-6) * `EC` * `RSA` * `oct` * JSON Web Signature (JWS) [RFC 7515](https://tools.ietf.org/html/rfc7515) * `"alg"` [RFC 7518 Section 3](https://tools.ietf.org/html/rfc7518#section-3) * `HS256` * `HS384` * `HS512` * `RS256` * `RS384` * `RS512` * `ES256` * `ES384` * `ES512` * `PS256` * `PS384` * `PS512` * `none` potatosalad-erlang-jose-30ac019/ALGORITHMS.md0000664000232200023220000001721515267717064021143 0ustar debalancedebalance# Algorithms | Algorithm | Purpose | OTP 17 | OTP 18 | OTP 19 | Fallback | Definition | | ----------------- | ---------- | ------ | ------ | ------ | -------- | ---------- | | AES CBC 128-bit | Encryption | X | X | X | [`jose_jwa_aes`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_aes.erl) | [NIST.800-38A](http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf) | | AES CBC 192-bit | Encryption | | | X | [`jose_jwa_aes`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_aes.erl) | [NIST.800-38A](http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf) | | AES CBC 256-bit | Encryption | X | X | X | [`jose_jwa_aes`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_aes.erl) | [NIST.800-38A](http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf) | | AES CTR 128-bit | Encryption | X | X | X | | [NIST.800-38A](http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf) | | AES CTR 192-bit | Encryption | X | X | X | | [NIST.800-38A](http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf) | | AES CTR 256-bit | Encryption | X | X | X | | [NIST.800-38A](http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf) | | AES ECB 128-bit | Encryption | | X | X | [`jose_jwa_aes`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_aes.erl) | [NIST.800-38A](http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf) | | AES ECB 192-bit | Encryption | | | X | [`jose_jwa_aes`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_aes.erl) | [NIST.800-38A](http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf) | | AES ECB 256-bit | Encryption | | X | X | [`jose_jwa_aes`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_aes.erl) | [NIST.800-38A](http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf) | | AES GCM 128-bit | Encryption | | X | X | [`jose_jwa_aes`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_aes.erl) | [NIST.800-38D](http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf) | | AES GCM 192-bit | Encryption | | X | X | [`jose_jwa_aes`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_aes.erl) | [NIST.800-38D](http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf) | | AES GCM 256-bit | Encryption | | X | X | [`jose_jwa_aes`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_aes.erl) | [NIST.800-38D](http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf) | | ChaCha20/Poly1305 | Encryption | | | | [`jose_jwa_chacha20_poly1305`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_chacha20_poly1305.erl) | [RFC 7539](https://tools.ietf.org/html/rfc7539) | | RSAES-OAEP | Encryption | X | X | X | [`jose_jwa_pkcs1`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_pkcs1.erl) | [RFC 3447](https://tools.ietf.org/html/rfc3447) | | RSAES-OAEP-256 | Encryption | | | | [`jose_jwa_pkcs1`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_pkcs1.erl) | [RFC 3447](https://tools.ietf.org/html/rfc3447) | | RSAES-PKCS1-v1_5 | Encryption | X | X | X | [`jose_jwa_pkcs1`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_pkcs1.erl) | [RFC 3447](https://tools.ietf.org/html/rfc3447) | | RSASSA-PKCS1-v1_5 | Signature | X | X | X | [`jose_jwa_pkcs1`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_pkcs1.erl) | [RFC 3447](https://tools.ietf.org/html/rfc3447) | | RSASSA-PSS | Signature | | | | [`jose_jwa_pkcs1`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_pkcs1.erl) | [RFC 3447](https://tools.ietf.org/html/rfc3447) | There are also several "helper" algorithms used with the above that have no native implementations currently in OTP: | Algorithm | Purpose | Fallback | Definition | | ----------------- | --------------- | -------- | ---------- | | AES Key Wrap | Key Wrap | [`jose_jwa_aes_kw`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_aes_kw.erl) | [RFC 3394](https://tools.ietf.org/html/rfc3394) | | Concat KDF | Key Derivation | [`jose_jwa_concat_kdf`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_concat_kdf.erl) | [NIST.800-56A](https://dx.doi.org/10.6028/NIST.SP.800-56Ar2) | | MGF1 | Mask Generation | [`jose_jwa_pkcs1`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_pkcs1.erl) | [RFC 3447](https://tools.ietf.org/html/rfc3447) | | PBKDF1 | Key Derivation | [`jose_jwa_pkcs5`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_pkcs5.erl) | [RFC 2898](https://tools.ietf.org/html/rfc2898) | | PBKDF2 | Key Derivation | [`jose_jwa_pkcs5`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_pkcs5.erl) | [RFC 2898](https://tools.ietf.org/html/rfc2898) | | PKCS #7 Padding | Padding | [`jose_jwa_pkcs7`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_pkcs7.erl) | [RFC 2315](https://tools.ietf.org/html/rfc2315) | The following are algorithms related to the draft [CFRG ECDH and signatures in JOSE](https://tools.ietf.org/html/draft-ietf-jose-cfrg-curves): | Algorithm | Purpose | External | Fallback | Definition | | --------- | ------------ | -------- | -------- | ---------- | | Ed25519 | Signature | [`libdecaf`](https://github.com/potatosalad/erlang-libdecaf), [`libsodium`](https://github.com/potatosalad/erlang-libsodium) | [`jose_jwa_curve25519`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_curve25519.erl) | [EdDSA](https://tools.ietf.org/html/draft-irtf-cfrg-eddsa#section-5.1) | | Ed25519ph | Signature | [`libdecaf`](https://github.com/potatosalad/erlang-libdecaf), [`libsodium`](https://github.com/potatosalad/erlang-libsodium) | [`jose_jwa_curve25519`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_curve25519.erl) | [EdDSA](https://tools.ietf.org/html/draft-irtf-cfrg-eddsa#section-5.1) | | Ed448 | Signature | [`libdecaf`](https://github.com/potatosalad/erlang-libdecaf) | [`jose_jwa_curve448`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_curve448.erl) | [EdDSA](https://tools.ietf.org/html/draft-irtf-cfrg-eddsa#section-5.2) | | Ed448ph | Signature | [`libdecaf`](https://github.com/potatosalad/erlang-libdecaf) | [`jose_jwa_curve448`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_curve448.erl) | [EdDSA](https://tools.ietf.org/html/draft-irtf-cfrg-eddsa#section-5.2) | | SHAKE256 | Hash | [`keccakf1600`](https://github.com/potatosalad/erlang-keccakf1600), [`libdecaf`](https://github.com/potatosalad/erlang-libdecaf) | [`jose_jwa_sha3`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_sha3.erl) | [FIPS 202](http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | | X25519 | Key Exchange | [`libsodium`](https://github.com/potatosalad/erlang-libsodium) | [`jose_jwa_curve25519`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_curve25519.erl) | [RFC 7748](https://tools.ietf.org/html/rfc7748#section-5) | | X448 | Key Exchange | [`libdecaf`](https://github.com/potatosalad/erlang-libdecaf) | [`jose_jwa_curve448`](https://github.com/potatosalad/erlang-jose/blob/main/src/jose_jwa_curve448.erl) | [RFC 7748](https://tools.ietf.org/html/rfc7748#section-5) | potatosalad-erlang-jose-30ac019/test/0000775000232200023220000000000015267717064020121 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/test/jose_public_key.hrl0000777000232200023220000000000015267717064031504 2../include/jose_public_key.hrlustar debalancedebalancepotatosalad-erlang-jose-30ac019/test/jose_jwa_chacha20_poly1305_SUITE.erl0000664000232200023220000005265215267717064026416 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% @end %%% Created : 08 Aug 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwa_chacha20_poly1305_SUITE). -include_lib("common_test/include/ct.hrl"). -include("jose.hrl"). %% ct. -export([all/0]). -export([groups/0]). -export([init_per_suite/1]). -export([end_per_suite/1]). -export([init_per_group/2]). -export([end_per_group/2]). %% Tests. -export([aead/0]). -export([aead/1]). -export([block/0]). -export([block/1]). -export([encrypt/0]). -export([encrypt/1]). -export([key/0]). -export([key/1]). -export([mac/0]). -export([mac/1]). all() -> [ {group, chacha20}, {group, chacha20_poly1305}, {group, poly1305} ]. groups() -> [ {chacha20, [parallel], [ block, encrypt ]}, {chacha20_poly1305, [parallel], [ aead ]}, {poly1305, [parallel], [ key, mac ]} ]. init_per_suite(Config) -> application:set_env(jose, crypto_fallback, true), application:set_env(jose, unsecured_signing, true), _ = application:ensure_all_started(jose), Config. end_per_suite(_Config) -> _ = application:stop(jose), ok. init_per_group(Group, Config) -> jose_ct:start(Group, group_config(Group, Config)). end_per_group(_Group, Config) -> jose_ct:stop(Config), ok. %%==================================================================== %% Tests %%==================================================================== aead() -> [{doc, "Test ChaCha20/Poly1305 AEAD function"}]. aead(Config) when is_list(Config) -> AEADs = lazy_eval(proplists:get_value(aead, Config)), lists:foreach(fun aead_cipher/1, AEADs). block() -> [{doc, "Test ChaCha20 Block function"}]. block(Config) when is_list(Config) -> Blocks = lazy_eval(proplists:get_value(block, Config)), lists:foreach(fun chacha20_block/1, Blocks). encrypt() -> [{doc, "Test ChaCha20 Encryption function"}]. encrypt(Config) when is_list(Config) -> Encrypts = lazy_eval(proplists:get_value(encrypt, Config)), lists:foreach(fun chacha20_encrypt/1, Encrypts). key() -> [{doc, "Test Poly1305 Key Generation function"}]. key(Config) when is_list(Config) -> Keys = lazy_eval(proplists:get_value(key, Config)), lists:foreach(fun poly1305_key/1, Keys). mac() -> [{doc, "Test Poly1305 MAC function"}]. mac(Config) when is_list(Config) -> MACs = lazy_eval(proplists:get_value(mac, Config)), lists:foreach(fun poly1305_mac/1, MACs). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private aead_cipher({Key, PlainText, IV, AAD, CipherText, CipherTag}) -> case jose_jwa_chacha20_poly1305:encrypt(PlainText, AAD, IV, Key) of {CipherText, CipherTag} -> ok; Other0 -> ct:fail({{jose_jwa_chacha20_poly1305, encrypt, [PlainText, AAD, IV, Key]}, {expected, {CipherText, CipherTag}}, {got, Other0}}) end, case jose_jwa_chacha20_poly1305:decrypt(CipherText, CipherTag, AAD, IV, Key) of PlainText -> ok; Other1 -> ct:fail({{jose_jwa_chacha20_poly1305, decrypt, [CipherText, CipherTag, AAD, IV, Key]}, {expected, PlainText}, {got, Other1}}) end. %% @private chacha20_block({Key, Nonce, Counter, State}) -> case jose_jwa_chacha20:block(Key, Counter, Nonce) of State -> ok; Other0 -> ct:fail({{jose_jwa_chacha20, block, [Key, Counter, Nonce]}, {expected, State}, {got, Other0}}) end. %% @private chacha20_encrypt({Key, Nonce, Counter, PlainText, CipherText}) -> case jose_jwa_chacha20:encrypt(Key, Counter, Nonce, PlainText) of CipherText -> ok; Other0 -> ct:fail({{jose_jwa_chacha20, encrypt, [Key, Counter, Nonce, PlainText]}, {expected, CipherText}, {got, Other0}}) end, case jose_jwa_chacha20:encrypt(Key, Counter, Nonce, CipherText) of PlainText -> ok; Other1 -> ct:fail({{jose_jwa_chacha20, encrypt, [Key, Counter, Nonce, CipherText]}, {expected, PlainText}, {got, Other1}}) end. %% @private poly1305_key({Key, Nonce, OneTimeKey}) -> case jose_jwa_chacha20_poly1305:poly1305_key_gen(Key, Nonce) of OneTimeKey -> ok; Other0 -> ct:fail({{jose_jwa_chacha20_poly1305, poly1305_key_gen, [Key, Nonce]}, {expected, OneTimeKey}, {got, Other0}}) end. %% @private poly1305_mac({OneTimeKey, Text, Tag}) -> case jose_jwa_poly1305:mac(Text, OneTimeKey) of Tag -> ok; Other0 -> ct:fail({{jose_jwa_poly1305, mac, [Text, OneTimeKey]}, {expected, Tag}, {got, Other0}}) end. %% @private group_config(chacha20, Config) -> Block = chacha20_block(), Encrypt = chacha20_encrypt(), [{block, Block}, {encrypt, Encrypt} | Config]; group_config(chacha20_poly1305, Config) -> AEAD = chacha20_poly1305(), [{aead, AEAD} | Config]; group_config(poly1305, Config) -> Key = poly1305_key(), MAC = poly1305_mac(), [{key, Key}, {mac, MAC} | Config]. %% @private hexstr2bin(S) -> list_to_binary(hexstr2list(S)). %% @private hexstr2list([X,Y|T]) -> [mkint(X)*16 + mkint(Y) | hexstr2list(T)]; hexstr2list([]) -> []. %% @private hexts(TS) -> [begin list_to_tuple([begin case V of _ when is_list(V) -> hexstr2bin(V); _ -> V end end || V <- tuple_to_list(T)]) end || T <- TS]. %% Building huge terms (like long_msg/0) in init_per_group seems to cause %% test_server crash with 'no_answer_from_tc_supervisor' sometimes on some %% machines. Therefore lazy evaluation when test case has started. lazy_eval(F) when is_function(F) -> F(); lazy_eval(Lst) when is_list(Lst) -> lists:map(fun lazy_eval/1, Lst); lazy_eval(Tpl) when is_tuple(Tpl) -> list_to_tuple(lists:map(fun lazy_eval/1, tuple_to_list(Tpl))); lazy_eval(Term) -> Term. %% @private mkint(C) when $0 =< C, C =< $9 -> C - $0; mkint(C) when $A =< C, C =< $F -> C - $A + 10; mkint(C) when $a =< C, C =< $f -> C - $a + 10. %% ChaCha20 Block test vectors from: %% https://tools.ietf.org/html/rfc7539#appendix-A.1 chacha20_block() -> hexts([ % Test Vector #1 { "00000000000000000000000000000000" %% Key "00000000000000000000000000000000", "000000000000000000000000", %% Nonce 0, %% Block Counter "76b8e0ada0f13d90405d6ae55386bd28" %% State "bdd219b8a08ded1aa836efcc8b770dc7" "da41597c5157488d7724e03fb8d84a37" "6a43b8f41518a11cc387b669b2ee6586" }, % Test Vector #2 { "00000000000000000000000000000000" %% Key "00000000000000000000000000000000", "000000000000000000000000", %% Nonce 1, %% Block Counter "9f07e7be5551387a98ba977c732d080d" %% State "cb0f29a048e3656912c6533e32ee7aed" "29b721769ce64e43d57133b074d839d5" "31ed1f28510afb45ace10a1f4b794d6f" }, % Test Vector #3 { "00000000000000000000000000000000" %% Key "00000000000000000000000000000001", "000000000000000000000000", %% Nonce 1, %% Block Counter "3aeb5224ecf849929b9d828db1ced4dd" %% State "832025e8018b8160b82284f3c949aa5a" "8eca00bbb4a73bdad192b5c42f73f2fd" "4e273644c8b36125a64addeb006c13a0" }, % Test Vector #4 { "00ff0000000000000000000000000000" %% Key "00000000000000000000000000000000", "000000000000000000000000", %% Nonce 2, %% Block Counter "72d54dfbf12ec44b362692df94137f32" %% State "8fea8da73990265ec1bbbea1ae9af0ca" "13b25aa26cb4a648cb9b9d1be65b2c09" "24a66c54d545ec1b7374f4872e99f096" }, % Test Vector #5 { "00000000000000000000000000000000" %% Key "00000000000000000000000000000000", "000000000000000000000002", %% Nonce 0, %% Block Counter "c2c64d378cd536374ae204b9ef933fcd" %% State "1a8b2288b3dfa49672ab765b54ee27c7" "8a970e0e955c14f3a88e741b97c286f7" "5f8fc299e8148362fa198a39531bed6d" } ]). %% ChaCha20 Encryption test vectors from: %% https://tools.ietf.org/html/rfc7539#appendix-A.2 chacha20_encrypt() -> hexts([ % Test Vector #1 { "00000000000000000000000000000000" %% Key "00000000000000000000000000000000", "000000000000000000000000", %% Nonce 0, %% Block Counter "00000000000000000000000000000000" %% PlainText "00000000000000000000000000000000" "00000000000000000000000000000000" "00000000000000000000000000000000", "76b8e0ada0f13d90405d6ae55386bd28" %% CipherText "bdd219b8a08ded1aa836efcc8b770dc7" "da41597c5157488d7724e03fb8d84a37" "6a43b8f41518a11cc387b669b2ee6586" }, % Test Vector #2 { "00000000000000000000000000000000" %% Key "00000000000000000000000000000001", "000000000000000000000002", %% Nonce 1, %% Block Counter "416e79207375626d697373696f6e2074" %% PlainText "6f20746865204945544620696e74656e" "6465642062792074686520436f6e7472" "696275746f7220666f72207075626c69" "636174696f6e20617320616c6c206f72" "2070617274206f6620616e2049455446" "20496e7465726e65742d447261667420" "6f722052464320616e6420616e792073" "746174656d656e74206d616465207769" "7468696e2074686520636f6e74657874" "206f6620616e20494554462061637469" "7669747920697320636f6e7369646572" "656420616e20224945544620436f6e74" "7269627574696f6e222e205375636820" "73746174656d656e747320696e636c75" "6465206f72616c2073746174656d656e" "747320696e2049455446207365737369" "6f6e732c2061732077656c6c20617320" "7772697474656e20616e6420656c6563" "74726f6e696320636f6d6d756e696361" "74696f6e73206d61646520617420616e" "792074696d65206f7220706c6163652c" "20776869636820617265206164647265" "7373656420746f", "a3fbf07df3fa2fde4f376ca23e827370" %% CipherText "41605d9f4f4f57bd8cff2c1d4b7955ec" "2a97948bd3722915c8f3d337f7d37005" "0e9e96d647b7c39f56e031ca5eb6250d" "4042e02785ececfa4b4bb5e8ead0440e" "20b6e8db09d881a7c6132f420e527950" "42bdfa7773d8a9051447b3291ce1411c" "680465552aa6c405b7764d5e87bea85a" "d00f8449ed8f72d0d662ab052691ca66" "424bc86d2df80ea41f43abf937d3259d" "c4b2d0dfb48a6c9139ddd7f76966e928" "e635553ba76c5c879d7b35d49eb2e62b" "0871cdac638939e25e8a1e0ef9d5280f" "a8ca328b351c3c765989cbcf3daa8b6c" "cc3aaf9f3979c92b3720fc88dc95ed84" "a1be059c6499b9fda236e7e818b04b0b" "c39c1e876b193bfe5569753f88128cc0" "8aaa9b63d1a16f80ef2554d7189c411f" "5869ca52c5b83fa36ff216b9c1d30062" "bebcfd2dc5bce0911934fda79a86f6e6" "98ced759c3ff9b6477338f3da4f9cd85" "14ea9982ccafb341b2384dd902f3d1ab" "7ac61dd29c6f21ba5b862f3730e37cfd" "c4fd806c22f221" }, % Test Vector #3 { "1c9240a5eb55d38af333888604f6b5f0" %% Key "473917c1402b80099dca5cbc207075c0", "000000000000000000000002", %% Nonce 42, %% Block Counter "2754776173206272696c6c69672c2061" %% PlainText "6e642074686520736c6974687920746f" "7665730a446964206779726520616e64" "2067696d626c6520696e207468652077" "6162653a0a416c6c206d696d73792077" "6572652074686520626f726f676f7665" "732c0a416e6420746865206d6f6d6520" "7261746873206f757467726162652e", "62e6347f95ed87a45ffae7426f27a1df" %% CipherText "5fb69110044c0d73118effa95b01e5cf" "166d3df2d721caf9b21e5fb14c616871" "fd84c54f9d65b283196c7fe4f60553eb" "f39c6402c42234e32a356b3e764312a6" "1a5532055716ead6962568f87d3f3f77" "04c6a8d1bcd1bf4d50d6154b6da731b1" "87b58dfd728afa36757a797ac188d1" } ]). %% ChaCha20/Poly1305 AEAD test vectors from: %% https://tools.ietf.org/html/rfc7539#section-2.8.2 %% https://tools.ietf.org/html/rfc7539#appendix-A.5 chacha20_poly1305() -> hexts([ { "808182838485868788898a8b8c8d8e8f" %% Key "909192939495969798999a9b9c9d9e9f", "4c616469657320616e642047656e746c" %% PlainText "656d656e206f662074686520636c6173" "73206f66202739393a20496620492063" "6f756c64206f6666657220796f75206f" "6e6c79206f6e652074697020666f7220" "746865206675747572652c2073756e73" "637265656e20776f756c642062652069" "742e", "070000004041424344454647", %% IV "50515253c0c1c2c3c4c5c6c7", %% AAD "d31a8d34648e60db7b86afbc53ef7ec2" %% CipherText "a4aded51296e08fea9e2b5a736ee62d6" "3dbea45e8ca9671282fafb69da92728b" "1a71de0a9e060b2905d6a5b67ecd3b36" "92ddbd7f2d778b8c9803aee328091b58" "fab324e4fad675945585808b4831d7bc" "3ff4def08e4b7a9de576d26586cec64b" "6116", "1ae10b594f09e26a7e902ecbd0600691" %% CipherTag }, { "1c9240a5eb55d38af333888604f6b5f0" %% Key "473917c1402b80099dca5cbc207075c0", "496e7465726e65742d44726166747320" %% PlainText "61726520647261667420646f63756d65" "6e74732076616c696420666f72206120" "6d6178696d756d206f6620736978206d" "6f6e74687320616e64206d6179206265" "20757064617465642c207265706c6163" "65642c206f72206f62736f6c65746564" "206279206f7468657220646f63756d65" "6e747320617420616e792074696d652e" "20497420697320696e617070726f7072" "6961746520746f2075736520496e7465" "726e65742d4472616674732061732072" "65666572656e6365206d617465726961" "6c206f7220746f206369746520746865" "6d206f74686572207468616e20617320" "2fe2809c776f726b20696e2070726f67" "726573732e2fe2809d", "000000000102030405060708", %% IV "f33388860000000000004e91", %% AAD "64a0861575861af460f062c79be643bd" %% CipherText "5e805cfd345cf389f108670ac76c8cb2" "4c6cfc18755d43eea09ee94e382d26b0" "bdb7b73c321b0100d4f03b7f355894cf" "332f830e710b97ce98c8a84abd0b9481" "14ad176e008d33bd60f982b1ff37c855" "9797a06ef4f0ef61c186324e2b350638" "3606907b6a7c02b0f9f6157b53c867e4" "b9166c767b804d46a59b5216cde7a4e9" "9040c5a40433225ee282a1b0a06c523e" "af4534d7f83fa1155b0047718cbc546a" "0d072b04b3564eea1b422273f548271a" "0bb2316053fa76991955ebd63159434e" "cebb4e466dae5a1073a6727627097a10" "49e617d91d361094fa68f0ff77987130" "305beaba2eda04df997b714d6c6f2c29" "a6ad5cb4022b02709b", "eead9d67890cbb22392336fea1851f38" %% CipherTag } ]). %% Poly1305 Key Generation test vectors from: %% https://tools.ietf.org/html/rfc7539#appendix-A.4 poly1305_key() -> hexts([ % Test Vector #1 { "00000000000000000000000000000000" %% Key "00000000000000000000000000000000", "000000000000000000000000", %% Nonce "76b8e0ada0f13d90405d6ae55386bd28" %% One-Time Key "bdd219b8a08ded1aa836efcc8b770dc7" }, % Test Vector #2 { "00000000000000000000000000000000" %% Key "00000000000000000000000000000001", "000000000000000000000002", %% Nonce "ecfa254f845f647473d3cb140da9e876" %% One-Time Key "06cb33066c447b87bc2666dde3fbb739" }, % Test Vector #3 { "1c9240a5eb55d38af333888604f6b5f0" %% Key "473917c1402b80099dca5cbc207075c0", "000000000000000000000002", %% Nonce "965e3bc6f9ec7ed9560808f4d229f94b" %% One-Time Key "137ff275ca9b3fcbdd59deaad23310ae" } ]). %% Poly1305 MAC test vectors from: %% https://tools.ietf.org/html/rfc7539#appendix-A.3 poly1305_mac() -> hexts([ % Test Vector #1 { "00000000000000000000000000000000" %% One-Time Key "00000000000000000000000000000000", "00000000000000000000000000000000" %% Text "00000000000000000000000000000000" "00000000000000000000000000000000" "00000000000000000000000000000000", "00000000000000000000000000000000" %% Tag }, % Test Vector #2 { "00000000000000000000000000000000" %% One-Time Key "36e5f6b5c5e06070f0efca96227a863e", "416e79207375626d697373696f6e2074" %% Text "6f20746865204945544620696e74656e" "6465642062792074686520436f6e7472" "696275746f7220666f72207075626c69" "636174696f6e20617320616c6c206f72" "2070617274206f6620616e2049455446" "20496e7465726e65742d447261667420" "6f722052464320616e6420616e792073" "746174656d656e74206d616465207769" "7468696e2074686520636f6e74657874" "206f6620616e20494554462061637469" "7669747920697320636f6e7369646572" "656420616e20224945544620436f6e74" "7269627574696f6e222e205375636820" "73746174656d656e747320696e636c75" "6465206f72616c2073746174656d656e" "747320696e2049455446207365737369" "6f6e732c2061732077656c6c20617320" "7772697474656e20616e6420656c6563" "74726f6e696320636f6d6d756e696361" "74696f6e73206d61646520617420616e" "792074696d65206f7220706c6163652c" "20776869636820617265206164647265" "7373656420746f", "36e5f6b5c5e06070f0efca96227a863e" %% Tag }, % Test Vector #3 { "36e5f6b5c5e06070f0efca96227a863e" %% One-Time Key "00000000000000000000000000000000", "416e79207375626d697373696f6e2074" %% Text "6f20746865204945544620696e74656e" "6465642062792074686520436f6e7472" "696275746f7220666f72207075626c69" "636174696f6e20617320616c6c206f72" "2070617274206f6620616e2049455446" "20496e7465726e65742d447261667420" "6f722052464320616e6420616e792073" "746174656d656e74206d616465207769" "7468696e2074686520636f6e74657874" "206f6620616e20494554462061637469" "7669747920697320636f6e7369646572" "656420616e20224945544620436f6e74" "7269627574696f6e222e205375636820" "73746174656d656e747320696e636c75" "6465206f72616c2073746174656d656e" "747320696e2049455446207365737369" "6f6e732c2061732077656c6c20617320" "7772697474656e20616e6420656c6563" "74726f6e696320636f6d6d756e696361" "74696f6e73206d61646520617420616e" "792074696d65206f7220706c6163652c" "20776869636820617265206164647265" "7373656420746f", "f3477e7cd95417af89a6b8794c310cf0" %% Tag }, % Test Vector #4 { "1c9240a5eb55d38af333888604f6b5f0" %% One-Time Key "473917c1402b80099dca5cbc207075c0", "2754776173206272696c6c69672c2061" %% Text "6e642074686520736c6974687920746f" "7665730a446964206779726520616e64" "2067696d626c6520696e207468652077" "6162653a0a416c6c206d696d73792077" "6572652074686520626f726f676f7665" "732c0a416e6420746865206d6f6d6520" "7261746873206f757467726162652e", "4541669a7eaaee61e708dc7cbcc5eb62" %% Tag }, % Test Vector #5 { "02000000000000000000000000000000" %% One-Time Key "00000000000000000000000000000000", "ffffffffffffffffffffffffffffffff", %% Text "03000000000000000000000000000000" %% Tag }, % Test Vector #6 { "02000000000000000000000000000000" %% One-Time Key "ffffffffffffffffffffffffffffffff", "02000000000000000000000000000000", %% Text "03000000000000000000000000000000" %% Tag }, % Test Vector #7 { "01000000000000000000000000000000" %% One-Time Key "00000000000000000000000000000000", "ffffffffffffffffffffffffffffffff" %% Text "f0ffffffffffffffffffffffffffffff" "11000000000000000000000000000000", "05000000000000000000000000000000" %% Tag }, % Test Vector #8 { "01000000000000000000000000000000" %% One-Time Key "00000000000000000000000000000000", "ffffffffffffffffffffffffffffffff" %% Text "fbfefefefefefefefefefefefefefefe" "01010101010101010101010101010101", "00000000000000000000000000000000" %% Tag }, % Test Vector #9 { "02000000000000000000000000000000" %% One-Time Key "00000000000000000000000000000000", "fdffffffffffffffffffffffffffffff", %% Text "faffffffffffffffffffffffffffffff" %% Tag }, % Test Vector #10 { "01000000000000000400000000000000" %% One-Time Key "00000000000000000000000000000000", "e33594d7505e43b90000000000000000" %% Text "3394d7505e4379cd0100000000000000" "00000000000000000000000000000000" "01000000000000000000000000000000", "14000000000000005500000000000000" %% Tag }, % Test Vector #11 { "01000000000000000400000000000000" %% One-Time Key "00000000000000000000000000000000", "e33594d7505e43b90000000000000000" %% Text "3394d7505e4379cd0100000000000000" "00000000000000000000000000000000", "13000000000000000000000000000000" %% Tag } ]). potatosalad-erlang-jose-30ac019/test/jose_jwk_SUITE.erl0000664000232200023220000003363315267717064023421 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwk_SUITE). -include_lib("common_test/include/ct.hrl"). -include("jose.hrl"). -include("jose_public_key.hrl"). %% ct. -export([all/0]). -export([groups/0]). -export([init_per_suite/1]). -export([end_per_suite/1]). -export([init_per_group/2]). -export([end_per_group/2]). %% Tests. -export([encrypt_and_decrypt/1]). -export([password_export_uses_strong_pbes2_parameters/1]). -export([shared_secret_mismatched_kty/1]). -export([kty_ec_from_der_and_to_der/1]). -export([kty_ec_from_map_and_to_map/1]). -export([kty_ec_from_pem_and_to_pem/1]). -export([kty_ec_box_encrypt_and_box_decrypt/1]). -export([kty_ec_sign_and_verify/1]). -export([kty_oct_from_map_and_to_map/1]). -export([kty_oct_block_encrypt_and_block_decrypt/1]). -export([kty_oct_sign_and_verify/1]). -export([kty_okp_ed25519_from_der_and_to_der/1]). -export([kty_okp_ed25519_from_map_and_to_map/1]). -export([kty_okp_ed25519_from_pem_and_to_pem/1]). -export([kty_okp_ed25519_sign_and_verify/1]). -export([kty_okp_ed25519ph_from_map_and_to_map/1]). -export([kty_okp_ed25519ph_sign_and_verify/1]). -export([kty_okp_ed448_from_der_and_to_der/1]). -export([kty_okp_ed448_from_map_and_to_map/1]). -export([kty_okp_ed448_from_pem_and_to_pem/1]). -export([kty_okp_ed448_sign_and_verify/1]). -export([kty_okp_ed448ph_from_map_and_to_map/1]). -export([kty_okp_ed448ph_sign_and_verify/1]). -export([kty_okp_x25519_from_der_and_to_der/1]). -export([kty_okp_x25519_from_map_and_to_map/1]). -export([kty_okp_x25519_from_pem_and_to_pem/1]). -export([kty_okp_x25519_box_encrypt_and_box_decrypt/1]). -export([kty_okp_x448_from_der_and_to_der/1]). -export([kty_okp_x448_from_map_and_to_map/1]). -export([kty_okp_x448_from_pem_and_to_pem/1]). -export([kty_okp_x448_box_encrypt_and_box_decrypt/1]). -export([kty_rsa_convert_sfm_to_crt/1]). -export([kty_rsa_from_der_and_to_der/1]). -export([kty_rsa_from_map_and_to_map/1]). -export([kty_rsa_from_pem_and_to_pem/1]). -export([kty_rsa_block_encrypt_and_block_decrypt/1]). -export([kty_rsa_sign_and_verify/1]). -export([set_from_map_and_to_map/1]). all() -> [ {group, jose_jwk}, {group, jose_jwk_kty_ec}, {group, jose_jwk_kty_oct}, {group, jose_jwk_kty_okp_ed25519}, {group, jose_jwk_kty_okp_ed25519ph}, {group, jose_jwk_kty_okp_ed448}, {group, jose_jwk_kty_okp_ed448ph}, {group, jose_jwk_kty_okp_x25519}, {group, jose_jwk_kty_okp_x448}, {group, jose_jwk_kty_rsa}, {group, jose_jwk_set} ]. groups() -> [ {jose_jwk, [parallel], [ encrypt_and_decrypt, password_export_uses_strong_pbes2_parameters, shared_secret_mismatched_kty ]}, {jose_jwk_kty_ec, [parallel], [ kty_ec_from_der_and_to_der, kty_ec_from_map_and_to_map, kty_ec_from_pem_and_to_pem, kty_ec_box_encrypt_and_box_decrypt, kty_ec_sign_and_verify ]}, {jose_jwk_kty_oct, [parallel], [ kty_oct_from_map_and_to_map, kty_oct_block_encrypt_and_block_decrypt, kty_oct_sign_and_verify ]}, {jose_jwk_kty_okp_ed25519, [parallel], [ kty_okp_ed25519_from_der_and_to_der, kty_okp_ed25519_from_map_and_to_map, kty_okp_ed25519_from_pem_and_to_pem, kty_okp_ed25519_sign_and_verify ]}, {jose_jwk_kty_okp_ed25519ph, [parallel], [ kty_okp_ed25519ph_from_map_and_to_map, kty_okp_ed25519ph_sign_and_verify ]}, {jose_jwk_kty_okp_ed448, [parallel], [ kty_okp_ed448_from_der_and_to_der, kty_okp_ed448_from_map_and_to_map, kty_okp_ed448_from_pem_and_to_pem, kty_okp_ed448_sign_and_verify ]}, {jose_jwk_kty_okp_ed448ph, [parallel], [ kty_okp_ed448ph_from_map_and_to_map, kty_okp_ed448ph_sign_and_verify ]}, {jose_jwk_kty_okp_x25519, [parallel], [ kty_okp_x25519_from_der_and_to_der, kty_okp_x25519_from_map_and_to_map, kty_okp_x25519_from_pem_and_to_pem, kty_okp_x25519_box_encrypt_and_box_decrypt ]}, {jose_jwk_kty_okp_x448, [parallel], [ kty_okp_x448_from_der_and_to_der, kty_okp_x448_from_map_and_to_map, kty_okp_x448_from_pem_and_to_pem, kty_okp_x448_box_encrypt_and_box_decrypt ]}, {jose_jwk_kty_rsa, [parallel], [ kty_rsa_convert_sfm_to_crt, kty_rsa_from_der_and_to_der, kty_rsa_from_map_and_to_map, kty_rsa_from_pem_and_to_pem, kty_rsa_block_encrypt_and_block_decrypt, kty_rsa_sign_and_verify ]}, {jose_jwk_set, [parallel], [ set_from_map_and_to_map ]} ]. init_per_suite(Config) -> application:set_env(jose, crypto_fallback, true), application:set_env(jose, unsecured_signing, true), _ = application:ensure_all_started(jose), ct_property_test:init_per_suite(Config). end_per_suite(_Config) -> _ = application:stop(jose), ok. init_per_group(Group, Config) -> jose_ct:start(Group, Config). end_per_group(_Group, Config) -> jose_ct:stop(Config), ok. %%==================================================================== %% Tests %%==================================================================== encrypt_and_decrypt(Config) -> ct_property_test:quickcheck( jose_jwk_props:prop_encrypt_and_decrypt(), Config). %% Regression test for GHSA-fpj2-9h59-32pp: two already-decoded JWKs whose %% "kty" modules differ used to fall through to shared_secret(from(Your), %% from(My)), and because from/1 returns a #jose_jwk{} unchanged, that %% re-entered shared_secret/2 with identical arguments forever. Each call is %% bounded below so that a regression fails the suite instead of hanging it. shared_secret_mismatched_kty(_Config) -> MySecretKey = jose_jwk:generate_key({ec, <<"P-256">>}), YourECPublicKey = jose_jwk:to_public(jose_jwk:generate_key({ec, <<"P-256">>})), YourRSAPublicKey = jose_jwk:to_public(jose_jwk:generate_key({rsa, 2048})), YourOKPPublicKey = jose_jwk:to_public(jose_jwk:generate_key({okp, 'X25519'})), {_, YourRSAPublicMap} = jose_jwk:to_map(YourRSAPublicKey), %% matching kty modules still derive a shared secret {ok, SharedSecret} = shared_secret(YourECPublicKey, MySecretKey), true = is_binary(SharedSecret), %% mismatched kty modules fail closed instead of looping {error, {badarg, [jose_jwk_kty_rsa, jose_jwk_kty_ec]}} = shared_secret(YourRSAPublicKey, MySecretKey), {error, {badarg, [jose_jwk_kty_okp_x25519, jose_jwk_kty_ec]}} = shared_secret(YourOKPPublicKey, MySecretKey), %% ... including when the peer key arrives undecoded, the way it does from %% a JWK document or a JWE "epk" header {error, {badarg, [jose_jwk_kty_rsa, jose_jwk_kty_ec]}} = shared_secret(YourRSAPublicMap, MySecretKey), %% ... and when a key carries no "kty" at all, such as a JWK Set {error, {badarg, [undefined, jose_jwk_kty_ec]}} = shared_secret(jose_jwk:from_map(#{ <<"keys">> => [] }), MySecretKey), ok. kty_ec_from_der_and_to_der(Config) -> ct_property_test:quickcheck( jose_jwk_kty_ec_props:prop_from_der_and_to_der(), Config). kty_ec_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jwk_kty_ec_props:prop_from_map_and_to_map(), Config). kty_ec_from_pem_and_to_pem(Config) -> ct_property_test:quickcheck( jose_jwk_kty_ec_props:prop_from_pem_and_to_pem(), Config). kty_ec_box_encrypt_and_box_decrypt(Config) -> ct_property_test:quickcheck( jose_jwk_kty_ec_props:prop_box_encrypt_and_box_decrypt(), Config). kty_ec_sign_and_verify(Config) -> ct_property_test:quickcheck( jose_jwk_kty_ec_props:prop_sign_and_verify(), Config). kty_oct_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jwk_kty_oct_props:prop_from_map_and_to_map(), Config). kty_oct_block_encrypt_and_block_decrypt(Config) -> ct_property_test:quickcheck( jose_jwk_kty_oct_props:prop_block_encrypt_and_block_decrypt(), Config). kty_oct_sign_and_verify(Config) -> ct_property_test:quickcheck( jose_jwk_kty_oct_props:prop_sign_and_verify(), Config). kty_okp_ed25519_from_der_and_to_der(Config) -> ct_property_test:quickcheck( jose_jwk_kty_okp_ed25519_props:prop_from_der_and_to_der(), Config). kty_okp_ed25519_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jwk_kty_okp_ed25519_props:prop_from_map_and_to_map(), Config). kty_okp_ed25519_from_pem_and_to_pem(Config) -> ct_property_test:quickcheck( jose_jwk_kty_okp_ed25519_props:prop_from_pem_and_to_pem(), Config). kty_okp_ed25519_sign_and_verify(Config) -> ct_property_test:quickcheck( jose_jwk_kty_okp_ed25519_props:prop_sign_and_verify(), Config). kty_okp_ed25519ph_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jwk_kty_okp_ed25519ph_props:prop_from_map_and_to_map(), Config). kty_okp_ed25519ph_sign_and_verify(Config) -> ct_property_test:quickcheck( jose_jwk_kty_okp_ed25519ph_props:prop_sign_and_verify(), Config). kty_okp_ed448_from_der_and_to_der(Config) -> ct_property_test:quickcheck( jose_jwk_kty_okp_ed448_props:prop_from_der_and_to_der(), Config). kty_okp_ed448_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jwk_kty_okp_ed448_props:prop_from_map_and_to_map(), Config). kty_okp_ed448_from_pem_and_to_pem(Config) -> ct_property_test:quickcheck( jose_jwk_kty_okp_ed448_props:prop_from_pem_and_to_pem(), Config). kty_okp_ed448_sign_and_verify(Config) -> ct_property_test:quickcheck( jose_jwk_kty_okp_ed448_props:prop_sign_and_verify(), Config). kty_okp_ed448ph_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jwk_kty_okp_ed448ph_props:prop_from_map_and_to_map(), Config). kty_okp_ed448ph_sign_and_verify(Config) -> ct_property_test:quickcheck( jose_jwk_kty_okp_ed448ph_props:prop_sign_and_verify(), Config). kty_okp_x25519_from_der_and_to_der(Config) -> ct_property_test:quickcheck( jose_jwk_kty_okp_x25519_props:prop_from_der_and_to_der(), Config). kty_okp_x25519_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jwk_kty_okp_x25519_props:prop_from_map_and_to_map(), Config). kty_okp_x25519_from_pem_and_to_pem(Config) -> ct_property_test:quickcheck( jose_jwk_kty_okp_x25519_props:prop_from_pem_and_to_pem(), Config). kty_okp_x25519_box_encrypt_and_box_decrypt(Config) -> ct_property_test:quickcheck( jose_jwk_kty_okp_x25519_props:prop_box_encrypt_and_box_decrypt(), Config). kty_okp_x448_from_der_and_to_der(Config) -> ct_property_test:quickcheck( jose_jwk_kty_okp_x448_props:prop_from_der_and_to_der(), Config). kty_okp_x448_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jwk_kty_okp_x448_props:prop_from_map_and_to_map(), Config). kty_okp_x448_from_pem_and_to_pem(Config) -> ct_property_test:quickcheck( jose_jwk_kty_okp_x448_props:prop_from_pem_and_to_pem(), Config). kty_okp_x448_box_encrypt_and_box_decrypt(Config) -> ct_property_test:quickcheck( jose_jwk_kty_okp_x448_props:prop_box_encrypt_and_box_decrypt(), Config). kty_rsa_convert_sfm_to_crt(Config) -> ct_property_test:quickcheck( jose_jwk_kty_rsa_props:prop_convert_sfm_to_crt(), Config). kty_rsa_from_der_and_to_der(Config) -> ct_property_test:quickcheck( jose_jwk_kty_rsa_props:prop_from_der_and_to_der(), Config). kty_rsa_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jwk_kty_rsa_props:prop_from_map_and_to_map(), Config). kty_rsa_from_pem_and_to_pem(Config) -> ct_property_test:quickcheck( jose_jwk_kty_rsa_props:prop_from_pem_and_to_pem(), Config). kty_rsa_block_encrypt_and_block_decrypt(Config) -> ct_property_test:quickcheck( jose_jwk_kty_rsa_props:prop_block_encrypt_and_block_decrypt(), Config). kty_rsa_sign_and_verify(Config) -> ct_property_test:quickcheck( jose_jwk_kty_rsa_props:prop_sign_and_verify(), Config). set_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jwk_set_props:prop_from_map_and_to_map(), Config). %% GHSA-h4gx-8jq2-mfhc: exporting a password-protected PKCS#8 private key used %% to hardcode PBKDF2-HMAC-SHA256 with 2048 iterations and a 64-bit salt, on %% both the DER and the PEM export path. Assert that every blob we emit now %% meets current guidance -- OWASP recommends 600,000 iterations for %% PBKDF2-HMAC-SHA256 and NIST SP 800-132 requires at least a 128-bit salt -- %% and that the stronger parameters still round-trip. password_export_uses_strong_pbes2_parameters(_Config) -> Password = <<"correct-horse">>, JWK = jose_jwk:generate_key({namedCurve, secp256r1}), %% DER export, reached from Elixir as JOSE.JWK.to_der/2. {_, DER} = jose_jwk:to_der(Password, JWK), #'EncryptedPrivateKeyInfo'{encryptionAlgorithm = AlgorithmInfo} = jose_public_key:der_decode(DER), ok = assert_strong_pbes2(jose_public_key:decrypt_parameters(AlgorithmInfo)), JWK = jose_jwk:from_der(Password, DER), %% PEM export, reached from Elixir as JOSE.JWK.to_pem/2. {_, PEM} = jose_jwk:to_pem(Password, JWK), [{'PrivateKeyInfo', _, CipherInfo}] = jose_public_key:pem_decode(PEM), ok = assert_strong_pbes2(CipherInfo), JWK = jose_jwk:from_pem(Password, PEM), ok. %%==================================================================== %% Internal functions %%==================================================================== %% @private assert_strong_pbes2({_Cipher, #'PBES2-params'{keyDerivationFunc = KeyDerivationFunc}}) -> #'PBES2-params_keyDerivationFunc'{ algorithm = ?'id-PBKDF2', parameters = #'PBKDF2-params'{ salt = {specified, Salt}, iterationCount = IterationCount, prf = #'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA256'} } } = KeyDerivationFunc, %% NIST SP 800-132: the salt must be at least 128 bits. true = (bit_size(Salt) >= 128), %% OWASP Password Storage Cheat Sheet: 600,000 iterations for %% PBKDF2-HMAC-SHA256. true = (IterationCount >= 600000), ok. %% @private %% Calls jose_jwk:shared_secret/2 under a hard timeout so that an unbounded %% loop shows up as a test failure rather than a hung test run. shared_secret(YourJWK, MyJWK) -> {Pid, Ref} = erlang:spawn_monitor(fun () -> Result = try jose_jwk:shared_secret(YourJWK, MyJWK) of SharedSecret -> {ok, SharedSecret} catch Class:Reason -> {Class, Reason} end, erlang:exit({shared_secret, Result}) end), receive {'DOWN', Ref, process, Pid, {shared_secret, Result}} -> Result after 5000 -> _ = erlang:demonitor(Ref, [flush]), true = erlang:exit(Pid, kill), ct:fail(shared_secret_did_not_terminate) end. potatosalad-erlang-jose-30ac019/test/jose_jwa_aes_SUITE.erl0000664000232200023220000006667015267717064024246 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc Advanced Encryption Standard (AES) %%% Cipher Block Chaining (CBC), as defined in NIST.800-38A %%% Electronic Codebook (ECB), as defined in NIST.800-38A %%% Galois/Counter Mode (GCM) and GMAC, as defined in NIST.800-38D %%% See NIST.800-38A: http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf %%% See NIST.800-38D: http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf %%% See http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf %%% See https://github.com/erlang/otp/blob/OTP-18.0/lib/crypto/test/crypto_SUITE.erl %%% @end %%% Created : 10 Aug 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwa_aes_SUITE). -include_lib("common_test/include/ct.hrl"). -include("jose.hrl"). %% Plain Text used in NIST example vectors -define(NIST_PLAIN_TEXT, hexstr2bin( "6bc1bee22e409f96e93d7e117393172a" "ae2d8a571e03ac9c9eb76fac45af8e51" "30c81c46a35ce411e5fbc1191a0a52ef" "f69f2445df4f9b17ad2b417be66c3710")). %% ct. -export([all/0]). -export([groups/0]). -export([init_per_suite/1]). -export([end_per_suite/1]). -export([init_per_group/2]). -export([end_per_group/2]). %% Tests. -export([aead/0]). -export([aead/1]). -export([aead_tag_length/0]). -export([aead_tag_length/1]). -export([block/0]). -export([block/1]). all() -> [ {group, aes_cbc128}, {group, aes_cbc192}, {group, aes_cbc256}, {group, aes_ecb128}, {group, aes_ecb192}, {group, aes_ecb256}, {group, aes_gcm128}, {group, aes_gcm192}, {group, aes_gcm256} ]. groups() -> [ {aes_cbc128, [], [block]}, {aes_cbc192, [], [block]}, {aes_cbc256, [], [block]}, {aes_ecb128, [], [block]}, {aes_ecb192, [], [block]}, {aes_ecb256, [], [block]}, {aes_gcm128, [], [aead, aead_tag_length]}, {aes_gcm192, [], [aead, aead_tag_length]}, {aes_gcm256, [], [aead, aead_tag_length]} ]. init_per_suite(Config) -> application:set_env(jose, crypto_fallback, true), application:set_env(jose, unsecured_signing, true), _ = application:ensure_all_started(jose), Config. end_per_suite(_Config) -> _ = application:stop(jose), ok. init_per_group(Group, Config) -> jose_ct:start(Group, group_config(Group, Config)). end_per_group(_Group, Config) -> jose_ct:stop(Config), ok. %%==================================================================== %% Tests %%==================================================================== aead() -> [{doc, "Test AEAD ciphers"}]. aead(Config) when is_list(Config) -> AEADs = lazy_eval(proplists:get_value(aead, Config)), lists:foreach(fun aead_cipher/1, AEADs). aead_tag_length() -> [{doc, "AEAD ciphers must only ever verify a full 128-bit authentication tag"}]. aead_tag_length(Config) when is_list(Config) -> AEADs = lazy_eval(proplists:get_value(aead, Config)), lists:foreach(fun aead_tag_length_cipher/1, AEADs). block() -> [{doc, "Test block ciphers"}]. block(Config) when is_list(Config) -> Blocks = proplists:get_value(block, Config), lists:foreach(fun block_cipher/1, Blocks). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private aead_cipher({Type, Key, PlainText, IV, AAD, CipherText, CipherTag}) -> Plain = iolist_to_binary(PlainText), case jose_jwa_aes:block_encrypt(Type, Key, IV, {AAD, Plain}) of {CipherText, CipherTag} -> ok; Other0 -> ct:fail({{jose_jwa_aes, block_encrypt, [Plain, PlainText]}, {expected, {CipherText, CipherTag}}, {got, Other0}}) end, case jose_jwa_aes:block_decrypt(Type, Key, IV, {AAD, CipherText, CipherTag}) of Plain -> ok; Other1 -> ct:fail({{jose_jwa_aes, block_decrypt, [CipherText]}, {expected, Plain}, {got, Other1}}) end. %% @private %% Regression for GHSA-6gxw-6f7r-cjr6: the authentication tag arrives %% verbatim from the attacker-supplied JWE token, so its length must never %% decide how many bits are verified. Every truncation of the *correct* %% tag -- including the 8-bit prefix that used to be accepted -- must be %% rejected, and an over-length tag must return `error' rather than crash. aead_tag_length_cipher({Type, Key, PlainText, IV, AAD, CipherText, CipherTag}) -> Plain = iolist_to_binary(PlainText), 128 = bit_size(CipherTag), %% Sanity: the full-length tag still authenticates on both code paths. Plain = jose_jwa_aes:block_decrypt(Type, Key, IV, {AAD, CipherText, CipherTag}), Plain = jose_jwa:block_decrypt(Type, Key, IV, {AAD, CipherText, CipherTag}), BadTags = [binary:part(CipherTag, 0, Len) || Len <- lists:seq(0, 15)] ++ [<< CipherTag/binary, 0 >>, << CipherTag/binary, 0:64 >>], _ = [begin try jose_jwa_aes:block_decrypt(Type, Key, IV, {AAD, CipherText, BadTag}) of error -> ok; Other0 -> ct:fail({{jose_jwa_aes, block_decrypt, [Type, {tag_bits, bit_size(BadTag)}]}, {expected, error}, {got, Other0}}) catch Class0:Reason0 -> ct:fail({{jose_jwa_aes, block_decrypt, [Type, {tag_bits, bit_size(BadTag)}]}, {expected, error}, {got, {Class0, Reason0}}}) end, try jose_jwa:block_decrypt(Type, Key, IV, {AAD, CipherText, BadTag}) of error -> ok; Other1 -> ct:fail({{jose_jwa, block_decrypt, [Type, {tag_bits, bit_size(BadTag)}]}, {expected, error}, {got, Other1}}) catch Class1:Reason1 -> ct:fail({{jose_jwa, block_decrypt, [Type, {tag_bits, bit_size(BadTag)}]}, {expected, error}, {got, {Class1, Reason1}}}) end end || BadTag <- BadTags], ok. %% @private block_cipher({Type, Key, PlainText}) -> Plain = iolist_to_binary(PlainText), CipherText = jose_jwa_aes:block_encrypt(Type, Key, PlainText), case jose_jwa_aes:block_decrypt(Type, Key, CipherText) of Plain -> ok; Other -> ct:fail({{jose_jwa_aes, block_decrypt, [Type, Key, CipherText]}, {expected, Plain}, {got, Other}}) end; block_cipher({Type={aes_ecb, _}, Key, PlainText, CipherText}) -> Plain = iolist_to_binary(PlainText), case jose_jwa_aes:block_encrypt(Type, Key, Plain) of CipherText -> ok; Other0 -> ct:fail({{jose_jwa_aes, block_encrypt, [Type, Key, Plain]}, {expected, CipherText}, {got, Other0}}) end, case jose_jwa_aes:block_decrypt(Type, Key, CipherText) of Plain -> ok; Other1 -> ct:fail({{jose_jwa_aes, block_decrypt, [Type, Key, CipherText]}, {expected, Plain}, {got, Other1}}) end; block_cipher({Type, Key, IV, PlainText}) -> Plain = iolist_to_binary(PlainText), CipherText = jose_jwa_aes:block_encrypt(Type, Key, IV, PlainText), case jose_jwa_aes:block_decrypt(Type, Key, IV, CipherText) of Plain -> ok; Other -> ct:fail({{jose_jwa_aes, block_decrypt, [Type, Key, IV, CipherText]}, {expected, Plain}, {got, Other}}) end; block_cipher({Type, Key, IV, PlainText, CipherText}) -> Plain = iolist_to_binary(PlainText), case jose_jwa_aes:block_encrypt(Type, Key, IV, Plain) of CipherText -> ok; Other0 -> ct:fail({{jose_jwa_aes, block_encrypt, [Plain, PlainText]}, {expected, CipherText}, {got, Other0}}) end, case jose_jwa_aes:block_decrypt(Type, Key, IV, CipherText) of Plain -> ok; Other1 -> ct:fail({{jose_jwa_aes, block_decrypt, [CipherText]}, {expected, Plain}, {got, Other1}}) end. %% @private group_config(aes_cbc128, Config) -> Block = aes_cbc128(), [{block, Block} | Config]; group_config(aes_cbc192, Config) -> Block = aes_cbc192(), [{block, Block} | Config]; group_config(aes_cbc256, Config) -> Block = aes_cbc256(), [{block, Block} | Config]; group_config(aes_ecb128, Config) -> Block = aes_ecb128(), [{block, Block} | Config]; group_config(aes_ecb192, Config) -> Block = aes_ecb192(), [{block, Block} | Config]; group_config(aes_ecb256, Config) -> Block = aes_ecb256(), [{block, Block} | Config]; group_config(aes_gcm128, Config) -> AEAD = aes_gcm128(), [{aead, AEAD} | Config]; group_config(aes_gcm192, Config) -> AEAD = aes_gcm192(), [{aead, AEAD} | Config]; group_config(aes_gcm256, Config) -> AEAD = aes_gcm256(), [{aead, AEAD} | Config]. %% @private hexstr2bin(S) -> list_to_binary(hexstr2list(S)). %% @private hexstr2list([X,Y|T]) -> [mkint(X)*16 + mkint(Y) | hexstr2list(T)]; hexstr2list([]) -> []. %% Building huge terms (like long_msg/0) in init_per_group seems to cause %% test_server crash with 'no_answer_from_tc_supervisor' sometimes on some %% machines. Therefore lazy evaluation when test case has started. lazy_eval(F) when is_function(F) -> F(); lazy_eval(Lst) when is_list(Lst) -> lists:map(fun lazy_eval/1, Lst); lazy_eval(Tpl) when is_tuple(Tpl) -> list_to_tuple(lists:map(fun lazy_eval/1, tuple_to_list(Tpl))); lazy_eval(Term) -> Term. %% @private mkint(C) when $0 =< C, C =< $9 -> C - $0; mkint(C) when $A =< C, C =< $F -> C - $A + 10; mkint(C) when $a =< C, C =< $f -> C - $a + 10. %% @private %% See http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf aes_cbc128() -> [ {{aes_cbc, 128}, hexstr2bin("2b7e151628aed2a6abf7158809cf4f3c"), hexstr2bin("000102030405060708090a0b0c0d0e0f"), hexstr2bin("6bc1bee22e409f96e93d7e117393172a")}, {{aes_cbc, 128}, hexstr2bin("2b7e151628aed2a6abf7158809cf4f3c"), hexstr2bin("7649ABAC8119B246CEE98E9B12E9197D"), hexstr2bin("ae2d8a571e03ac9c9eb76fac45af8e51")}, {{aes_cbc, 128}, hexstr2bin("2b7e151628aed2a6abf7158809cf4f3c"), hexstr2bin("5086CB9B507219EE95DB113A917678B2"), hexstr2bin("30c81c46a35ce411e5fbc1191a0a52ef")}, {{aes_cbc, 128}, hexstr2bin("2b7e151628aed2a6abf7158809cf4f3c"), hexstr2bin("73BED6B8E3C1743B7116E69E22229516"), hexstr2bin("f69f2445df4f9b17ad2b417be66c3710")}, %% F.2.1 CBC-AES128.Encrypt %% F.2.2 CBC-AES128.Decrypt {{aes_cbc, 128}, hexstr2bin("2b7e151628aed2a6abf7158809cf4f3c"), hexstr2bin("000102030405060708090a0b0c0d0e0f"), ?NIST_PLAIN_TEXT, hexstr2bin("7649abac8119b246cee98e9b12e9197d" "5086cb9b507219ee95db113a917678b2" "73bed6b8e3c1743b7116e69e22229516" "3ff1caa1681fac09120eca307586e1a7")} ]. %% @private %% See http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf aes_cbc192() -> [ %% F.2.3 CBC-AES192.Encrypt %% F.2.4 CBC-AES192.Decrypt {{aes_cbc, 192}, hexstr2bin("8e73b0f7da0e6452c810f32b809079e5" "62f8ead2522c6b7b"), hexstr2bin("000102030405060708090a0b0c0d0e0f"), ?NIST_PLAIN_TEXT, hexstr2bin("4f021db243bc633d7178183a9fa071e8" "b4d9ada9ad7dedf4e5e738763f69145a" "571b242012fb7ae07fa9baac3df102e0" "08b0e27988598881d920a9e64f5615cd")} ]. %% @private %% See http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf aes_cbc256() -> [ {{aes_cbc, 256}, hexstr2bin("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"), hexstr2bin("000102030405060708090A0B0C0D0E0F"), hexstr2bin("6bc1bee22e409f96e93d7e117393172a")}, {{aes_cbc, 256}, hexstr2bin("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"), hexstr2bin("F58C4C04D6E5F1BA779EABFB5F7BFBD6"), hexstr2bin("ae2d8a571e03ac9c9eb76fac45af8e51")}, {{aes_cbc, 256}, hexstr2bin("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"), hexstr2bin("9CFC4E967EDB808D679F777BC6702C7D"), hexstr2bin("30c81c46a35ce411e5fbc1191a0a52ef")}, {{aes_cbc, 256}, hexstr2bin("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"), hexstr2bin("39F23369A9D9BACFA530E26304231461"), hexstr2bin("f69f2445df4f9b17ad2b417be66c3710")}, %% F.2.5 CBC-AES256.Encrypt %% F.2.6 CBC-AES256.Decrypt {{aes_cbc, 256}, hexstr2bin("603deb1015ca71be2b73aef0857d7781" "1f352c073b6108d72d9810a30914dff4"), hexstr2bin("000102030405060708090a0b0c0d0e0f"), ?NIST_PLAIN_TEXT, hexstr2bin("f58c4c04d6e5f1ba779eabfb5f7bfbd6" "9cfc4e967edb808d679f777bc6702c7d" "39f23369a9d9bacfa530e26304231461" "b2eb05e2c39be9fcda6c19078c6a9d1b")} ]. %% @private %% See http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf aes_ecb128() -> [ {{aes_ecb, 128}, <<"YELLOW SUBMARINE">>, <<"YELLOW SUBMARINE">>}, {{aes_ecb, 128}, <<"0000000000000000">>, <<"0000000000000000">>}, {{aes_ecb, 128}, <<"FFFFFFFFFFFFFFFF">>, <<"FFFFFFFFFFFFFFFF">>}, {{aes_ecb, 128}, <<"3000000000000000">>, <<"1000000000000001">>}, {{aes_ecb, 128}, <<"1111111111111111">>, <<"1111111111111111">>}, {{aes_ecb, 128}, <<"0123456789ABCDEF">>, <<"1111111111111111">>}, {{aes_ecb, 128}, <<"0000000000000000">>, <<"0000000000000000">>}, {{aes_ecb, 128}, <<"FEDCBA9876543210">>, <<"0123456789ABCDEF">>}, {{aes_ecb, 128}, <<"7CA110454A1A6E57">>, <<"01A1D6D039776742">>}, {{aes_ecb, 128}, <<"0131D9619DC1376E">>, <<"5CD54CA83DEF57DA">>}, {{aes_ecb, 128}, <<"07A1133E4A0B2686">>, <<"0248D43806F67172">>}, {{aes_ecb, 128}, <<"3849674C2602319E">>, <<"51454B582DDF440A">>}, {{aes_ecb, 128}, <<"04B915BA43FEB5B6">>, <<"42FD443059577FA2">>}, {{aes_ecb, 128}, <<"0113B970FD34F2CE">>, <<"059B5E0851CF143A">>}, {{aes_ecb, 128}, <<"0170F175468FB5E6">>, <<"0756D8E0774761D2">>}, {{aes_ecb, 128}, <<"43297FAD38E373FE">>, <<"762514B829BF486A">>}, {{aes_ecb, 128}, <<"07A7137045DA2A16">>, <<"3BDD119049372802">>}, {{aes_ecb, 128}, <<"04689104C2FD3B2F">>, <<"26955F6835AF609A">>}, {{aes_ecb, 128}, <<"37D06BB516CB7546">>, <<"164D5E404F275232">>}, {{aes_ecb, 128}, <<"1F08260D1AC2465E">>, <<"6B056E18759F5CCA">>}, {{aes_ecb, 128}, <<"584023641ABA6176">>, <<"004BD6EF09176062">>}, {{aes_ecb, 128}, <<"025816164629B007">>, <<"480D39006EE762F2">>}, {{aes_ecb, 128}, <<"49793EBC79B3258F">>, <<"437540C8698F3CFA">>}, {{aes_ecb, 128}, <<"018310DC409B26D6">>, <<"1D9D5C5018F728C2">>}, {{aes_ecb, 128}, <<"1C587F1C13924FEF">>, <<"305532286D6F295A">>}, {{aes_ecb, 128}, <<"0101010101010101">>, <<"0123456789ABCDEF">>}, {{aes_ecb, 128}, <<"1F1F1F1F0E0E0E0E">>, <<"0123456789ABCDEF">>}, {{aes_ecb, 128}, <<"E0FEE0FEF1FEF1FE">>, <<"0123456789ABCDEF">>}, {{aes_ecb, 128}, <<"0000000000000000">>, <<"FFFFFFFFFFFFFFFF">>}, {{aes_ecb, 128}, <<"FFFFFFFFFFFFFFFF">>, <<"0000000000000000">>}, {{aes_ecb, 128}, <<"0123456789ABCDEF">>, <<"0000000000000000">>}, {{aes_ecb, 128}, <<"FEDCBA9876543210">>, <<"FFFFFFFFFFFFFFFF">>}, %% F.1.1 ECB-AES128.Encrypt %% F.1.2 ECB-AES128.Decrypt {{aes_ecb, 128}, hexstr2bin("2b7e151628aed2a6abf7158809cf4f3c"), ?NIST_PLAIN_TEXT, hexstr2bin("3ad77bb40d7a3660a89ecaf32466ef97" "f5d3d58503b9699de785895a96fdbaaf" "43b1cd7f598ece23881b00e3ed030688" "7b0c785e27e8ad3f8223207104725dd4")} ]. %% @private %% See http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf aes_ecb192() -> [ %% F.1.3 ECB-AES192.Encrypt %% F.1.4 ECB-AES192.Decrypt {{aes_ecb, 192}, hexstr2bin("8e73b0f7da0e6452c810f32b809079e5" "62f8ead2522c6b7b"), ?NIST_PLAIN_TEXT, hexstr2bin("bd334f1d6e45f25ff712a214571fa5cc" "974104846d0ad3ad7734ecb3ecee4eef" "ef7afd2270e2e60adce0ba2face6444e" "9a4b41ba738d6c72fb16691603c18e0e")} ]. %% @private %% See http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf aes_ecb256() -> [ %% F.1.5 ECB-AES256.Encrypt %% F.1.6 ECB-AES256.Decrypt {{aes_ecb, 256}, hexstr2bin("603deb1015ca71be2b73aef0857d7781" "1f352c073b6108d72d9810a30914dff4"), ?NIST_PLAIN_TEXT, hexstr2bin("f3eed1bdb5d2a03c064b5a7e3db181f8" "591ccb10d410ed26dc5ba74a31362870" "b6ed21b99ca6f4f9f153e7b1beafed1d" "23304b7a39f9f3ff067d8d8f9e24ecc7")} ]. %% AES GCM test vectors from http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf aes_gcm128() -> [ %% Test Case 1 {{aes_gcm, 128}, hexstr2bin("00000000000000000000000000000000"), %% Key hexstr2bin(""), %% PlainText hexstr2bin("000000000000000000000000"), %% IV hexstr2bin(""), %% AAD hexstr2bin(""), %% CipherText hexstr2bin("58e2fccefa7e3061367f1d57a4e7455a")}, %% CipherTag %% Test Case 2 {{aes_gcm, 128}, hexstr2bin("00000000000000000000000000000000"), %% Key hexstr2bin("00000000000000000000000000000000"), %% PlainText hexstr2bin("000000000000000000000000"), %% IV hexstr2bin(""), %% AAD hexstr2bin("0388dace60b6a392f328c2b971b2fe78"), %% CipherText hexstr2bin("ab6e47d42cec13bdf53a67b21257bddf")}, %% CipherTag %% Test Case 3 {{aes_gcm, 128}, hexstr2bin("feffe9928665731c6d6a8f9467308308"), %% Key hexstr2bin("d9313225f88406e5a55909c5aff5269a" %% PlainText "86a7a9531534f7da2e4c303d8a318a72" "1c3c0c95956809532fcf0e2449a6b525" "b16aedf5aa0de657ba637b391aafd255"), hexstr2bin("cafebabefacedbaddecaf888"), %% IV hexstr2bin(""), %% AAD hexstr2bin("42831ec2217774244b7221b784d0d49c" %% CipherText "e3aa212f2c02a4e035c17e2329aca12e" "21d514b25466931c7d8f6a5aac84aa05" "1ba30b396a0aac973d58e091473f5985"), hexstr2bin("4d5c2af327cd64a62cf35abd2ba6fab4")}, %% CipherTag %% Test Case 4 {{aes_gcm, 128}, hexstr2bin("feffe9928665731c6d6a8f9467308308"), %% Key hexstr2bin("d9313225f88406e5a55909c5aff5269a" %% PlainText "86a7a9531534f7da2e4c303d8a318a72" "1c3c0c95956809532fcf0e2449a6b525" "b16aedf5aa0de657ba637b39"), hexstr2bin("cafebabefacedbaddecaf888"), %% IV hexstr2bin("feedfacedeadbeeffeedfacedeadbeef" %% AAD "abaddad2"), hexstr2bin("42831ec2217774244b7221b784d0d49c" %% CipherText "e3aa212f2c02a4e035c17e2329aca12e" "21d514b25466931c7d8f6a5aac84aa05" "1ba30b396a0aac973d58e091"), hexstr2bin("5bc94fbc3221a5db94fae95ae7121a47")}, %% CipherTag %% Test Case 5 {{aes_gcm, 128}, hexstr2bin("feffe9928665731c6d6a8f9467308308"), %% Key hexstr2bin("d9313225f88406e5a55909c5aff5269a" %% PlainText "86a7a9531534f7da2e4c303d8a318a72" "1c3c0c95956809532fcf0e2449a6b525" "b16aedf5aa0de657ba637b39"), hexstr2bin("cafebabefacedbad"), %% IV hexstr2bin("feedfacedeadbeeffeedfacedeadbeef" %% AAD "abaddad2"), hexstr2bin("61353b4c2806934a777ff51fa22a4755" %% CipherText "699b2a714fcdc6f83766e5f97b6c7423" "73806900e49f24b22b097544d4896b42" "4989b5e1ebac0f07c23f4598"), hexstr2bin("3612d2e79e3b0785561be14aaca2fccb")}, %% CipherTag %% Test Case 6 {{aes_gcm, 128}, hexstr2bin("feffe9928665731c6d6a8f9467308308"), %% Key hexstr2bin("d9313225f88406e5a55909c5aff5269a" %% PlainText "86a7a9531534f7da2e4c303d8a318a72" "1c3c0c95956809532fcf0e2449a6b525" "b16aedf5aa0de657ba637b39"), hexstr2bin("9313225df88406e555909c5aff5269aa" %% IV "6a7a9538534f7da1e4c303d2a318a728" "c3c0c95156809539fcf0e2429a6b5254" "16aedbf5a0de6a57a637b39b"), hexstr2bin("feedfacedeadbeeffeedfacedeadbeef" %% AAD "abaddad2"), hexstr2bin("8ce24998625615b603a033aca13fb894" %% CipherText "be9112a5c3a211a8ba262a3cca7e2ca7" "01e4a9a4fba43c90ccdcb281d48c7c6f" "d62875d2aca417034c34aee5"), hexstr2bin("619cc5aefffe0bfa462af43c1699d050")} %% CipherTag ]. %% AES GCM test vectors from http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf aes_gcm192() -> [ %% Test Case 7 {{aes_gcm, 192}, hexstr2bin("00000000000000000000000000000000" %% Key "0000000000000000"), hexstr2bin(""), %% PlainText hexstr2bin("000000000000000000000000"), %% IV hexstr2bin(""), %% AAD hexstr2bin(""), %% CipherText hexstr2bin("cd33b28ac773f74ba00ed1f312572435")}, %% CipherTag %% Test Case 8 {{aes_gcm, 192}, hexstr2bin("00000000000000000000000000000000" %% Key "0000000000000000"), hexstr2bin("00000000000000000000000000000000"), %% PlainText hexstr2bin("000000000000000000000000"), %% IV hexstr2bin(""), %% AAD hexstr2bin("98e7247c07f0fe411c267e4384b0f600"), %% CipherText hexstr2bin("2ff58d80033927ab8ef4d4587514f0fb")}, %% CipherTag %% Test Case 9 {{aes_gcm, 192}, hexstr2bin("feffe9928665731c6d6a8f9467308308" %% Key "feffe9928665731c"), hexstr2bin("d9313225f88406e5a55909c5aff5269a" %% PlainText "86a7a9531534f7da2e4c303d8a318a72" "1c3c0c95956809532fcf0e2449a6b525" "b16aedf5aa0de657ba637b391aafd255"), hexstr2bin("cafebabefacedbaddecaf888"), %% IV hexstr2bin(""), %% ADD hexstr2bin("3980ca0b3c00e841eb06fac4872a2757" %% CipherText "859e1ceaa6efd984628593b40ca1e19c" "7d773d00c144c525ac619d18c84a3f47" "18e2448b2fe324d9ccda2710acade256"), hexstr2bin("9924a7c8587336bfb118024db8674a14")}, %% CipherTag %% Test Case 10 {{aes_gcm, 192}, hexstr2bin("feffe9928665731c6d6a8f9467308308" %% Key "feffe9928665731c"), hexstr2bin("d9313225f88406e5a55909c5aff5269a" %% PlainText "86a7a9531534f7da2e4c303d8a318a72" "1c3c0c95956809532fcf0e2449a6b525" "b16aedf5aa0de657ba637b39"), hexstr2bin("cafebabefacedbaddecaf888"), %% IV hexstr2bin("feedfacedeadbeeffeedfacedeadbeef" %% AAD "abaddad2"), hexstr2bin("3980ca0b3c00e841eb06fac4872a2757" %% CipherText "859e1ceaa6efd984628593b40ca1e19c" "7d773d00c144c525ac619d18c84a3f47" "18e2448b2fe324d9ccda2710"), hexstr2bin("2519498e80f1478f37ba55bd6d27618c")}, %% CipherTag %% Test Case 11 {{aes_gcm, 192}, hexstr2bin("feffe9928665731c6d6a8f9467308308" %% Key "feffe9928665731c"), hexstr2bin("d9313225f88406e5a55909c5aff5269a" %% PlainText "86a7a9531534f7da2e4c303d8a318a72" "1c3c0c95956809532fcf0e2449a6b525" "b16aedf5aa0de657ba637b39"), hexstr2bin("cafebabefacedbad"), %% IV hexstr2bin("feedfacedeadbeeffeedfacedeadbeef" %% AAD "abaddad2"), hexstr2bin("0f10f599ae14a154ed24b36e25324db8" %% CipherText "c566632ef2bbb34f8347280fc4507057" "fddc29df9a471f75c66541d4d4dad1c9" "e93a19a58e8b473fa0f062f7"), hexstr2bin("65dcc57fcf623a24094fcca40d3533f8")}, %% CipherTag %% Test Case 12 {{aes_gcm, 192}, hexstr2bin("feffe9928665731c6d6a8f9467308308" %% Key "feffe9928665731c"), hexstr2bin("d9313225f88406e5a55909c5aff5269a" %% PlainText "86a7a9531534f7da2e4c303d8a318a72" "1c3c0c95956809532fcf0e2449a6b525" "b16aedf5aa0de657ba637b39"), hexstr2bin("9313225df88406e555909c5aff5269aa" %% IV "6a7a9538534f7da1e4c303d2a318a728" "c3c0c95156809539fcf0e2429a6b5254" "16aedbf5a0de6a57a637b39b"), hexstr2bin("feedfacedeadbeeffeedfacedeadbeef" %% AAD "abaddad2"), hexstr2bin("d27e88681ce3243c4830165a8fdcf9ff" %% CipherText "1de9a1d8e6b447ef6ef7b79828666e45" "81e79012af34ddd9e2f037589b292db3" "e67c036745fa22e7e9b7373b"), hexstr2bin("dcf566ff291c25bbb8568fc3d376a6d9")} %% CipherTag ]. %% AES GCM test vectors from http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf aes_gcm256() -> [ %% Test Case 13 {{aes_gcm, 256}, hexstr2bin("00000000000000000000000000000000" %% Key "00000000000000000000000000000000"), hexstr2bin(""), %% PlainText hexstr2bin("000000000000000000000000"), %% IV hexstr2bin(""), %% AAD hexstr2bin(""), %% CipherText hexstr2bin("530f8afbc74536b9a963b4f1c4cb738b")}, %% CipherTag %% Test Case 14 {{aes_gcm, 256}, hexstr2bin("00000000000000000000000000000000" %% Key "00000000000000000000000000000000"), hexstr2bin("00000000000000000000000000000000"), %% PlainText hexstr2bin("000000000000000000000000"), %% IV hexstr2bin(""), %% AAD hexstr2bin("cea7403d4d606b6e074ec5d3baf39d18"), %% CipherText hexstr2bin("d0d1c8a799996bf0265b98b5d48ab919")}, %% CipherTag %% Test Case 15 {{aes_gcm, 256}, hexstr2bin("feffe9928665731c6d6a8f9467308308" %% Key "feffe9928665731c6d6a8f9467308308"), hexstr2bin("d9313225f88406e5a55909c5aff5269a" %% PlainText "86a7a9531534f7da2e4c303d8a318a72" "1c3c0c95956809532fcf0e2449a6b525" "b16aedf5aa0de657ba637b391aafd255"), hexstr2bin("cafebabefacedbaddecaf888"), %% IV hexstr2bin(""), %% AAD hexstr2bin("522dc1f099567d07f47f37a32a84427d" %% CipherText "643a8cdcbfe5c0c97598a2bd2555d1aa" "8cb08e48590dbb3da7b08b1056828838" "c5f61e6393ba7a0abcc9f662898015ad"), hexstr2bin("b094dac5d93471bdec1a502270e3cc6c")}, %% CipherTag %% Test Case 16 {{aes_gcm, 256}, hexstr2bin("feffe9928665731c6d6a8f9467308308" %% Key "feffe9928665731c6d6a8f9467308308"), hexstr2bin("d9313225f88406e5a55909c5aff5269a" %% PlainText "86a7a9531534f7da2e4c303d8a318a72" "1c3c0c95956809532fcf0e2449a6b525" "b16aedf5aa0de657ba637b39"), hexstr2bin("cafebabefacedbaddecaf888"), %% IV hexstr2bin("feedfacedeadbeeffeedfacedeadbeef" %% AAD "abaddad2"), hexstr2bin("522dc1f099567d07f47f37a32a84427d" %% CipherText "643a8cdcbfe5c0c97598a2bd2555d1aa" "8cb08e48590dbb3da7b08b1056828838" "c5f61e6393ba7a0abcc9f662"), hexstr2bin("76fc6ece0f4e1768cddf8853bb2d551b")}, %% CipherTag %% Test Case 17 {{aes_gcm, 256}, hexstr2bin("feffe9928665731c6d6a8f9467308308" %% Key "feffe9928665731c6d6a8f9467308308"), hexstr2bin("d9313225f88406e5a55909c5aff5269a" %% PlainText "86a7a9531534f7da2e4c303d8a318a72" "1c3c0c95956809532fcf0e2449a6b525" "b16aedf5aa0de657ba637b39"), hexstr2bin("cafebabefacedbad"), %% IV hexstr2bin("feedfacedeadbeeffeedfacedeadbeef" %% AAD "abaddad2"), hexstr2bin("c3762df1ca787d32ae47c13bf19844cb" %% CipherText "af1ae14d0b976afac52ff7d79bba9de0" "feb582d33934a4f0954cc2363bc73f78" "62ac430e64abe499f47c9b1f"), hexstr2bin("3a337dbf46a792c45e454913fe2ea8f2")}, %% CipherTag %% Test Case 18 {{aes_gcm, 256}, hexstr2bin("feffe9928665731c6d6a8f9467308308" %% Key "feffe9928665731c6d6a8f9467308308"), hexstr2bin("d9313225f88406e5a55909c5aff5269a" %% PlainText "86a7a9531534f7da2e4c303d8a318a72" "1c3c0c95956809532fcf0e2449a6b525" "b16aedf5aa0de657ba637b39"), hexstr2bin("9313225df88406e555909c5aff5269aa" %% IV "6a7a9538534f7da1e4c303d2a318a728" "c3c0c95156809539fcf0e2429a6b5254" "16aedbf5a0de6a57a637b39b"), hexstr2bin("feedfacedeadbeeffeedfacedeadbeef" %% AAD "abaddad2"), hexstr2bin("5a8def2f0c9e53f1f75d7853659e2a20" %% CipherText "eeb2b22aafde6419a058ab4f6f746bf4" "0fc0c3b780f244452da3ebf1c5d82cde" "a2418997200ef82e44ae7e3f"), hexstr2bin("a44a8266ee1c8eb0c8b5d4cf5ae9f19a")} %% CipherTag ]. potatosalad-erlang-jose-30ac019/test/cavp_SUITE.erl0000664000232200023220000020662015267717064022535 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 11 Aug 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(cavp_SUITE). -include_lib("common_test/include/ct.hrl"). -include_lib("public_key/include/public_key.hrl"). -include_lib("stdlib/include/zip.hrl"). %% ct. -export([all/0]). -export([groups/0]). -export([init_per_suite/1]). -export([end_per_suite/1]). -export([init_per_group/2]). -export([end_per_group/2]). %% Tests. -export([concatenation_kdf/1]). -export([curve25519/1]). -export([curve448/1]). -export([ed25519/1]). -export([ed25519ph/1]). -export([ed448/1]). -export([ed448ph/1]). -export([emc_rsa_oaep_encrypt_and_decrypt/1]). -export([emc_rsa_pss_sign_and_verify/1]). -export([fips_aes_encrypt_and_decrypt/1]). -export([fips_aes_gcm_encrypt_and_decrypt/1]). -export([fips_aeskw_unwrap/1]). -export([fips_aeskw_wrap/1]). -export([fips_rsa_pss_sign/1]). -export([fips_rsa_pss_verify/1]). -export([fips_sha3/1]). -export([pbkdf1/1]). -export([pbkdf2/1]). -export([pkcs7_pad_and_unpad/1]). -export([x25519/1]). -export([x448/1]). %% Macros. -define(tv_ok(T, M, F, A, E), case erlang:apply(M, F, A) of E -> ok; T -> ct:fail({{M, F, A}, {expected, E}, {got, T}}) end). all() -> [ {group, '186-3rsatestvectors'}, {group, 'aesmmt'}, {group, 'curve25519'}, {group, 'curve448'}, {group, 'gcmtestvectors'}, {group, 'KAT_AES'}, {group, 'keccaktestvectors'}, {group, 'kwtestvectors'}, {group, 'nist-800-56A'}, {group, 'pkcs-1v2-1-vec'}, {group, 'pkcs-5'}, {group, 'pkcs-7'} ]. groups() -> [ {'186-3rsatestvectors', [parallel], [ fips_rsa_pss_sign, fips_rsa_pss_verify ]}, {'aesmmt', [], [ fips_aes_encrypt_and_decrypt ]}, {'curve25519', [parallel], [ curve25519, ed25519, ed25519ph, x25519 ]}, {'curve448', [parallel], [ curve448, ed448, ed448ph, x448 ]}, {'gcmtestvectors', [], [ fips_aes_gcm_encrypt_and_decrypt ]}, {'KAT_AES', [], [ fips_aes_encrypt_and_decrypt ]}, {'keccaktestvectors', [], [ fips_sha3 ]}, {'kwtestvectors', [parallel], [ fips_aeskw_unwrap, fips_aeskw_wrap ]}, {'nist-800-56A', [], [ concatenation_kdf ]}, {'pkcs-1v2-1-vec', [parallel], [ emc_rsa_oaep_encrypt_and_decrypt, emc_rsa_pss_sign_and_verify ]}, {'pkcs-5', [parallel], [ pbkdf1, pbkdf2 ]}, {'pkcs-7', [], [ pkcs7_pad_and_unpad ]} ]. init_per_suite(Config) -> application:set_env(jose, crypto_fallback, true), application:set_env(jose, unsecured_signing, true), _ = application:ensure_all_started(jose), data_setup(Config). end_per_suite(_Config) -> _ = application:stop(jose), ok. init_per_group(G='186-3rsatestvectors', Config) -> SigGenFile = data_file("186-3rsatestvectors/SigGenPSS_186-3.txt", Config), SigVerFile = data_file("186-3rsatestvectors/SigVerPSS_186-3.rsp", Config), [{sig_gen_file, SigGenFile}, {sig_ver_file, SigVerFile} | jose_ct:start(G, Config)]; init_per_group(G='aesmmt', Config) -> Folder = data_file("aesmmt", Config), {ok, Entries} = file:list_dir(Folder), Files = [filename:join([Folder, Entry]) || Entry <- Entries], [{aes_files, Files} | jose_ct:start(G, Config)]; init_per_group(G='curve25519', Config) -> [ {curve25519, [ { 31029842492115040904895560451863089656472772604678260265531221036453811406496, % Input scalar 34426434033919594451155107781188821651316167215306631574996226621102155684838, % Input u-coordinate hexstr2lint("c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552") % Output u-coordinate }, { 35156891815674817266734212754503633747128614016119564763269015315466259359304, % Input scalar 8883857351183929894090759386610649319417338800022198945255395922347792736741, % Input u-coordinate hexstr2lint("95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957") % Output u-coordinate } ]}, {ed25519, [ { % TEST 1 hexstr2bin( "9d61b19deffd5a60ba844af492ec2cc4" "4449c5697b326919703bac031cae7f60"), % SECRET KEY hexstr2bin( "d75a980182b10ab7d54bfed3c964073a" "0ee172f3daa62325af021a68f707511a"), % PUBLIC KEY <<>>, % MESSAGE hexstr2bin( "e5564300c360ac729086e2cc806e828a" "84877f1eb8e5d974d873e06522490155" "5fb8821590a33bacc61e39701cf9b46b" "d25bf5f0595bbe24655141438e7a100b") % SIGNATURE }, { % TEST 2 hexstr2bin( "4ccd089b28ff96da9db6c346ec114e0f" "5b8a319f35aba624da8cf6ed4fb8a6fb"), % SECRET KEY hexstr2bin( "3d4017c3e843895a92b70aa74d1b7ebc" "9c982ccf2ec4968cc0cd55f12af4660c"), % PUBLIC KEY hexstr2bin("72"), % MESSAGE hexstr2bin( "92a009a9f0d4cab8720e820b5f642540" "a2b27b5416503f8fb3762223ebdb69da" "085ac1e43e15996e458f3613d0f11d8c" "387b2eaeb4302aeeb00d291612bb0c00") % SIGNATURE }, { % TEST 3 hexstr2bin( "c5aa8df43f9f837bedb7442f31dcb7b1" "66d38535076f094b85ce3a2e0b4458f7"), % SECRET KEY hexstr2bin( "fc51cd8e6218a1a38da47ed00230f058" "0816ed13ba3303ac5deb911548908025"), % PUBLIC KEY hexstr2bin("af82"), % MESSAGE hexstr2bin( "6291d657deec24024827e69c3abe01a3" "0ce548a284743a445e3680d7db5ac3ac" "18ff9b538d16f290ae67f760984dc659" "4a7c15e9716ed28dc027beceea1ec40a") % SIGNATURE }, { % TEST 1024 hexstr2bin( "f5e5767cf153319517630f226876b86c" "8160cc583bc013744c6bf255f5cc0ee5"), % SECRET KEY hexstr2bin( "278117fc144c72340f67d0f2316e8386" "ceffbf2b2428c9c51fef7c597f1d426e"), % PUBLIC KEY hexstr2bin( "08b8b2b733424243760fe426a4b54908" "632110a66c2f6591eabd3345e3e4eb98" "fa6e264bf09efe12ee50f8f54e9f77b1" "e355f6c50544e23fb1433ddf73be84d8" "79de7c0046dc4996d9e773f4bc9efe57" "38829adb26c81b37c93a1b270b20329d" "658675fc6ea534e0810a4432826bf58c" "941efb65d57a338bbd2e26640f89ffbc" "1a858efcb8550ee3a5e1998bd177e93a" "7363c344fe6b199ee5d02e82d522c4fe" "ba15452f80288a821a579116ec6dad2b" "3b310da903401aa62100ab5d1a36553e" "06203b33890cc9b832f79ef80560ccb9" "a39ce767967ed628c6ad573cb116dbef" "efd75499da96bd68a8a97b928a8bbc10" "3b6621fcde2beca1231d206be6cd9ec7" "aff6f6c94fcd7204ed3455c68c83f4a4" "1da4af2b74ef5c53f1d8ac70bdcb7ed1" "85ce81bd84359d44254d95629e9855a9" "4a7c1958d1f8ada5d0532ed8a5aa3fb2" "d17ba70eb6248e594e1a2297acbbb39d" "502f1a8c6eb6f1ce22b3de1a1f40cc24" "554119a831a9aad6079cad88425de6bd" "e1a9187ebb6092cf67bf2b13fd65f270" "88d78b7e883c8759d2c4f5c65adb7553" "878ad575f9fad878e80a0c9ba63bcbcc" "2732e69485bbc9c90bfbd62481d9089b" "eccf80cfe2df16a2cf65bd92dd597b07" "07e0917af48bbb75fed413d238f5555a" "7a569d80c3414a8d0859dc65a46128ba" "b27af87a71314f318c782b23ebfe808b" "82b0ce26401d2e22f04d83d1255dc51a" "ddd3b75a2b1ae0784504df543af8969b" "e3ea7082ff7fc9888c144da2af58429e" "c96031dbcad3dad9af0dcbaaaf268cb8" "fcffead94f3c7ca495e056a9b47acdb7" "51fb73e666c6c655ade8297297d07ad1" "ba5e43f1bca32301651339e22904cc8c" "42f58c30c04aafdb038dda0847dd988d" "cda6f3bfd15c4b4c4525004aa06eeff8" "ca61783aacec57fb3d1f92b0fe2fd1a8" "5f6724517b65e614ad6808d6f6ee34df" "f7310fdc82aebfd904b01e1dc54b2927" "094b2db68d6f903b68401adebf5a7e08" "d78ff4ef5d63653a65040cf9bfd4aca7" "984a74d37145986780fc0b16ac451649" "de6188a7dbdf191f64b5fc5e2ab47b57" "f7f7276cd419c17a3ca8e1b939ae49e4" "88acba6b965610b5480109c8b17b80e1" "b7b750dfc7598d5d5011fd2dcc5600a3" "2ef5b52a1ecc820e308aa342721aac09" "43bf6686b64b2579376504ccc493d97e" "6aed3fb0f9cd71a43dd497f01f17c0e2" "cb3797aa2a2f256656168e6c496afc5f" "b93246f6b1116398a346f1a641f3b041" "e989f7914f90cc2c7fff357876e506b5" "0d334ba77c225bc307ba537152f3f161" "0e4eafe595f6d9d90d11faa933a15ef1" "369546868a7f3a45a96768d40fd9d034" "12c091c6315cf4fde7cb68606937380d" "b2eaaa707b4c4185c32eddcdd306705e" "4dc1ffc872eeee475a64dfac86aba41c" "0618983f8741c5ef68d3a101e8a3b8ca" "c60c905c15fc910840b94c00a0b9d0"), % MESSAGE hexstr2bin( "0aab4c900501b3e24d7cdf4663326a3a" "87df5e4843b2cbdb67cbf6e460fec350" "aa5371b1508f9f4528ecea23c436d94b" "5e8fcd4f681e30a6ac00a9704a188a03") % SIGNATURE }, { % TEST SHA(abc) hexstr2bin( "833fe62409237b9d62ec77587520911e" "9a759cec1d19755b7da901b96dca3d42"), % SECRET KEY hexstr2bin( "ec172b93ad5e563bf4932c70e1245034" "c35467ef2efd4d64ebf819683467e2bf"), % PUBLIC KEY hexstr2bin( "ddaf35a193617abacc417349ae204131" "12e6fa4e89a97ea20a9eeee64b55d39a" "2192992a274fc1a836ba3c23a3feebbd" "454d4423643ce80e2a9ac94fa54ca49f"), % MESSAGE hexstr2bin( "dc2a4459e7369633a52b1bf277839a00" "201009a3efbf3ecb69bea2186c26b589" "09351fc9ac90b3ecfdfbc7c66431e030" "3dca179c138ac17ad9bef1177331a704") % SIGNATURE } ]}, {ed25519ph, [ { % TEST abc hexstr2bin( "833fe62409237b9d62ec77587520911e" "9a759cec1d19755b7da901b96dca3d42"), % SECRET KEY hexstr2bin( "ec172b93ad5e563bf4932c70e1245034" "c35467ef2efd4d64ebf819683467e2bf"), % PUBLIC KEY hexstr2bin("616263"), % MESSAGE hexstr2bin( "98a70222f0b8121aa9d30f813d683f80" "9e462b469c7ff87639499bb94e6dae41" "31f85042463c2a355a2003d062adf5aa" "a10b8c61e636062aaad11c2a26083406") % SIGNATURE } ]}, {x25519, [ { hexstr2bin("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a"), % Alice's private key, f hexstr2bin("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a"), % Alice's public key, X25519(f, 9) hexstr2bin("5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb"), % Bob's private key, g hexstr2bin("de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f"), % Bob's public key, X25519(g, 9) hexstr2bin("4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742") % Their shared secret, K } ]} | jose_ct:start(G, Config) ]; init_per_group(G='curve448', Config) -> [ {curve448, [ { 599189175373896402783756016145213256157230856085026129926891459468622403380588640249457727683869421921443004045221642549886377526240828, % Input scalar 382239910814107330116229961234899377031416365240571325148346555922438025162094455820962429142971339584360034337310079791515452463053830, % Input u-coordinate hexstr2lint("ce3e4ff95a60dc6697da1db1d85e6afbdf79b50a2412d7546d5f239fe14fbaadeb445fc66a01b0779d98223961111e21766282f73dd96b6f") % Output u-coordinate }, { 633254335906970592779259481534862372382525155252028961056404001332122152890562527156973881968934311400345568203929409663925541994577184, % Input scalar 622761797758325444462922068431234180649590390024811299761625153767228042600197997696167956134770744996690267634159427999832340166786063, % Input u-coordinate hexstr2lint("884a02576239ff7a2f2f63b2db6a9ff37047ac13568e1e30fe63c4a7ad1b3ee3a5700df34321d62077e63633c575c1c954514e99da7c179d") % Output u-coordinate } ]}, {ed448, [ { % Blank hexstr2bin( "6c82a562cb808d10d632be89c8513ebf" "6c929f34ddfa8c9f63c9960ef6e348a3" "528c8a3fcc2f044e39a3fc5b94492f8f" "032e7549a20098f95b"), % SECRET KEY hexstr2bin( "5fd7449b59b461fd2ce787ec616ad46a" "1da1342485a70e1f8a0ea75d80e96778" "edf124769b46c7061bd6783df1e50f6c" "d1fa1abeafe8256180"), % PUBLIC KEY <<>>, % MESSAGE hexstr2bin( "533a37f6bbe457251f023c0d88f976ae" "2dfb504a843e34d2074fd823d41a591f" "2b233f034f628281f2fd7a22ddd47d78" "28c59bd0a21bfd3980ff0d2028d4b18a" "9df63e006c5d1c2d345b925d8dc00b41" "04852db99ac5c7cdda8530a113a0f4db" "b61149f05a7363268c71d95808ff2e65" "2600") % SIGNATURE }, { % 1 octet hexstr2bin( "c4eab05d357007c632f3dbb48489924d" "552b08fe0c353a0d4a1f00acda2c463a" "fbea67c5e8d2877c5e3bc397a659949e" "f8021e954e0a12274e"), % SECRET KEY hexstr2bin( "43ba28f430cdff456ae531545f7ecd0a" "c834a55d9358c0372bfa0c6c6798c086" "6aea01eb00742802b8438ea4cb82169c" "235160627b4c3a9480"), % PUBLIC KEY hexstr2bin("03"), % MESSAGE hexstr2bin( "26b8f91727bd62897af15e41eb43c377" "efb9c610d48f2335cb0bd0087810f435" "2541b143c4b981b7e18f62de8ccdf633" "fc1bf037ab7cd779805e0dbcc0aae1cb" "cee1afb2e027df36bc04dcecbf154336" "c19f0af7e0a6472905e799f1953d2a0f" "f3348ab21aa4adafd1d234441cf807c0" "3a00") % SIGNATURE }, { % 1 octet (with context) hexstr2bin( "c4eab05d357007c632f3dbb48489924d" "552b08fe0c353a0d4a1f00acda2c463a" "fbea67c5e8d2877c5e3bc397a659949e" "f8021e954e0a12274e"), % SECRET KEY hexstr2bin( "43ba28f430cdff456ae531545f7ecd0a" "c834a55d9358c0372bfa0c6c6798c086" "6aea01eb00742802b8438ea4cb82169c" "235160627b4c3a9480"), % PUBLIC KEY hexstr2bin("03"), % MESSAGE hexstr2bin("666f6f"), % CONTEXT hexstr2bin( "d4f8f6131770dd46f40867d6fd5d5055" "de43541f8c5e35abbcd001b32a89f7d2" "151f7647f11d8ca2ae279fb842d60721" "7fce6e042f6815ea000c85741de5c8da" "1144a6a1aba7f96de42505d7a7298524" "fda538fccbbb754f578c1cad10d54d0d" "5428407e85dcbc98a49155c13764e66c" "3c00") % SIGNATURE }, { % 11 octets hexstr2bin( "cd23d24f714274e744343237b93290f5" "11f6425f98e64459ff203e8985083ffd" "f60500553abc0e05cd02184bdb89c4cc" "d67e187951267eb328"), % SECRET KEY hexstr2bin( "dcea9e78f35a1bf3499a831b10b86c90" "aac01cd84b67a0109b55a36e9328b1e3" "65fce161d71ce7131a543ea4cb5f7e9f" "1d8b00696447001400"), % PUBLIC KEY hexstr2bin("0c3e544074ec63b0265e0c"), % MESSAGE hexstr2bin( "1f0a8888ce25e8d458a21130879b840a" "9089d999aaba039eaf3e3afa090a09d3" "89dba82c4ff2ae8ac5cdfb7c55e94d5d" "961a29fe0109941e00b8dbdeea6d3b05" "1068df7254c0cdc129cbe62db2dc957d" "bb47b51fd3f213fb8698f064774250a5" "028961c9bf8ffd973fe5d5c206492b14" "0e00") % SIGNATURE }, { % 12 octets hexstr2bin( "258cdd4ada32ed9c9ff54e63756ae582" "fb8fab2ac721f2c8e676a72768513d93" "9f63dddb55609133f29adf86ec9929dc" "cb52c1c5fd2ff7e21b"), % SECRET KEY hexstr2bin( "3ba16da0c6f2cc1f30187740756f5e79" "8d6bc5fc015d7c63cc9510ee3fd44adc" "24d8e968b6e46e6f94d19b945361726b" "d75e149ef09817f580"), % PUBLIC KEY hexstr2bin("64a65f3cdedcdd66811e2915"), % MESSAGE hexstr2bin( "7eeeab7c4e50fb799b418ee5e3197ff6" "bf15d43a14c34389b59dd1a7b1b85b4a" "e90438aca634bea45e3a2695f1270f07" "fdcdf7c62b8efeaf00b45c2c96ba457e" "b1a8bf075a3db28e5c24f6b923ed4ad7" "47c3c9e03c7079efb87cb110d3a99861" "e72003cbae6d6b8b827e4e6c143064ff" "3c00") % SIGNATURE }, { % 13 octets hexstr2bin( "7ef4e84544236752fbb56b8f31a23a10" "e42814f5f55ca037cdcc11c64c9a3b29" "49c1bb60700314611732a6c2fea98eeb" "c0266a11a93970100e"), % SECRET KEY hexstr2bin( "b3da079b0aa493a5772029f0467baebe" "e5a8112d9d3a22532361da294f7bb381" "5c5dc59e176b4d9f381ca0938e13c6c0" "7b174be65dfa578e80"), % PUBLIC KEY hexstr2bin("64a65f3cdedcdd66811e2915e7"), % MESSAGE hexstr2bin( "6a12066f55331b6c22acd5d5bfc5d712" "28fbda80ae8dec26bdd306743c5027cb" "4890810c162c027468675ecf645a8317" "6c0d7323a2ccde2d80efe5a1268e8aca" "1d6fbc194d3f77c44986eb4ab4177919" "ad8bec33eb47bbb5fc6e28196fd1caf5" "6b4e7e0ba5519234d047155ac727a105" "3100") % SIGNATURE }, { % 64 octets hexstr2bin( "d65df341ad13e008567688baedda8e9d" "cdc17dc024974ea5b4227b6530e339bf" "f21f99e68ca6968f3cca6dfe0fb9f4fa" "b4fa135d5542ea3f01"), % SECRET KEY hexstr2bin( "df9705f58edbab802c7f8363cfe5560a" "b1c6132c20a9f1dd163483a26f8ac53a" "39d6808bf4a1dfbd261b099bb03b3fb5" "0906cb28bd8a081f00"), % PUBLIC KEY hexstr2bin( "bd0f6a3747cd561bdddf4640a332461a" "4a30a12a434cd0bf40d766d9c6d458e5" "512204a30c17d1f50b5079631f64eb31" "12182da3005835461113718d1a5ef944"), % MESSAGE hexstr2bin( "554bc2480860b49eab8532d2a533b7d5" "78ef473eeb58c98bb2d0e1ce488a98b1" "8dfde9b9b90775e67f47d4a1c3482058" "efc9f40d2ca033a0801b63d45b3b722e" "f552bad3b4ccb667da350192b61c508c" "f7b6b5adadc2c8d9a446ef003fb05cba" "5f30e88e36ec2703b349ca229c267083" "3900") % SIGNATURE }, { % 64 octets hexstr2bin( "d65df341ad13e008567688baedda8e9d" "cdc17dc024974ea5b4227b6530e339bf" "f21f99e68ca6968f3cca6dfe0fb9f4fa" "b4fa135d5542ea3f01"), % SECRET KEY hexstr2bin( "df9705f58edbab802c7f8363cfe5560a" "b1c6132c20a9f1dd163483a26f8ac53a" "39d6808bf4a1dfbd261b099bb03b3fb5" "0906cb28bd8a081f00"), % PUBLIC KEY hexstr2bin( "bd0f6a3747cd561bdddf4640a332461a" "4a30a12a434cd0bf40d766d9c6d458e5" "512204a30c17d1f50b5079631f64eb31" "12182da3005835461113718d1a5ef944"), % MESSAGE hexstr2bin( "554bc2480860b49eab8532d2a533b7d5" "78ef473eeb58c98bb2d0e1ce488a98b1" "8dfde9b9b90775e67f47d4a1c3482058" "efc9f40d2ca033a0801b63d45b3b722e" "f552bad3b4ccb667da350192b61c508c" "f7b6b5adadc2c8d9a446ef003fb05cba" "5f30e88e36ec2703b349ca229c267083" "3900") % SIGNATURE }, { % 256 octets hexstr2bin( "2ec5fe3c17045abdb136a5e6a913e32a" "b75ae68b53d2fc149b77e504132d3756" "9b7e766ba74a19bd6162343a21c8590a" "a9cebca9014c636df5"), % SECRET KEY hexstr2bin( "79756f014dcfe2079f5dd9e718be4171" "e2ef2486a08f25186f6bff43a9936b9b" "fe12402b08ae65798a3d81e22e9ec80e" "7690862ef3d4ed3a00"), % PUBLIC KEY hexstr2bin( "15777532b0bdd0d1389f636c5f6b9ba7" "34c90af572877e2d272dd078aa1e567c" "fa80e12928bb542330e8409f31745041" "07ecd5efac61ae7504dabe2a602ede89" "e5cca6257a7c77e27a702b3ae39fc769" "fc54f2395ae6a1178cab4738e543072f" "c1c177fe71e92e25bf03e4ecb72f47b6" "4d0465aaea4c7fad372536c8ba516a60" "39c3c2a39f0e4d832be432dfa9a706a6" "e5c7e19f397964ca4258002f7c0541b5" "90316dbc5622b6b2a6fe7a4abffd9610" "5eca76ea7b98816af0748c10df048ce0" "12d901015a51f189f3888145c03650aa" "23ce894c3bd889e030d565071c59f409" "a9981b51878fd6fc110624dcbcde0bf7" "a69ccce38fabdf86f3bef6044819de11"), % MESSAGE hexstr2bin( "c650ddbb0601c19ca11439e1640dd931" "f43c518ea5bea70d3dcde5f4191fe53f" "00cf966546b72bcc7d58be2b9badef28" "743954e3a44a23f880e8d4f1cfce2d7a" "61452d26da05896f0a50da66a239a8a1" "88b6d825b3305ad77b73fbac0836ecc6" "0987fd08527c1a8e80d5823e65cafe2a" "3d00") % SIGNATURE }, { % 1023 octets hexstr2bin( "872d093780f5d3730df7c212664b37b8" "a0f24f56810daa8382cd4fa3f77634ec" "44dc54f1c2ed9bea86fafb7632d8be19" "9ea165f5ad55dd9ce8"), % SECRET KEY hexstr2bin( "a81b2e8a70a5ac94ffdbcc9badfc3feb" "0801f258578bb114ad44ece1ec0e799d" "a08effb81c5d685c0c56f64eecaef8cd" "f11cc38737838cf400"), % PUBLIC KEY hexstr2bin( "6ddf802e1aae4986935f7f981ba3f035" "1d6273c0a0c22c9c0e8339168e675412" "a3debfaf435ed651558007db4384b650" "fcc07e3b586a27a4f7a00ac8a6fec2cd" "86ae4bf1570c41e6a40c931db27b2faa" "15a8cedd52cff7362c4e6e23daec0fbc" "3a79b6806e316efcc7b68119bf46bc76" "a26067a53f296dafdbdc11c77f7777e9" "72660cf4b6a9b369a6665f02e0cc9b6e" "dfad136b4fabe723d2813db3136cfde9" "b6d044322fee2947952e031b73ab5c60" "3349b307bdc27bc6cb8b8bbd7bd32321" "9b8033a581b59eadebb09b3c4f3d2277" "d4f0343624acc817804728b25ab79717" "2b4c5c21a22f9c7839d64300232eb66e" "53f31c723fa37fe387c7d3e50bdf9813" "a30e5bb12cf4cd930c40cfb4e1fc6225" "92a49588794494d56d24ea4b40c89fc0" "596cc9ebb961c8cb10adde976a5d602b" "1c3f85b9b9a001ed3c6a4d3b1437f520" "96cd1956d042a597d561a596ecd3d173" "5a8d570ea0ec27225a2c4aaff26306d1" "526c1af3ca6d9cf5a2c98f47e1c46db9" "a33234cfd4d81f2c98538a09ebe76998" "d0d8fd25997c7d255c6d66ece6fa56f1" "1144950f027795e653008f4bd7ca2dee" "85d8e90f3dc315130ce2a00375a318c7" "c3d97be2c8ce5b6db41a6254ff264fa6" "155baee3b0773c0f497c573f19bb4f42" "40281f0b1f4f7be857a4e59d416c06b4" "c50fa09e1810ddc6b1467baeac5a3668" "d11b6ecaa901440016f389f80acc4db9" "77025e7f5924388c7e340a732e554440" "e76570f8dd71b7d640b3450d1fd5f041" "0a18f9a3494f707c717b79b4bf75c984" "00b096b21653b5d217cf3565c9597456" "f70703497a078763829bc01bb1cbc8fa" "04eadc9a6e3f6699587a9e75c94e5bab" "0036e0b2e711392cff0047d0d6b05bd2" "a588bc109718954259f1d86678a579a3" "120f19cfb2963f177aeb70f2d4844826" "262e51b80271272068ef5b3856fa8535" "aa2a88b2d41f2a0e2fda7624c2850272" "ac4a2f561f8f2f7a318bfd5caf969614" "9e4ac824ad3460538fdc25421beec2cc" "6818162d06bbed0c40a387192349db67" "a118bada6cd5ab0140ee273204f628aa" "d1c135f770279a651e24d8c14d75a605" "9d76b96a6fd857def5e0b354b27ab937" "a5815d16b5fae407ff18222c6d1ed263" "be68c95f32d908bd895cd76207ae7264" "87567f9a67dad79abec316f683b17f2d" "02bf07e0ac8b5bc6162cf94697b3c27c" "d1fea49b27f23ba2901871962506520c" "392da8b6ad0d99f7013fbc06c2c17a56" "9500c8a7696481c1cd33e9b14e40b82e" "79a5f5db82571ba97bae3ad3e0479515" "bb0e2b0f3bfcd1fd33034efc6245eddd" "7ee2086ddae2600d8ca73e214e8c2b0b" "db2b047c6a464a562ed77b73d2d841c4" "b34973551257713b753632efba348169" "abc90a68f42611a40126d7cb21b58695" "568186f7e569d2ff0f9e745d0487dd2e" "b997cafc5abf9dd102e62ff66cba87"), % MESSAGE hexstr2bin( "e301345a41a39a4d72fff8df69c98075" "a0cc082b802fc9b2b6bc503f926b65bd" "df7f4c8f1cb49f6396afc8a70abe6d8a" "ef0db478d4c6b2970076c6a0484fe76d" "76b3a97625d79f1ce240e7c576750d29" "5528286f719b413de9ada3e8eb78ed57" "3603ce30d8bb761785dc30dbc320869e" "1a00") % SIGNATURE } ]}, {ed448ph, [ { % TEST abc hexstr2bin( "833fe62409237b9d62ec77587520911e" "9a759cec1d19755b7da901b96dca3d42" "ef7822e0d5104127dc05d6dbefde69e3" "ab2cec7c867c6e2c49"), % SECRET KEY hexstr2bin( "259b71c19f83ef77a7abd26524cbdb31" "61b590a48f7d17de3ee0ba9c52beb743" "c09428a131d6b1b57303d90d8132c276" "d5ed3d5d01c0f53880"), % PUBLIC KEY hexstr2bin("616263"), % MESSAGE hexstr2bin( "963cf799d20fdf51c460310c1cf65d0e" "83c4ef5aa73332ba5b4c1e7635ff9e9b" "6a12b16436fa3681b92575e7eba40ee2" "79c487ad724b6d1080e1860e63dbdd58" "9f5125505b4de024264625e61b097956" "8703f9d9e2bbf5523a1886ee6da1ecb2" "0552bb506eb35a042658ec534bfc1c2c" "1a00") % SIGNATURE }, { % TEST abc (with context) hexstr2bin( "833fe62409237b9d62ec77587520911e" "9a759cec1d19755b7da901b96dca3d42" "ef7822e0d5104127dc05d6dbefde69e3" "ab2cec7c867c6e2c49"), % SECRET KEY hexstr2bin( "259b71c19f83ef77a7abd26524cbdb31" "61b590a48f7d17de3ee0ba9c52beb743" "c09428a131d6b1b57303d90d8132c276" "d5ed3d5d01c0f53880"), % PUBLIC KEY hexstr2bin("616263"), % MESSAGE hexstr2bin("666f6f"), % CONTEXT hexstr2bin( "86a6bf52f9e8f84f451b2f392a8d1c3a" "414425fac0068f74aeead53b0e6b53d4" "555cea1726da4a65202880d407267087" "9e8e6fa4d9694c060054f2065dc206a6" "e615d0d8c99b95209b696c8125c5fbb9" "bc82a0f7ed3d99c4c11c47798ef0f7eb" "97b3b72ab4ac86eaf8b43449e8ac30ff" "3f00") % SIGNATURE } ]}, {x448, [ { hexstr2bin("9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28dd9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b"), % Alice's private key, f hexstr2bin("9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0"), % Alice's public key, X448(f, 9) hexstr2bin("1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d"), % Bob's private key, g hexstr2bin("3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b43027d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609"), % Bob's public key, X448(g, 9) hexstr2bin("07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282bb60c0b56fd2464c335543936521c24403085d59a449a5037514a879d") % Their shared secret, K } ]} | jose_ct:start(G, Config) ]; init_per_group(G='gcmtestvectors', Config) -> Folder = data_file("gcmtestvectors", Config), {ok, Entries} = file:list_dir(Folder), Files = [filename:join([Folder, Entry]) || Entry <- Entries], [{aes_gcm_files, Files}, {one_in, 10} | jose_ct:start(G, Config)]; init_per_group(G='KAT_AES', Config) -> Folder = data_file("KAT_AES", Config), {ok, Entries} = file:list_dir(Folder), Files = [filename:join([Folder, Entry]) || Entry <- Entries], [{aes_files, Files} | jose_ct:start(G, Config)]; init_per_group(G='keccaktestvectors', Config) -> Folder = data_file("keccaktestvectors", Config), {ok, Entries} = file:list_dir(Folder), Files = [filename:join([Folder, Entry]) || Entry <- Entries], [{sha3_files, Files} | jose_ct:start(G, Config)]; init_per_group(G='kwtestvectors', Config) -> Folder = data_file("kwtestvectors", Config), {ok, Entries} = file:list_dir(Folder), Files = [filename:join([Folder, Entry]) || Entry <- Entries], [{aeskw_files, Files}, {one_in, 5} | jose_ct:start(G, Config)]; init_per_group(G='nist-800-56A', Config) -> Vectors = [ %% See [https://tools.ietf.org/html/rfc7518#appendix-C] {sha256, <<158,86,217,29,129,113,53,211,114,131,66,131,191,132,38,156,251,49,110,163,218,128,106,72,246,218,167,121,140,254,144,196>>, {<<"A128GCM">>, <<"Alice">>, <<"Bob">>, << 0, 0, 0, 128 >>, <<>>}, 128, <<86,170,141,234,248,35,109,32,92,34,40,205,113,167,16,26>>}, %% See [https://bitbucket.org/b_c/jose4j/src/cb968fdb10bdef6ecedf279b030f9b3af59f5e8e/src/test/java/org/jose4j/jwe/kdf/ConcatKeyDerivationFunctionTest.java] {sha256, jose_jwa_base64url:decode(<<"Sq8rGLm4rEtzScmnSsY5r1n-AqBl_iBU8FxN80Uc0S0">>), {<<"A256CBC-HS512">>, <<>>, <<>>, << 0, 0, 2, 0 >>, <<>>}, 512, jose_jwa_base64url:decode(<<"pgs50IOZ6BxfqvTSie4t9OjWxGr4whiHo1v9Dti93CRiJE2PP60FojLatVVrcjg3BxpuFjnlQxL97GOwAfcwLA">>)}, {sha256, jose_jwa_base64url:decode(<<"LfkHot2nGTVlmfxbgxQfMg">>), {<<"A128CBC-HS256">>, <<>>, <<>>, << 0, 0, 1, 0 >>, <<>>}, 256, jose_jwa_base64url:decode(<<"vphyobtvExGXF7TaOvAkx6CCjHQNYamP2ET8xkhTu-0">>)}, {sha256, jose_jwa_base64url:decode(<<"KSDnQpf2iurUsAbcuI4YH-FKfk2gecN6cWHTYlBzrd8">>), {<<"meh">>, <<"Alice">>, <<"Bob">>, << 0, 0, 4, 0 >>, <<>>}, 1024, jose_jwa_base64url:decode(<<"yRbmmZJpxv3H1aq3FgzESa453frljIaeMz6pt5rQZ4Q5Hs-4RYoFRXFh_qBsbTjlsj8JxIYTWj-cp5LKtgi1fBRsf_5yTEcLDv4pKH2fNxjbEOKuVVDWA1_Qv2IkEC0_QSi3lSSELcJaNX-hDG8occ7oQv-w8lg6lLJjg58kOes">>)}, {sha256, jose_jwa_base64url:decode(<<"zp9Hot2noTVlmfxbkXqfn1">>), {<<"A192CBC-HS384">>, <<>>, <<>>, << 0, 0, 1, 128 >>, <<>>}, 384, jose_jwa_base64url:decode(<<"SNOvl6h5iSYWJ_EhlnvK8o6om9iyR8HkKMQtQYGkYKkVY0HFMleoUm-H6-kLz8sW">>)} ], [{vectors, Vectors} | jose_ct:start(G, Config)]; init_per_group(G='pkcs-1v2-1-vec', Config) -> OAEPVectFile = data_file("pkcs-1v2-1-vec/oaep-vect.txt", Config), PSSVectFile = data_file("pkcs-1v2-1-vec/pss-vect.txt", Config), [{oaep_vect_file, OAEPVectFile}, {pss_vect_file, PSSVectFile} | jose_ct:start(G, Config)]; init_per_group(G='pkcs-5', Config) -> PBKDF1Vectors = [ %% See [https://github.com/erlang/otp/blob/OTP-18.0/lib/public_key/test/pbe_SUITE.erl] {sha, <<"password">>, <<16#78,16#57,16#8E,16#5A,16#5D,16#63,16#CB,16#06>>, 1000, 16, << 16#DC, 16#19, 16#84, 16#7E, 16#05, 16#C6, 16#4D, 16#2F, 16#AF, 16#10, 16#EB, 16#FB, 16#4A, 16#3D, 16#2A, 16#20 >>} ], PBKDF2Vectors = [ %% See [https://tools.ietf.org/html/rfc6070] {sha, <<"password">>, <<"salt">>, 1, 20, hex:hex_to_bin(<<"0c60c80f961f0e71f3a9b524af6012062fe037a6">>)}, {sha, <<"password">>, <<"salt">>, 2, 20, hex:hex_to_bin(<<"ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957">>)}, {sha, <<"password">>, <<"salt">>, 4096, 20, hex:hex_to_bin(<<"4b007901b765489abead49d926f721d065a429c1">>)}, {sha, <<"password">>, <<"salt">>, 16777216, 20, hex:hex_to_bin(<<"eefe3d61cd4da4e4e9945b3d6ba2158c2634e984">>)}, {sha, <<"passwordPASSWORDpassword">>, <<"saltSALTsaltSALTsaltSALTsaltSALTsalt">>, 4096, 25, hex:hex_to_bin(<<"3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038">>)}, {sha, <<"pass\0word">>, <<"sa\0lt">>, 4096, 16, hex:hex_to_bin(<<"56fa6aa75548099dcc37d7f03425e0c3">>)}, %% See [http://stackoverflow.com/a/5136918/818187] {sha256, <<"password">>, <<"salt">>, 1, 32, hex:hex_to_bin(<<"120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b">>)}, {sha256, <<"password">>, <<"salt">>, 2, 32, hex:hex_to_bin(<<"ae4d0c95af6b46d32d0adff928f06dd02a303f8ef3c251dfd6e2d85a95474c43">>)}, {sha256, <<"password">>, <<"salt">>, 4096, 32, hex:hex_to_bin(<<"c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a">>)}, {sha256, <<"password">>, <<"salt">>, 16777216, 32, hex:hex_to_bin(<<"cf81c66fe8cfc04d1f31ecb65dab4089f7f179e89b3b0bcb17ad10e3ac6eba46">>)}, {sha256, <<"passwordPASSWORDpassword">>, <<"saltSALTsaltSALTsaltSALTsaltSALTsalt">>, 4096, 40, hex:hex_to_bin(<<"348c89dbcbd32b2f32d814b8116e84cf2b17347ebc1800181c4e2a1fb8dd53e1c635518c7dac47e9">>)}, {sha256, <<"pass\0word">>, <<"sa\0lt">>, 4096, 16, hex:hex_to_bin(<<"89b69d0516f829893c696226650a8687">>)} ], [{pbkdf1_vectors, PBKDF1Vectors}, {pbkdf2_vectors, PBKDF2Vectors} | jose_ct:start(G, Config)]; init_per_group(G='pkcs-7', Config) -> Vectors = [begin {hex:hex_to_bin(K), hex:hex_to_bin(V)} end || {K, V} <- [ {<< >>, <<"10101010101010101010101010101010">>}, {<<"00" >>, <<"000f0f0f0f0f0f0f0f0f0f0f0f0f0f0f">>}, {<<"0000" >>, <<"00000e0e0e0e0e0e0e0e0e0e0e0e0e0e">>}, {<<"000000" >>, <<"0000000d0d0d0d0d0d0d0d0d0d0d0d0d">>}, {<<"00000000" >>, <<"000000000c0c0c0c0c0c0c0c0c0c0c0c">>}, {<<"0000000000" >>, <<"00000000000b0b0b0b0b0b0b0b0b0b0b">>}, {<<"000000000000" >>, <<"0000000000000a0a0a0a0a0a0a0a0a0a">>}, {<<"00000000000000" >>, <<"00000000000000090909090909090909">>}, {<<"0000000000000000" >>, <<"00000000000000000808080808080808">>}, {<<"000000000000000000" >>, <<"00000000000000000007070707070707">>}, {<<"00000000000000000000" >>, <<"00000000000000000000060606060606">>}, {<<"0000000000000000000000" >>, <<"00000000000000000000000505050505">>}, {<<"000000000000000000000000" >>, <<"00000000000000000000000004040404">>}, {<<"00000000000000000000000000" >>, <<"00000000000000000000000000030303">>}, {<<"0000000000000000000000000000" >>, <<"00000000000000000000000000000202">>}, {<<"000000000000000000000000000000" >>, <<"00000000000000000000000000000001">>}, {<<"00000000000000000000000000000000">>, <<"0000000000000000000000000000000010101010101010101010101010101010">>} ]], [{vectors, Vectors} | jose_ct:start(G, Config)]. end_per_group(_Group, Config) -> jose_ct:stop(Config), ok. %%==================================================================== %% Tests %%==================================================================== concatenation_kdf(Config) -> Vectors = ?config(vectors, Config), concatenation_kdf(Vectors, Config). curve25519(Config) -> Vectors = ?config(curve25519, Config), lists:foreach(fun curve25519_vector/1, Vectors). curve448(Config) -> Vectors = ?config(curve448, Config), lists:foreach(fun curve448_vector/1, Vectors). ed25519(Config) -> Vectors = ?config(ed25519, Config), lists:foreach(fun ed25519_vector/1, Vectors). ed25519ph(Config) -> Vectors = ?config(ed25519ph, Config), lists:foreach(fun ed25519ph_vector/1, Vectors). ed448(Config) -> Vectors = ?config(ed448, Config), lists:foreach(fun ed448_vector/1, Vectors). ed448ph(Config) -> Vectors = ?config(ed448ph, Config), lists:foreach(fun ed448ph_vector/1, Vectors). emc_rsa_oaep_encrypt_and_decrypt(Config) -> Vectors = emc_testvector:from_file(?config(oaep_vect_file, Config)), emc_rsa_oaep_encrypt_and_decrypt(Vectors, Config). emc_rsa_pss_sign_and_verify(Config) -> Vectors = emc_testvector:from_file(?config(pss_vect_file, Config)), emc_rsa_pss_sign_and_verify(Vectors, Config). fips_aes_encrypt_and_decrypt(Config) -> Files = ?config(aes_files, Config), lists:foldl(fun fips_aes_encrypt_and_decrypt/2, Config, Files). fips_aes_gcm_encrypt_and_decrypt(Config) -> Files = ?config(aes_gcm_files, Config), lists:foldl(fun fips_aes_gcm_encrypt_and_decrypt/2, Config, Files). fips_aeskw_unwrap(Config) -> Filter = fun(File) -> case filename:basename(File) of "KW_AD_" ++ _ -> true; _ -> false end end, Files = [File || File <- ?config(aeskw_files, Config), Filter(File)], lists:foldl(fun fips_aeskw_unwrap/2, Config, Files). fips_aeskw_wrap(Config) -> Filter = fun(File) -> case filename:basename(File) of "KW_AE_" ++ _ -> true; _ -> false end end, Files = [File || File <- ?config(aeskw_files, Config), Filter(File)], lists:foldl(fun fips_aeskw_wrap/2, Config, Files). fips_rsa_pss_sign(Config) -> Vectors = fips_testvector:from_file(?config(sig_gen_file, Config)), fips_rsa_pss_sign(Vectors, Config). fips_rsa_pss_verify(Config) -> Vectors = fips_testvector:from_file(?config(sig_ver_file, Config)), fips_rsa_pss_verify(Vectors, Config). fips_sha3(Config) -> Files = [File || File <- ?config(sha3_files, Config)], lists:foldl(fun fips_sha3/2, Config, Files). pbkdf1(Config) -> Vectors = ?config(pbkdf1_vectors, Config), pbkdf1(Vectors, Config). pbkdf2(Config) -> Vectors = ?config(pbkdf2_vectors, Config), pbkdf2(Vectors, Config). pkcs7_pad_and_unpad(Config) -> Vectors = ?config(vectors, Config), pkcs7_pad_and_unpad(Vectors, Config). x25519(Config) -> Vectors = ?config(x25519, Config), lists:foreach(fun x25519_vector/1, Vectors). x448(Config) -> Vectors = ?config(x448, Config), lists:foreach(fun x448_vector/1, Vectors). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private archive_file(File, Config) -> filename:join([?config(data_dir, Config), "archive", File]). %% @private concatenation_kdf([{Hash, Z, OtherInfo, KeyDataLen, DerivedKey} | Vectors], Config) -> case jose_jwa_concat_kdf:kdf(Hash, Z, OtherInfo, KeyDataLen) of DerivedKey -> concatenation_kdf(Vectors, Config); Other -> ct:fail({{jose_jwa_concat_kdf, kdf, [Hash, Z, OtherInfo, KeyDataLen]}, {expected, DerivedKey}, {got, Other}}) end; concatenation_kdf([], _Config) -> ok. %% @private curve25519_vector({InputK, InputU, OutputU}) -> ?tv_ok(T0, jose_jwa_x25519, curve25519, [InputK, InputU], OutputU). %% @private curve448_vector({InputK, InputU, OutputU}) -> ?tv_ok(T0, jose_jwa_x448, curve448, [InputK, InputU], OutputU). %% @private data_file(File, Config) -> filename:join([?config(data_dir, Config), File]). %% @private data_setup(Config) -> ArchiveDir = data_file("archive", Config), case filelib:is_dir(ArchiveDir) of true -> ok; false -> ok = file:make_dir(ArchiveDir) end, lists:foldl(fun(F, C) -> io:format(user, "\e[0;36m[FETCH] ~s\e[0m", [F]), {ok, Progress} = jose_ct:progress_start(), NewC = data_setup(F, C), ok = jose_ct:progress_stop(Progress), NewC end, Config, [ "186-3rsatestvectors.zip", "aesmmt.zip", "gcmtestvectors.zip", "KAT_AES.zip", "keccaktestvectors", "kwtestvectors.zip", "pkcs-1v2-1-vec.zip" ]). %% @private data_setup(F = "186-3rsatestvectors.zip", Config) -> Zip = archive_file(F, Config), Dir = data_file("186-3rsatestvectors", Config), URL = "https://github.com/potatosalad/test-vector-archive/raw/1.0.0/archive/186-3rsatestvectors.zip", ok = data_setup(Zip, Dir, URL), Filter = fun (#zip_file{name = "SigGenPSS_186-3.txt"}) -> true; (#zip_file{name = "SigVerPSS_186-3.rsp"}) -> true; (_) -> false end, ok = data_setup(Zip, Dir, "SigGenPSS_186-3.txt", Filter), Config; data_setup(F = "aesmmt.zip", Config) -> Zip = archive_file(F, Config), Dir = data_file("aesmmt", Config), URL = "https://github.com/potatosalad/test-vector-archive/raw/1.0.0/archive/aesmmt.zip", ok = data_setup(Zip, Dir, URL), Filter = fun (#zip_file{name = "CBC" ++ _}) -> true; (#zip_file{name = "ECB" ++ _}) -> true; (_) -> false end, ok = data_setup(Zip, Dir, "CBCMMT128.rsp", Filter), Config; data_setup(F = "gcmtestvectors.zip", Config) -> Zip = archive_file(F, Config), Dir = data_file("gcmtestvectors", Config), URL = "https://github.com/potatosalad/test-vector-archive/raw/1.0.0/archive/gcmtestvectors.zip", ok = data_setup(Zip, Dir, URL), case filelib:is_file(filename:join([Dir, "gcmDecrypt128.rsp"])) of true -> ok; false -> {ok, FileList} = zip:unzip(Zip, [ {cwd, Dir} ]), _ = [begin file:change_mode(File, 8#00644) end || File <- FileList], ok end, Config; data_setup(F = "KAT_AES.zip", Config) -> Zip = archive_file(F, Config), Dir = data_file("KAT_AES", Config), URL = "https://github.com/potatosalad/test-vector-archive/raw/1.0.0/archive/KAT_AES.zip", ok = data_setup(Zip, Dir, URL), Filter = fun (#zip_file{name = "CBC" ++ _}) -> true; (#zip_file{name = "ECB" ++ _}) -> true; (_) -> false end, ok = data_setup(Zip, Dir, "CBCGFSbox128.rsp", Filter), Config; data_setup(F = "keccaktestvectors", Config) -> BaseURL = "https://raw.githubusercontent.com/gvanas/KeccakCodePackage/1893f17c8029d0e6423f1fa4de4d15f76b188a27/TestVectors/", Files = [ "ShortMsgKAT_SHA3-224.txt", "ShortMsgKAT_SHA3-256.txt", "ShortMsgKAT_SHA3-384.txt", "ShortMsgKAT_SHA3-512.txt", "ShortMsgKAT_SHAKE128.txt", "ShortMsgKAT_SHAKE256.txt" ], URLs = [BaseURL ++ File || File <- Files], Directory = data_file(F, Config), DataFiles = [data_file(filename:join(F, File), Config) || File <- Files], ok = data_setup_multiple(DataFiles, Directory, URLs), Config; data_setup(F = "kwtestvectors.zip", Config) -> Zip = archive_file(F, Config), Dir = data_file("kwtestvectors", Config), URL = "https://github.com/potatosalad/test-vector-archive/raw/1.0.0/archive/kwtestvectors.zip", ok = data_setup(Zip, Dir, URL), Filter = fun (#zip_file{name = "KW_" ++ Name}) -> case lists:reverse(Name) of "txt.vni_" ++ _ -> false; _ -> true end; (_) -> false end, ok = data_setup(Zip, Dir, "KW_AD_128.txt", Filter), Config; data_setup(F = "pkcs-1v2-1-vec.zip", Config) -> Zip = archive_file(F, Config), Dir = data_file("pkcs-1v2-1-vec", Config), URL = "https://github.com/potatosalad/test-vector-archive/raw/1.0.0/archive/pkcs-1v2-1-vec.zip", ok = data_setup(Zip, Dir, URL), Filter = fun (#zip_file{name = "oaep-vect.txt"}) -> true; (#zip_file{name = "pss-vect.txt"}) -> true; (_) -> false end, ok = data_setup(Zip, Dir, "oaep-vect.txt", Filter), Config. %% @private data_setup(Zip, Directory, URL) -> case filelib:is_file(Zip) of true -> ok; false -> ok = fetch:fetch(URL, Zip) end, case filelib:is_dir(Directory) of true -> ok; false -> ok = file:make_dir(Directory) end, ok. %% @private data_setup(Zip, Dir, Check, Filter) -> case filelib:is_file(filename:join([Dir, Check])) of true -> ok; false -> Options = case is_function(Filter, 1) of false -> [{cwd, Dir}]; true -> [{cwd, Dir}, {file_filter, Filter}] end, {ok, FileList} = zip:unzip(Zip, Options), _ = [begin file:change_mode(File, 8#00644) end || File <- FileList], ok end. %% @private data_setup_multiple([DataFile | DataFiles], Directory, [URL | URLs]) -> case filelib:is_dir(Directory) of true -> ok; false -> ok = file:make_dir(Directory) end, case filelib:is_file(DataFile) of true -> ok; false -> ok = fetch:fetch(URL, DataFile) end, data_setup_multiple(DataFiles, Directory, URLs); data_setup_multiple([], _Directory, []) -> ok. %% @private ed25519_vector({Secret, PK, Message, Signature}) -> SK = << Secret/binary, PK/binary >>, ?tv_ok(T0, jose_jwa_curve25519, eddsa_secret_to_public, [Secret], PK), ?tv_ok(T1, jose_jwa_curve25519, ed25519_sign, [Message, SK], Signature), ?tv_ok(T2, jose_jwa_curve25519, ed25519_verify, [Signature, Message, PK], true). %% @private ed25519ph_vector({Secret, PK, Message, Signature}) -> SK = << Secret/binary, PK/binary >>, ?tv_ok(T0, jose_jwa_curve25519, eddsa_secret_to_public, [Secret], PK), ?tv_ok(T1, jose_jwa_curve25519, ed25519ph_sign, [Message, SK], Signature), ?tv_ok(T2, jose_jwa_curve25519, ed25519ph_verify, [Signature, Message, PK], true). %% @private ed448_vector({Secret, PK, Message, Signature}) -> SK = << Secret/binary, PK/binary >>, ?tv_ok(T0, jose_jwa_curve448, eddsa_secret_to_public, [Secret], PK), ?tv_ok(T1, jose_jwa_curve448, ed448_sign, [Message, SK], Signature), ?tv_ok(T2, jose_jwa_curve448, ed448_verify, [Signature, Message, PK], true); ed448_vector({Secret, PK, Message, Context, Signature}) -> SK = << Secret/binary, PK/binary >>, ?tv_ok(T0, jose_jwa_curve448, eddsa_secret_to_public, [Secret], PK), ?tv_ok(T1, jose_jwa_curve448, ed448_sign, [Message, SK, Context], Signature), ?tv_ok(T2, jose_jwa_curve448, ed448_verify, [Signature, Message, PK, Context], true). %% @private ed448ph_vector({Secret, PK, Message, Signature}) -> SK = << Secret/binary, PK/binary >>, ?tv_ok(T0, jose_jwa_curve448, eddsa_secret_to_public, [Secret], PK), ?tv_ok(T1, jose_jwa_curve448, ed448ph_sign, [Message, SK], Signature), ?tv_ok(T2, jose_jwa_curve448, ed448ph_verify, [Signature, Message, PK], true); ed448ph_vector({Secret, PK, Message, Context, Signature}) -> SK = << Secret/binary, PK/binary >>, ?tv_ok(T0, jose_jwa_curve448, eddsa_secret_to_public, [Secret], PK), ?tv_ok(T1, jose_jwa_curve448, ed448ph_sign, [Message, SK, Context], Signature), ?tv_ok(T2, jose_jwa_curve448, ed448ph_verify, [Signature, Message, PK, Context], true). %% @private emc_rsa_oaep_encrypt_and_decrypt([ divider, {example, Example}, {component, <<"Components of the RSA Key Pair">>}, {vector, {<<"RSA modulus n">>, N}}, {vector, {<<"RSA public exponent e">>, E}}, {vector, {<<"RSA private exponent d">>, D}}, {vector, {<<"Prime p">>, P}}, {vector, {<<"Prime q">>, Q}}, {vector, {<<"p's CRT exponent dP">>, DP}}, {vector, {<<"q's CRT exponent dQ">>, DQ}}, {vector, {<<"CRT coefficient qInv">>, QI}} | Vectors ], Config) -> RSAPrivateKey = #'RSAPrivateKey'{ version = 'two-prime', otherPrimeInfos = 'asn1_NOVALUE', privateExponent = crypto:bytes_to_integer(D), exponent1 = crypto:bytes_to_integer(DP), exponent2 = crypto:bytes_to_integer(DQ), publicExponent = crypto:bytes_to_integer(E), modulus = crypto:bytes_to_integer(N), prime1 = crypto:bytes_to_integer(P), prime2 = crypto:bytes_to_integer(Q), coefficient = crypto:bytes_to_integer(QI) }, RSAPublicKey = #'RSAPublicKey'{ publicExponent = crypto:bytes_to_integer(E), modulus = crypto:bytes_to_integer(N) }, io:format("~s", [Example]), emc_rsa_oaep_encrypt_and_decrypt(Vectors, {RSAPrivateKey, RSAPublicKey}, Config); emc_rsa_oaep_encrypt_and_decrypt([divider], _Config) -> ok; emc_rsa_oaep_encrypt_and_decrypt([], _Config) -> ok. %% @private emc_rsa_oaep_encrypt_and_decrypt([ {component, Component}, {vector, {<<"Message to be encrypted">>, Message}}, {vector, {<<"Seed">>, Seed}}, {vector, {<<"Encryption">>, Encryption}} | Vectors ], {RSAPrivateKey, RSAPublicKey}, Config) -> io:format("\t~s", [Component]), HashFun = sha, Label = <<>>, case jose_jwa_pkcs1:rsaes_oaep_encrypt(HashFun, Message, Label, Seed, RSAPublicKey) of {ok, Encryption} -> ok; EncryptError -> ct:fail({{jose_jwa_pkcs1, rsaes_oaep_encrypt, [HashFun, Message, Label, Seed, RSAPublicKey]}, {expected, {ok, Encryption}}, {got, EncryptError}}) end, case jose_jwa_pkcs1:rsaes_oaep_decrypt(HashFun, Encryption, Label, RSAPrivateKey) of Message -> emc_rsa_oaep_encrypt_and_decrypt(Vectors, {RSAPrivateKey, RSAPublicKey}, Config); DecryptError -> ct:fail({{jose_jwa_pkcs1, rsaes_oaep_decrypt, [HashFun, Encryption, Label, RSAPrivateKey]}, {expected, Message}, {got, DecryptError}}) end; emc_rsa_oaep_encrypt_and_decrypt(Vectors = [divider | _], _RSAKeyPair, Config) -> emc_rsa_oaep_encrypt_and_decrypt(Vectors, Config). %% @private emc_rsa_pss_sign_and_verify([ divider, {example, Example}, {component, <<"Components of the RSA Key Pair">>}, {vector, {<<"RSA modulus n">>, N}}, {vector, {<<"RSA public exponent e">>, E}}, {vector, {<<"RSA private exponent d">>, D}}, {vector, {<<"Prime p">>, P}}, {vector, {<<"Prime q">>, Q}}, {vector, {<<"p's CRT exponent dP">>, DP}}, {vector, {<<"q's CRT exponent dQ">>, DQ}}, {vector, {<<"CRT coefficient qInv">>, QI}} | Vectors ], Config) -> RSAPrivateKey = #'RSAPrivateKey'{ version = 'two-prime', otherPrimeInfos = 'asn1_NOVALUE', privateExponent = crypto:bytes_to_integer(D), exponent1 = crypto:bytes_to_integer(DP), exponent2 = crypto:bytes_to_integer(DQ), publicExponent = crypto:bytes_to_integer(E), modulus = crypto:bytes_to_integer(N), prime1 = crypto:bytes_to_integer(P), prime2 = crypto:bytes_to_integer(Q), coefficient = crypto:bytes_to_integer(QI) }, RSAPublicKey = #'RSAPublicKey'{ publicExponent = crypto:bytes_to_integer(E), modulus = crypto:bytes_to_integer(N) }, io:format("~s", [Example]), emc_rsa_pss_sign_and_verify(Vectors, {RSAPrivateKey, RSAPublicKey}, Config); emc_rsa_pss_sign_and_verify([divider], _Config) -> ok; emc_rsa_pss_sign_and_verify([], _Config) -> ok. %% @private emc_rsa_pss_sign_and_verify([ {component, Component}, {vector, {<<"Message to be signed">>, Message}}, {vector, {<<"Salt">>, Salt}}, {vector, {<<"Signature">>, Signature}} | Vectors ], {RSAPrivateKey, RSAPublicKey}, Config) -> io:format("\t~s", [Component]), HashFun = sha, case jose_jwa_pkcs1:rsassa_pss_sign(HashFun, Message, Salt, RSAPrivateKey) of {ok, Signature} -> ok; Other -> ct:fail({{jose_jwa_pkcs1, rsassa_pss_sign, [HashFun, Message, Salt, RSAPrivateKey]}, {expected, {ok, Signature}}, {got, Other}}) end, SaltLen = byte_size(Salt), case jose_jwa_pkcs1:rsassa_pss_verify(HashFun, Message, Signature, SaltLen, RSAPublicKey) of true -> emc_rsa_pss_sign_and_verify(Vectors, {RSAPrivateKey, RSAPublicKey}, Config); false -> ct:fail({{jose_jwa_pkcs1, rsassa_pss_verify, [HashFun, Message, Signature, SaltLen, RSAPublicKey]}, {expected, true}, {got, false}}) end; emc_rsa_pss_sign_and_verify(Vectors = [divider | _], _RSAKeyPair, Config) -> emc_rsa_pss_sign_and_verify(Vectors, Config). %% @private fips_aes_encrypt_and_decrypt(File, Config) -> VectorsName = iolist_to_binary(filename:basename(File)), Algorithm = case VectorsName of << "CBC", _/binary >> -> aes_cbc; << "ECB", _/binary >> -> aes_ecb end, {Pos, 3} = binary:match(VectorsName, [<<"128">>, <<"192">>, <<"256">>]), Bits = binary_to_integer(binary:part(VectorsName, Pos, 3)), Cipher = {Algorithm, Bits}, Vectors = fips_testvector:from_file(File), io:format("~s", [VectorsName]), fips_aes_encrypt_and_decrypt(Vectors, Cipher, Config). %% @private fips_aes_encrypt_and_decrypt([ {vector, {<<"COUNT">>, Count}, _}, {vector, {<<"KEY">>, Key}, _}, {vector, {<<"PLAINTEXT">>, PlainText}, _}, {vector, {<<"CIPHERTEXT">>, CipherText}, _} | Vectors ], Cipher, Config) -> io:format("\tENCRYPT = ~w", [Count]), case jose_jwa_aes:block_encrypt(Cipher, Key, PlainText) of CipherText -> fips_aes_encrypt_and_decrypt(Vectors, Cipher, Config); EncryptError -> ct:fail({{jose_jwa_aes, block_encrypt, [Cipher, Key, PlainText]}, {expected, CipherText}, {got, EncryptError}}) end; fips_aes_encrypt_and_decrypt([ {vector, {<<"COUNT">>, Count}, _}, {vector, {<<"KEY">>, Key}, _}, {vector, {<<"CIPHERTEXT">>, CipherText}, _}, {vector, {<<"PLAINTEXT">>, PlainText}, _} | Vectors ], Cipher, Config) -> io:format("\tDECRYPT = ~w", [Count]), case jose_jwa_aes:block_decrypt(Cipher, Key, CipherText) of PlainText -> fips_aes_encrypt_and_decrypt(Vectors, Cipher, Config); DecryptError -> ct:fail({{jose_jwa_aes, block_decrypt, [Cipher, Key, CipherText]}, {expected, PlainText}, {got, DecryptError}}) end; fips_aes_encrypt_and_decrypt([ {vector, {<<"COUNT">>, Count}, _}, {vector, {<<"KEY">>, Key}, _}, {vector, {<<"IV">>, IV}, _}, {vector, {<<"PLAINTEXT">>, PlainText}, _}, {vector, {<<"CIPHERTEXT">>, CipherText}, _} | Vectors ], Cipher, Config) -> io:format("\tENCRYPT = ~w", [Count]), case jose_jwa_aes:block_encrypt(Cipher, Key, IV, PlainText) of CipherText -> fips_aes_encrypt_and_decrypt(Vectors, Cipher, Config); EncryptError -> ct:fail({{jose_jwa_aes, block_encrypt, [Cipher, Key, IV, PlainText]}, {expected, CipherText}, {got, EncryptError}}) end; fips_aes_encrypt_and_decrypt([ {vector, {<<"COUNT">>, Count}, _}, {vector, {<<"KEY">>, Key}, _}, {vector, {<<"IV">>, IV}, _}, {vector, {<<"CIPHERTEXT">>, CipherText}, _}, {vector, {<<"PLAINTEXT">>, PlainText}, _} | Vectors ], Cipher, Config) -> io:format("\tDECRYPT = ~w", [Count]), case jose_jwa_aes:block_decrypt(Cipher, Key, IV, CipherText) of PlainText -> fips_aes_encrypt_and_decrypt(Vectors, Cipher, Config); DecryptError -> ct:fail({{jose_jwa_aes, block_decrypt, [Cipher, Key, IV, CipherText]}, {expected, PlainText}, {got, DecryptError}}) end; fips_aes_encrypt_and_decrypt([ {flag, Flag} | Vectors ], Cipher, Config) when Flag =:= <<"DECRYPT">> orelse Flag =:= <<"ENCRYPT">> -> fips_aes_encrypt_and_decrypt(Vectors, Cipher, Config); fips_aes_encrypt_and_decrypt([], _Cipher, Config) -> Config. %% @private fips_aes_gcm_encrypt_and_decrypt(File, Config) -> VectorsName = iolist_to_binary(filename:basename(File)), {Algorithm, Mode} = case VectorsName of << "gcmDecrypt", _/binary >> -> {aes_gcm, decrypt}; << "gcmEncrypt", _/binary >> -> {aes_gcm, encrypt} end, {Pos, 3} = binary:match(VectorsName, [<<"128">>, <<"192">>, <<"256">>]), Bits = binary_to_integer(binary:part(VectorsName, Pos, 3)), Cipher = {Algorithm, Bits}, Vectors = fips_testvector:from_file(File), io:format("~s", [VectorsName]), fips_aes_gcm_encrypt_and_decrypt(Vectors, {Cipher, Mode, undefined}, Config). %% @private fips_aes_gcm_encrypt_and_decrypt([ {vector, {<<"Count">>, Count}, _}, {vector, {<<"Key">>, Key}, _}, {vector, {<<"IV">>, IV}, _}, {vector, {<<"CT">>, CT}, _}, {vector, {<<"AAD">>, AAD}, _}, {vector, {<<"Tag">>, Tag}, _}, {token, <<"FAIL">>} | Vectors ], {Cipher, decrypt, {Keylen, IVlen, PTlen, AADlen, Taglen, Counts}}, Config) when bit_size(Key) =:= Keylen andalso bit_size(IV) =:= IVlen andalso bit_size(CT) =:= PTlen andalso bit_size(AAD) =:= AADlen andalso bit_size(Tag) =:= Taglen -> case rand:uniform(?config(one_in, Config)) of 1 -> case jose_jwa_aes:block_decrypt(Cipher, Key, IV, {AAD, CT, Tag}) of error -> fips_aes_gcm_encrypt_and_decrypt(Vectors, {Cipher, decrypt, {Keylen, IVlen, PTlen, AADlen, Taglen, << Counts/binary, (integer_to_binary(Count))/binary, "..." >>}}, Config); OtherDecrypt -> io:format("\t\tCounts = ~s", [<< Counts/binary, (integer_to_binary(Count))/binary, "..." >>]), ct:fail({{jose_jwa_aes, block_decrypt, [Cipher, Key, IV, {AAD, CT, Tag}]}, {expected, error}, {got, OtherDecrypt}}) end; _ -> fips_aes_gcm_encrypt_and_decrypt(Vectors, {Cipher, decrypt, {Keylen, IVlen, PTlen, AADlen, Taglen, << Counts/binary, (integer_to_binary(Count))/binary, "..." >>}}, Config) end; fips_aes_gcm_encrypt_and_decrypt([ {vector, {<<"Count">>, Count}, _}, {vector, {<<"Key">>, Key}, _}, {vector, {<<"IV">>, IV}, _}, {vector, {<<"CT">>, CT}, _}, {vector, {<<"AAD">>, AAD}, _}, {vector, {<<"Tag">>, Tag}, _}, {vector, {<<"PT">>, PT}, _} | Vectors ], {Cipher, decrypt, {Keylen, IVlen, PTlen, AADlen, Taglen, Counts}}, Config) when bit_size(Key) =:= Keylen andalso bit_size(IV) =:= IVlen andalso bit_size(CT) =:= PTlen andalso bit_size(AAD) =:= AADlen andalso bit_size(Tag) =:= Taglen andalso bit_size(PT) =:= PTlen -> case rand:uniform(?config(one_in, Config)) of 1 -> %% Truncated tags are rejected; only the full 128-bit tag authenticates. Expected = case Taglen of 128 -> PT; _ -> error end, case jose_jwa_aes:block_decrypt(Cipher, Key, IV, {AAD, CT, Tag}) of Expected -> fips_aes_gcm_encrypt_and_decrypt(Vectors, {Cipher, decrypt, {Keylen, IVlen, PTlen, AADlen, Taglen, << Counts/binary, (integer_to_binary(Count))/binary, "..." >>}}, Config); OtherDecrypt -> io:format("\t\tCounts = ~s", [<< Counts/binary, (integer_to_binary(Count))/binary, "..." >>]), io:format("{Cipher, Key, IV, CT, AAD, Tag, PT} = ~w~n", [{Cipher, Key, IV, CT, AAD, Tag, PT}]), ct:fail({{jose_jwa_aes, block_decrypt, [Cipher, Key, IV, {AAD, CT, Tag}]}, {expected, Expected}, {got, OtherDecrypt}}) end; _ -> fips_aes_gcm_encrypt_and_decrypt(Vectors, {Cipher, decrypt, {Keylen, IVlen, PTlen, AADlen, Taglen, << Counts/binary, (integer_to_binary(Count))/binary, "..." >>}}, Config) end; fips_aes_gcm_encrypt_and_decrypt([ {vector, {<<"Count">>, Count}, _}, {vector, {<<"Key">>, Key}, _}, {vector, {<<"IV">>, IV}, _}, {vector, {<<"PT">>, PT}, _}, {vector, {<<"AAD">>, AAD}, _}, {vector, {<<"CT">>, CT}, _}, {vector, {<<"Tag">>, Tag}, _} | Vectors ], {Cipher, encrypt, {Keylen, IVlen, PTlen, AADlen, Taglen, Counts}}, Config) when bit_size(Key) =:= Keylen andalso bit_size(IV) =:= IVlen andalso bit_size(PT) =:= PTlen andalso bit_size(AAD) =:= AADlen andalso bit_size(CT) =:= PTlen andalso bit_size(Tag) =:= Taglen -> case rand:uniform(?config(one_in, Config)) of 1 -> case jose_jwa_aes:block_encrypt(Cipher, Key, IV, {AAD, PT}) of {CT, << Tag:Taglen/bitstring, _/bitstring >>} -> fips_aes_gcm_encrypt_and_decrypt(Vectors, {Cipher, encrypt, {Keylen, IVlen, PTlen, AADlen, Taglen, << Counts/binary, (integer_to_binary(Count))/binary, "..." >>}}, Config); OtherEncrypt -> io:format("\t\tCounts = ~s", [<< Counts/binary, (integer_to_binary(Count))/binary, "..." >>]), ct:fail({{jose_jwa_aes, block_encrypt, [Cipher, Key, IV, {AAD, PT}]}, {expected, {CT, Tag}}, {got, OtherEncrypt}}) end; _ -> fips_aes_gcm_encrypt_and_decrypt(Vectors, {Cipher, encrypt, {Keylen, IVlen, PTlen, AADlen, Taglen, << Counts/binary, (integer_to_binary(Count))/binary, "..." >>}}, Config) end; fips_aes_gcm_encrypt_and_decrypt([ {option, {<<"Keylen">>, Keylen}}, {option, {<<"IVlen">>, IVlen}}, {option, {<<"PTlen">>, PTlen}}, {option, {<<"AADlen">>, AADlen}}, {option, {<<"Taglen">>, Taglen}} | Vectors ], {Cipher, Mode, undefined}, Config) -> Options = { binary_to_integer(Keylen), binary_to_integer(IVlen), binary_to_integer(PTlen), binary_to_integer(AADlen), binary_to_integer(Taglen), <<>> }, fips_aes_gcm_encrypt_and_decrypt(Vectors, {Cipher, Mode, Options}, Config); fips_aes_gcm_encrypt_and_decrypt(Vectors, {Cipher, Mode, {_Keylen, _IVlen, _PTlen, _AADlen, _Taglen, _Counts}}, Config) -> fips_aes_gcm_encrypt_and_decrypt(Vectors, {Cipher, Mode, undefined}, Config); fips_aes_gcm_encrypt_and_decrypt([], _Cipher, Config) -> Config. %% @private fips_aeskw_unwrap(File, Config) -> << "KW_AD_", BitsBin:3/binary, _/binary >> = iolist_to_binary(filename:basename(File)), Bits = binary_to_integer(BitsBin), Vectors = fips_testvector:from_file(File), io:format("~s", [filename:basename(File)]), fips_aeskw_unwrap(Vectors, {Bits, undefined}, Config). %% @private fips_aeskw_unwrap([ {vector, {<<"COUNT">>, Count}, _}, {vector, {<<"K">>, K}, _}, {vector, {<<"C">>, C}, _}, {vector, {<<"P">>, P}, _} | Vectors ], {Bits, Len}, Config) when is_integer(Len) andalso bit_size(K) =:= Bits andalso bit_size(P) =:= Len -> case rand:uniform(?config(one_in, Config)) of 1 -> case jose_jwa_aes_kw:unwrap(C, K) of P -> fips_aeskw_unwrap(Vectors, {Bits, Len}, Config); Other -> io:format("\t\tCOUNT = ~w", [Count]), ct:fail({{jose_jwa_aes_kw, unwrap, [C, K]}, {expected, P}, {got, Other}}) end; _ -> fips_aeskw_unwrap(Vectors, {Bits, Len}, Config) end; fips_aeskw_unwrap([ {vector, {<<"COUNT">>, Count}, _}, {vector, {<<"K">>, K}, _}, {vector, {<<"C">>, C}, _}, {token, <<"FAIL">>} | Vectors ], {Bits, Len}, Config) when is_integer(Len) andalso bit_size(K) =:= Bits -> case rand:uniform(?config(one_in, Config)) of 1 -> try jose_jwa_aes_kw:unwrap(C, K) of Other -> io:format("\t\tCOUNT = ~w", [Count]), ct:fail({{jose_jwa_aes_kw, unwrap, [C, K]}, {expected, badarg}, {got, Other}}) catch _:_ -> fips_aeskw_unwrap(Vectors, {Bits, Len}, Config) end; _ -> fips_aeskw_unwrap(Vectors, {Bits, Len}, Config) end; fips_aeskw_unwrap([{option, {<<"PLAINTEXTLENGTH">>, LenBin}} | Vectors], {Bits, _}, Config) -> Len = binary_to_integer(LenBin), io:format("\tPLAINTEXTLENGTH = ~w", [Len]), fips_aeskw_unwrap(Vectors, {Bits, Len}, Config); fips_aeskw_unwrap([], _, Config) -> Config. %% @private fips_aeskw_wrap(File, Config) -> << "KW_AE_", BitsBin:3/binary, _/binary >> = iolist_to_binary(filename:basename(File)), Bits = binary_to_integer(BitsBin), Vectors = fips_testvector:from_file(File), io:format("~s", [filename:basename(File)]), fips_aeskw_wrap(Vectors, {Bits, undefined}, Config). %% @private fips_aeskw_wrap([ {vector, {<<"COUNT">>, Count}, _}, {vector, {<<"K">>, K}, _}, {vector, {<<"P">>, P}, _}, {vector, {<<"C">>, C}, _} | Vectors ], {Bits, Len}, Config) when is_integer(Len) andalso bit_size(K) =:= Bits andalso bit_size(P) =:= Len -> case rand:uniform(?config(one_in, Config)) of 1 -> case jose_jwa_aes_kw:wrap(P, K) of C -> fips_aeskw_wrap(Vectors, {Bits, Len}, Config); Other -> io:format("\t\tCOUNT = ~w", [Count]), ct:fail({{jose_jwa_aes_kw, wrap, [P, K]}, {expected, C}, {got, Other}}) end; _ -> fips_aeskw_wrap(Vectors, {Bits, Len}, Config) end; fips_aeskw_wrap([{option, {<<"PLAINTEXTLENGTH">>, LenBin}} | Vectors], {Bits, _}, Config) -> Len = binary_to_integer(LenBin), io:format("\tPLAINTEXTLENGTH = ~w", [Len]), fips_aeskw_wrap(Vectors, {Bits, Len}, Config); fips_aeskw_wrap([], _, Config) -> Config. %% @private fips_rsa_pss_sign([ {option, {<<"mod">>, ModVal}}, {vector, {<<"n">>, N}, _}, {vector, {<<"e">>, E}, _}, {vector, {<<"d">>, D}, _} | Vectors ], Config) -> ModulusSize = binary_to_integer(ModVal), RSAPrivateKey = #'RSAPrivateKey'{ modulus = crypto:bytes_to_integer(N), privateExponent = crypto:bytes_to_integer(D), publicExponent = crypto:bytes_to_integer(E) }, fips_rsa_pss_sign(Vectors, ModulusSize, RSAPrivateKey, Config); fips_rsa_pss_sign([], _Config) -> ok. %% @private fips_rsa_pss_sign([ {vector, {<<"SHAAlg">>, SHAAlg}, _}, {vector, {<<"Msg">>, Msg}, _}, {vector, {<<"S">>, S}, _}, {vector, {<<"SaltVal">>, SaltVal}, _} | Vectors ], ModulusSize, RSAPrivateKey, Config) -> HashFun = shaalg_to_hash_fun(SHAAlg), case jose_jwa_pkcs1:rsassa_pss_sign(HashFun, Msg, SaltVal, RSAPrivateKey) of {ok, S} -> ok; Other -> ct:fail({{jose_jwa_pkcs1, rsassa_pss_sign, [HashFun, Msg, SaltVal, RSAPrivateKey]}, {expected, {ok, S}}, {got, Other}}) end, RSAPublicKey = rsa_private_to_public(RSAPrivateKey), SaltLen = byte_size(SaltVal), case jose_jwa_pkcs1:rsassa_pss_verify(HashFun, Msg, S, SaltLen, RSAPublicKey) of true -> fips_rsa_pss_sign(Vectors, ModulusSize, RSAPrivateKey, Config); false -> ct:fail({{jose_jwa_pkcs1, rsassa_pss_verify, [HashFun, Msg, S, SaltLen, RSAPublicKey]}, {expected, true}, {got, false}}) end; fips_rsa_pss_sign(Vectors, _ModulusSize, _RSAPrivateKey, Config) -> fips_rsa_pss_sign(Vectors, Config). %% @private fips_rsa_pss_verify([ {option, {<<"mod">>, ModVal}}, {vector, {<<"n">>, N}, _}, {vector, {<<"p">>, P}, _}, {vector, {<<"q">>, Q}, _} | Vectors ], Config) -> ModulusSize = binary_to_integer(ModVal), RSAPrivateKey = #'RSAPrivateKey'{ modulus = crypto:bytes_to_integer(N), prime1 = crypto:bytes_to_integer(P), prime2 = crypto:bytes_to_integer(Q) }, fips_rsa_pss_verify(Vectors, ModulusSize, RSAPrivateKey, Config); fips_rsa_pss_verify([], _Config) -> ok. %% @private fips_rsa_pss_verify([ {vector, {<<"n">>, N}, _}, {vector, {<<"p">>, P}, _}, {vector, {<<"q">>, Q}, _} | Vectors ], ModulusSize, Config) -> RSAPrivateKey = #'RSAPrivateKey'{ modulus = crypto:bytes_to_integer(N), prime1 = crypto:bytes_to_integer(P), prime2 = crypto:bytes_to_integer(Q) }, fips_rsa_pss_verify(Vectors, ModulusSize, RSAPrivateKey, Config); fips_rsa_pss_verify(Vectors, _ModulusSize, Config) -> fips_rsa_pss_verify(Vectors, Config). %% @private fips_rsa_pss_verify([ {vector, {<<"SHAAlg">>, SHAAlg}, _}, {vector, {<<"e">>, E}, _}, {vector, {<<"d">>, D}, _}, {vector, {<<"Msg">>, Msg}, _}, {vector, {<<"S">>, S}, _}, {vector, {<<"SaltVal">>, SaltVal}, _}, {vector, {<<"EM", _/binary>>, _}, _}, {vector, {<<"Result">>, << R, _/binary >>}, _} | Vectors ], ModulusSize, RSAPrivateKey0, Config) -> Expected = case R of $F -> false; $P -> true end, HashFun = shaalg_to_hash_fun(SHAAlg), RSAPrivateKey = RSAPrivateKey0#'RSAPrivateKey'{ privateExponent = crypto:bytes_to_integer(D), publicExponent = crypto:bytes_to_integer(E) }, RSAPublicKey = rsa_private_to_public(RSAPrivateKey), SaltLen = case SaltVal of << 0 >> -> 0; _ -> byte_size(SaltVal) end, case jose_jwa_pkcs1:rsassa_pss_verify(HashFun, Msg, S, SaltLen, RSAPublicKey) of Expected -> fips_rsa_pss_verify(Vectors, ModulusSize, RSAPrivateKey0, Config); Other -> ct:fail({{jose_jwa_pkcs1, rsassa_pss_verify, [HashFun, Msg, S, SaltLen, RSAPublicKey]}, {expected, Expected}, {got, Other}}) end; fips_rsa_pss_verify([ {vector, {<<"SHAAlg">>, SHAAlg}, _}, {vector, {<<"e">>, E}, _}, {vector, {<<"d">>, D}, _}, {vector, {<<"Msg">>, Msg}, _}, {vector, {<<"S">>, S}, _}, {vector, {<<"SaltVal">>, SaltVal}, _}, {vector, {<<"Result">>, << R, _/binary >>}, _} | Vectors ], ModulusSize, RSAPrivateKey0, Config) -> Expected = case R of $F -> false; $P -> true end, HashFun = shaalg_to_hash_fun(SHAAlg), RSAPrivateKey = RSAPrivateKey0#'RSAPrivateKey'{ privateExponent = crypto:bytes_to_integer(D), publicExponent = crypto:bytes_to_integer(E) }, RSAPublicKey = rsa_private_to_public(RSAPrivateKey), SaltLen = case SaltVal of << 0 >> -> 0; _ -> byte_size(SaltVal) end, case jose_jwa_pkcs1:rsassa_pss_verify(HashFun, Msg, S, SaltLen, RSAPublicKey) of Expected -> fips_rsa_pss_verify(Vectors, ModulusSize, RSAPrivateKey0, Config); Other -> ct:fail({{jose_jwa_pkcs1, rsassa_pss_verify, [HashFun, Msg, S, SaltLen, RSAPublicKey]}, {expected, Expected}, {got, Other}}) end; fips_rsa_pss_verify(Vectors, ModulusSize, _RSAPrivateKey, Config) -> fips_rsa_pss_verify(Vectors, ModulusSize, Config). %% @private fips_sha3(File, Config) -> Options = case iolist_to_binary(filename:basename(File)) of << "ShortMsgKAT_SHA3-", BitsBin:3/binary, _/binary >> -> Bits = binary_to_integer(BitsBin), Bytes = (Bits + 7) div 8, Function = list_to_atom("sha3_" ++ integer_to_list(Bits)), Arity = 1, {Function, Arity, Bytes}; << "ShortMsgKAT_SHAKE", BitsBin:3/binary, _/binary >> -> Bits = binary_to_integer(BitsBin), Bytes = 512, Function = list_to_atom("shake" ++ integer_to_list(Bits)), Arity = 2, {Function, Arity, Bytes} end, Vectors = fips_testvector:from_file(File), io:format("~s", [filename:basename(File)]), fips_sha3(Vectors, Options, Config). %% @private fips_sha3([ {vector, {<<"Len">>, Len}, _}, {vector, {<<"Msg">>, Msg}, _}, {vector, {<<"MD">>, MD}, _} | Vectors ], {Function, Arity=1, OutputByteLen}, Config) when Len rem 8 =:= 0 -> InputBytes = binary:part(Msg, 0, Len div 8), case jose_jwa_sha3:Function(InputBytes) of MD -> fips_sha3(Vectors, {Function, Arity, OutputByteLen}, Config); Other -> ct:fail({{jose_jwa_sha3, Function, [InputBytes]}, {expected, MD}, {got, Other}}) end; fips_sha3([ {vector, {<<"Len">>, Len}, _}, {vector, {<<"Msg">>, Msg}, _}, {vector, {<<"Squeezed">>, Squeezed}, _} | Vectors ], {Function, Arity=2, OutputByteLen}, Config) when Len rem 8 =:= 0 -> InputBytes = binary:part(Msg, 0, Len div 8), case jose_jwa_sha3:Function(InputBytes, OutputByteLen) of Squeezed -> fips_sha3(Vectors, {Function, Arity, OutputByteLen}, Config); Other -> ct:fail({{jose_jwa_sha3, Function, [InputBytes, OutputByteLen]}, {expected, Squeezed}, {got, Other}}) end; fips_sha3([ {vector, {<<"Len">>, _Len}, _}, {vector, {<<"Msg">>, _Msg}, _}, {vector, {<<"MD">>, _MD}, _} | Vectors ], Options, Config) -> fips_sha3(Vectors, Options, Config); fips_sha3([ {vector, {<<"Len">>, _Len}, _}, {vector, {<<"Msg">>, _Msg}, _}, {vector, {<<"Squeezed">>, _Squeezed}, _} | Vectors ], Options, Config) -> fips_sha3(Vectors, Options, Config); fips_sha3([], _Opts, _Config) -> ok. %% @private hexstr2bin(S) -> list_to_binary(hexstr2list(S)). %% @private hexstr2lint(S) -> Bin = hexstr2bin(S), Size = byte_size(Bin), << Int:Size/unsigned-little-integer-unit:8 >> = Bin, Int. %% @private hexstr2list([X,Y|T]) -> [mkint(X)*16 + mkint(Y) | hexstr2list(T)]; hexstr2list([]) -> []. %% @private mkint(C) when $0 =< C, C =< $9 -> C - $0; mkint(C) when $A =< C, C =< $F -> C - $A + 10; mkint(C) when $a =< C, C =< $f -> C - $a + 10. %% @private pbkdf1([{Hash, Password, Salt, Iterations, DerivedKeyLen, DerivedKey} | Vectors], Config) -> case jose_jwa_pkcs5:pbkdf1(Hash, Password, Salt, Iterations, DerivedKeyLen) of {ok, DerivedKey} -> pbkdf1(Vectors, Config); Other -> ct:fail({{jose_jwa_pkcs5, pbkdf1, [Hash, Password, Salt, Iterations, DerivedKeyLen]}, {expected, {ok, DerivedKey}}, {got, Other}}) end; pbkdf1([], _Config) -> ok. %% @private pbkdf2([{Mac, Password, Salt, Iterations, DerivedKeyLen, DerivedKey} | Vectors], Config) -> case jose_jwa_pkcs5:pbkdf2(Mac, Password, Salt, Iterations, DerivedKeyLen) of {ok, DerivedKey} -> pbkdf2(Vectors, Config); Other -> ct:fail({{jose_jwa_pkcs5, pbkdf2, [Mac, Password, Salt, Iterations, DerivedKeyLen]}, {expected, {ok, DerivedKey}}, {got, Other}}) end; pbkdf2([], _Config) -> ok. %% @private pkcs7_pad_and_unpad([{Unpadded, Padded} | Vectors], Config) -> case jose_jwa_pkcs7:pad(Unpadded) of Padded -> ok; PadOther -> ct:fail({{jose_jwa_pkcs7, pad, [Unpadded]}, {expected, Padded}, {got, PadOther}}) end, case jose_jwa_pkcs7:unpad(Padded) of Unpadded -> pkcs7_pad_and_unpad(Vectors, Config); UnpadOther -> ct:fail({{jose_jwa_pkcs7, unpad, [Padded]}, {expected, Unpadded}, {got, UnpadOther}}) end; pkcs7_pad_and_unpad([], _Config) -> ok. %% @private rsa_private_to_public(#'RSAPrivateKey'{ modulus = Modulus, publicExponent = PublicExponent }) -> #'RSAPublicKey'{ modulus = Modulus, publicExponent = PublicExponent }. %% @private shaalg_to_hash_fun(<<"SHA1">>) -> sha; shaalg_to_hash_fun(<<"SHA224">>) -> sha224; shaalg_to_hash_fun(<<"SHA256">>) -> sha256; shaalg_to_hash_fun(<<"SHA384">>) -> sha384; shaalg_to_hash_fun(<<"SHA512">>) -> sha512. %% @private x25519_vector({AliceSK, AlicePK, BobSK, BobPK, Shared}) -> ?tv_ok(T0, jose_jwa_curve25519, x25519_secret_to_public, [AliceSK], AlicePK), ?tv_ok(T1, jose_jwa_curve25519, x25519_secret_to_public, [BobSK], BobPK), ?tv_ok(T2, jose_jwa_curve25519, x25519_shared_secret, [AliceSK, BobPK], Shared), ?tv_ok(T3, jose_jwa_curve25519, x25519_shared_secret, [BobSK, AlicePK], Shared). %% @private x448_vector({AliceSK, AlicePK, BobSK, BobPK, Shared}) -> ?tv_ok(T0, jose_jwa_curve448, x448_secret_to_public, [AliceSK], AlicePK), ?tv_ok(T1, jose_jwa_curve448, x448_secret_to_public, [BobSK], BobPK), ?tv_ok(T2, jose_jwa_curve448, x448_shared_secret, [AliceSK, BobPK], Shared), ?tv_ok(T3, jose_jwa_curve448, x448_shared_secret, [BobSK, AlicePK], Shared). potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/0000775000232200023220000000000015267717064023023 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jwe/0000775000232200023220000000000015267717064023610 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jwe/a.1.config0000664000232200023220000001142215267717064025356 0ustar debalancedebalance{"a.1.txt", <<84,104,101,32,116,114,117,101,32,115,105,103,110,32,111,102,32,105,110,116,101,108,108,105,103,101,110,99,101,32,105,115,32,110,111,116,32,107,110,111,119,108,101,100,103,101,32,98,117,116,32,105,109,97,103,105,110,97,116,105,111,110,46>>}. {"a.1.1.jwe+json", <<"{\"alg\":\"RSA-OAEP\",\"enc\":\"A256GCM\"}">>}. {"a.1.1.jwe+json.b64", <<"eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ">>}. {"a.1.2.cek", <<177,161,244,128,84,143,225,115,63,180,3,255,107,154,212,246,138,7,110,91,112,46,34,105,47,130,203,46,122,234,64,252>>}. {"a.1.3.jwk+json", <<"{\"kty\":\"RSA\",\"n\":\"oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUWcJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3Spsk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2asbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMStPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2djYgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw\",\"e\":\"AQAB\",\"d\":\"kLdtIj6GbDks_ApCSTYQtelcNttlKiOyPzMrXHeI-yk1F7-kpDxY4-WY5NWV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD93Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghkqDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vlt3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSndVTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ\",\"p\":\"1r52Xk46c-LsfB5P442p7atdPUrxQSy4mti_tZI3Mgf2EuFVbUoDBvaRQ-SWxkbkmoEzL7JXroSBjSrK3YIQgYdMgyAEPTPjXv_hI2_1eTSPVZfzL0lffNn03IXqWF5MDFuoUYE0hzb2vhrlN_rKrbfDIwUbTrjjgieRbwC6Cl0\",\"q\":\"wLb35x7hmQWZsWJmB_vle87ihgZ19S8lBEROLIsZG4ayZVe9Hi9gDVCOBmUDdaDYVTSNx_8Fyw1YYa9XGrGnDew00J28cRUoeBB_jKI1oma0Orv1T9aXIWxKwd4gvxFImOWr3QRL9KEBRzk2RatUBnmDZJTIAfwTs0g68UZHvtc\",\"dp\":\"ZK-YwE7diUh0qR1tR7w8WHtolDx3MZ_OTowiFvgfeQ3SiresXjm9gZ5KLhMXvo-uz-KUJWDxS5pFQ_M0evdo1dKiRTjVw_x4NyqyXPM5nULPkcpU827rnpZzAJKpdhWAgqrXGKAECQH0Xt4taznjnd_zVpAmZZq60WPMBMfKcuE\",\"dq\":\"Dq0gfgJ1DdFGXiLvQEZnuKEN0UUmsJBxkjydc3j4ZYdBiMRAy86x0vHCjywcMlYYg4yoC4YZa9hNVcsjqA3FeiL19rk8g6Qn29Tt0cj8qqyFpz9vNDBUfCAiJVeESOjJDZPYHdHY8v1b-o-Z2X5tvLx-TCekf7oxyeKDUqKWjis\",\"qi\":\"VIMpMYbPf47dT1w_zDUXfPimsSegnMOA1zTaX7aGk_8urY6R8-ZW1FxU7AlWAyLWybqq6t16VFd7hQd0y6flUK4SlOydB61gwanOsXGOAOv82cHq0E3eL4HrtZkUuKvnPrMnsUUFlfUdybVzxyjz9JF_XyaY14ardLSjf4L_FNY\"}">>}. {"a.1.3.cek.encrypted", <<56,163,154,192,58,53,222,4,105,218,136,218,29,94,203,22,150,92,129,94,211,232,53,89,41,60,138,56,196,216,82,98,168,76,37,73,70,7,36,8,191,100,136,196,244,220,145,158,138,155,4,117,141,230,199,247,173,45,182,214,74,177,107,211,153,11,205,196,171,226,162,128,171,182,13,237,239,99,193,4,91,219,121,223,107,167,61,119,228,173,156,137,134,200,80,219,74,253,56,185,91,177,34,158,89,154,205,96,55,18,138,43,96,218,215,128,124,75,138,243,85,25,109,117,140,26,155,249,67,167,149,231,100,6,41,65,214,251,232,87,72,40,182,149,154,168,31,193,126,215,89,28,111,219,125,182,139,235,195,197,23,234,55,58,63,180,68,202,206,149,75,205,248,176,67,39,178,60,98,193,32,238,122,96,158,222,57,183,111,210,55,188,215,206,180,166,150,166,106,250,55,229,72,40,69,214,216,104,23,40,135,212,28,127,41,80,175,174,168,115,171,197,89,116,92,103,246,83,216,182,176,84,37,147,35,45,219,172,99,226,233,73,37,124,42,72,49,242,35,127,184,134,117,114,135,206>>}. {"a.1.3.cek.encrypted.b64", <<"OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg">>}. {"a.1.4.iv", <<227,197,117,252,2,219,233,68,180,225,77,219>>}. {"a.1.4.iv.b64", <<"48V1_ALb6US04U3b">>}. {"a.1.5.aad", <<101,121,74,104,98,71,99,105,79,105,74,83,85,48,69,116,84,48,70,70,85,67,73,115,73,109,86,117,89,121,73,54,73,107,69,121,78,84,90,72,81,48,48,105,102,81>>}. {"a.1.6.txt.cipher", <<229,236,166,241,53,191,115,196,174,43,73,109,39,122,233,96,140,206,120,52,51,237,48,11,190,219,186,80,111,104,50,142,47,167,59,61,181,127,196,21,40,82,242,32,123,143,168,226,73,216,176,144,138,247,106,60,16,205,160,109,64,63,192>>}. {"a.1.6.txt.tag", <<92,80,104,49,133,25,161,215,173,101,219,211,136,91,210,145>>}. {"a.1.6.txt.cipher.b64", <<"5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A">>}. {"a.1.6.txt.tag.b64", <<"XFBoMYUZodetZdvTiFvSkQ">>}. {"a.1.7.jwe+compact", <<"eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg.48V1_ALb6US04U3b.5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A.XFBoMYUZodetZdvTiFvSkQ">>}.potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jwe/a.2.config0000664000232200023220000001077615267717064025372 0ustar debalancedebalance{"a.2.txt", <<76,105,118,101,32,108,111,110,103,32,97,110,100,32,112,114,111,115,112,101,114,46>>}. {"a.2.1.jwe+json", <<"{\"alg\":\"RSA1_5\",\"enc\":\"A128CBC-HS256\"}">>}. {"a.2.1.jwe+json.b64", <<"eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0">>}. {"a.2.2.cek", <<4,211,31,197,84,157,252,254,11,100,157,250,63,170,106,206,107,124,212,45,111,107,9,219,200,177,0,240,143,156,44,207>>}. {"a.2.3.jwk+json", <<"{\"kty\":\"RSA\",\"n\":\"sXchDaQebHnPiGvyDOAT4saGEUetSyo9MKLOoWFsueri23bOdgWp4Dy1WlUzewbgBHod5pcM9H95GQRV3JDXboIRROSBigeC5yjU1hGzHHyXss8UDprecbAYxknTcQkhslANGRUZmdTOQ5qTRsLAt6BTYuyvVRdhS8exSZEy_c4gs_7svlJJQ4H9_NxsiIoLwAEk7-Q3UXERGYw_75IDrGA84-lA_-Ct4eTlXHBIY2EaV7t7LjJaynVJCpkv4LKjTTAumiGUIuQhrNhZLuF_RJLqHpM2kgWFLU7-VTdL1VbC2tejvcI2BlMkEpk1BzBZI0KQB0GaDWFLN-aEAw3vRw\",\"e\":\"AQAB\",\"d\":\"VFCWOqXr8nvZNyaaJLXdnNPXZKRaWCjkU5Q2egQQpTBMwhprMzWzpR8Sxq1OPThh_J6MUD8Z35wky9b8eEO0pwNS8xlh1lOFRRBoNqDIKVOku0aZb-rynq8cxjDTLZQ6Fz7jSjR1Klop-YKaUHc9GsEofQqYruPhzSA-QgajZGPbE_0ZaVDJHfyd7UUBUKunFMScbflYAAOYJqVIVwaYR5zWEEceUjNnTNo_CVSj-VvXLO5VZfCUAVLgW4dpf1SrtZjSt34YLsRarSb127reG_DUwg9Ch-KyvjT1SkHgUWRVGcyly7uvVGRSDwsXypdrNinPA4jlhoNdizK2zF2CWQ\",\"p\":\"9gY2w6I6S6L0juEKsbeDAwpd9WMfgqFoeA9vEyEUuk4kLwBKcoe1x4HG68ik918hdDSE9vDQSccA3xXHOAFOPJ8R9EeIAbTi1VwBYnbTp87X-xcPWlEPkrdoUKW60tgs1aNd_Nnc9LEVVPMS390zbFxt8TN_biaBgelNgbC95sM\",\"q\":\"uKlCKvKv_ZJMVcdIs5vVSU_6cPtYI1ljWytExV_skstvRSNi9r66jdd9-yBhVfuG4shsp2j7rGnIio901RBeHo6TPKWVVykPu1iYhQXw1jIABfw-MVsN-3bQ76WLdt2SDxsHs7q7zPyUyHXmps7ycZ5c72wGkUwNOjYelmkiNS0\",\"dp\":\"w0kZbV63cVRvVX6yk3C8cMxo2qCM4Y8nsq1lmMSYhG4EcL6FWbX5h9yuvngs4iLEFk6eALoUS4vIWEwcL4txw9LsWH_zKI-hwoReoP77cOdSL4AVcraHawlkpyd2TWjE5evgbhWtOxnZee3cXJBkAi64Ik6jZxbvk-RR3pEhnCs\",\"dq\":\"o_8V14SezckO6CNLKs_btPdFiO9_kC1DsuUTd2LAfIIVeMZ7jn1Gus_Ff7B7IVx3p5KuBGOVF8L-qifLb6nQnLysgHDh132NDioZkhH7mI7hPG-PYE_odApKdnqECHWw0J-F0JWnUd6D2B_1TvF9mXA2Qx-iGYn8OVV1Bsmp6qU\",\"qi\":\"eNho5yRBEBxhGBtQRww9QirZsB66TrfFReG_CcteI1aCneT0ELGhYlRlCtUkTRclIfuEPmNsNDPbLoLqqCVznFbvdB7x-Tl-m0l_eFTj2KiqwGqE9PZB9nNTwMVvH3VRRSLWACvPnSiwP8N5Usy-WRXS-V7TbpxIhvepTfE0NNo\"}">>}. {"a.2.3.cek.encrypted", <<80,104,72,58,11,130,236,139,132,189,255,205,61,86,151,176,99,40,44,233,176,189,205,70,202,169,72,40,226,181,156,223,120,156,115,232,150,209,145,133,104,112,237,156,116,250,65,102,212,210,103,240,177,61,93,40,71,231,223,226,240,157,15,31,150,89,200,215,198,203,108,70,117,66,212,238,193,205,23,161,169,218,243,203,128,214,127,253,215,139,43,17,135,103,179,220,28,2,212,206,131,158,128,66,62,240,78,186,141,125,132,227,60,137,43,31,152,199,54,72,34,212,115,11,152,101,70,42,219,233,142,66,151,250,126,146,141,216,190,73,50,177,146,5,52,247,28,197,21,59,170,247,181,89,131,241,169,182,246,99,15,36,102,166,182,172,197,136,230,120,60,58,219,243,149,94,222,150,154,194,110,227,225,112,39,89,233,112,207,211,241,124,174,69,221,179,107,196,225,127,167,112,226,12,242,16,24,28,120,182,244,213,244,153,194,162,69,160,244,248,63,165,141,4,207,249,193,79,131,0,169,233,127,167,101,151,125,56,112,111,248,29,232,90,29,147,110,169,146,114,165,204,71,136,41,252>>}. {"a.2.3.cek.encrypted.b64", <<"UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7PcHALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPhcCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPgwCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A">>}. {"a.2.4.iv", <<3,22,60,12,43,67,104,105,108,108,105,99,111,116,104,101>>}. {"a.2.4.iv.b64", <<"AxY8DCtDaGlsbGljb3RoZQ">>}. {"a.2.5.aad", <<101,121,74,104,98,71,99,105,79,105,74,83,85,48,69,120,88,122,85,105,76,67,74,108,98,109,77,105,79,105,74,66,77,84,73,52,81,48,74,68,76,85,104,84,77,106,85,50,73,110,48>>}. {"a.2.6.txt.cipher", <<40,57,83,181,119,33,133,148,198,185,243,24,152,230,6,75,129,223,127,19,210,82,183,230,168,33,215,104,143,112,56,102>>}. {"a.2.6.txt.tag", <<246,17,244,190,4,95,98,3,231,0,115,157,242,203,100,191>>}. {"a.2.6.txt.cipher.b64", <<"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY">>}. {"a.2.6.txt.tag.b64", <<"9hH0vgRfYgPnAHOd8stkvw">>}. {"a.2.7.jwe+compact", <<"eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7PcHALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPhcCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPgwCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A.AxY8DCtDaGlsbGljb3RoZQ.KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY.9hH0vgRfYgPnAHOd8stkvw">>}.potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jwe/a.3.config0000664000232200023220000000312715267717064025363 0ustar debalancedebalance{"a.3.txt", <<76,105,118,101,32,108,111,110,103,32,97,110,100,32,112,114,111,115,112,101,114,46>>}. {"a.3.1.jwe+json", <<"{\"alg\":\"A128KW\",\"enc\":\"A128CBC-HS256\"}">>}. {"a.3.1.jwe+json.b64", <<"eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0">>}. {"a.3.2.cek", <<4,211,31,197,84,157,252,254,11,100,157,250,63,170,106,206,107,124,212,45,111,107,9,219,200,177,0,240,143,156,44,207>>}. {"a.3.3.jwk+json", <<"{\"kty\":\"oct\",\"k\":\"GawgguFyGrWKav7AX4VKUg\"}">>}. {"a.3.3.cek.encrypted", <<232,160,123,211,183,76,245,132,200,128,123,75,190,216,22,67,201,138,193,186,9,91,122,31,246,90,28,139,57,3,76,124,193,11,98,37,173,61,104,57>>}. {"a.3.3.cek.encrypted.b64", <<"6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ">>}. {"a.3.4.iv", <<3,22,60,12,43,67,104,105,108,108,105,99,111,116,104,101>>}. {"a.3.4.iv.b64", <<"AxY8DCtDaGlsbGljb3RoZQ">>}. {"a.3.5.aad", <<101,121,74,104,98,71,99,105,79,105,74,66,77,84,73,52,83,49,99,105,76,67,74,108,98,109,77,105,79,105,74,66,77,84,73,52,81,48,74,68,76,85,104,84,77,106,85,50,73,110,48>>}. {"a.3.6.txt.cipher", <<40,57,83,181,119,33,133,148,198,185,243,24,152,230,6,75,129,223,127,19,210,82,183,230,168,33,215,104,143,112,56,102>>}. {"a.3.6.txt.tag", <<83,73,191,98,104,205,211,128,201,189,199,133,32,38,194,85>>}. {"a.3.6.txt.cipher.b64", <<"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY">>}. {"a.3.6.txt.tag.b64", <<"U0m_YmjN04DJvceFICbCVQ">>}. {"a.3.7.jwe+compact", <<"eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ.AxY8DCtDaGlsbGljb3RoZQ.KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY.U0m_YmjN04DJvceFICbCVQ">>}.potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/rfc7520/0000775000232200023220000000000015267717064024113 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/test/jose_SUITE_data/rfc7520/5.9.config0000664000232200023220000000605715267717064025625 0ustar debalancedebalance{"figure.72", <<89,111,117,32,99,97,110,32,116,114,117,115,116,32,117,115,32,116,111,32,115,116,105,99,107,32,119,105,116,104,32,121,111,117,32,116,104,114,111,117,103,104,32,116,104,105,99,107,32,97,110,100,32,116,104,105,110,226,128,147,116,111,32,116,104,101,32,98,105,116,116,101,114,32,101,110,100,46,32,65,110,100,32,121,111,117,32,99,97,110,32,116,114,117,115,116,32,117,115,32,116,111,32,107,101,101,112,32,97,110,121,32,115,101,99,114,101,116,32,111,102,32,121,111,117,114,115,226,128,147,99,108,111,115,101,114,32,116,104,97,110,32,121,111,117,32,107,101,101,112,32,105,116,32,121,111,117,114,115,101,108,102,46,32,66,117,116,32,121,111,117,32,99,97,110,110,111,116,32,116,114,117,115,116,32,117,115,32,116,111,32,108,101,116,32,121,111,117,32,102,97,99,101,32,116,114,111,117,98,108,101,32,97,108,111,110,101,44,32,97,110,100,32,103,111,32,111,102,102,32,119,105,116,104,111,117,116,32,97,32,119,111,114,100,46,32,87,101,32,97,114,101,32,121,111,117,114,32,102,114,105,101,110,100,115,44,32,70,114,111,100,111,46>>}. {"figure.151", <<"{\"kty\":\"oct\",\"kid\":\"81b20965-8332-43d9-a468-82160ad91ac8\",\"use\":\"enc\",\"alg\":\"A128KW\",\"k\":\"GZy6sIZ6wl9NJOKB-jnmVQ\"}">>}. {"figure.162", <<"bY_BDcIwDEVX-QNU3QEOrIA4pqlDokYxchxVvbEDGzIJbioOSJwc-f___HPjBu8KVFpVtAplVE1-wZo0YjNZo3C7R5v72pV5f5X382VWjYQpqZKAyjziZOr2B7kQPSy6oZIXUnDYbVKN4jNXi2u0yB7t1qSHTjmMODf9QgvrDzfTIQXnyQRuUya4zIWG3vTOdir0v7BRHFYWq3k1k1A_gSDJqtcBF-GZxw8">>}. {"figure.163", <<"hC-MpLZSuwWv8sexS6ydfw">>}. {"figure.164", <<"p9pUq6XHY0jfEZIl">>}. {"figure.165", <<"5vUT2WOtQxKWcekM_IzVQwkGgzlFDwPi">>}. {"figure.166", <<"{\"alg\":\"A128KW\",\"kid\":\"81b20965-8332-43d9-a468-82160ad91ac8\",\"enc\":\"A128GCM\",\"zip\":\"DEF\"}">>}. {"figure.167", <<"eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIiwiemlwIjoiREVGIn0">>}. {"figure.168", <<"HbDtOsdai1oYziSx25KEeTxmwnh8L8jKMFNc1k3zmMI6VB8hry57tDZ61jXyezSPt0fdLVfe6Jf5y5-JaCap_JQBcb5opbmT60uWGml8blyiMQmOn9J--XhhlYg0m-BHaqfDO5iTOWxPxFMUedx7WCy8mxgDHj0aBMG6152PsM-w5E_o2B3jDbrYBKhpYA7qi3AyijnCJ7BP9rr3U8kxExCpG3mK420TjOw">>}. {"figure.169", <<"VILuUwuIxaLVmh5X-T7kmA">>}. {"figure.170", <<"eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIiwiemlwIjoiREVGIn0.5vUT2WOtQxKWcekM_IzVQwkGgzlFDwPi.p9pUq6XHY0jfEZIl.HbDtOsdai1oYziSx25KEeTxmwnh8L8jKMFNc1k3zmMI6VB8hry57tDZ61jXyezSPt0fdLVfe6Jf5y5-JaCap_JQBcb5opbmT60uWGml8blyiMQmOn9J--XhhlYg0m-BHaqfDO5iTOWxPxFMUedx7WCy8mxgDHj0aBMG6152PsM-w5E_o2B3jDbrYBKhpYA7qi3AyijnCJ7BP9rr3U8kxExCpG3mK420TjOw.VILuUwuIxaLVmh5X-T7kmA">>}. {"figure.172", <<"{\"protected\":\"eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIiwiemlwIjoiREVGIn0\",\"encrypted_key\":\"5vUT2WOtQxKWcekM_IzVQwkGgzlFDwPi\",\"iv\":\"p9pUq6XHY0jfEZIl\",\"ciphertext\":\"HbDtOsdai1oYziSx25KEeTxmwnh8L8jKMFNc1k3zmMI6VB8hry57tDZ61jXyezSPt0fdLVfe6Jf5y5-JaCap_JQBcb5opbmT60uWGml8blyiMQmOn9J--XhhlYg0m-BHaqfDO5iTOWxPxFMUedx7WCy8mxgDHj0aBMG6152PsM-w5E_o2B3jDbrYBKhpYA7qi3AyijnCJ7BP9rr3U8kxExCpG3mK420TjOw\",\"tag\":\"VILuUwuIxaLVmh5X-T7kmA\"}">>}.potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jose_cfrg_curves/0000775000232200023220000000000015267717064026353 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jose_cfrg_curves/a.6.config0000664000232200023220000000161315267717064030127 0ustar debalancedebalance{"a.6.bob-jwk+json", <<"{\"kty\":\"OKP\",\"crv\":\"X25519\",\"kid\":\"Bob\",\"x\":\"3p7bfXt9wbTTW2HC7OQ1Nz-DQ8hbeGdNrfx-FG-IK08\"}">>}. {"a.6.bob-secret+hex", <<"5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb">>}. {"a.6.bob-pk+hex", <<"de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f">>}. {"a.6.epk-secret+hex", <<"77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a">>}. {"a.6.epk-pk+hex", <<"8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a">>}. {"a.6.epk-jwk+json", <<"{\"kty\":\"OKP\",\"crv\":\"X25519\",\"x\":\"hSDwCYkwp1R0i33ctD73Wg2_Og0mOBr066SpjqqbTmo\"}">>}. {"a.6.jwe+json", <<"{\"alg\":\"ECDH-ES+A128KW\",\"epk\":{\"kty\":\"OKP\",\"crv\":\"X25519\",\"x\":\"hSDwCYkwp1R0i33ctD73Wg2_Og0mOBr066SpjqqbTmo\"},\"enc\":\"A128GCM\",\"kid\":\"Bob\"}">>}. {"a.6.z+hex", <<"4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742">>}.potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jose_cfrg_curves/a.1.config0000664000232200023220000000025415267717064030122 0ustar debalancedebalance{"a.1.secret", <<"9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60">>}. {"a.1.pk", <<"d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a">>}. potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jose_cfrg_curves/a.5.config0000664000232200023220000000040715267717064030126 0ustar debalancedebalance{"a.5.sig+compact", <<"eyJhbGciOiJFZERTQSJ9.RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc.hgyY0il_MGCjP0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt9g7sVvpAr_MuM0KAg">>}. {"a.5.jws+json", <<"{\"alg\":\"EdDSA\"}">>}. {"a.5.txt", <<"Example of Ed25519 signing">>}.potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jose_cfrg_curves/a.4.config0000664000232200023220000000131715267717064030126 0ustar debalancedebalance{"a.4.jws+json", <<"{\"alg\":\"EdDSA\"}">>}. {"a.4.jws+b64", <<"eyJhbGciOiJFZERTQSJ9">>}. {"a.4.txt", <<"Example of Ed25519 signing">>}. {"a.4.txt+b64", <<"RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc">>}. {"a.4.signing-input", <<"eyJhbGciOiJFZERTQSJ9.RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc">>}. {"a.4.sig+hex", <<"860c98d2297f3060a33f42739672d61b53cf3adefed3d3c672f320dc021b411e9d59b8628dc351e248b88b29468e0e41855b0fb7d83bb15be902bfccb8cd0a02">>}. {"a.4.sig+b64", <<"hgyY0il_MGCjP0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt9g7sVvpAr_MuM0KAg">>}. {"a.4.sig+compact", <<"eyJhbGciOiJFZERTQSJ9.RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc.hgyY0il_MGCjP0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt9g7sVvpAr_MuM0KAg">>}.potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jose_cfrg_curves/a.1.jwk+json0000664000232200023220000000020115267717064030405 0ustar debalancedebalance{"kty":"OKP","crv":"Ed25519","d":"nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A","x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"}potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jose_cfrg_curves/a.2.jwk+json0000664000232200023220000000011715267717064030414 0ustar debalancedebalance{"kty":"OKP","crv":"Ed25519","x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"}potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jose_cfrg_curves/a.7.config0000664000232200023220000000232715267717064030133 0ustar debalancedebalance{"a.7.bob-jwk+json", <<"{\"kty\":\"OKP\",\"crv\":\"X448\",\"kid\":\"Dave\",\"x\":\"PreoKbDNIPW8_AtZm2_sz22kYnEHvbDU80W0MCfYuXL8PjT7QjKhPKcG3LV67D2uB73BxnvzNgk\"}">>}. {"a.7.bob-secret+hex", <<"1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d">>}. {"a.7.bob-pk+hex", <<"3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b43027d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609">>}. {"a.7.epk-secret+hex", <<"9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28dd9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b">>}. {"a.7.epk-pk+hex", <<"9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0">>}. {"a.7.epk-jwk+json", <<"{\"kty\":\"OKP\",\"crv\":\"X448\",\"x\":\"mwj3zDG34-Z9ItWuoSEHSic70rg94Jxj-qc9LCLF2bvINmRyQdlT1AxbEtqIEg1TF3-A5TLEH6A\"}">>}. {"a.7.jwe+json", <<"{\"alg\":\"ECDH-ES+A256KW\",\"epk\":{\"kty\":\"OKP\",\"crv\":\"X448\",\"x\":\"mwj3zDG34-Z9ItWuoSEHSic70rg94Jxj-qc9LCLF2bvINmRyQdlT1AxbEtqIEg1TF3-A5TLEH6A\"},\"enc\":\"A256GCM\",\"kid\":\"Dave\"}">>}. {"a.7.z+hex", <<"07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282bb60c0b56fd2464c335543936521c24403085d59a449a5037514a879d">>}.potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jose_cfrg_curves/a.3.config0000664000232200023220000000043715267717064030127 0ustar debalancedebalance{"a.3.jwk+json", <<"{\"crv\":\"Ed25519\",\"kty\":\"OKP\",\"x\":\"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo\"}">>}. {"a.3.thumbprint+hex", <<"90facafea9b1556698540f70c0117a22ea37bd5cf3ed3c47093c1707282b4b89">>}. {"a.3.thumbprint+b64", <<"kPrK_qmxVWaYVA9wwBF6Iuo3vVzz7TxHCTwXBygrS4k">>}.potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jwk/0000775000232200023220000000000015267717064023616 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jwk/c.config0000664000232200023220000002607715267717064025243 0ustar debalancedebalance{"c.1.jwk+json", <<"{\"kty\":\"RSA\",\"kid\":\"juliet@capulet.lit\",\"use\":\"enc\",\"n\":\"t6Q8PWSi1dkJj9hTP8hNYFlvadM7DflW9mWepOJhJ66w7nyoK1gPNqFMSQRyO125Gp-TEkodhWr0iujjHVx7BcV0llS4w5ACGgPrcAd6ZcSR0-Iqom-QFcNP8Sjg086MwoqQU_LYywlAGZ21WSdS_PERyGFiNnj3QQlO8Yns5jCtLCRwLHL0Pb1fEv45AuRIuUfVcPySBWYnDyGxvjYGDSM-AqWS9zIQ2ZilgT-GqUmipg0XOC0Cc20rgLe2ymLHjpHciCKVAbY5-L32-lSeZO-Os6U15_aXrk9Gw8cPUaX1_I8sLGuSiVdt3C_Fn2PZ3Z8i744FPFGGcG1qs2Wz-Q\",\"e\":\"AQAB\",\"d\":\"GRtbIQmhOZtyszfgKdg4u_N-R_mZGU_9k7JQ_jn1DnfTuMdSNprTeaSTyWfSNkuaAwnOEbIQVy1IQbWVV25NY3ybc_IhUJtfri7bAXYEReWaCl3hdlPKXy9UvqPYGR0kIXTQRqns-dVJ7jahlI7LyckrpTmrM8dWBo4_PMaenNnPiQgO0xnuToxutRZJfJvG4Ox4ka3GORQd9CsCZ2vsUDmsXOfUENOyMqADC6p1M3h33tsurY15k9qMSpG9OX_IJAXmxzAh_tWiZOwk2K4yxH9tS3Lq1yX8C1EWmeRDkK2ahecG85-oLKQt5VEpWHKmjOi_gJSdSgqcN96X52esAQ\",\"p\":\"2rnSOV4hKSN8sS4CgcQHFbs08XboFDqKum3sc4h3GRxrTmQdl1ZK9uw-PIHfQP0FkxXVrx-WE-ZEbrqivH_2iCLUS7wAl6XvARt1KkIaUxPPSYB9yk31s0Q8UK96E3_OrADAYtAJs-M3JxCLfNgqh56HDnETTQhH3rCT5T3yJws\",\"q\":\"1u_RiFDP7LBYh3N4GXLT9OpSKYP0uQZyiaZwBtOCBNJgQxaj10RWjsZu0c6Iedis4S7B_coSKB0Kj9PaPaBzg-IySRvvcQuPamQu66riMhjVtG6TlV8CLCYKrYl52ziqK0E_ym2QnkwsUX7eYTB7LbAHRK9GqocDE5B0f808I4s\",\"dp\":\"KkMTWqBUefVwZ2_Dbj1pPQqyHSHjj90L5x_MOzqYAJMcLMZtbUtwKqvVDq3tbEo3ZIcohbDtt6SbfmWzggabpQxNxuBpoOOf_a_HgMXK_lhqigI4y_kqS1wY52IwjUn5rgRrJ-yYo1h41KR-vz2pYhEAeYrhttWtxVqLCRViD6c\",\"dq\":\"AvfS0-gRxvn0bwJoMSnFxYcK1WnuEjQFluMGfwGitQBWtfZ1Er7t1xDkbN9GQTB9yqpDoYaN06H7CFtrkxhJIBQaj6nkF5KKS3TQtQ5qCzkOkmxIe3KRbBymXxkb5qwUpX5ELD5xFc6FeiafWYY63TmmEAu_lRFCOJ3xDea-ots\",\"qi\":\"lSQi-w9CpyUReMErP1RsBLk7wNtOvs5EQpPqmuMvqW57NBUczScEoPwmUqqabu9V0-Py4dQ57_bapoKRu1R90bvuFnU63SHWEFglZQvJDMeAvmj4sm-Fp0oYu_neotgQ0hzbI5gry7ajdYy9-2lNx_76aBZoOUu9HCJ-UsfSOI8\"}">>}. {"c.2.b64", <<"eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJwMnMiOiIyV0NUY0paMVJ2ZF9DSnVKcmlwUTF3IiwicDJjIjo0MDk2LCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiY3R5IjoiandrK2pzb24ifQ">>}. {"c.2.jwe+json", <<"{\"alg\":\"PBES2-HS256+A128KW\",\"p2s\":\"2WCTcJZ1Rvd_CJuJripQ1w\",\"p2c\":4096,\"enc\":\"A128CBC-HS256\",\"cty\":\"jwk+json\"}">>}. {"c.3.cek", <<111,27,25,52,66,29,20,78,92,176,56,240,65,208,82,112,161,131,36,55,202,236,185,172,129,23,153,194,195,48,253,182>>}. {"c.4.derivedkey", <<110,171,169,92,129,92,109,117,233,242,116,233,170,14,24,75>>}. {"c.4.salt", <<80,66,69,83,50,45,72,83,50,53,54,43,65,49,50,56,75,87,0,217,96,147,112,150,117,70,247,127,8,155,137,174,42,80,215>>}. {"c.4.txt", <<"Thus from my lips, by yours, my sin is purged.">>}. {"c.5.encryptedkey", <<78,186,151,59,11,141,81,240,213,245,83,211,53,188,134,188,66,125,36,200,222,124,5,103,249,52,117,184,140,81,246,158,161,177,20,33,245,57,59,4>>}. {"c.6.iv", <<97,239,99,214,171,54,216,57,145,72,7,93,34,31,149,156>>}. {"c.7.aad", <<"{\"alg\":\"PBES2-HS256+A128KW\",\"p2s\":\"2WCTcJZ1Rvd_CJuJripQ1w\",\"p2c\":4096,\"enc\":\"A128CBC-HS256\",\"cty\":\"jwk+json\"}">>}. {"c.8.ciphertag", <<208,113,102,132,236,236,67,223,39,53,98,99,32,121,17,236>>}. {"c.8.ciphertxt", <<3,8,65,242,92,107,148,168,197,159,77,139,25,97,42,131,110,199,225,56,61,127,38,64,108,91,247,167,150,98,112,122,99,235,132,50,28,46,56,170,169,89,220,145,38,157,148,224,66,140,8,169,146,117,222,54,242,28,31,11,129,227,226,169,66,117,133,254,140,216,115,203,131,60,60,47,233,132,121,13,35,188,53,19,172,77,59,54,211,158,172,25,60,111,0,80,201,158,160,210,68,55,12,67,136,130,87,216,197,95,62,20,155,205,5,140,27,168,221,65,114,78,157,254,46,206,182,52,135,87,239,3,34,186,126,220,151,17,33,237,57,96,172,183,58,45,248,103,241,142,136,7,53,16,173,181,7,93,92,252,1,53,212,242,8,255,11,239,181,24,148,136,111,24,161,244,23,106,69,157,215,243,189,240,166,169,249,72,38,201,99,223,173,229,9,222,82,79,157,176,248,85,239,121,163,1,31,48,98,206,61,249,104,216,201,227,105,48,194,193,10,36,160,159,241,166,84,54,188,211,243,242,40,46,45,193,193,160,169,101,201,1,73,47,105,142,88,28,42,132,26,61,58,63,142,243,77,26,179,153,166,46,203,208,49,55,229,34,178,4,109,180,204,204,115,1,103,193,5,91,215,214,195,1,110,208,53,144,36,105,12,54,25,129,101,15,183,150,250,147,115,227,58,250,5,128,232,63,15,14,19,141,124,253,142,137,189,135,26,44,240,27,88,132,105,127,6,71,37,41,124,187,165,140,34,200,123,80,228,24,231,176,132,171,138,145,152,116,224,50,141,51,147,91,186,7,246,106,217,148,244,227,244,45,220,121,165,224,148,181,17,181,128,197,101,237,11,169,229,149,199,78,56,15,14,190,91,216,222,247,213,74,40,8,96,20,168,119,96,26,24,52,37,82,127,57,176,147,118,59,7,224,33,117,72,155,29,82,26,215,189,140,119,28,152,118,93,222,194,192,148,115,83,253,216,212,108,88,83,175,172,220,97,79,110,42,223,170,161,34,164,144,193,76,122,92,160,41,178,175,6,35,96,113,96,158,90,129,101,26,45,70,180,189,230,15,5,247,150,209,94,171,26,13,142,212,129,1,176,5,0,112,203,174,185,119,76,233,189,54,172,189,245,223,253,205,12,88,9,126,157,225,90,40,229,191,63,30,160,224,69,3,140,109,70,89,37,213,245,194,210,180,188,63,210,139,221,2,144,200,20,177,216,29,227,242,106,12,135,142,139,144,82,225,162,171,176,108,99,6,43,193,161,116,234,216,1,242,21,124,162,98,205,124,193,38,12,242,90,101,76,204,184,124,58,180,16,240,26,76,195,250,212,191,185,191,97,198,186,73,225,75,14,90,123,121,172,101,50,160,221,141,253,205,126,77,9,87,198,110,104,182,141,120,51,25,232,3,32,80,6,156,8,18,4,135,221,142,25,135,2,129,132,115,227,74,141,28,119,11,141,117,134,198,62,150,254,97,75,197,251,99,89,204,224,226,67,83,175,89,0,81,29,38,207,89,140,255,197,177,164,128,62,116,224,180,109,169,28,2,59,176,130,252,44,178,81,24,181,176,75,44,61,91,12,37,21,255,83,130,197,16,231,60,217,56,131,118,168,202,58,52,84,124,162,185,174,162,226,242,112,68,246,202,16,208,52,154,58,129,80,102,33,171,6,186,177,14,195,88,136,6,0,155,28,100,162,207,162,222,117,248,170,208,114,87,31,57,176,33,57,83,253,12,168,110,194,59,22,86,48,227,196,22,176,218,122,149,21,249,195,178,174,250,20,34,120,60,139,201,99,40,18,177,17,54,54,6,3,222,128,160,88,11,27,0,81,192,36,41,169,146,8,47,64,136,28,64,209,67,135,202,20,234,182,91,204,146,195,187,0,72,77,11,111,152,204,252,177,212,89,33,50,132,184,44,183,186,19,250,69,176,201,102,140,14,143,212,212,160,123,208,185,27,155,68,77,133,198,2,126,155,215,22,91,30,217,176,172,244,156,174,143,75,90,21,102,1,160,59,253,188,88,57,185,197,83,24,22,180,174,47,207,52,1,141,146,119,233,68,228,224,228,193,248,155,202,90,7,213,88,33,108,107,14,86,8,120,250,58,142,35,164,238,221,219,35,123,88,199,192,143,104,83,17,166,243,247,11,166,67,68,204,132,23,110,103,228,14,55,122,88,57,180,178,237,52,130,214,245,102,123,67,73,175,1,127,112,148,94,132,164,197,153,217,87,25,89,93,63,22,66,166,90,251,101,10,145,66,17,124,36,255,165,226,97,16,86,112,154,88,105,253,56,209,229,122,103,51,24,228,190,3,236,48,182,121,176,140,128,117,87,251,224,37,23,248,21,218,85,251,136,84,147,143,144,46,155,183,251,89,86,23,26,237,100,167,32,130,173,237,89,55,110,70,142,127,65,230,208,109,69,19,253,84,130,130,193,92,58,108,150,42,136,249,234,86,241,182,19,117,246,26,181,92,101,155,44,103,235,173,30,140,90,29,183,190,77,53,206,127,5,87,8,187,184,92,4,157,22,18,105,251,39,88,182,181,103,148,233,6,63,70,188,7,101,216,127,77,31,12,233,7,147,106,30,150,77,145,13,205,48,56,245,220,89,252,127,51,180,36,31,55,18,214,230,254,217,197,65,247,27,215,117,247,108,157,121,11,63,150,195,83,6,134,242,41,24,105,204,5,63,192,14,159,113,72,140,128,51,215,80,215,39,149,94,79,128,34,5,129,82,83,121,187,37,146,27,32,177,167,71,9,195,30,199,196,205,252,207,69,8,120,27,190,51,43,75,249,234,167,116,206,203,199,43,108,87,48,155,140,228,210,85,25,161,96,67,8,205,64,39,75,88,44,238,227,16,0,100,93,129,18,4,149,50,68,72,99,35,111,254,27,102,175,108,233,87,181,44,169,18,139,79,208,14,202,192,5,162,222,231,149,24,211,49,120,101,39,206,87,147,204,200,251,104,115,5,127,117,195,79,151,18,224,52,0,245,4,85,255,103,217,0,116,198,80,91,167,192,154,199,197,149,237,51,2,131,30,226,95,105,48,68,135,208,144,120,176,145,157,8,171,80,94,61,92,92,220,157,13,138,51,23,185,124,31,77,1,87,241,43,239,55,122,86,210,48,208,204,112,144,80,147,106,219,47,253,31,134,176,16,135,219,95,17,129,83,236,125,136,112,86,228,252,71,129,218,174,156,236,12,27,159,11,138,252,253,207,31,115,214,118,239,203,16,211,205,99,22,51,163,107,162,246,199,67,127,34,108,197,53,117,58,199,3,190,74,70,190,65,235,175,97,157,215,252,189,245,100,229,248,46,90,126,237,4,159,128,58,7,156,236,69,191,85,240,179,224,249,152,49,195,223,60,78,186,157,155,217,58,105,116,164,217,111,215,150,218,252,84,86,248,140,240,226,61,106,208,95,60,163,6,0,235,253,162,96,62,234,251,249,35,21,7,211,233,86,50,33,203,67,248,60,190,123,48,167,226,90,191,71,56,183,165,17,85,76,238,140,211,168,53,223,194,4,97,149,156,120,137,76,33,229,243,194,208,198,202,139,28,114,46,224,92,254,83,100,134,158,92,70,78,61,62,138,24,173,216,66,198,70,254,47,59,193,53,6,139,19,153,253,28,199,122,160,27,67,234,209,227,139,4,50,7,178,183,89,252,32,128,137,55,52,29,89,12,111,42,181,51,170,132,132,207,170,228,254,178,213,0,136,175,8>>}. {"c.9.jwe+txt", <<"eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJwMnMiOiIyV0NUY0paMVJ2ZF9DSnVKcmlwUTF3IiwicDJjIjo0MDk2LCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiY3R5IjoiandrK2pzb24ifQ.TrqXOwuNUfDV9VPTNbyGvEJ9JMjefAVn-TR1uIxR9p6hsRQh9Tk7BA.Ye9j1qs22DmRSAddIh-VnA.AwhB8lxrlKjFn02LGWEqg27H4Tg9fyZAbFv3p5ZicHpj64QyHC44qqlZ3JEmnZTgQowIqZJ13jbyHB8LgePiqUJ1hf6M2HPLgzw8L-mEeQ0jvDUTrE07NtOerBk8bwBQyZ6g0kQ3DEOIglfYxV8-FJvNBYwbqN1Bck6d_i7OtjSHV-8DIrp-3JcRIe05YKy3Oi34Z_GOiAc1EK21B11c_AE11PII_wvvtRiUiG8YofQXakWd1_O98Kap-UgmyWPfreUJ3lJPnbD4Ve95owEfMGLOPflo2MnjaTDCwQokoJ_xplQ2vNPz8iguLcHBoKllyQFJL2mOWBwqhBo9Oj-O800as5mmLsvQMTflIrIEbbTMzHMBZ8EFW9fWwwFu0DWQJGkMNhmBZQ-3lvqTc-M6-gWA6D8PDhONfP2Oib2HGizwG1iEaX8GRyUpfLuljCLIe1DkGOewhKuKkZh04DKNM5Nbugf2atmU9OP0Ldx5peCUtRG1gMVl7Qup5ZXHTjgPDr5b2N731UooCGAUqHdgGhg0JVJ_ObCTdjsH4CF1SJsdUhrXvYx3HJh2Xd7CwJRzU_3Y1GxYU6-s3GFPbirfqqEipJDBTHpcoCmyrwYjYHFgnlqBZRotRrS95g8F95bRXqsaDY7UgQGwBQBwy665d0zpvTasvfXf_c0MWAl-neFaKOW_Px6g4EUDjG1GWSXV9cLStLw_0ovdApDIFLHYHePyagyHjouQUuGiq7BsYwYrwaF06tgB8hV8omLNfMEmDPJaZUzMuHw6tBDwGkzD-tS_ub9hxrpJ4UsOWnt5rGUyoN2N_c1-TQlXxm5oto14MxnoAyBQBpwIEgSH3Y4ZhwKBhHPjSo0cdwuNdYbGPpb-YUvF-2NZzODiQ1OvWQBRHSbPWYz_xbGkgD504LRtqRwCO7CC_CyyURi1sEssPVsMJRX_U4LFEOc82TiDdqjKOjRUfKK5rqLi8nBE9soQ0DSaOoFQZiGrBrqxDsNYiAYAmxxkos-i3nX4qtByVx85sCE5U_0MqG7COxZWMOPEFrDaepUV-cOyrvoUIng8i8ljKBKxETY2BgPegKBYCxsAUcAkKamSCC9AiBxA0UOHyhTqtlvMksO7AEhNC2-YzPyx1FkhMoS4LLe6E_pFsMlmjA6P1NSge9C5G5tETYXGAn6b1xZbHtmwrPScro9LWhVmAaA7_bxYObnFUxgWtK4vzzQBjZJ36UTk4OTB-JvKWgfVWCFsaw5WCHj6Oo4jpO7d2yN7WMfAj2hTEabz9wumQ0TMhBduZ-QON3pYObSy7TSC1vVme0NJrwF_cJRehKTFmdlXGVldPxZCplr7ZQqRQhF8JP-l4mEQVnCaWGn9ONHlemczGOS-A-wwtnmwjIB1V_vgJRf4FdpV-4hUk4-QLpu3-1lWFxrtZKcggq3tWTduRo5_QebQbUUT_VSCgsFcOmyWKoj56lbxthN19hq1XGWbLGfrrR6MWh23vk01zn8FVwi7uFwEnRYSafsnWLa1Z5TpBj9GvAdl2H9NHwzpB5NqHpZNkQ3NMDj13Fn8fzO0JB83Etbm_tnFQfcb13X3bJ15Cz-Ww1MGhvIpGGnMBT_ADp9xSIyAM9dQ1yeVXk-AIgWBUlN5uyWSGyCxp0cJwx7HxM38z0UIeBu-MytL-eqndM7LxytsVzCbjOTSVRmhYEMIzUAnS1gs7uMQAGRdgRIElTJESGMjb_4bZq9s6Ve1LKkSi0_QDsrABaLe55UY0zF4ZSfOV5PMyPtocwV_dcNPlxLgNAD1BFX_Z9kAdMZQW6fAmsfFle0zAoMe4l9pMESH0JB4sJGdCKtQXj1cXNydDYozF7l8H00BV_Er7zd6VtIw0MxwkFCTatsv_R-GsBCH218RgVPsfYhwVuT8R4HarpzsDBufC4r8_c8fc9Z278sQ081jFjOja6L2x0N_ImzFNXU6xwO-Ska-QeuvYZ3X_L31ZOX4Llp-7QSfgDoHnOxFv1Xws-D5mDHD3zxOup2b2TppdKTZb9eW2vxUVviM8OI9atBfPKMGAOv9omA-6vv5IxUH0-lWMiHLQ_g8vnswp-Jav0c4t6URVUzujNOoNd_CBGGVnHiJTCHl88LQxsqLHHIu4Fz-U2SGnlxGTj0-ihit2ELGRv4vO8E1BosTmf0cx3qgG0Pq0eOLBDIHsrdZ_CCAiTc0HVkMbyq1M6qEhM-q5P6y1QCIrwg.0HFmhOzsQ98nNWJjIHkR7A">>}.potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jwk/c.1.jwk+json0000664000232200023220000000316615267717064025667 0ustar debalancedebalance{"kty":"RSA","kid":"juliet@capulet.lit","use":"enc","n":"t6Q8PWSi1dkJj9hTP8hNYFlvadM7DflW9mWepOJhJ66w7nyoK1gPNqFMSQRyO125Gp-TEkodhWr0iujjHVx7BcV0llS4w5ACGgPrcAd6ZcSR0-Iqom-QFcNP8Sjg086MwoqQU_LYywlAGZ21WSdS_PERyGFiNnj3QQlO8Yns5jCtLCRwLHL0Pb1fEv45AuRIuUfVcPySBWYnDyGxvjYGDSM-AqWS9zIQ2ZilgT-GqUmipg0XOC0Cc20rgLe2ymLHjpHciCKVAbY5-L32-lSeZO-Os6U15_aXrk9Gw8cPUaX1_I8sLGuSiVdt3C_Fn2PZ3Z8i744FPFGGcG1qs2Wz-Q","e":"AQAB","d":"GRtbIQmhOZtyszfgKdg4u_N-R_mZGU_9k7JQ_jn1DnfTuMdSNprTeaSTyWfSNkuaAwnOEbIQVy1IQbWVV25NY3ybc_IhUJtfri7bAXYEReWaCl3hdlPKXy9UvqPYGR0kIXTQRqns-dVJ7jahlI7LyckrpTmrM8dWBo4_PMaenNnPiQgO0xnuToxutRZJfJvG4Ox4ka3GORQd9CsCZ2vsUDmsXOfUENOyMqADC6p1M3h33tsurY15k9qMSpG9OX_IJAXmxzAh_tWiZOwk2K4yxH9tS3Lq1yX8C1EWmeRDkK2ahecG85-oLKQt5VEpWHKmjOi_gJSdSgqcN96X52esAQ","p":"2rnSOV4hKSN8sS4CgcQHFbs08XboFDqKum3sc4h3GRxrTmQdl1ZK9uw-PIHfQP0FkxXVrx-WE-ZEbrqivH_2iCLUS7wAl6XvARt1KkIaUxPPSYB9yk31s0Q8UK96E3_OrADAYtAJs-M3JxCLfNgqh56HDnETTQhH3rCT5T3yJws","q":"1u_RiFDP7LBYh3N4GXLT9OpSKYP0uQZyiaZwBtOCBNJgQxaj10RWjsZu0c6Iedis4S7B_coSKB0Kj9PaPaBzg-IySRvvcQuPamQu66riMhjVtG6TlV8CLCYKrYl52ziqK0E_ym2QnkwsUX7eYTB7LbAHRK9GqocDE5B0f808I4s","dp":"KkMTWqBUefVwZ2_Dbj1pPQqyHSHjj90L5x_MOzqYAJMcLMZtbUtwKqvVDq3tbEo3ZIcohbDtt6SbfmWzggabpQxNxuBpoOOf_a_HgMXK_lhqigI4y_kqS1wY52IwjUn5rgRrJ-yYo1h41KR-vz2pYhEAeYrhttWtxVqLCRViD6c","dq":"AvfS0-gRxvn0bwJoMSnFxYcK1WnuEjQFluMGfwGitQBWtfZ1Er7t1xDkbN9GQTB9yqpDoYaN06H7CFtrkxhJIBQaj6nkF5KKS3TQtQ5qCzkOkmxIe3KRbBymXxkb5qwUpX5ELD5xFc6FeiafWYY63TmmEAu_lRFCOJ3xDea-ots","qi":"lSQi-w9CpyUReMErP1RsBLk7wNtOvs5EQpPqmuMvqW57NBUczScEoPwmUqqabu9V0-Py4dQ57_bapoKRu1R90bvuFnU63SHWEFglZQvJDMeAvmj4sm-Fp0oYu_neotgQ0hzbI5gry7ajdYy9-2lNx_76aBZoOUu9HCJ-UsfSOI8"}potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jwk/c.2.jwe+json0000664000232200023220000000015515267717064025655 0ustar debalancedebalance{"alg":"PBES2-HS256+A128KW","p2s":"2WCTcJZ1Rvd_CJuJripQ1w","p2c":4096,"enc":"A128CBC-HS256","cty":"jwk+json"}potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/rsa-multi.pem0000664000232200023220000000201015267717064025434 0ustar debalancedebalance-----BEGIN RSA PRIVATE KEY----- MIICyAIBAQKBgGs61DcjCl4A/KJcIGy4H06ZyaVd8i+HQoyLhNbF1mSt/GbbfhIg COqIHSbG2wcTi7en5HChpfuieP5oTxIWPbATGo0p22i2HfwiQ3vCs5lI5bUEa38B LHPY6NBphDST74ODwhUGzcyCeFq5vA2Kc7sOSLC1YqV9R7/12kesLp+FAgMBAAEC gYApeDdxWW5de5E4s1tg4HZNdhU1Yga1pOsAchJKPHeBPrL0G+jm/36r7gMRlROD eCS3HQ/id8JM9vvdJTDkruLFpjuRJJ4J+7ecu6oc23Zz6+PxS6CJrJr5iTo6TcM+ ktmx2Wm4BBPZYwe8yzoSCZhXuU75C063r+rLuFSXYypAAQIRAN7iFqWrvIa2Q5UU 9G1fakMCEQCnWJQqst2Lio5LKmrtDqAZAhBdcGVUMSpJQ5ITpwRN8glnAhEAg28l yDd9m8hyJAUnwBJ5mQIRAM55rA6yEXm/OkZJF+Gkp6swggFYMDcCEQCuPzHHhBfb DkKUVhrvvbrNAhBTcpxKaq5qECg+lgYMbpa5AhB4DZVFlG43I1/F6PPFae1nMDcC EQDsULiKdbFMJoxzA3aVVPqFAhAwAg4bguFr+QDbdL6og8UJAhAPZR25SnZcZOSm +WpCFO+PMDcCEQCHLlPV6I+V5kR5Jlxv6t0fAhAOdhtL3/Q0YgzjNQJgiqybAhBt 1NnjprH6FBuaD5gL50tyMDgCEQCif1kbMPnVWDd4JiCmsgVRAhEAn8cIHqxxe01a fVFAGahBgQIQCHW4I73pxUofd9g9POX8ZTA3AhEAw/kGDS4Dehb7iWmsDHGA4wIQ Z3Bp4UsISskWu9ErHuZKQQIQZl+jNgR8jNK/BJekl0PsTDA4AhEEkKjcHEsf4l5r Ekr8WpFfKwIRBB/aoDtj2GPCIIS/gO4mx08CEFpfy3k0MT3bSESfKYaWzD4= -----END RSA PRIVATE KEY-----potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jws/0000775000232200023220000000000015267717064023626 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jws/a.5.jws+json0000664000232200023220000000001615267717064025700 0ustar debalancedebalance{"alg":"none"}potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jws/a.1.config0000664000232200023220000000167515267717064025405 0ustar debalancedebalance{"a.1.1.b64", <<"eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9">>}. {"a.1.1.compact", <<"eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk">>}. {"a.1.1.jwk+json", <<"{\"kty\":\"oct\",\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"}">>}. {"a.1.1.jws+json", <<"{\"typ\":\"JWT\",\r\n \"alg\":\"HS256\"}">>}. {"a.1.1.payload", <<"{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n \"http://example.com/is_root\":true}">>}. {"a.1.1.payload-b64", <<"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ">>}. {"a.1.1.signature-b64", <<"dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk">>}. {"a.1.1.signing-input", <<"eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ">>}. potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jws/a.5.config0000664000232200023220000000112715267717064025401 0ustar debalancedebalance{"a.5.b64", <<"eyJhbGciOiJub25lIn0">>}. {"a.5.compact", <<"eyJhbGciOiJub25lIn0.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.">>}. {"a.5.jws+json", <<"{\"alg\":\"none\"}">>}. {"a.5.payload", <<"{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n \"http://example.com/is_root\":true}">>}. {"a.5.payload-b64", <<"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ">>}. {"a.5.signing-input", <<"eyJhbGciOiJub25lIn0.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ">>}. potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jws/a.4.config0000664000232200023220000000201015267717064025370 0ustar debalancedebalance{"a.4.1.b64", <<"eyJhbGciOiJFUzUxMiJ9">>}. {"a.4.1.compact", <<"eyJhbGciOiJFUzUxMiJ9.UGF5bG9hZA.AdwMgeerwtHoh-l192l60hp9wAHZFVJbLfD_UxMi70cwnZOYaRI1bKPWROc-mZZqwqT2SI-KGDKB34XO0aw_7XdtAG8GaSwFKdCAPZgoXD2YBJZCPEX3xKpRwcdOO8KpEHwJjyqOgzDO7iKvU8vcnwNrmxYbSW9ERBXukOXolLzeO_Jn">>}. {"a.4.1.jwk+json", <<"{\"kty\":\"EC\",\"crv\":\"P-521\",\"x\":\"AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk\",\"y\":\"ADSmRA43Z1DSNx_RvcLI87cdL07l6jQyyBXMoxVg_l2Th-x3S1WDhjDly79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2\",\"d\":\"AY5pb7A0UFiB3RELSD64fTLOSV_jazdF7fLYyuTw8lOfRhWg6Y6rUrPAxerEzgdRhajnu0ferB0d53vM9mE15j2C\"}">>}. {"a.4.1.jws+json", <<"{\"alg\":\"ES512\"}">>}. {"a.4.1.payload", <<"Payload">>}. {"a.4.1.payload-b64", <<"UGF5bG9hZA">>}. {"a.4.1.signature-b64", <<"AdwMgeerwtHoh-l192l60hp9wAHZFVJbLfD_UxMi70cwnZOYaRI1bKPWROc-mZZqwqT2SI-KGDKB34XO0aw_7XdtAG8GaSwFKdCAPZgoXD2YBJZCPEX3xKpRwcdOO8KpEHwJjyqOgzDO7iKvU8vcnwNrmxYbSW9ERBXukOXolLzeO_Jn">>}. {"a.4.1.signing-input", <<"eyJhbGciOiJFUzUxMiJ9.UGF5bG9hZA">>}. potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jws/a.3.1.jwk+json0000664000232200023220000000026015267717064026026 0ustar debalancedebalance{"kty":"EC","crv":"P-256","x":"f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU","y":"x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0","d":"jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI"}potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jws/a.2.config0000664000232200023220000000570315267717064025402 0ustar debalancedebalance{"a.2.1.b64", <<"eyJhbGciOiJSUzI1NiJ9">>}. {"a.2.1.compact", <<"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw">>}. {"a.2.1.jwk+json", <<"{\"kty\":\"RSA\",\"n\":\"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ\",\"e\":\"AQAB\",\"d\":\"Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ\",\"p\":\"4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPGBY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc\",\"q\":\"uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc\",\"dp\":\"BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3QCLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0\",\"dq\":\"h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-kyNlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU\",\"qi\":\"IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2oy26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLUW0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U\"}">>}. {"a.2.1.jws+json", <<"{\"alg\":\"RS256\"}">>}. {"a.2.1.payload", <<"{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n \"http://example.com/is_root\":true}">>}. {"a.2.1.payload-b64", <<"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ">>}. {"a.2.1.signature-b64", <<"cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw">>}. {"a.2.1.signing-input", <<"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ">>}. potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jws/a.4.1.jwk+json0000664000232200023220000000046715267717064026040 0ustar debalancedebalance{"kty":"EC","crv":"P-521","x":"AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk","y":"ADSmRA43Z1DSNx_RvcLI87cdL07l6jQyyBXMoxVg_l2Th-x3S1WDhjDly79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2","d":"AY5pb7A0UFiB3RELSD64fTLOSV_jazdF7fLYyuTw8lOfRhWg6Y6rUrPAxerEzgdRhajnu0ferB0d53vM9mE15j2C"}potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jws/a.2.1.jwk+json0000664000232200023220000000311715267717064026031 0ustar debalancedebalance{"kty":"RSA","n":"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ","e":"AQAB","d":"Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ","p":"4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPGBY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc","q":"uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc","dp":"BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3QCLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0","dq":"h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-kyNlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU","qi":"IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2oy26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLUW0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U"}potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jws/a.1.1.jwk+json0000664000232200023220000000015215267717064026024 0ustar debalancedebalance{"kty":"oct","k":"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow"}potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jws/a.1.1.jws+json0000664000232200023220000000003615267717064026035 0ustar debalancedebalance{"typ":"JWT", "alg":"HS256"}potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jws/a.3.1.jws+json0000664000232200023220000000001715267717064026036 0ustar debalancedebalance{"alg":"ES256"}potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jws/a.2.1.jws+json0000664000232200023220000000001715267717064026035 0ustar debalancedebalance{"alg":"RS256"}potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jws/a.4.1.jws+json0000664000232200023220000000001715267717064026037 0ustar debalancedebalance{"alg":"ES512"}potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jws/a.3.config0000664000232200023220000000202415267717064025374 0ustar debalancedebalance{"a.3.1.b64", <<"eyJhbGciOiJFUzI1NiJ9">>}. {"a.3.1.compact", <<"eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q">>}. {"a.3.1.jwk+json", <<"{\"kty\":\"EC\",\"crv\":\"P-256\",\"x\":\"f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU\",\"y\":\"x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0\",\"d\":\"jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI\"}">>}. {"a.3.1.jws+json", <<"{\"alg\":\"ES256\"}">>}. {"a.3.1.payload", <<"{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n \"http://example.com/is_root\":true}">>}. {"a.3.1.payload-b64", <<"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ">>}. {"a.3.1.signature-b64", <<"DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q">>}. {"a.3.1.signing-input", <<"eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ">>}. potatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jose_ecdh_1pu/0000775000232200023220000000000015267717064025533 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/test/jose_SUITE_data/jose_ecdh_1pu/a.config0000664000232200023220000000247015267717064027145 0ustar debalancedebalance{"a.ussk.jwk+json", <<"{\"kty\":\"EC\",\"crv\":\"P-256\",\"x\":\"WKn-ZIGevcwGIyyrzFoZNBdaq9_TsqzGl96oc0CWuis\",\"y\":\"y77t-RvAHRKTsSGdIYUfweuOvwrvDD-Q3Hv5J0fSKbE\",\"d\":\"Hndv7ZZjs_ke8o9zXYo3iq-Yr8SewI5vrqd0pAvEPqg\"}">>}. {"a.vssk.jwk+json", <<"{\"kty\":\"EC\",\"crv\":\"P-256\",\"x\":\"weNJy2HscCSM6AEDTDg04biOvhFhyyWvOHQfeF_PxMQ\",\"y\":\"e8lnCO-AlStT-NJVX-crhB7QRYhiix03illJOVAOyck\",\"d\":\"VEmDZpDXXK8p8N0Cndsxs924q6nS1RXFASRl6BfUqdw\"}">>}. {"a.uesk.jwk+json", <<"{\"kty\":\"EC\",\"crv\":\"P-256\",\"x\":\"gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0\",\"y\":\"SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps\",\"d\":\"0_NxaRPUMQoAJt50Gz8YiTr8gRTwyEaCumd-MToTmIo\"}">>}. {"a.jwe+json", <<"{\"alg\":\"ECDH-1PU\",\"enc\":\"A256GCM\",\"apu\":\"QWxpY2U\",\"apv\":\"Qm9i\",\"epk\":{\"kty\":\"EC\",\"crv\":\"P-256\",\"x\":\"gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0\",\"y\":\"SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps\"}}">>}. {"a.ze+hex", <<"9e56d91d817135d372834283bf84269cfb316ea3da806a48f6daa7798cfe90c4">>}. {"a.zs+hex", <<"e3ca3474384c9f62b30bfd4c688b3e7d4110a1b4badc3cc54ef7b81241efd50d">>}. {"a.z+hex", <<"9e56d91d817135d372834283bf84269cfb316ea3da806a48f6daa7798cfe90c4e3ca3474384c9f62b30bfd4c688b3e7d4110a1b4badc3cc54ef7b81241efd50d">>}. {"a.cek+hex", <<"6caf13723d14850ad4b42cd6dde935bffd2fff00a9ba70de05c203a5e1722ca7">>}.potatosalad-erlang-jose-30ac019/test/jose_jwk_kty_rsa_sfm_SUITE.erl0000664000232200023220000002477215267717064026026 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @doc %%% Regression tests for the (n, e, d) -> CRT prime recovery performed %%% by jose_jwk_kty_rsa:from_map/1 when an RSA private JWK omits every %%% CRT parameter. See GHSA-3m45-vrpj-xmrq: a JWK holding integers %%% that cannot come from a real RSA key used to keep the recovery %%% search running until an internal 1000 ms watchdog killed it, and an %%% oversized modulus could run far past that watchdog because a single %%% crypto:mod_pow/3 NIF cannot be interrupted. %%% @end %%%------------------------------------------------------------------- -module(jose_jwk_kty_rsa_sfm_SUITE). -include_lib("common_test/include/ct.hrl"). -include_lib("public_key/include/public_key.hrl"). %% ct. -export([all/0, init_per_suite/1, end_per_suite/1]). %% Tests. -export([sfm_key_still_recovers_crt_parameters/1]). -export([sfm_prime_modulus_is_rejected_quickly/1]). -export([sfm_consistent_prime_modulus_is_rejected_quickly/1]). -export([sfm_oversized_modulus_is_rejected_quickly/1]). -export([sfm_structurally_invalid_key_is_rejected_quickly/1]). -export([sfm_large_consistent_primes_have_a_work_budget/1]). -export([sfm_large_exponents_have_a_work_budget/1]). -export([sfm_recovered_factors_validate_exponents/1]). -export([sfm_shared_factor_bases_are_not_returned_as_primes/1]). -export([sfm_small_base_residue_key_still_imports/1]). -export([sfm_large_valid_keys_still_import_and_operate/1]). %% Every rejection must land before the 1000 ms watchdog, which is the %% whole point: reaching it means the search ran. Kept just under the %% watchdog rather than at the measured cost (14 - 120 ms for the most %% expensive case) so that a slow or loaded machine cannot flake. -define(REJECT_TIMEOUT_MS, 900). all() -> [ sfm_key_still_recovers_crt_parameters, sfm_prime_modulus_is_rejected_quickly, sfm_consistent_prime_modulus_is_rejected_quickly, sfm_oversized_modulus_is_rejected_quickly, sfm_structurally_invalid_key_is_rejected_quickly, sfm_large_consistent_primes_have_a_work_budget, sfm_large_exponents_have_a_work_budget, sfm_recovered_factors_validate_exponents, sfm_shared_factor_bases_are_not_returned_as_primes, sfm_small_base_residue_key_still_imports, sfm_large_valid_keys_still_import_and_operate ]. %% Public signing/decryption controls start JOSE. Stop it at the suite %% boundary so the next suite can apply its own application environment. init_per_suite(Config) -> {ok, _} = application:ensure_all_started(jose), Config. end_per_suite(_Config) -> ok = application:stop(jose). %%==================================================================== %% Tests %%==================================================================== %% A genuine private key that omits p, q, dp, dq and qi must still be %% recovered, byte for byte, into the same key as the full CRT JWK. sfm_key_still_recovers_crt_parameters(_Config) -> PrivateKey = #'RSAPrivateKey'{} = public_key:generate_key({rsa, 1024, 65537}), CRTMap = #{ <<"kty">> => <<"RSA">>, <<"n">> => b64(PrivateKey#'RSAPrivateKey'.modulus), <<"e">> => b64(PrivateKey#'RSAPrivateKey'.publicExponent), <<"d">> => b64(PrivateKey#'RSAPrivateKey'.privateExponent), <<"p">> => b64(PrivateKey#'RSAPrivateKey'.prime1), <<"q">> => b64(PrivateKey#'RSAPrivateKey'.prime2), <<"dp">> => b64(PrivateKey#'RSAPrivateKey'.exponent1), <<"dq">> => b64(PrivateKey#'RSAPrivateKey'.exponent2), <<"qi">> => b64(PrivateKey#'RSAPrivateKey'.coefficient) }, SFMMap = maps:without([<<"p">>, <<"q">>, <<"dp">>, <<"dq">>, <<"qi">>], CRTMap), {CRTKey, #{}} = jose_jwk_kty_rsa:from_map(CRTMap), {SFMKey, #{}} = jose_jwk_kty_rsa:from_map(SFMMap), CRTKey = SFMKey, PrivateKey = SFMKey, ok. %% GHSA-3m45-vrpj-xmrq proof of concept: n is prime, e = 65537, d = 3. %% The primes can never be recovered, so the key has to be rejected %% without running the search. sfm_prime_modulus_is_rejected_quickly(_Config) -> Prime = prime_1024(), rejects_quickly(#{ <<"kty">> => <<"RSA">>, <<"n">> => b64(Prime), <<"e">> => b64(65537), <<"d">> => b64(3) }). %% Harder variant: n is prime and (n, e, d) is self consistent, so %% 2^(ed-1) = 1 (mod n) holds and only the bound on the search base %% stops the loop. sfm_consistent_prime_modulus_is_rejected_quickly(_Config) -> Prime = prime_1024(), {E, D} = consistent_ed(Prime, 3), 1 = jose_jwa_math:expmod(2, D * E - 1, Prime), rejects_quickly(#{ <<"kty">> => <<"RSA">>, <<"n">> => b64(Prime), <<"e">> => b64(E), <<"d">> => b64(D) }). %% An oversized modulus makes one modular exponentiation cost more than %% the whole watchdog window, so it has to be rejected before any %% modular arithmetic happens. sfm_oversized_modulus_is_rejected_quickly(_Config) -> rejects_quickly(#{ <<"kty">> => <<"RSA">>, <<"n">> => b64(odd_of(16384)), <<"e">> => b64(odd_of(16384)), <<"d">> => b64(odd_of(16384)) }). %% e = 2 and d = 1 make d*e-1 odd, which used to spin the search %% forever without doing any work at all. sfm_structurally_invalid_key_is_rejected_quickly(_Config) -> Prime = prime_1024(), rejects_quickly(#{ <<"kty">> => <<"RSA">>, <<"n">> => b64(Prime), <<"e">> => b64(2), <<"d">> => b64(1) }). %% Public imports: the Proth prime has a very long squaring chain; the %% Mersenne primes have long odd exponents. Both exhausted the old search. %% 900 ms leaves scheduler/NIF tolerance above the 200/500 ms work deadlines, %% while failing if the import reaches the inherited 1000 ms watchdog. sfm_large_consistent_primes_have_a_work_budget(_Config) -> Primes = [(1 bsl 4423) - 1, 549755814211 * (1 bsl 2008) + 1, (1 bsl 8191) - 1], lists:foreach(fun(N) -> {E, D} = consistent_ed(N, 65537), rejects_public_quickly(sfm_map(N, E, D)) end, Primes). sfm_large_exponents_have_a_work_budget(_Config) -> lists:foreach(fun(N) -> rejects_public_quickly(sfm_map(N, N - 2, N - 2)) end, [(1 bsl 8192) - 1, (1 bsl 8191) - 1]). %% Random gcd recovery must not accept factors with incompatible d/e or %% a repeated prime (whose CRT coefficient has no multiplicative inverse). sfm_recovered_factors_validate_exponents(_Config) -> rejects_public_quickly(sfm_map(21, 5, 3)), rejects_public_quickly(sfm_map(9, 5, 5)), {_, #{}} = jose_jwk_kty_rsa:from_map(sfm_map(21, 5, 5)), ok. %% 2^64 - 1 has many small factors, so roughly half of the random bases %% share one with n. Returning gcd(A, n) as a prime skipped the %% A^(ed-1) = 1 (mod n) check and imported p = 3, q = n / 3 (composite) %% for about a quarter of these inconsistent keys. Repeat enough times %% that the old behavior cannot pass by chance (0.75^64 < 2^-26). sfm_shared_factor_bases_are_not_returned_as_primes(_Config) -> N = (1 bsl 64) - 1, Map = sfm_map(N, N - 2, N - 2), lists:foreach(fun(_) -> Result = try jose_jwk:from_map(Map) of Other -> {returned, Other} catch Class:Reason -> {Class, Reason} end, {error, {badarg, [#'RSAPrivateKey'{}]}} = Result end, lists:seq(1, 64)). %% These two primes have matching characters for all 50 old even bases. %% The original unbounded search imports the key; the inherited finite %% deterministic search rejects it. Pair public recovery with real use. sfm_small_base_residue_key_still_imports(_Config) -> P = 16#965afc7ec3d0075fd7caffc21ca876053a86577b73632df5c8eb17bb18880e83855ac6b9396594fc94f09a1a609aae8cc81d8a70b6f33df18593535e53209fbf, Q = 16#95d8b95692268bdee09ace199a4aa89e12a0aed4e9b63f852e1938332d4ca10691409b2b41133c89b2be86b9c6ea82e69ed7ff58f8d93d8698e2a8c4c20f6acf, E = 65537, L = ((P - 1) * (Q - 1)) div gcd(P - 1, Q - 1), D = mod_inverse(E, L), Map = sfm_map(P * Q, E, D), lists:foreach(fun(_) -> import_and_operate(Map) end, lists:seq(1, 16)). sfm_large_valid_keys_still_import_and_operate(_Config) -> lists:foreach(fun(Bits) -> Key = public_key:generate_key({rsa, Bits, 65537}), CRT = jose_jwk_kty_rsa:to_map(Key, #{}), SFM = maps:without([<<"p">>, <<"q">>, <<"dp">>, <<"dq">>, <<"qi">>], CRT), Recovered = import_and_operate(SFM), Recovered = import_and_operate(CRT), Key = Recovered end, [2048, 4096, 8192]). %% @private import_and_operate(Map) -> {ok, _} = application:ensure_all_started(jose), {US, JWK} = timer:tc(fun() -> jose_jwk:from_map(Map) end), Public = jose_jwk:to_public(JWK), Message = <<"recovered RSA key control">>, Signed = jose_jws:sign(JWK, Message, #{<<"alg">> => <<"RS256">>}), {true, Message, _} = jose_jws:verify(Public, Signed), Encrypted = jose_jwe:block_encrypt(Public, Message, #{<<"alg">> => <<"RSA-OAEP">>, <<"enc">> => <<"A256GCM">>}), {Message, _} = jose_jwe:block_decrypt(JWK, Encrypted), {_, Key} = jose_jwk:to_key(JWK), ct:pal("valid RSA modulus_bytes=~p recovered/imported in ~p us", [byte_size(binary:encode_unsigned(Key#'RSAPrivateKey'.modulus)), US]), Key. %% @private rejects_public_quickly(Map) -> {US, Result} = timer:tc(fun() -> try jose_jwk:from_map(Map) of Other -> {returned, Other} catch Class:Reason -> {Class, Reason} end end), {error, {badarg, [#'RSAPrivateKey'{}]}} = Result, ct:pal("public import rejected ~p-byte JWK in ~p us", [byte_size(jose:encode(Map)), US]), true = (US < ?REJECT_TIMEOUT_MS * 1000), ok. %% @private sfm_map(N, E, D) -> #{<<"kty">> => <<"RSA">>, <<"n">> => b64(N), <<"e">> => b64(E), <<"d">> => b64(D)}. %% @private gcd(A, 0) -> A; gcd(A, B) -> gcd(B, A rem B). %%==================================================================== %% Internal functions %%==================================================================== %% @private b64(Integer) when is_integer(Integer) -> jose_jwa_base64url:encode(binary:encode_unsigned(Integer)). %% @private consistent_ed(N, E) -> case mod_inverse(E, N - 1) of D when is_integer(D) andalso D > 2 -> {E, D}; _ -> consistent_ed(N, E + 2) end. %% @private egcd(0, B) -> {B, 0, 1}; egcd(A, B) -> {G, Y, X} = egcd(B rem A, A), {G, X - (B div A) * Y, Y}. %% @private mod_inverse(A, M) -> case egcd(A, M) of {1, X, _Y} -> jose_jwa_math:mod(X, M); _ -> no_inverse end. %% @private odd_of(Bits) -> ((1 bsl (Bits - 1)) bor 1) + 2 * rand:uniform(1 bsl (Bits - 2)). %% @private prime_1024() -> PrivateKey = #'RSAPrivateKey'{} = public_key:generate_key({rsa, 2048, 65537}), PrivateKey#'RSAPrivateKey'.prime1. %% @private rejects_quickly(Map) -> {Elapsed, Result} = timer:tc(fun() -> try jose_jwk_kty_rsa:from_map(Map) of Other -> {returned, Other} catch Class:Reason -> {Class, Reason} end end), {error, {badarg, [#'RSAPrivateKey'{}]}} = Result, ct:pal("rejected in ~p us", [Elapsed]), true = (Elapsed < ?REJECT_TIMEOUT_MS * 1000), ok. potatosalad-erlang-jose-30ac019/test/property_test/0000775000232200023220000000000015267717064023044 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/test/property_test/jose_jwe_alg_ecdh_1pu_props.erl0000664000232200023220000000727315267717064031204 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwe_alg_ecdh_1pu_props). -include_lib("public_key/include/public_key.hrl"). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). alg() -> oneof([ <<"ECDH-1PU">>, <<"ECDH-1PU+A128GCMKW">>, <<"ECDH-1PU+A192GCMKW">>, <<"ECDH-1PU+A256GCMKW">>, <<"ECDH-1PU+A128KW">>, <<"ECDH-1PU+A192KW">>, <<"ECDH-1PU+A256KW">>, <<"ECDH-1PU+C20PKW">>, <<"ECDH-1PU+XC20PKW">> ]). alg_map() -> ?LET({ALG, APU, APV}, {alg(), binary(), binary()}, #{ <<"alg">> => ALG, <<"apu">> => jose_jwa_base64url:encode(APU), <<"apv">> => jose_jwa_base64url:encode(APV) }). ec_curve() -> oneof([ secp256r1, secp384r1, secp521r1, x25519, x448 ]). % ec_curve() -> % ?SUCHTHAT(CurveId, % oneof(crypto:ec_curves()), % begin % try pubkey_cert_records:namedCurves(CurveId) of % Curve when is_tuple(Curve) -> % true; % _ -> % false % catch % _:_ -> % false % end % end). % ec_keypair() -> % ?LET(CurveId, % ec_curve(), % ec_keypair(CurveId)). ec_keypair(x25519) -> SecretJWK = jose_jwk:generate_key({okp, 'X25519'}), {_, SecretKey} = jose_jwk:to_key(SecretJWK), {_, PublicKey} = jose_jwk:to_public_key(SecretJWK), {SecretKey, PublicKey}; ec_keypair(x448) -> SecretJWK = jose_jwk:generate_key({okp, 'X448'}), {_, SecretKey} = jose_jwk:to_key(SecretJWK), {_, PublicKey} = jose_jwk:to_public_key(SecretJWK), {SecretKey, PublicKey}; ec_keypair(CurveId) -> ECPrivateKey = #'ECPrivateKey'{parameters=ECParameters, publicKey=Octets0} = public_key:generate_key({namedCurve, pubkey_cert_records:namedCurves(CurveId)}), Octets = case Octets0 of {_, Octets1} -> Octets1; _ -> Octets0 end, ECPoint = #'ECPoint'{point=Octets}, ECPublicKey = {ECPoint, ECParameters}, {ECPrivateKey, ECPublicKey}. enc() -> oneof([ <<"A128GCM">>, <<"A192GCM">>, <<"A256GCM">>, <<"C20P">>, <<"XC20P">> ]). jwk_jwe_maps() -> ?LET({ALGMap, ENC, {VStaticSecret, VStaticPublic}, {UStaticSecret, UStaticPublic}, {UEphemeralSecret, UEphemeralPublic}}, ?LET(CurveId, ec_curve(), {alg_map(), enc(), ec_keypair(CurveId), ec_keypair(CurveId), ec_keypair(CurveId)}), begin VStaticSecretKey = jose_jwk:from_key(VStaticSecret), VStaticPublicKey = jose_jwk:from_key(VStaticPublic), UStaticSecretKey = jose_jwk:from_key(UStaticSecret), UStaticPublicKey = jose_jwk:from_key(UStaticPublic), UEphemeralSecretKey = jose_jwk:from_key(UEphemeralSecret), UEphemeralPublicKey = jose_jwk:from_key(UEphemeralPublic), {_, UEphemeralPublicKeyMap} = jose_jwk:to_public_map(UEphemeralPublicKey), VBox = {UStaticPublicKey, VStaticSecretKey}, UBox = {VStaticPublicKey, UStaticSecretKey, UEphemeralSecretKey}, JWKs = {VBox, UBox}, JWEMap = maps:merge(#{ <<"enc">> => ENC, <<"epk">> => UEphemeralPublicKeyMap }, ALGMap), {JWKs, JWEMap} end). jwk_jwe_gen() -> ?LET({JWKs, JWEMap}, jwk_jwe_maps(), {JWKs, jose_jwe:from_map(JWEMap)}). prop_from_map_and_to_map() -> ?FORALL(JWEMap, ?LET({{_JWKs, JWEMap}, Extras}, {jwk_jwe_maps(), binary_map()}, maps:merge(Extras, JWEMap)), begin JWE = jose_jwe:from_map(JWEMap), JWEMap =:= element(2, jose_jwe:to_map(JWE)) end). prop_key_encrypt_and_key_decrypt() -> ?FORALL({{VBox, UBox}, JWE}, jwk_jwe_gen(), begin {DecKey, DecJWE} = jose_jwe:next_cek(UBox, JWE), {EncKey, EncJWE} = jose_jwe:key_encrypt(UBox, DecKey, DecJWE), DecKey =:= jose_jwe:key_decrypt(VBox, EncKey, EncJWE) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwk_kty_ec_props.erl0000664000232200023220000001076115267717064030151 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwk_kty_ec_props). -include_lib("public_key/include/public_key.hrl"). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). ec_curve() -> oneof([ secp256r1, secp384r1, secp521r1 ]). ec_keypair(CurveId) -> ECPrivateKey = #'ECPrivateKey'{parameters=ECParameters, publicKey=Octets0} = public_key:generate_key({namedCurve, pubkey_cert_records:namedCurves(CurveId)}), Octets = case Octets0 of {_, Octets1} -> Octets1; _ -> Octets0 end, ECPoint = #'ECPoint'{point=Octets}, ECPublicKey = {ECPoint, ECParameters}, {ECPrivateKey, ECPublicKey}. jwk_map() -> ?LET(CurveId, ec_curve(), begin AliceKeys = {AlicePrivateKey, _} = ec_keypair(CurveId), BobKeys = ec_keypair(CurveId), AlicePrivateJWK = jose_jwk:from_key(AlicePrivateKey), {_, AlicePrivateJWKMap} = jose_jwk:to_map(AlicePrivateJWK), Keys = {AliceKeys, BobKeys}, {Keys, AlicePrivateJWKMap} end). jwk_gen() -> ?LET({Keys, AlicePrivateJWKMap}, jwk_map(), {Keys, jose_jwk:from_map(AlicePrivateJWKMap)}). prop_from_der_and_to_der() -> ?FORALL({_Keys, AlicePrivateJWK, Password}, ?LET({{Keys, AlicePrivateJWK}, Bytes}, {jwk_gen(), binary()}, {Keys, AlicePrivateJWK, jose_jwa_base64url:encode(Bytes)}), begin AlicePrivateDER = element(2, jose_jwk:to_der(AlicePrivateJWK)), EncryptedAlicePrivateDER = element(2, jose_jwk:to_der(Password, AlicePrivateJWK)), AlicePublicJWK = jose_jwk:to_public(AlicePrivateJWK), AlicePublicDER = element(2, jose_jwk:to_der(AlicePublicJWK)), AlicePrivateJWK =:= jose_jwk:from_der(AlicePrivateDER) andalso AlicePrivateJWK =:= jose_jwk:from_der(Password, EncryptedAlicePrivateDER) andalso AlicePublicJWK =:= jose_jwk:from_der(AlicePublicDER) end). prop_from_map_and_to_map() -> ?FORALL({{{AlicePrivateKey, AlicePublicKey}, _}, AlicePrivateJWKMap}, ?LET({{Keys, JWKMap}, Extras}, {jwk_map(), binary_map()}, {Keys, maps:merge(Extras, JWKMap)}), begin AlicePrivateJWK = jose_jwk:from_map(AlicePrivateJWKMap), AlicePublicJWK = jose_jwk:to_public(AlicePrivateJWK), AlicePublicJWKMap = element(2, jose_jwk:to_map(AlicePublicJWK)), AlicePublicThumbprint = jose_jwk:thumbprint(AlicePublicJWK), AlicePrivateJWKMap =:= element(2, jose_jwk:to_map(AlicePrivateJWK)) andalso AlicePrivateKey =:= element(2, jose_jwk:to_key(AlicePrivateJWK)) andalso AlicePublicKey =:= element(2, jose_jwk:to_public_key(AlicePrivateJWK)) andalso AlicePublicJWKMap =:= element(2, jose_jwk:to_public_map(AlicePrivateJWK)) andalso AlicePublicThumbprint =:= jose_jwk:thumbprint(AlicePrivateJWK) end). prop_from_pem_and_to_pem() -> ?FORALL({_Keys, AlicePrivateJWK, Password}, ?LET({{Keys, AlicePrivateJWK}, Bytes}, {jwk_gen(), binary()}, {Keys, AlicePrivateJWK, jose_jwa_base64url:encode(Bytes)}), begin AlicePrivatePEM = element(2, jose_jwk:to_pem(AlicePrivateJWK)), EncryptedAlicePrivatePEM = element(2, jose_jwk:to_pem(Password, AlicePrivateJWK)), AlicePublicJWK = jose_jwk:to_public(AlicePrivateJWK), AlicePublicPEM = element(2, jose_jwk:to_pem(AlicePublicJWK)), AlicePrivateJWK =:= jose_jwk:from_pem(AlicePrivatePEM) andalso AlicePrivateJWK =:= jose_jwk:from_pem(Password, EncryptedAlicePrivatePEM) andalso AlicePublicJWK =:= jose_jwk:from_pem(AlicePublicPEM) end). prop_box_encrypt_and_box_decrypt() -> ?FORALL({{{_, {BobPrivateKey, BobPublicKey}}, AlicePrivateJWK}, PlainText}, {jwk_gen(), binary()}, begin BobPrivateJWK = jose_jwk:from_key(BobPrivateKey), BobPublicJWK = jose_jwk:from_key(BobPublicKey), Encrypted = jose_jwk:box_encrypt_ecdh_es(PlainText, BobPublicJWK, AlicePrivateJWK), CompactEncrypted = jose_jwe:compact(Encrypted), Decrypted = {_, JWE} = jose_jwk:box_decrypt_ecdh_es(Encrypted, BobPrivateJWK), {PlainText, JWE} =:= Decrypted andalso {PlainText, JWE} =:= jose_jwk:block_decrypt(CompactEncrypted, BobPrivateJWK) end). prop_sign_and_verify() -> ?FORALL({_Keys, JWK, Message}, ?LET({Keys, JWK}, jwk_gen(), {Keys, JWK, binary()}), begin Signed = jose_jwk:sign(Message, JWK), CompactSigned = jose_jws:compact(Signed), Verified = {_, _, JWS} = jose_jwk:verify(Signed, JWK), {true, Message, JWS} =:= Verified andalso {true, Message, JWS} =:= jose_jwk:verify(CompactSigned, JWK) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jws_alg_none_props.erl0000664000232200023220000000255015267717064030462 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jws_alg_none_props). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). alg() -> return(<<"none">>). jwk_jws_maps() -> ?LET({ALG, Key}, {alg(), binary()}, begin JWKMap = #{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(Key) }, JWSMap = #{ <<"alg">> => ALG }, {Key, JWKMap, JWSMap} end). jwk_jws_gen() -> ?LET({Key, JWKMap, JWSMap}, jwk_jws_maps(), {Key, jose_jwk:from_map(JWKMap), jose_jws:from_map(JWSMap)}). prop_from_map_and_to_map() -> ?FORALL(JWSMap, ?LET({{_Key, _JWKMap, JWSMap}, Extras}, {jwk_jws_maps(), binary_map()}, maps:merge(Extras, JWSMap)), begin JWS = jose_jws:from_map(JWSMap), JWSMap =:= element(2, jose_jws:to_map(JWS)) end). prop_sign_and_verify() -> ?FORALL({{_Key, JWK, JWS}, Message}, {jwk_jws_gen(), binary()}, begin Signed = jose_jws:sign(JWK, Message, JWS), CompactSigned = jose_jws:compact(Signed), {true, Message, JWS} =:= jose_jws:verify(JWK, Signed) andalso {true, Message, JWS} =:= jose_jws:verify(JWK, CompactSigned) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwe_alg_xc20p_kw_props.erl0000664000232200023220000000354315267717064031145 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwe_alg_xc20p_kw_props). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). key_size() -> return(256). key_gen() -> ?LET(KeySize, key_size(), {KeySize, binary(KeySize div 8)}). alg_map(256) -> return(#{ <<"alg">> => <<"XC20PKW">> }). extra_map(_) -> oneof([ #{}, #{ <<"iv">> => jose_jwa_base64url:encode(crypto:strong_rand_bytes(12)), <<"tag">> => jose_jwa_base64url:encode(crypto:strong_rand_bytes(8)) } ]). jwk_jwe_maps() -> ?LET({_KeySize, Key, ALGMap}, ?LET({KeySize, Key}, key_gen(), {KeySize, Key, alg_map(KeySize)}), begin ENC = <<"XC20P">>, JWKMap = #{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(Key) }, JWEMap = maps:merge(#{ <<"enc">> => ENC }, ALGMap), {Key, JWKMap, JWEMap} end). jwk_jwe_gen() -> ?LET({Key, JWKMap, JWEMap}, jwk_jwe_maps(), {Key, jose_jwk:from_map(JWKMap), jose_jwe:from_map(JWEMap)}). prop_from_map_and_to_map() -> ?FORALL(JWEMap, ?LET({JWEMap, ExtraMap, Extras}, ?LET({_Key, _JWKMap, JWEMap}, jwk_jwe_maps(), {JWEMap, extra_map(JWEMap), binary_map()}), maps:merge(maps:merge(Extras, ExtraMap), JWEMap)), begin JWE = jose_jwe:from_map(JWEMap), JWEMap =:= element(2, jose_jwe:to_map(JWE)) end). prop_key_encrypt_and_key_decrypt() -> ?FORALL({_Key, JWK, JWE}, ?LET({Key, JWK, JWE}, jwk_jwe_gen(), {Key, oneof([Key, JWK]), JWE}), begin {DecKey, DecJWE} = jose_jwe:next_cek(JWK, JWE), {EncKey, EncJWE} = jose_jwe:key_encrypt(JWK, DecKey, DecJWE), DecKey =:= jose_jwe:key_decrypt(JWK, EncKey, EncJWE) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwa_aes_kw_props.erl0000664000232200023220000000112115267717064030120 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwa_aes_kw_props). -include_lib("proper/include/proper.hrl"). % -compile(export_all). kek_size() -> oneof([128, 192, 256]). key_wrapper_gen() -> ?LET({Bits, PlainText}, {kek_size(), binary()}, {Bits, binary(Bits div 8), jose_jwa_pkcs7:pad(PlainText)}). prop_wrap_and_unwrap() -> ?FORALL({_Bits, KEK, PlainText}, key_wrapper_gen(), begin CipherText = jose_jwa_aes_kw:wrap(PlainText, KEK), PlainText =:= jose_jwa_aes_kw:unwrap(CipherText, KEK) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_public_key.hrl0000777000232200023220000000000015267717064034642 2../../include/jose_public_key.hrlustar debalancedebalancepotatosalad-erlang-jose-30ac019/test/property_test/jose_jws_alg_eddsa_props.erl0000664000232200023220000000547515267717064030614 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jws_alg_eddsa_props). -include_lib("public_key/include/public_key.hrl"). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). alg() -> oneof([ <<"Ed25519">>, <<"Ed25519ph">>, <<"Ed448">>, <<"Ed448ph">>, <<"EdDSA">> ]). opt_map() -> oneof([ #{}, #{ <<"b64">> => true }, #{ <<"b64">> => false } ]). okp_type(<<"EdDSA">>, PK) when byte_size(PK) =:= 32 -> oneof([ 'Ed25519', 'Ed25519ph' ]); okp_type(<<"EdDSA">>, PK) when byte_size(PK) =:= 57 -> oneof([ 'Ed448', 'Ed448ph' ]); okp_type(<<"Ed25519">>, PK) when byte_size(PK) =:= 32 -> 'Ed25519'; okp_type(<<"Ed25519ph">>, PK) when byte_size(PK) =:= 32 -> 'Ed25519ph'; okp_type(<<"Ed448">>, PK) when byte_size(PK) =:= 57 -> 'Ed448'; okp_type(<<"Ed448ph">>, PK) when byte_size(PK) =:= 57 -> 'Ed448ph'. ed_curve_module(<< "EdDSA" >>) -> oneof([ jose_curve25519, jose_curve448 ]); ed_curve_module(<< "Ed25519", _/binary >>) -> jose_curve25519; ed_curve_module(<< "Ed448", _/binary >>) -> jose_curve448. eddsa_secret(jose_curve25519) -> binary(32); eddsa_secret(jose_curve448) -> binary(57). eddsa_keypair(EdCurveModule, Secret) -> {PK, SK} = EdCurveModule:eddsa_keypair(Secret), {SK, PK}. eddsa_keypair_gen(ALG) -> ?LET({EdCurveModule, Secret}, ?LET(EdCurveModule, ed_curve_module(ALG), {EdCurveModule, eddsa_secret(EdCurveModule)}), eddsa_keypair(EdCurveModule, Secret)). jwk_jws_maps() -> ?LET({{SK, PK}, Opts, OKP, ALG}, ?LET({{SK, PK}, ALG}, ?LET(ALG, alg(), {eddsa_keypair_gen(ALG), ALG}), {{SK, PK}, opt_map(), okp_type(ALG, PK), ALG}), begin JWKSigner = jose_jwk:from_okp({OKP, SK}), JWKVerifier = jose_jwk:from_okp({OKP, PK}), JWSMap = maps:merge(Opts, #{ <<"alg">> => ALG }), {JWKSigner, JWKVerifier, JWSMap} end). jwk_jws_gen() -> ?LET({JWKSigner, JWKVerifier, JWSMap}, jwk_jws_maps(), {JWKSigner, JWKVerifier, jose_jws:from_map(JWSMap)}). prop_from_map_and_to_map() -> ?FORALL(JWSMap, ?LET({ALG, Opts, Extras}, {alg(), opt_map(), binary_map()}, maps:merge(maps:merge(Extras, Opts), #{ <<"alg">> => ALG })), begin JWS = jose_jws:from_map(JWSMap), JWSMap =:= element(2, jose_jws:to_map(JWS)) end). prop_sign_and_verify() -> ?FORALL({{JWKSigner, JWKVerifier, JWS}, Message}, {jwk_jws_gen(), binary()}, begin Signed = jose_jws:sign(JWKSigner, Message, JWS), CompactSigned = jose_jws:compact(Signed), {true, Message, JWS} =:= jose_jws:verify(JWKVerifier, Signed) andalso {true, Message, JWS} =:= jose_jws:verify(JWKVerifier, CompactSigned) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwe_alg_c20p_kw_props.erl0000664000232200023220000000354015267717064030752 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwe_alg_c20p_kw_props). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). key_size() -> return(256). key_gen() -> ?LET(KeySize, key_size(), {KeySize, binary(KeySize div 8)}). alg_map(256) -> return(#{ <<"alg">> => <<"C20PKW">> }). extra_map(_) -> oneof([ #{}, #{ <<"iv">> => jose_jwa_base64url:encode(crypto:strong_rand_bytes(12)), <<"tag">> => jose_jwa_base64url:encode(crypto:strong_rand_bytes(8)) } ]). jwk_jwe_maps() -> ?LET({_KeySize, Key, ALGMap}, ?LET({KeySize, Key}, key_gen(), {KeySize, Key, alg_map(KeySize)}), begin ENC = <<"C20P">>, JWKMap = #{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(Key) }, JWEMap = maps:merge(#{ <<"enc">> => ENC }, ALGMap), {Key, JWKMap, JWEMap} end). jwk_jwe_gen() -> ?LET({Key, JWKMap, JWEMap}, jwk_jwe_maps(), {Key, jose_jwk:from_map(JWKMap), jose_jwe:from_map(JWEMap)}). prop_from_map_and_to_map() -> ?FORALL(JWEMap, ?LET({JWEMap, ExtraMap, Extras}, ?LET({_Key, _JWKMap, JWEMap}, jwk_jwe_maps(), {JWEMap, extra_map(JWEMap), binary_map()}), maps:merge(maps:merge(Extras, ExtraMap), JWEMap)), begin JWE = jose_jwe:from_map(JWEMap), JWEMap =:= element(2, jose_jwe:to_map(JWE)) end). prop_key_encrypt_and_key_decrypt() -> ?FORALL({_Key, JWK, JWE}, ?LET({Key, JWK, JWE}, jwk_jwe_gen(), {Key, oneof([Key, JWK]), JWE}), begin {DecKey, DecJWE} = jose_jwe:next_cek(JWK, JWE), {EncKey, EncJWE} = jose_jwe:key_encrypt(JWK, DecKey, DecJWE), DecKey =:= jose_jwe:key_decrypt(JWK, EncKey, EncJWE) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwk_kty_okp_ed448ph_props.erl0000664000232200023220000000445415267717064031615 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwk_kty_okp_ed448ph_props). -include_lib("public_key/include/public_key.hrl"). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). ed448ph_secret() -> binary(57). ed448ph_keypair(Secret) -> {PK, SK} = jose_curve448:eddsa_keypair(Secret), {SK, PK}. jwk_map() -> ?LET({AliceSecret, BobSecret}, {ed448ph_secret(), ed448ph_secret()}, begin AliceKeys = {AlicePrivateKey, _} = ed448ph_keypair(AliceSecret), BobKeys = ed448ph_keypair(BobSecret), AlicePrivateJWK = jose_jwk:from_okp({'Ed448ph', AlicePrivateKey}), {_, AlicePrivateJWKMap} = jose_jwk:to_map(AlicePrivateJWK), Keys = {AliceKeys, BobKeys}, {Keys, AlicePrivateJWKMap} end). jwk_gen() -> ?LET({Keys, AlicePrivateJWKMap}, jwk_map(), {Keys, jose_jwk:from_map(AlicePrivateJWKMap)}). prop_from_map_and_to_map() -> ?FORALL({{{AlicePrivateKey, AlicePublicKey}, _}, AlicePrivateJWKMap}, ?LET({{Keys, JWKMap}, Extras}, {jwk_map(), binary_map()}, {Keys, maps:merge(Extras, JWKMap)}), begin AlicePrivateJWK = jose_jwk:from_map(AlicePrivateJWKMap), AlicePublicJWK = jose_jwk:to_public(AlicePrivateJWK), AlicePublicJWKMap = element(2, jose_jwk:to_map(AlicePublicJWK)), AlicePublicThumbprint = jose_jwk:thumbprint(AlicePublicJWK), AlicePrivateJWKMap =:= element(2, jose_jwk:to_map(AlicePrivateJWK)) andalso AlicePrivateKey =:= element(2, jose_jwk:to_key(AlicePrivateJWK)) andalso AlicePublicKey =:= element(2, jose_jwk:to_public_key(AlicePrivateJWK)) andalso AlicePublicJWKMap =:= element(2, jose_jwk:to_public_map(AlicePrivateJWK)) andalso AlicePublicThumbprint =:= jose_jwk:thumbprint(AlicePrivateJWK) end). prop_sign_and_verify() -> ?FORALL({_Keys, JWK, Message}, ?LET({Keys, JWK}, jwk_gen(), {Keys, JWK, binary()}), begin Signed = jose_jwk:sign(Message, JWK), CompactSigned = jose_jws:compact(Signed), Verified = {_, _, JWS} = jose_jwk:verify(Signed, JWK), {true, Message, JWS} =:= Verified andalso {true, Message, JWS} =:= jose_jwk:verify(CompactSigned, JWK) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwe_alg_dir_props.erl0000664000232200023220000000365015267717064030265 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwe_alg_dir_props). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). key_size() -> oneof([128, 192, 256]). key_gen() -> ?LET(KeySize, key_size(), {KeySize, binary(KeySize div 8)}). jwk_jwe_maps() -> ?LET({KeySize, Key}, key_gen(), begin ALG = <<"dir">>, ENC = list_to_binary("A" ++ integer_to_list(KeySize) ++ "GCM"), JWKMap = #{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(Key) }, JWEMap = #{ <<"alg">> => ALG, <<"enc">> => ENC }, {Key, JWKMap, JWEMap} end). jwk_jwe_gen() -> ?LET({Key, JWKMap, JWEMap}, jwk_jwe_maps(), {Key, jose_jwk:from_map(JWKMap), jose_jwe:from_map(JWEMap)}). prop_from_map_and_to_map() -> ?FORALL(JWEMap, ?LET({{_Key, _JWKMap, JWEMap}, Extras}, {jwk_jwe_maps(), binary_map()}, maps:merge(Extras, JWEMap)), begin JWE = jose_jwe:from_map(JWEMap), JWEMap =:= element(2, jose_jwe:to_map(JWE)) end). prop_key_decrypt() -> ?FORALL({Key, JWK, JWE}, ?LET({Key, JWK, JWE}, jwk_jwe_gen(), {Key, oneof([Key, JWK]), JWE}), begin {DecKey, DecJWE} = jose_jwe:next_cek(JWK, JWE), Key =:= jose_jwe:key_decrypt(JWK, DecKey, DecJWE) end). prop_key_encrypt() -> ?FORALL({_Key, JWK, JWE}, ?LET({Key, JWK, JWE}, jwk_jwe_gen(), {Key, oneof([Key, JWK]), JWE}), begin {DecKey, DecJWE} = jose_jwe:next_cek(JWK, JWE), {<<>>, DecJWE} =:= jose_jwe:key_encrypt(JWK, DecKey, DecJWE) end). prop_next_cek() -> ?FORALL({Key, JWK, JWE}, ?LET({Key, JWK, JWE}, jwk_jwe_gen(), {Key, oneof([Key, JWK]), JWE}), begin {DecKey, _DecJWE} = jose_jwe:next_cek(JWK, JWE), Key =:= DecKey end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jws_alg_hmac_props.erl0000664000232200023220000000261615267717064030436 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jws_alg_hmac_props). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). alg() -> oneof([ <<"HS256">>, <<"HS384">>, <<"HS512">> ]). jwk_jws_maps() -> ?LET({ALG, Key}, {alg(), binary()}, begin JWKMap = #{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(Key) }, JWSMap = #{ <<"alg">> => ALG }, {Key, JWKMap, JWSMap} end). jwk_jws_gen() -> ?LET({Key, JWKMap, JWSMap}, jwk_jws_maps(), {Key, jose_jwk:from_map(JWKMap), jose_jws:from_map(JWSMap)}). prop_from_map_and_to_map() -> ?FORALL(JWSMap, ?LET({{_Key, _JWKMap, JWSMap}, Extras}, {jwk_jws_maps(), binary_map()}, maps:merge(Extras, JWSMap)), begin JWS = jose_jws:from_map(JWSMap), JWSMap =:= element(2, jose_jws:to_map(JWS)) end). prop_sign_and_verify() -> ?FORALL({{_Key, JWK, JWS}, Message}, {jwk_jws_gen(), binary()}, begin Signed = jose_jws:sign(JWK, Message, JWS), CompactSigned = jose_jws:compact(Signed), {true, Message, JWS} =:= jose_jws:verify(JWK, Signed) andalso {true, Message, JWS} =:= jose_jws:verify(JWK, CompactSigned) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwa_pkcs7_props.erl0000664000232200023220000000056615267717064027712 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwa_pkcs7_props). -include_lib("proper/include/proper.hrl"). % -compile(export_all). prop_pad_and_unpad() -> ?FORALL(Binary, binary(), begin PaddedBinary = jose_jwa_pkcs7:pad(Binary), Binary =:= jose_jwa_pkcs7:unpad(PaddedBinary) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwe_zip_props.erl0000664000232200023220000000620615267717064027466 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwe_zip_props). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). enc() -> oneof([ <<"A128GCM">>, <<"A192GCM">>, <<"A256GCM">>, <<"C20P">>, <<"XC20P">> ]). jwk_jwe_maps() -> ?LET(ENC, enc(), begin FakeJWEMap = #{ <<"alg">> => <<"RSA1_5">>, <<"enc">> => ENC }, FakeJWE = jose_jwe:from_map(FakeJWEMap), {Key, _} = jose_jwe:next_cek(undefined, FakeJWE), JWKMap = #{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(Key) }, JWEMap = #{ <<"alg">> => <<"dir">>, <<"enc">> => ENC, <<"zip">> => <<"DEF">> }, {Key, JWKMap, JWEMap} end). jwk_jwe_gen() -> ?LET({Key, JWKMap, JWEMap}, jwk_jwe_maps(), {Key, jose_jwk:from_map(JWKMap), jose_jwe:from_map(JWEMap)}). prop_ghsa_wfpv_qxp8_x5gm() -> ?FORALL({ZipSizeMaximum, Slack}, {range(1, 4096), range(1, 4096)}, begin OriginalZipSizeMaximum = jose:zip_size_maximum(), try jose:zip_size_maximum(ZipSizeMaximum) of ok -> %% A run of zero bytes is the classic DEFLATE bomb: it %% compresses at roughly 1000:1, so `Greater` is a tiny %% input that must not be allowed to inflate past the cap. Maximum = binary:copy(<<0>>, ZipSizeMaximum), Greater = binary:copy(<<0>>, ZipSizeMaximum + Slack), MaximumCompressed = jose_jwe_zip:compress(Maximum, zlib), GreaterCompressed = jose_jwe_zip:compress(Greater, zlib), MaximumResult = try jose_jwe_zip:uncompress(MaximumCompressed, zlib) catch MaximumClass:MaximumReason -> {MaximumClass, MaximumReason} end, GreaterResult = try jose_jwe_zip:uncompress(GreaterCompressed, zlib) catch GreaterClass:GreaterReason -> {GreaterClass, GreaterReason} end, conjunction([ {maximum_uncompress, Maximum =:= MaximumResult}, {greater_uncompress, {error, badarg} =:= GreaterResult} ]) after ok = jose:zip_size_maximum(OriginalZipSizeMaximum) end end). prop_from_map_and_to_map() -> ?FORALL(JWEMap, ?LET({{_Key, _JWKMap, JWEMap}, Extras}, {jwk_jwe_maps(), binary_map()}, maps:merge(Extras, JWEMap)), begin JWE = jose_jwe:from_map(JWEMap), JWEMap =:= element(2, jose_jwe:to_map(JWE)) end). prop_block_encrypt_and_block_decrypt() -> ?FORALL({CEK, IV, JWK, JWE, PlainText}, ?LET({CEK, JWK, JWE}, jwk_jwe_gen(), {CEK, jose_jwe:next_iv(JWE), JWK, JWE, binary()}), begin Encrypted = jose_jwe:block_encrypt(JWK, PlainText, CEK, IV, JWE), CompactEncrypted = jose_jwe:compact(Encrypted), PlainText =:= element(1, jose_jwe:block_decrypt(JWK, CompactEncrypted)) end). prop_compress_and_uncompress() -> ?FORALL({{_CEK, _JWK, JWE}, PlainText}, {jwk_jwe_gen(), binary()}, begin CipherText = jose_jwe:compress(PlainText, JWE), CipherText =/= PlainText andalso PlainText =:= jose_jwe:uncompress(CipherText, JWE) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwa_aes_props.erl0000664000232200023220000000725415267717064027434 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwa_aes_props). -include_lib("proper/include/proper.hrl"). % -compile(export_all). block_size() -> oneof([128, 192, 256]). cbc_iv() -> binary(16). gcm_iv() -> binary(12). cbc_block_encryptor_gen() -> ?LET({Bits, IV, PlainText}, {block_size(), cbc_iv(), binary()}, {Bits, binary(Bits div 8), IV, jose_jwa_pkcs7:pad(PlainText)}). ecb_block_encryptor_gen() -> ?LET({Bits, PlainText}, {block_size(), binary()}, {Bits, binary(Bits div 8), jose_jwa_pkcs7:pad(PlainText)}). gcm_block_encryptor_gen() -> ?LET({Bits, IV, AAD, PlainText}, {block_size(), gcm_iv(), binary(), binary()}, {Bits, binary(Bits div 8), IV, AAD, jose_jwa_pkcs7:pad(PlainText)}). prop_cbc_block_encrypt_and_cbc_block_decrypt() -> ?FORALL({Bits, Key, IV, PlainText}, cbc_block_encryptor_gen(), begin CipherText = jose_jwa_aes:block_encrypt({aes_cbc, Bits}, Key, IV, PlainText), PlainText =:= jose_jwa_aes:block_decrypt({aes_cbc, Bits}, Key, IV, CipherText) end). prop_cbc_block_encrypt_and_jwa_block_decrypt() -> ?FORALL({Bits, Key, IV, PlainText}, cbc_block_encryptor_gen(), begin Cipher = aes_cbc, CipherText = jose_jwa_aes:block_encrypt({Cipher, Bits}, Key, IV, PlainText), PlainText =:= jose_jwa:block_decrypt({Cipher, Bits}, Key, IV, CipherText) end). prop_jwa_block_encrypt_and_cbc_block_decrypt() -> ?FORALL({Bits, Key, IV, PlainText}, cbc_block_encryptor_gen(), begin Cipher = aes_cbc, CipherText = jose_jwa:block_encrypt({Cipher, Bits}, Key, IV, PlainText), PlainText =:= jose_jwa_aes:block_decrypt({Cipher, Bits}, Key, IV, CipherText) end). prop_jwa_block_encrypt_and_ecb_block_decrypt() -> ?FORALL({Bits, Key, PlainText}, ecb_block_encryptor_gen(), begin Cipher = aes_ecb, CipherText = << << (jose_jwa:block_encrypt({Cipher, Bits}, Key, Block))/binary >> || << Block:16/binary >> <= PlainText >>, PlainText =:= jose_jwa_aes:block_decrypt({aes_ecb, Bits}, Key, CipherText) end). prop_jwa_block_encrypt_and_gcm_block_decrypt() -> ?FORALL({Bits, Key, IV, AAD, PlainText}, gcm_block_encryptor_gen(), begin Cipher = aes_gcm, {CipherText, CipherTag} = jose_jwa:block_encrypt({Cipher, Bits}, Key, IV, {AAD, PlainText}), PlainText =:= jose_jwa_aes:block_decrypt({aes_gcm, Bits}, Key, IV, {AAD, CipherText, CipherTag}) end). prop_ecb_block_encrypt_and_ecb_block_decrypt() -> ?FORALL({Bits, Key, PlainText}, ecb_block_encryptor_gen(), begin CipherText = jose_jwa_aes:block_encrypt({aes_ecb, Bits}, Key, PlainText), PlainText =:= jose_jwa_aes:block_decrypt({aes_ecb, Bits}, Key, CipherText) end). prop_ecb_block_encrypt_and_jwa_block_decrypt() -> ?FORALL({Bits, Key, PlainText}, ecb_block_encryptor_gen(), begin CipherText = jose_jwa_aes:block_encrypt({aes_ecb, Bits}, Key, PlainText), Cipher = aes_ecb, PlainText =:= << << (jose_jwa:block_decrypt({Cipher, Bits}, Key, Block))/binary >> || << Block:16/binary >> <= CipherText >> end). prop_gcm_block_encrypt_and_gcm_block_decrypt() -> ?FORALL({Bits, Key, IV, AAD, PlainText}, gcm_block_encryptor_gen(), begin {CipherText, CipherTag} = jose_jwa_aes:block_encrypt({aes_gcm, Bits}, Key, IV, {AAD, PlainText}), PlainText =:= jose_jwa_aes:block_decrypt({aes_gcm, Bits}, Key, IV, {AAD, CipherText, CipherTag}) end). prop_gcm_block_encrypt_and_jwa_block_decrypt() -> ?FORALL({Bits, Key, IV, AAD, PlainText}, gcm_block_encryptor_gen(), begin {CipherText, CipherTag} = jose_jwa_aes:block_encrypt({aes_gcm, Bits}, Key, IV, {AAD, PlainText}), Cipher = aes_gcm, PlainText =:= jose_jwa:block_decrypt({Cipher, Bits}, Key, IV, {AAD, CipherText, CipherTag}) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jws_alg_ecdsa_props.erl0000664000232200023220000000670115267717064030604 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jws_alg_ecdsa_props). -include_lib("public_key/include/public_key.hrl"). -include_lib("proper/include/proper.hrl"). % % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). alg() -> oneof([ <<"ES256">>, <<"ES384">>, <<"ES512">> ]). alg(secp256r1) -> return(<<"ES256">>); alg(secp384r1) -> return(<<"ES384">>); alg(secp521r1) -> return(<<"ES512">>). opt_map() -> oneof([ #{}, #{ <<"b64">> => true }, #{ <<"b64">> => false } ]). ec_curve() -> oneof([ secp256r1, secp384r1, secp521r1 ]). ec_keypair(CurveId) -> ECPrivateKey = #'ECPrivateKey'{parameters=ECParameters, publicKey=Octets0} = public_key:generate_key({namedCurve, pubkey_cert_records:namedCurves(CurveId)}), Octets = case Octets0 of {_, Octets1} -> Octets1; _ -> Octets0 end, ECPoint = #'ECPoint'{point=Octets}, ECPublicKey = {ECPoint, ECParameters}, {ECPrivateKey, ECPublicKey}. jwk_jws_maps() -> ?LET({CurveId, ALG, {PrivateKey, PublicKey}, Opts}, ?LET(CurveId, ec_curve(), {CurveId, alg(CurveId), ec_keypair(CurveId), opt_map()}), begin JWKSigner = jose_jwk:from_key(PrivateKey), JWKVerifier = jose_jwk:from_key(PublicKey), JWSMap = maps:merge(Opts, #{ <<"alg">> => ALG }), {CurveId, {JWKSigner, JWKVerifier}, JWSMap} end). jwk_jws_gen() -> ?LET({CurveId, JWKs, JWSMap}, jwk_jws_maps(), {CurveId, JWKs, jose_jws:from_map(JWSMap)}). prop_from_map_and_to_map() -> ?FORALL(JWSMap, ?LET({ALG, Opts, Extras}, {alg(), opt_map(), binary_map()}, maps:merge(maps:merge(Extras, Opts), #{ <<"alg">> => ALG })), begin JWS = jose_jws:from_map(JWSMap), JWSMap =:= element(2, jose_jws:to_map(JWS)) end). prop_sign_and_verify() -> ?FORALL({{_CurveId, {JWKSigner, JWKVerifier}, JWS}, Message}, {jwk_jws_gen(), binary()}, begin Signed = jose_jws:sign(JWKSigner, Message, JWS), CompactSigned = jose_jws:compact(Signed), {true, Message, JWS} =:= jose_jws:verify(JWKVerifier, Signed) andalso {true, Message, JWS} =:= jose_jws:verify(JWKVerifier, CompactSigned) end). %% RFC 7518 3.4 fixes the ECDSA JWS Signature to the octet sequence R || S, %% each half zero-padded to the byte length of the curve order. Prepending %% (or stripping) leading zero octets on both halves keeps the encoded %% integers identical, so a verifier that splits on byte_size div 2 accepts an %% unbounded family of distinct signatures for the same key and payload. %% See GHSA-44hw-rpw9-mwg4. prop_verify_rejects_non_canonical_signature_length() -> ?FORALL({{_CurveId, {JWKSigner, JWKVerifier}, JWS}, Message, Pad}, {jwk_jws_gen(), binary(), integer(1, 8)}, begin {Modules, SignedMap = #{ <<"signature">> := SignatureB64 }} = jose_jws:sign(JWKSigner, Message, JWS), Signature = jose_jwa_base64url:decode(SignatureB64), HalfSize = byte_size(Signature) div 2, << R:HalfSize/binary, S:HalfSize/binary >> = Signature, Zeros = binary:copy(<< 0 >>, Pad), Repadded = << Zeros/binary, R/binary, Zeros/binary, S/binary >>, Malleable = {Modules, SignedMap#{ <<"signature">> => jose_jwa_base64url:encode(Repadded) }}, {true, Message, JWS} =:= jose_jws:verify(JWKVerifier, {Modules, SignedMap}) andalso false =:= element(1, jose_jws:verify(JWKVerifier, Malleable)) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwe_enc_xc20p_props.erl0000664000232200023220000000276615267717064030454 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwe_enc_xc20p_props). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). enc() -> return(<<"XC20P">>). jwk_jwe_maps() -> ?LET(ENC, enc(), begin JWEMap = #{ <<"alg">> => <<"dir">>, <<"enc">> => ENC }, Key = element(2, jose_jwk:to_oct(jose_jwe:generate_key(JWEMap))), JWKMap = #{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(Key) }, {Key, JWKMap, JWEMap} end). jwk_jwe_gen() -> ?LET({Key, JWKMap, JWEMap}, jwk_jwe_maps(), {Key, jose_jwk:from_map(JWKMap), jose_jwe:from_map(JWEMap)}). prop_from_map_and_to_map() -> ?FORALL(JWEMap, ?LET({{_Key, _JWKMap, JWEMap}, Extras}, {jwk_jwe_maps(), binary_map()}, maps:merge(Extras, JWEMap)), begin JWE = jose_jwe:from_map(JWEMap), JWEMap =:= element(2, jose_jwe:to_map(JWE)) end). prop_block_encrypt_and_block_decrypt() -> ?FORALL({CEK, IV, JWK, JWE, PlainText}, ?LET({CEK, JWK, JWE}, jwk_jwe_gen(), {CEK, jose_jwe:next_iv(JWE), JWK, JWE, binary()}), begin Encrypted = jose_jwe:block_encrypt(JWK, PlainText, CEK, IV, JWE), CompactEncrypted = jose_jwe:compact(Encrypted), PlainText =:= element(1, jose_jwe:block_decrypt(JWK, CompactEncrypted)) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwt_props.erl0000664000232200023220000000560715267717064026627 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwt_props). -include_lib("public_key/include/public_key.hrl"). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). modulus_size() -> integer(1024, 1280). % integer(256, 8192) | pos_integer(). exponent_size() -> return(65537). % pos_integer(). rsa_keypair(ModulusSize) -> ?LET(ExponentSize, exponent_size(), begin case public_key:generate_key({rsa, ModulusSize, ExponentSize}) of PrivateKey=#'RSAPrivateKey'{modulus=Modulus, publicExponent=PublicExponent} -> {PrivateKey, #'RSAPublicKey'{modulus=Modulus, publicExponent=PublicExponent}} end end). % ec_curve() -> % oneof([ % secp256r1, % secp384r1, % secp521r1 % ]). % ec_keypair(CurveId) -> % ECPrivateKey = #'ECPrivateKey'{parameters=ECParameters, publicKey=Octets0} = public_key:generate_key({namedCurve, pubkey_cert_records:namedCurves(CurveId)}), % Octets = case Octets0 of % {_, Octets1} -> % Octets1; % _ -> % Octets0 % end, % ECPoint = #'ECPoint'{point=Octets}, % ECPublicKey = {ECPoint, ECParameters}, % {ECPrivateKey, ECPublicKey}. % jwk_ec() -> % ?LET(CurveId, % ec_curve(), % begin % {PrivateKey, PublicKey} = ec_keypair(CurveId), % oneof([jose_jwk:from_key(PrivateKey), jose_jwk:from_key(PublicKey)]) % end). jwk_hmac() -> ?LET(Key, binary(32), jose_jwk:from_map(#{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(Key) })). jwk_rsa() -> ?LET({_ModulusSize, {PrivateKey, _PublicKey}}, ?LET(ModulusSize, modulus_size(), {ModulusSize, rsa_keypair(ModulusSize)}), return(jose_jwk:from_key(PrivateKey))). jwk() -> frequency([ {50, jwk_hmac()}, {1, jwk_rsa()} ]). % oneof([ % % jwk_ec(), % jwk_hmac(), % jwk_rsa() % ]). jwk_jwt() -> ?LET({JWK, JWTMap}, {jwk(), binary_map()}, {JWK, jose_jwt:from_map(JWTMap)}). prop_from_map_and_to_map() -> ?FORALL({_JWK, JWTMap}, {jwk(), binary_map()}, begin JWT = jose_jwt:from_map(JWTMap), JWTMap =:= element(2, jose_jwt:to_map(JWT)) end). prop_encrypt_and_decrypt() -> ?FORALL({JWK, JWT}, jwk_jwt(), begin Encrypted = jose_jwt:encrypt(JWK, JWT), CompactEncrypted = jose_jwe:compact(Encrypted), Decrypted = {JWE, _} = jose_jwt:decrypt(JWK, Encrypted), {JWE, JWT} =:= Decrypted andalso {JWE, JWT} =:= jose_jwt:decrypt(JWK, CompactEncrypted) end). prop_sign_and_verify() -> ?FORALL({JWK, JWT}, jwk_jwt(), begin Signed = jose_jwt:sign(JWK, JWT), CompactSigned = jose_jws:compact(Signed), Verified = {_, _, JWS} = jose_jwt:verify(JWK, Signed), {true, JWT, JWS} =:= Verified andalso {true, JWT, JWS} =:= jose_jwt:verify(JWK, CompactSigned) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwk_set_props.erl0000664000232200023220000000450015267717064027460 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwk_set_props). -include_lib("public_key/include/public_key.hrl"). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). modulus_size() -> integer(1024, 1280). % integer(256, 8192) | pos_integer(). exponent_size() -> return(65537). % pos_integer(). rsa_keypair(ModulusSize) -> ?LET(ExponentSize, exponent_size(), begin case public_key:generate_key({rsa, ModulusSize, ExponentSize}) of PrivateKey=#'RSAPrivateKey'{modulus=Modulus, publicExponent=PublicExponent} -> {PrivateKey, #'RSAPublicKey'{modulus=Modulus, publicExponent=PublicExponent}} end end). ec_curve() -> oneof([ secp256r1, secp384r1, secp521r1 ]). ec_keypair(CurveId) -> ECPrivateKey = #'ECPrivateKey'{parameters=ECParameters, publicKey=Octets0} = public_key:generate_key({namedCurve, pubkey_cert_records:namedCurves(CurveId)}), Octets = case Octets0 of {_, Octets1} -> Octets1; _ -> Octets0 end, ECPoint = #'ECPoint'{point=Octets}, ECPublicKey = {ECPoint, ECParameters}, {ECPrivateKey, ECPublicKey}. jwk_ec() -> ?LET(CurveId, ec_curve(), begin {PrivateKey, PublicKey} = ec_keypair(CurveId), oneof([jose_jwk:from_key(PrivateKey), jose_jwk:from_key(PublicKey)]) end). jwk_hmac() -> ?LET(Key, binary(), jose_jwk:from_map(#{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(Key) })). jwk_rsa() -> ?LET({_ModulusSize, {PrivateKey, PublicKey}}, ?LET(ModulusSize, modulus_size(), {ModulusSize, rsa_keypair(ModulusSize)}), oneof([jose_jwk:from_key(PrivateKey), jose_jwk:from_key(PublicKey)])). jwk() -> oneof([ jwk_ec(), jwk_hmac(), jwk_rsa() ]). jwk_set_map() -> ?LET(JWKs, list(jwk()), begin {JWKs, #{ <<"keys">> => [element(2, jose_jwk:to_map(JWK)) || JWK <- JWKs] }} end). prop_from_map_and_to_map() -> ?FORALL({_JWKs, JWKSetMap}, ?LET({{JWKs, JWKSetMap}, Extras}, {jwk_set_map(), binary_map()}, {JWKs, maps:merge(Extras, JWKSetMap)}), begin JWKSet = jose_jwk:from_map(JWKSetMap), JWKSetMap =:= element(2, jose_jwk:to_map(JWKSet)) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwe_enc_c20p_props.erl0000664000232200023220000000276415267717064030262 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwe_enc_c20p_props). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). enc() -> return(<<"C20P">>). jwk_jwe_maps() -> ?LET(ENC, enc(), begin JWEMap = #{ <<"alg">> => <<"dir">>, <<"enc">> => ENC }, Key = element(2, jose_jwk:to_oct(jose_jwe:generate_key(JWEMap))), JWKMap = #{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(Key) }, {Key, JWKMap, JWEMap} end). jwk_jwe_gen() -> ?LET({Key, JWKMap, JWEMap}, jwk_jwe_maps(), {Key, jose_jwk:from_map(JWKMap), jose_jwe:from_map(JWEMap)}). prop_from_map_and_to_map() -> ?FORALL(JWEMap, ?LET({{_Key, _JWKMap, JWEMap}, Extras}, {jwk_jwe_maps(), binary_map()}, maps:merge(Extras, JWEMap)), begin JWE = jose_jwe:from_map(JWEMap), JWEMap =:= element(2, jose_jwe:to_map(JWE)) end). prop_block_encrypt_and_block_decrypt() -> ?FORALL({CEK, IV, JWK, JWE, PlainText}, ?LET({CEK, JWK, JWE}, jwk_jwe_gen(), {CEK, jose_jwe:next_iv(JWE), JWK, JWE, binary()}), begin Encrypted = jose_jwe:block_encrypt(JWK, PlainText, CEK, IV, JWE), CompactEncrypted = jose_jwe:compact(Encrypted), PlainText =:= element(1, jose_jwe:block_decrypt(JWK, CompactEncrypted)) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwk_kty_okp_x25519_props.erl0000664000232200023220000001022115267717064031277 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwk_kty_okp_x25519_props). -include("jose_public_key.hrl"). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). x25519_secret() -> binary(32). x25519_keypair(Secret) -> {PK, S} = jose_curve25519:x25519_keypair(Secret), PublicKey = #'jose_X25519PublicKey'{publicKey=PK}, SecretKey = #'jose_X25519PrivateKey'{publicKey=PublicKey, privateKey=S}, {SecretKey, PublicKey}. jwk_map() -> ?LET({AliceSecret, BobSecret}, {x25519_secret(), x25519_secret()}, begin AliceKeys = {AlicePrivateKey, _} = x25519_keypair(AliceSecret), BobKeys = x25519_keypair(BobSecret), AlicePrivateJWK = jose_jwk:from_key(AlicePrivateKey), {_, AlicePrivateJWKMap} = jose_jwk:to_map(AlicePrivateJWK), Keys = {AliceKeys, BobKeys}, {Keys, AlicePrivateJWKMap} end). jwk_gen() -> ?LET({Keys, AlicePrivateJWKMap}, jwk_map(), {Keys, jose_jwk:from_map(AlicePrivateJWKMap)}). prop_from_der_and_to_der() -> ?FORALL({_Keys, AlicePrivateJWK, Password}, ?LET({{Keys, AlicePrivateJWK}, Bytes}, {jwk_gen(), binary()}, {Keys, AlicePrivateJWK, jose_jwa_base64url:encode(Bytes)}), begin AlicePrivateDER = element(2, jose_jwk:to_der(AlicePrivateJWK)), EncryptedAlicePrivateDER = element(2, jose_jwk:to_der(Password, AlicePrivateJWK)), AlicePublicJWK = jose_jwk:to_public(AlicePrivateJWK), AlicePublicDER = element(2, jose_jwk:to_der(AlicePublicJWK)), AlicePrivateJWK =:= jose_jwk:from_der(AlicePrivateDER) andalso AlicePrivateJWK =:= jose_jwk:from_der(Password, EncryptedAlicePrivateDER) andalso AlicePublicJWK =:= jose_jwk:from_der(AlicePublicDER) end). prop_from_map_and_to_map() -> ?FORALL({{{AlicePrivateKey, AlicePublicKey}, _}, AlicePrivateJWKMap}, ?LET({{Keys, JWKMap}, Extras}, {jwk_map(), binary_map()}, {Keys, maps:merge(Extras, JWKMap)}), begin AlicePrivateJWK = jose_jwk:from_map(AlicePrivateJWKMap), AlicePublicJWK = jose_jwk:to_public(AlicePrivateJWK), AlicePublicJWKMap = element(2, jose_jwk:to_map(AlicePublicJWK)), AlicePublicThumbprint = jose_jwk:thumbprint(AlicePublicJWK), AlicePrivateJWKMap =:= element(2, jose_jwk:to_map(AlicePrivateJWK)) andalso AlicePrivateKey =:= element(2, jose_jwk:to_key(AlicePrivateJWK)) andalso AlicePublicKey =:= element(2, jose_jwk:to_public_key(AlicePrivateJWK)) andalso AlicePublicJWKMap =:= element(2, jose_jwk:to_public_map(AlicePrivateJWK)) andalso AlicePublicThumbprint =:= jose_jwk:thumbprint(AlicePrivateJWK) end). prop_from_pem_and_to_pem() -> ?FORALL({_Keys, AlicePrivateJWK, Password}, ?LET({{Keys, AlicePrivateJWK}, Bytes}, {jwk_gen(), binary()}, {Keys, AlicePrivateJWK, jose_jwa_base64url:encode(Bytes)}), begin AlicePrivatePEM = element(2, jose_jwk:to_pem(AlicePrivateJWK)), EncryptedAlicePrivatePEM = element(2, jose_jwk:to_pem(Password, AlicePrivateJWK)), AlicePublicJWK = jose_jwk:to_public(AlicePrivateJWK), AlicePublicPEM = element(2, jose_jwk:to_pem(AlicePublicJWK)), EncryptedAlicePublicPEM = element(2, jose_jwk:to_pem(Password, AlicePublicJWK)), AlicePrivateJWK =:= jose_jwk:from_pem(AlicePrivatePEM) andalso AlicePrivateJWK =:= jose_jwk:from_pem(Password, EncryptedAlicePrivatePEM) andalso AlicePublicJWK =:= jose_jwk:from_pem(AlicePublicPEM) andalso AlicePublicJWK =:= jose_jwk:from_pem(Password, EncryptedAlicePublicPEM) end). prop_box_encrypt_and_box_decrypt() -> ?FORALL({{{_, {BobPrivateKey, BobPublicKey}}, AlicePrivateJWK}, PlainText}, {jwk_gen(), binary()}, begin BobPrivateJWK = jose_jwk:from_key(BobPrivateKey), BobPublicJWK = jose_jwk:from_key(BobPublicKey), Encrypted = jose_jwk:box_encrypt_ecdh_es(PlainText, BobPublicJWK, AlicePrivateJWK), CompactEncrypted = jose_jwe:compact(Encrypted), Decrypted = {_, JWE} = jose_jwk:box_decrypt_ecdh_es(Encrypted, BobPrivateJWK), {PlainText, JWE} =:= Decrypted andalso {PlainText, JWE} =:= jose_jwk:block_decrypt(CompactEncrypted, BobPrivateJWK) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jws_alg_rsa_pss_props.erl0000664000232200023220000000405415267717064031176 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jws_alg_rsa_pss_props). -include_lib("public_key/include/public_key.hrl"). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). alg() -> oneof([ <<"PS256">>, <<"PS384">>, <<"PS512">> ]). modulus_size() -> integer(1024, 1280). % integer(256, 8192) | pos_integer(). exponent_size() -> return(65537). % pos_integer(). rsa_keypair(ModulusSize) -> ?LET(ExponentSize, exponent_size(), begin case public_key:generate_key({rsa, ModulusSize, ExponentSize}) of PrivateKey=#'RSAPrivateKey'{modulus=Modulus, publicExponent=PublicExponent} -> {PrivateKey, #'RSAPublicKey'{modulus=Modulus, publicExponent=PublicExponent}} end end). jwk_jws_maps() -> ?LET({ModulusSize, ALG, {PrivateKey, PublicKey}}, ?LET(ModulusSize, modulus_size(), {ModulusSize, alg(), rsa_keypair(ModulusSize)}), begin JWKSigner = jose_jwk:from_key(PrivateKey), JWKVerifier = jose_jwk:from_key(PublicKey), JWSMap = #{ <<"alg">> => ALG }, {ModulusSize, {JWKSigner, JWKVerifier}, JWSMap} end). jwk_jws_gen() -> ?LET({ModulusSize, JWKs, JWSMap}, jwk_jws_maps(), {ModulusSize, JWKs, jose_jws:from_map(JWSMap)}). prop_from_map_and_to_map() -> ?FORALL(JWSMap, ?LET({ALG, Extras}, {alg(), binary_map()}, maps:merge(Extras, #{ <<"alg">> => ALG })), begin JWS = jose_jws:from_map(JWSMap), JWSMap =:= element(2, jose_jws:to_map(JWS)) end). prop_sign_and_verify() -> ?FORALL({{_ModulusSize, {JWKSigner, JWKVerifier}, JWS}, Message}, {jwk_jws_gen(), binary()}, begin Signed = jose_jws:sign(JWKSigner, Message, JWS), CompactSigned = jose_jws:compact(Signed), {true, Message, JWS} =:= jose_jws:verify(JWKVerifier, Signed) andalso {true, Message, JWS} =:= jose_jws:verify(JWKVerifier, CompactSigned) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwk_kty_okp_x448_props.erl0000664000232200023220000001017315267717064031137 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwk_kty_okp_x448_props). -include("jose_public_key.hrl"). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). x448_secret() -> binary(56). x448_keypair(Secret) -> {PK, S} = jose_curve448:x448_keypair(Secret), PublicKey = #'jose_X448PublicKey'{publicKey=PK}, SecretKey = #'jose_X448PrivateKey'{publicKey=PublicKey, privateKey=S}, {SecretKey, PublicKey}. jwk_map() -> ?LET({AliceSecret, BobSecret}, {x448_secret(), x448_secret()}, begin AliceKeys = {AlicePrivateKey, _} = x448_keypair(AliceSecret), BobKeys = x448_keypair(BobSecret), AlicePrivateJWK = jose_jwk:from_key(AlicePrivateKey), {_, AlicePrivateJWKMap} = jose_jwk:to_map(AlicePrivateJWK), Keys = {AliceKeys, BobKeys}, {Keys, AlicePrivateJWKMap} end). jwk_gen() -> ?LET({Keys, AlicePrivateJWKMap}, jwk_map(), {Keys, jose_jwk:from_map(AlicePrivateJWKMap)}). prop_from_der_and_to_der() -> ?FORALL({_Keys, AlicePrivateJWK, Password}, ?LET({{Keys, AlicePrivateJWK}, Bytes}, {jwk_gen(), binary()}, {Keys, AlicePrivateJWK, jose_jwa_base64url:encode(Bytes)}), begin AlicePrivateDER = element(2, jose_jwk:to_der(AlicePrivateJWK)), EncryptedAlicePrivateDER = element(2, jose_jwk:to_der(Password, AlicePrivateJWK)), AlicePublicJWK = jose_jwk:to_public(AlicePrivateJWK), AlicePublicDER = element(2, jose_jwk:to_der(AlicePublicJWK)), AlicePrivateJWK =:= jose_jwk:from_der(AlicePrivateDER) andalso AlicePrivateJWK =:= jose_jwk:from_der(Password, EncryptedAlicePrivateDER) andalso AlicePublicJWK =:= jose_jwk:from_der(AlicePublicDER) end). prop_from_map_and_to_map() -> ?FORALL({{{AlicePrivateKey, AlicePublicKey}, _}, AlicePrivateJWKMap}, ?LET({{Keys, JWKMap}, Extras}, {jwk_map(), binary_map()}, {Keys, maps:merge(Extras, JWKMap)}), begin AlicePrivateJWK = jose_jwk:from_map(AlicePrivateJWKMap), AlicePublicJWK = jose_jwk:to_public(AlicePrivateJWK), AlicePublicJWKMap = element(2, jose_jwk:to_map(AlicePublicJWK)), AlicePublicThumbprint = jose_jwk:thumbprint(AlicePublicJWK), AlicePrivateJWKMap =:= element(2, jose_jwk:to_map(AlicePrivateJWK)) andalso AlicePrivateKey =:= element(2, jose_jwk:to_key(AlicePrivateJWK)) andalso AlicePublicKey =:= element(2, jose_jwk:to_public_key(AlicePrivateJWK)) andalso AlicePublicJWKMap =:= element(2, jose_jwk:to_public_map(AlicePrivateJWK)) andalso AlicePublicThumbprint =:= jose_jwk:thumbprint(AlicePrivateJWK) end). prop_from_pem_and_to_pem() -> ?FORALL({_Keys, AlicePrivateJWK, Password}, ?LET({{Keys, AlicePrivateJWK}, Bytes}, {jwk_gen(), binary()}, {Keys, AlicePrivateJWK, jose_jwa_base64url:encode(Bytes)}), begin AlicePrivatePEM = element(2, jose_jwk:to_pem(AlicePrivateJWK)), EncryptedAlicePrivatePEM = element(2, jose_jwk:to_pem(Password, AlicePrivateJWK)), AlicePublicJWK = jose_jwk:to_public(AlicePrivateJWK), AlicePublicPEM = element(2, jose_jwk:to_pem(AlicePublicJWK)), EncryptedAlicePublicPEM = element(2, jose_jwk:to_pem(Password, AlicePublicJWK)), AlicePrivateJWK =:= jose_jwk:from_pem(AlicePrivatePEM) andalso AlicePrivateJWK =:= jose_jwk:from_pem(Password, EncryptedAlicePrivatePEM) andalso AlicePublicJWK =:= jose_jwk:from_pem(AlicePublicPEM) andalso AlicePublicJWK =:= jose_jwk:from_pem(Password, EncryptedAlicePublicPEM) end). prop_box_encrypt_and_box_decrypt() -> ?FORALL({{{_, {BobPrivateKey, BobPublicKey}}, AlicePrivateJWK}, PlainText}, {jwk_gen(), binary()}, begin BobPrivateJWK = jose_jwk:from_key(BobPrivateKey), BobPublicJWK = jose_jwk:from_key(BobPublicKey), Encrypted = jose_jwk:box_encrypt_ecdh_es(PlainText, BobPublicJWK, AlicePrivateJWK), CompactEncrypted = jose_jwe:compact(Encrypted), Decrypted = {_, JWE} = jose_jwk:box_decrypt_ecdh_es(Encrypted, BobPrivateJWK), {PlainText, JWE} =:= Decrypted andalso {PlainText, JWE} =:= jose_jwk:block_decrypt(CompactEncrypted, BobPrivateJWK) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwa_curve25519_props.erl0000664000232200023220000000345515267717064030415 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwa_curve25519_props). -include_lib("public_key/include/public_key.hrl"). -include_lib("proper/include/proper.hrl"). % -compile(export_all). eddsa_secret() -> binary(32). eddsa_keypair(Secret) -> {PK, SK} = jose_curve25519:eddsa_keypair(Secret), {SK, PK}. x25519_secret() -> binary(32). x25519_keypair(Secret) -> {PK, SK} = jose_curve25519:x25519_keypair(Secret), {SK, PK}. eddsa_keypair_gen() -> ?LET(Secret, eddsa_secret(), eddsa_keypair(Secret)). prop_eddsa_secret_to_public() -> ?FORALL({<< Secret:32/binary, _/binary >>, PK}, eddsa_keypair_gen(), begin PK =:= jose_jwa_curve25519:eddsa_secret_to_public(Secret) end). prop_ed25519_sign_and_verify() -> ?FORALL({{SK, PK}, M}, {eddsa_keypair_gen(), binary()}, begin S = jose_jwa_curve25519:ed25519_sign(M, SK), jose_jwa_curve25519:ed25519_verify(S, M, PK) end). prop_ed25519ph_sign_and_verify() -> ?FORALL({{SK, PK}, M}, {eddsa_keypair_gen(), binary()}, begin S = jose_jwa_curve25519:ed25519ph_sign(M, SK), jose_jwa_curve25519:ed25519ph_verify(S, M, PK) end). x25519_keypair_gen() -> ?LET(Secret, x25519_secret(), x25519_keypair(Secret)). x25519_keypairs_gen() -> ?LET({AliceSecret, BobSecret}, {x25519_secret(), x25519_secret()}, {x25519_keypair(AliceSecret), x25519_keypair(BobSecret)}). prop_x25519_secret_to_public() -> ?FORALL({SK, PK}, x25519_keypair_gen(), begin PK =:= jose_jwa_curve25519:x25519_secret_to_public(SK) end). prop_x25519_shared_secret() -> ?FORALL({{AliceSK, AlicePK}, {BobSK, BobPK}}, x25519_keypairs_gen(), begin K = jose_jwa_curve25519:x25519_shared_secret(AliceSK, BobPK), K =:= jose_jwa_curve25519:x25519_shared_secret(BobSK, AlicePK) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwe_alg_ecdh_es_props.erl0000664000232200023220000000674315267717064031107 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwe_alg_ecdh_es_props). -include_lib("public_key/include/public_key.hrl"). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). alg() -> oneof([ <<"ECDH-ES">>, <<"ECDH-ES+A128GCMKW">>, <<"ECDH-ES+A192GCMKW">>, <<"ECDH-ES+A256GCMKW">>, <<"ECDH-ES+A128KW">>, <<"ECDH-ES+A192KW">>, <<"ECDH-ES+A256KW">>, <<"ECDH-ES+C20PKW">>, <<"ECDH-ES+XC20PKW">> ]). alg_map() -> ?LET({ALG, APU, APV}, {alg(), binary(), binary()}, #{ <<"alg">> => ALG, <<"apu">> => jose_jwa_base64url:encode(APU), <<"apv">> => jose_jwa_base64url:encode(APV) }). ec_curve() -> oneof([ secp256r1, secp384r1, secp521r1, x25519, x448 ]). % ec_curve() -> % ?SUCHTHAT(CurveId, % oneof(crypto:ec_curves()), % begin % try pubkey_cert_records:namedCurves(CurveId) of % Curve when is_tuple(Curve) -> % true; % _ -> % false % catch % _:_ -> % false % end % end). % ec_keypair() -> % ?LET(CurveId, % ec_curve(), % ec_keypair(CurveId)). ec_keypair(x25519) -> SecretJWK = jose_jwk:generate_key({okp, 'X25519'}), {_, SecretKey} = jose_jwk:to_key(SecretJWK), {_, PublicKey} = jose_jwk:to_public_key(SecretJWK), {SecretKey, PublicKey}; ec_keypair(x448) -> SecretJWK = jose_jwk:generate_key({okp, 'X448'}), {_, SecretKey} = jose_jwk:to_key(SecretJWK), {_, PublicKey} = jose_jwk:to_public_key(SecretJWK), {SecretKey, PublicKey}; ec_keypair(CurveId) -> ECPrivateKey = #'ECPrivateKey'{parameters=ECParameters, publicKey=Octets0} = public_key:generate_key({namedCurve, pubkey_cert_records:namedCurves(CurveId)}), Octets = case Octets0 of {_, Octets1} -> Octets1; _ -> Octets0 end, ECPoint = #'ECPoint'{point=Octets}, ECPublicKey = {ECPoint, ECParameters}, {ECPrivateKey, ECPublicKey}. enc() -> oneof([ <<"A128GCM">>, <<"A192GCM">>, <<"A256GCM">>, <<"C20P">>, <<"XC20P">> ]). jwk_jwe_maps() -> ?LET({ALGMap, ENC, {BobPrivateKey, BobPublicKey}, {AlicePrivateKey, AlicePublicKey}}, ?LET(CurveId, ec_curve(), {alg_map(), enc(), ec_keypair(CurveId), ec_keypair(CurveId)}), begin BobPrivateJWK = jose_jwk:from_key(BobPrivateKey), BobPublicJWK = jose_jwk:from_key(BobPublicKey), AlicePrivateJWK = jose_jwk:from_key(AlicePrivateKey), AlicePublicJWK = jose_jwk:from_key(AlicePublicKey), {_, AlicePublicJWKMap} = jose_jwk:to_public_map(AlicePrivateJWK), BobBox = {AlicePublicJWK, BobPrivateJWK}, AliceBox = {BobPublicJWK, AlicePrivateJWK}, JWKs = {BobBox, AliceBox}, JWEMap = maps:merge(#{ <<"enc">> => ENC, <<"epk">> => AlicePublicJWKMap }, ALGMap), {JWKs, JWEMap} end). jwk_jwe_gen() -> ?LET({JWKs, JWEMap}, jwk_jwe_maps(), {JWKs, jose_jwe:from_map(JWEMap)}). prop_from_map_and_to_map() -> ?FORALL(JWEMap, ?LET({{_JWKs, JWEMap}, Extras}, {jwk_jwe_maps(), binary_map()}, maps:merge(Extras, JWEMap)), begin JWE = jose_jwe:from_map(JWEMap), JWEMap =:= element(2, jose_jwe:to_map(JWE)) end). prop_key_encrypt_and_key_decrypt() -> ?FORALL({{BobBox, AliceBox}, JWE}, jwk_jwe_gen(), begin {DecKey, DecJWE} = jose_jwe:next_cek(AliceBox, JWE), {EncKey, EncJWE} = jose_jwe:key_encrypt(AliceBox, DecKey, DecJWE), DecKey =:= jose_jwe:key_decrypt(BobBox, EncKey, EncJWE) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwe_alg_aes_kw_props.erl0000664000232200023220000000431615267717064030760 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwe_alg_aes_kw_props). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). key_size() -> oneof([128, 192, 256]). key_gen() -> ?LET(KeySize, key_size(), {KeySize, binary(KeySize div 8)}). alg_map(128) -> oneof([ #{ <<"alg">> => <<"A128KW">> }, #{ <<"alg">> => <<"A128GCMKW">> } ]); alg_map(192) -> oneof([ #{ <<"alg">> => <<"A192KW">> }, #{ <<"alg">> => <<"A192GCMKW">> } ]); alg_map(256) -> oneof([ #{ <<"alg">> => <<"A256KW">> }, #{ <<"alg">> => <<"A256GCMKW">> } ]). aes_gcm_map(#{ <<"alg">> := << "A", _, _, _, "GCMKW" >> }) -> oneof([ #{}, #{ <<"iv">> => jose_jwa_base64url:encode(crypto:strong_rand_bytes(12)), <<"tag">> => jose_jwa_base64url:encode(crypto:strong_rand_bytes(8)) } ]); aes_gcm_map(_) -> #{}. jwk_jwe_maps() -> ?LET({KeySize, Key, ALGMap}, ?LET({KeySize, Key}, key_gen(), {KeySize, Key, alg_map(KeySize)}), begin ENC = list_to_binary("A" ++ integer_to_list(KeySize) ++ "GCM"), JWKMap = #{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(Key) }, JWEMap = maps:merge(#{ <<"enc">> => ENC }, ALGMap), {Key, JWKMap, JWEMap} end). jwk_jwe_gen() -> ?LET({Key, JWKMap, JWEMap}, jwk_jwe_maps(), {Key, jose_jwk:from_map(JWKMap), jose_jwe:from_map(JWEMap)}). prop_from_map_and_to_map() -> ?FORALL(JWEMap, ?LET({JWEMap, GCMMap, Extras}, ?LET({_Key, _JWKMap, JWEMap}, jwk_jwe_maps(), {JWEMap, aes_gcm_map(JWEMap), binary_map()}), maps:merge(maps:merge(Extras, GCMMap), JWEMap)), begin JWE = jose_jwe:from_map(JWEMap), JWEMap =:= element(2, jose_jwe:to_map(JWE)) end). prop_key_encrypt_and_key_decrypt() -> ?FORALL({_Key, JWK, JWE}, ?LET({Key, JWK, JWE}, jwk_jwe_gen(), {Key, oneof([Key, JWK]), JWE}), begin {DecKey, DecJWE} = jose_jwe:next_cek(JWK, JWE), {EncKey, EncJWE} = jose_jwe:key_encrypt(JWK, DecKey, DecJWE), DecKey =:= jose_jwe:key_decrypt(JWK, EncKey, EncJWE) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwa_props.erl0000664000232200023220000000057415267717064026602 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwa_props). -include_lib("proper/include/proper.hrl"). % -compile(export_all). prop_constant_time_compare() -> ?FORALL(Binary, ?SUCHTHAT(Binary, binary(), byte_size(Binary) >= 1), begin jose_jwa:constant_time_compare(Binary, Binary) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwa_pkcs1_props.erl0000664000232200023220000001333515267717064027702 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwa_pkcs1_props). -include_lib("public_key/include/public_key.hrl"). -include_lib("proper/include/proper.hrl"). % -compile(export_all). digest_type() -> oneof([md5, sha, sha224, sha256, sha384, sha512, {hmac, md5, <<>>}, {hmac, sha, <<>>}, {hmac, sha224, <<>>}, {hmac, sha256, <<>>}, {hmac, sha384, <<>>}, {hmac, sha512, <<>>}]). pkcs1_digest() -> oneof([md5, sha, sha256, sha384, sha512]). salt_size() -> non_neg_integer(). modulus_size() -> integer(1024, 1280). % integer(256, 8192) | pos_integer(). exponent_size() -> return(65537). % pos_integer(). rsa_keypair(ModulusSize) -> ?LET(ExponentSize, exponent_size(), begin case public_key:generate_key({rsa, ModulusSize, ExponentSize}) of PrivateKey=#'RSAPrivateKey'{modulus=Modulus, publicExponent=PublicExponent} -> {PrivateKey, #'RSAPublicKey'{modulus=Modulus, publicExponent=PublicExponent}} end end). %%==================================================================== %% RSAES-OAEP %%==================================================================== rsaes_oaep_encryptor_gen() -> ?LET({DigestType, ModulusSize, PlainText}, ?SUCHTHAT({DigestType, ModulusSize, PlainText}, {digest_type(), modulus_size(), binary()}, ModulusSize >= ((byte_size(do_hash(DigestType, <<>>)) * 2 + 2 + byte_size(PlainText)) * 8)), {rsa_keypair(ModulusSize), ModulusSize, DigestType, PlainText}). rsaes_oaep_encryptor_with_label_gen() -> ?LET({DigestType, ModulusSize, PlainText}, ?SUCHTHAT({DigestType, ModulusSize, PlainText}, {digest_type(), modulus_size(), binary()}, ModulusSize >= ((byte_size(do_hash(DigestType, <<>>)) * 2 + 2 + byte_size(PlainText)) * 8)), {rsa_keypair(ModulusSize), ModulusSize, DigestType, binary(), PlainText}). prop_rsaes_oaep_encrypt_and_decrypt() -> ?FORALL({{PrivateKey, PublicKey}, _ModulusSize, DigestType, PlainText}, rsaes_oaep_encryptor_gen(), begin {ok, CipherText} = jose_jwa_pkcs1:rsaes_oaep_encrypt(DigestType, PlainText, PublicKey), PlainText =:= jose_jwa_pkcs1:rsaes_oaep_decrypt(DigestType, CipherText, PrivateKey) end). prop_rsaes_oaep_encrypt_and_decrypt_with_label() -> ?FORALL({{PrivateKey, PublicKey}, _ModulusSize, DigestType, Label, PlainText}, rsaes_oaep_encryptor_with_label_gen(), begin {ok, CipherText} = jose_jwa_pkcs1:rsaes_oaep_encrypt(DigestType, PlainText, Label, PublicKey), PlainText =:= jose_jwa_pkcs1:rsaes_oaep_decrypt(DigestType, CipherText, Label, PrivateKey) end). %%==================================================================== %% RSAES-PKCS1-v1_5 %%==================================================================== rsaes_pkcs1_encryptor_gen() -> ?LET({ModulusSize, PlainText}, ?SUCHTHAT({ModulusSize, PlainText}, {modulus_size(), binary()}, ModulusSize >= ((byte_size(PlainText) + 11) * 8)), {rsa_keypair(ModulusSize), ModulusSize, PlainText}). prop_rsaes_pkcs1_encrypt_and_decrypt() -> ?FORALL({{PrivateKey, PublicKey}, _ModulusSize, PlainText}, rsaes_pkcs1_encryptor_gen(), begin {ok, CipherText} = jose_jwa_pkcs1:rsaes_pkcs1_encrypt(PlainText, PublicKey), PlainText =:= jose_jwa_pkcs1:rsaes_pkcs1_decrypt(CipherText, PrivateKey) end). %%==================================================================== %% RSASSA-PKCS1-v1_5 %%==================================================================== rsassa_pkcs1_signer_gen() -> ?LET({DigestType, ModulusSize}, ?SUCHTHAT({DigestType, ModulusSize}, {pkcs1_digest(), modulus_size()}, ModulusSize >= (bit_size(do_hash(DigestType, <<>>)) * 3 + 16)), {rsa_keypair(ModulusSize), ModulusSize, DigestType, binary()}). prop_rsassa_pkcs1_sign_and_verify() -> ?FORALL({{PrivateKey, PublicKey}, _, DigestType, Message}, rsassa_pkcs1_signer_gen(), begin {ok, Signature} = jose_jwa_pkcs1:rsassa_pkcs1_sign(DigestType, Message, PrivateKey), jose_jwa_pkcs1:rsassa_pkcs1_verify(DigestType, Message, Signature, PublicKey) end). %%==================================================================== %% RSASSA-PSS %%==================================================================== rsassa_pss_signer_gen() -> ?LET({DigestType, ModulusSize}, ?SUCHTHAT({DigestType, ModulusSize}, {digest_type(), modulus_size()}, ModulusSize >= (bit_size(do_hash(DigestType, <<>>)) * 2 + 16)), {rsa_keypair(ModulusSize), ModulusSize, DigestType, binary()}). rsassa_pss_signer_with_salt_gen() -> ?LET({DigestType, ModulusSize, SaltSize}, ?SUCHTHAT({DigestType, ModulusSize, SaltSize}, {digest_type(), modulus_size(), salt_size()}, ModulusSize >= (bit_size(do_hash(DigestType, <<>>)) + (SaltSize * 8) + 16)), {rsa_keypair(ModulusSize), ModulusSize, DigestType, binary(SaltSize), binary()}). prop_rsassa_pss_sign_and_verify() -> ?FORALL({{PrivateKey, PublicKey}, _, DigestType, Message}, rsassa_pss_signer_gen(), begin {ok, Signature} = jose_jwa_pkcs1:rsassa_pss_sign(DigestType, Message, PrivateKey), jose_jwa_pkcs1:rsassa_pss_verify(DigestType, Message, Signature, PublicKey) end). prop_rsassa_pss_sign_and_verify_with_salt() -> ?FORALL({{PrivateKey, PublicKey}, _ModulusSize, DigestType, Salt, Message}, rsassa_pss_signer_with_salt_gen(), begin {ok, Signature} = jose_jwa_pkcs1:rsassa_pss_sign(DigestType, Message, Salt, PrivateKey), jose_jwa_pkcs1:rsassa_pss_verify(DigestType, Message, Signature, byte_size(Salt), PublicKey) end). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- do_hash(DigestType, PlainText) when is_atom(DigestType) -> crypto:hash(DigestType, PlainText); do_hash({hmac, DigestType, Key}, PlainText) -> jose_crypto_compat:mac(hmac, DigestType, Key, PlainText). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwe_alg_rsa_props.erl0000664000232200023220000000762415267717064030301 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwe_alg_rsa_props). -include_lib("public_key/include/public_key.hrl"). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). modulus_size() -> integer(1048, 1280). % integer(256, 8192) | pos_integer(). exponent_size() -> return(65537). % pos_integer(). rsa_keypair(ModulusSize) -> ?LET(ExponentSize, exponent_size(), begin case public_key:generate_key({rsa, ModulusSize, ExponentSize}) of PrivateKey=#'RSAPrivateKey'{modulus=Modulus, publicExponent=PublicExponent} -> {PrivateKey, #'RSAPublicKey'{modulus=Modulus, publicExponent=PublicExponent}} end end). alg() -> oneof([ <<"RSA1_5">>, <<"RSA-OAEP">>, <<"RSA-OAEP-256">> ]). enc() -> oneof([ <<"A128CBC-HS256">>, <<"A192CBC-HS384">>, <<"A256CBC-HS512">>, <<"A128GCM">>, <<"A192GCM">>, <<"A256GCM">>, <<"C20P">>, <<"XC20P">> ]). jwk_jwe_maps(ModulusSize) -> ?LET({ALG, ENC, {AlicePrivateKey, AlicePublicKey}}, {alg(), enc(), rsa_keypair(ModulusSize)}, begin AlicePrivateJWK = jose_jwk:from_key(AlicePrivateKey), AlicePublicJWK = jose_jwk:from_key(AlicePublicKey), JWKs = {AlicePrivateJWK, AlicePublicJWK}, JWEMap = #{ <<"alg">> => ALG, <<"enc">> => ENC }, {JWKs, JWEMap} end). jwk_jwe_gen() -> ?LET({JWKs, JWEMap}, ?LET(ModulusSize, modulus_size(), jwk_jwe_maps(ModulusSize)), {JWKs, jose_jwe:from_map(JWEMap)}). prop_from_map_and_to_map() -> ?FORALL(JWEMap, ?LET({{_JWKs, JWEMap}, Extras}, ?LET(ModulusSize, integer(1024, 1280), {jwk_jwe_maps(ModulusSize), binary_map()}), maps:merge(Extras, JWEMap)), begin JWE = jose_jwe:from_map(JWEMap), JWEMap =:= element(2, jose_jwe:to_map(JWE)) end). prop_key_encrypt_and_key_decrypt() -> ?FORALL({{AlicePrivateJWK, AlicePublicJWK}, JWE}, jwk_jwe_gen(), begin {DecKey, DecJWE} = jose_jwe:next_cek(AlicePublicJWK, JWE), {EncKey, EncJWE} = jose_jwe:key_encrypt(AlicePublicJWK, DecKey, DecJWE), DecKey =:= jose_jwe:key_decrypt(AlicePrivateJWK, EncKey, EncJWE) end). %% GHSA-vwvr-f994-6j9j: RSA key unwrapping must not tell the caller whether the %% decrypted RSA block was well formed. RFC 7516, Section 11.5 (and RFC 3218) %% require that format, padding, and length errors of the JWE Encrypted Key be %% indistinguishable, and that a random CEK be substituted on failure. Anything %% else is the one-bit oracle a Bleichenbacher adaptive chosen-ciphertext attack %% against RSAES-PKCS1-v1_5 ("RSA1_5") consumes. %% %% Every ciphertext below is buildable by an attacker who knows only the public %% key, which is precisely the reported threat model. prop_ghsa_vwvr_f994_6j9j() -> ?FORALL({{AlicePrivateJWK, AlicePublicJWK}, JWE}, jwk_jwe_gen(), begin {DecKey, DecJWE} = jose_jwe:next_cek(AlicePublicJWK, JWE), {EncKey, EncJWE} = jose_jwe:key_encrypt(AlicePublicJWK, DecKey, DecJWE), K = byte_size(EncKey), MalformedKeys = [ binary:copy(<< 16#00 >>, K), % c = 0, decrypts to an all-zero block binary:copy(<< 16#FF >>, K), % c >= n, rejected by the modexp itself crypto:strong_rand_bytes(K), % non-conforming padding binary:part(EncKey, 0, K - 1), % length error (short) << EncKey/binary, 16#00 >> % length error (long) ], DecKey =:= jose_jwe:key_decrypt(AlicePrivateJWK, EncKey, EncJWE) andalso lists:all(fun(MalformedKey) -> try jose_jwe:key_decrypt(AlicePrivateJWK, MalformedKey, EncJWE) of SubstituteKey when is_binary(SubstituteKey) -> byte_size(SubstituteKey) =:= byte_size(DecKey) andalso SubstituteKey =/= DecKey; _Distinguishable -> false catch _:_ -> false end end, MalformedKeys) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwe_alg_pbes2_props.erl0000664000232200023220000000764215267717064030527 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwe_alg_pbes2_props). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). alg() -> oneof([ <<"PBES2-HS256+A128GCMKW">>, <<"PBES2-HS384+A192GCMKW">>, <<"PBES2-HS512+A256GCMKW">>, <<"PBES2-HS256+A128KW">>, <<"PBES2-HS384+A192KW">>, <<"PBES2-HS512+A256KW">>, <<"PBES2-HS512+C20PKW">>, <<"PBES2-HS512+XC20PKW">> ]). alg_map() -> ?LET({ALG, P2C, P2S}, {alg(), integer(1, 256), binary()}, #{ <<"alg">> => ALG, <<"p2c">> => P2C, <<"p2s">> => jose_jwa_base64url:encode(P2S) }). enc() -> oneof([ <<"A128GCM">>, <<"A192GCM">>, <<"A256GCM">> ]). jwk_jwe_maps() -> ?LET({ALGMap, ENC, Password}, {alg_map(), enc(), binary()}, begin JWKMap = #{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(Password) }, JWEMap = maps:merge(#{ <<"enc">> => ENC }, ALGMap), {Password, JWKMap, JWEMap} end). jwk_jwe_gen() -> ?LET({Key, JWKMap, JWEMap}, jwk_jwe_maps(), {Key, jose_jwk:from_map(JWKMap), jose_jwe:from_map(JWEMap)}). prop_cve_2023_50966() -> ?FORALL({_Key, JWK, BaseJWE, PBES2CountMaximum}, ?LET({{Key, JWK, BaseJWE}, PBES2CountMaximum}, {jwk_jwe_gen(), range(2, 100)}, {Key, oneof([Key, JWK]), BaseJWE, PBES2CountMaximum}), begin OriginalPBES2CountMaximum = jose:pbes2_count_maximum(), try jose:pbes2_count_maximum(PBES2CountMaximum) of ok -> LesserJWE = jose_jwe:merge(BaseJWE, #{<<"p2c">> => PBES2CountMaximum - 1}), MaximumJWE = jose_jwe:merge(BaseJWE, #{<<"p2c">> => PBES2CountMaximum}), GreaterJWE = jose_jwe:merge(BaseJWE, #{<<"p2c">> => PBES2CountMaximum + 1}), {LesserDecKey1, LesserDecJWE} = jose_jwe:next_cek(JWK, LesserJWE), {LesserEncKey, LesserEncJWE} = jose_jwe:key_encrypt(JWK, LesserDecKey1, LesserDecJWE), LesserDecKey2 = jose_jwe:key_decrypt(JWK, LesserEncKey, LesserEncJWE), {MaximumDecKey1, MaximumDecJWE} = jose_jwe:next_cek(JWK, MaximumJWE), {MaximumEncKey, MaximumEncJWE} = jose_jwe:key_encrypt(JWK, MaximumDecKey1, MaximumDecJWE), MaximumDecKey2 = jose_jwe:key_decrypt(JWK, MaximumEncKey, MaximumEncJWE), {GreaterDecKey, GreaterDecJWE} = jose_jwe:next_cek(JWK, GreaterJWE), GreaterEncResult = try jose_jwe:key_encrypt(JWK, GreaterDecKey, GreaterDecJWE) catch GreaterEncClass:GreaterEncReason -> {GreaterEncClass, GreaterEncReason} end, {GreaterEncKey, GreaterEncJWE1} = jose_jwe:key_encrypt(JWK, GreaterDecKey, MaximumDecJWE), GreaterEncJWE2 = jose_jwe:merge(GreaterEncJWE1, #{<<"p2c">> => 1000000000}), GreaterDecResult = try jose_jwe:key_decrypt(JWK, GreaterEncKey, GreaterEncJWE2) catch GreaterDecClass:GreaterDecReason -> {GreaterDecClass, GreaterDecReason} end, conjunction([ {lesser, LesserDecKey1 =:= LesserDecKey2}, {maximum, MaximumDecKey1 =:= MaximumDecKey2}, {greater_key_encrypt, {error, badarg} =:= GreaterEncResult}, {greater_key_decrypt, {error, badarg} =:= GreaterDecResult} ]) after ok = jose:pbes2_count_maximum(OriginalPBES2CountMaximum) end end). prop_from_map_and_to_map() -> ?FORALL(JWEMap, ?LET({{_Key, _JWKMap, JWEMap}, Extras}, {jwk_jwe_maps(), binary_map()}, maps:merge(Extras, JWEMap)), begin JWE = jose_jwe:from_map(JWEMap), JWEMap =:= element(2, jose_jwe:to_map(JWE)) end). prop_key_encrypt_and_key_decrypt() -> ?FORALL({_Key, JWK, JWE}, ?LET({Key, JWK, JWE}, jwk_jwe_gen(), {Key, oneof([Key, JWK]), JWE}), begin {DecKey, DecJWE} = jose_jwe:next_cek(JWK, JWE), {EncKey, EncJWE} = jose_jwe:key_encrypt(JWK, DecKey, DecJWE), DecKey =:= jose_jwe:key_decrypt(JWK, EncKey, EncJWE) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwa_pkcs5_props.erl0000664000232200023220000000372115267717064027704 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwa_pkcs5_props). -include_lib("proper/include/proper.hrl"). % -compile(export_all). hash_fun() -> oneof([md5, sha, sha224, sha256, sha384, sha512, {hmac, md5, <<>>}, {hmac, sha, <<>>}, {hmac, sha224, <<>>}, {hmac, sha256, <<>>}, {hmac, sha384, <<>>}, {hmac, sha512, <<>>}]). mac_fun() -> oneof([md5, sha, sha224, sha256, sha384, sha512, {hmac, md5}, {hmac, sha}, {hmac, sha224}, {hmac, sha256}, {hmac, sha384}, {hmac, sha512}]). prop_pbkdf1() -> ?FORALL({Hash, Password, Salt}, {hash_fun(), binary(), binary()}, begin {ok, DerivedKey} = jose_jwa_pkcs5:pbkdf1(Hash, Password, Salt), DerivedKey =:= element(2, jose_jwa_pkcs5:pbkdf1(Hash, Password, Salt)) end). prop_pbkdf1_iterations() -> ?FORALL({Hash, Password, Salt, Iterations}, {hash_fun(), binary(), binary(), integer(1, 10)}, begin {ok, DerivedKey} = jose_jwa_pkcs5:pbkdf1(Hash, Password, Salt, Iterations), DerivedKey =:= element(2, jose_jwa_pkcs5:pbkdf1(Hash, Password, Salt, Iterations)) end). prop_pbkdf2() -> ?FORALL({Mac, Password, Salt}, {mac_fun(), binary(), binary()}, begin {ok, DerivedKey} = jose_jwa_pkcs5:pbkdf2(Mac, Password, Salt), DerivedKey =:= element(2, jose_jwa_pkcs5:pbkdf2(Mac, Password, Salt)) end). prop_pbkdf2_iterations() -> ?FORALL({Mac, Password, Salt, Iterations}, {mac_fun(), binary(), binary(), integer(1, 10)}, begin {ok, DerivedKey} = jose_jwa_pkcs5:pbkdf2(Mac, Password, Salt, Iterations), DerivedKey =:= element(2, jose_jwa_pkcs5:pbkdf2(Mac, Password, Salt, Iterations)) end). prop_pbkdf2_iterations_keylen() -> ?FORALL({Mac, Password, Salt, Iterations, KeyLen}, {mac_fun(), binary(), binary(), integer(1, 10), integer(0, 128)}, begin {ok, DerivedKey} = jose_jwa_pkcs5:pbkdf2(Mac, Password, Salt, Iterations, KeyLen), DerivedKey =:= element(2, jose_jwa_pkcs5:pbkdf2(Mac, Password, Salt, Iterations, KeyLen)) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwk_props.erl0000664000232200023220000002654515267717064026622 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwk_props). -include_lib("public_key/include/public_key.hrl"). -include_lib("proper/include/proper.hrl"). % -compile(export_all). alg_map() -> oneof([ #{ <<"alg">> => <<"RSA1_5">> }, #{ <<"alg">> => <<"RSA-OAEP">> }, #{ <<"alg">> => <<"RSA-OAEP-256">> }, #{ <<"alg">> => <<"A128KW">> }, #{ <<"alg">> => <<"A192KW">> }, #{ <<"alg">> => <<"A256KW">> }, #{ <<"alg">> => <<"dir">> }, #{ <<"alg">> => <<"ECDH-1PU">> }, #{ <<"alg">> => <<"ECDH-1PU+A128GCMKW">> }, #{ <<"alg">> => <<"ECDH-1PU+A192GCMKW">> }, #{ <<"alg">> => <<"ECDH-1PU+A256GCMKW">> }, #{ <<"alg">> => <<"ECDH-1PU+A128KW">> }, #{ <<"alg">> => <<"ECDH-1PU+A192KW">> }, #{ <<"alg">> => <<"ECDH-1PU+A256KW">> }, #{ <<"alg">> => <<"ECDH-1PU+C20PKW">> }, #{ <<"alg">> => <<"ECDH-1PU+XC20PKW">> }, #{ <<"alg">> => <<"ECDH-ES">> }, #{ <<"alg">> => <<"ECDH-ES+A128GCMKW">> }, #{ <<"alg">> => <<"ECDH-ES+A192GCMKW">> }, #{ <<"alg">> => <<"ECDH-ES+A256GCMKW">> }, #{ <<"alg">> => <<"ECDH-ES+A128KW">> }, #{ <<"alg">> => <<"ECDH-ES+A192KW">> }, #{ <<"alg">> => <<"ECDH-ES+A256KW">> }, #{ <<"alg">> => <<"ECDH-ES+C20PKW">> }, #{ <<"alg">> => <<"ECDH-ES+XC20PKW">> }, #{ <<"alg">> => <<"A128GCMKW">> }, #{ <<"alg">> => <<"A192GCMKW">> }, #{ <<"alg">> => <<"A256GCMKW">> }, ?LET({P2C, P2S}, {integer(1, 256), binary()}, #{ <<"alg">> => <<"PBES2-HS256+A128GCMKW">>, <<"p2c">> => P2C, <<"p2s">> => jose_jwa_base64url:encode(P2S) }), ?LET({P2C, P2S}, {integer(1, 256), binary()}, #{ <<"alg">> => <<"PBES2-HS384+A192GCMKW">>, <<"p2c">> => P2C, <<"p2s">> => jose_jwa_base64url:encode(P2S) }), ?LET({P2C, P2S}, {integer(1, 256), binary()}, #{ <<"alg">> => <<"PBES2-HS512+A256GCMKW">>, <<"p2c">> => P2C, <<"p2s">> => jose_jwa_base64url:encode(P2S) }), ?LET({P2C, P2S}, {integer(1, 256), binary()}, #{ <<"alg">> => <<"PBES2-HS256+A128KW">>, <<"p2c">> => P2C, <<"p2s">> => jose_jwa_base64url:encode(P2S) }), ?LET({P2C, P2S}, {integer(1, 256), binary()}, #{ <<"alg">> => <<"PBES2-HS384+A192KW">>, <<"p2c">> => P2C, <<"p2s">> => jose_jwa_base64url:encode(P2S) }), ?LET({P2C, P2S}, {integer(1, 256), binary()}, #{ <<"alg">> => <<"PBES2-HS512+A256KW">>, <<"p2c">> => P2C, <<"p2s">> => jose_jwa_base64url:encode(P2S) }), ?LET({P2C, P2S}, {integer(1, 256), binary()}, #{ <<"alg">> => <<"PBES2-HS512+C20PKW">>, <<"p2c">> => P2C, <<"p2s">> => jose_jwa_base64url:encode(P2S) }), ?LET({P2C, P2S}, {integer(1, 256), binary()}, #{ <<"alg">> => <<"PBES2-HS512+XC20PKW">>, <<"p2c">> => P2C, <<"p2s">> => jose_jwa_base64url:encode(P2S) }) ]). enc_map() -> oneof([ #{ <<"enc">> => <<"A128CBC-HS256">> }, #{ <<"enc">> => <<"A192CBC-HS384">> }, #{ <<"enc">> => <<"A256CBC-HS512">> }, #{ <<"enc">> => <<"A128GCM">> }, #{ <<"enc">> => <<"A192GCM">> }, #{ <<"enc">> => <<"A256GCM">> }, #{ <<"enc">> => <<"C20P">> }, #{ <<"enc">> => <<"XC20P">> } ]). jwk_encryptor_gen() -> ?LET({ALGMap, ENCMap}, ?SUCHTHAT({#{ <<"alg">> := _ALG }, #{ <<"enc">> := _ENC }}, {alg_map(), enc_map()}, true), begin ALG = maps:get(<<"alg">>, ALGMap), ENC = maps:get(<<"enc">>, ENCMap), JWE = jose_jwe:from_map(maps:merge(ENCMap, ALGMap)), case {ALG, ENC} of {<<"RSA", _/binary>>, _} -> ?LET({RSAPrivateKey, RSAPublicKey}, ?LET(ModulusSize, modulus_size(), rsa_keypair(ModulusSize)), begin PrivateJWK = jose_jwk:from_key(RSAPrivateKey), PublicJWK = jose_jwk:from_key(RSAPublicKey), {{rsa, RSAPrivateKey, RSAPublicKey}, JWE, {PrivateJWK, PublicJWK}} end); {<<"A128KW">>, _} -> K = crypto:strong_rand_bytes(16), {K, JWE, jose_jwk:from_map(#{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(K) })}; {<<"A192KW">>, _} -> K = crypto:strong_rand_bytes(24), {K, JWE, jose_jwk:from_map(#{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(K) })}; {<<"A256KW">>, _} -> K = crypto:strong_rand_bytes(32), {K, JWE, jose_jwk:from_map(#{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(K) })}; {<<"dir">>, <<"A128CBC-HS256">>} -> K = crypto:strong_rand_bytes(32), {K, JWE, jose_jwk:from_map(#{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(K) })}; {<<"dir">>, <<"A192CBC-HS384">>} -> K = crypto:strong_rand_bytes(48), {K, JWE, jose_jwk:from_map(#{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(K) })}; {<<"dir">>, <<"A256CBC-HS512">>} -> K = crypto:strong_rand_bytes(64), {K, JWE, jose_jwk:from_map(#{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(K) })}; {<<"dir">>, <<"A128GCM">>} -> K = crypto:strong_rand_bytes(16), {K, JWE, jose_jwk:from_map(#{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(K) })}; {<<"dir">>, <<"A192GCM">>} -> K = crypto:strong_rand_bytes(24), {K, JWE, jose_jwk:from_map(#{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(K) })}; {<<"dir">>, <<"A256GCM">>} -> K = crypto:strong_rand_bytes(32), {K, JWE, jose_jwk:from_map(#{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(K) })}; {<<"dir">>, <<"C20P">>} -> K = crypto:strong_rand_bytes(32), {K, JWE, jose_jwk:from_map(#{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(K) })}; {<<"dir">>, <<"XC20P">>} -> K = crypto:strong_rand_bytes(32), {K, JWE, jose_jwk:from_map(#{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(K) })}; {<<"ECDH-1PU", _/binary>>, _} -> ?LET(CurveId, ec_curve(), begin VStaticKeypair = {VStaticSecret, VStaticPublic} = ec_keypair(CurveId), UStaticKeypair = {UStaticSecret, UStaticPublic} = ec_keypair(CurveId), UEphemeralKeypair = {UEphemeralSecret, UEphemeralPublic} = ec_keypair(CurveId), VStaticSecretKey = jose_jwk:from_key(VStaticSecret), VStaticPublicKey = jose_jwk:from_key(VStaticPublic), UStaticSecretKey = jose_jwk:from_key(UStaticSecret), UStaticPublicKey = jose_jwk:from_key(UStaticPublic), UEphemeralSecretKey = jose_jwk:from_key(UEphemeralSecret), UEphemeralPublicKey = jose_jwk:from_key(UEphemeralPublic), {{ecdh_1pu, VStaticKeypair, UStaticKeypair, UEphemeralKeypair}, JWE, {{VStaticSecretKey, VStaticPublicKey}, {UStaticSecretKey, UStaticPublicKey}, {UEphemeralSecretKey, UEphemeralPublicKey}}} end); {<<"ECDH-ES", _/binary>>, _} -> ?LET(CurveId, ec_curve(), begin AliceKeypair = {AlicePrivateKey, AlicePublicKey} = ec_keypair(CurveId), BobKeypair = {BobPrivateKey, BobPublicKey} = ec_keypair(CurveId), AlicePrivateJWK = jose_jwk:from_key(AlicePrivateKey), AlicePublicJWK = jose_jwk:from_key(AlicePublicKey), BobPrivateJWK = jose_jwk:from_key(BobPrivateKey), BobPublicJWK = jose_jwk:from_key(BobPublicKey), {{ecdh_es, AliceKeypair, BobKeypair}, JWE, {{AlicePrivateJWK, AlicePublicJWK}, {BobPrivateJWK, BobPublicJWK}}} end); {<<"A128GCMKW">>, _} -> K = crypto:strong_rand_bytes(16), {K, JWE, jose_jwk:from_map(#{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(K) })}; {<<"A192GCMKW">>, _} -> K = crypto:strong_rand_bytes(24), {K, JWE, jose_jwk:from_map(#{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(K) })}; {<<"A256GCMKW">>, _} -> K = crypto:strong_rand_bytes(32), {K, JWE, jose_jwk:from_map(#{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(K) })}; {<<"C20PKW">>, _} -> K = crypto:strong_rand_bytes(32), {K, JWE, jose_jwk:from_map(#{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(K) })}; {<<"XC20PKW">>, _} -> K = crypto:strong_rand_bytes(32), {K, JWE, jose_jwk:from_map(#{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(K) })}; {<<"PBES2", _/binary>>, _} -> ?LET(Key, binary(), begin Password = jose_jwa_base64url:encode(Key), {Password, JWE, jose_jwk:from_map(#{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(Password) })} end) end end). ec_curve() -> oneof([ secp256r1, secp384r1, secp521r1, x25519, x448 ]). ec_keypair(x25519) -> SecretJWK = jose_jwk:generate_key({okp, 'X25519'}), {_, SecretKey} = jose_jwk:to_key(SecretJWK), {_, PublicKey} = jose_jwk:to_public_key(SecretJWK), {SecretKey, PublicKey}; ec_keypair(x448) -> SecretJWK = jose_jwk:generate_key({okp, 'X448'}), {_, SecretKey} = jose_jwk:to_key(SecretJWK), {_, PublicKey} = jose_jwk:to_public_key(SecretJWK), {SecretKey, PublicKey}; ec_keypair(CurveId) -> ECPrivateKey = #'ECPrivateKey'{parameters=ECParameters, publicKey=Octets0} = public_key:generate_key({namedCurve, pubkey_cert_records:namedCurves(CurveId)}), Octets = case Octets0 of {_, Octets1} -> Octets1; _ -> Octets0 end, ECPoint = #'ECPoint'{point=Octets}, ECPublicKey = {ECPoint, ECParameters}, {ECPrivateKey, ECPublicKey}. modulus_size() -> integer(1048, 1280). % integer(256, 8192) | pos_integer(). exponent_size() -> return(65537). % pos_integer(). rsa_keypair(ModulusSize) -> ?LET(ExponentSize, exponent_size(), begin case public_key:generate_key({rsa, ModulusSize, ExponentSize}) of PrivateKey=#'RSAPrivateKey'{modulus=Modulus, publicExponent=PublicExponent} -> {PrivateKey, #'RSAPublicKey'{modulus=Modulus, publicExponent=PublicExponent}} end end). prop_encrypt_and_decrypt() -> ?FORALL({Keys, JWE, JWKs, PlainText}, ?LET({Keys, JWE, JWKs}, jwk_encryptor_gen(), {Keys, JWE, JWKs, binary()}), begin case {Keys, JWKs} of {{ecdh_1pu, _, _, _}, {{VStaticSecretKey, VStaticPublicKey}, {UStaticSecretKey, UStaticPublicKey}, {UEphemeralSecretKey, UEphemeralPublicKey}}} -> JWEMap = jose_jwe:to_map(JWE), Encrypted = jose_jwk:box_encrypt_ecdh_1pu(PlainText, JWEMap, VStaticPublicKey, UStaticSecretKey, UEphemeralSecretKey), CompactEncrypted = jose_jwe:compact(Encrypted), Decrypted = {_, NewJWE} = jose_jwk:box_decrypt_ecdh_1pu(Encrypted, UStaticPublicKey, VStaticSecretKey), {PlainText, NewJWE} =:= Decrypted andalso {PlainText, NewJWE} =:= jose_jwe:block_decrypt({UStaticPublicKey, VStaticSecretKey, UEphemeralPublicKey}, CompactEncrypted); {{ecdh_es, _, _}, {{AlicePrivateJWK, _AlicePublicJWK}, {BobPrivateJWK, BobPublicJWK}}} -> JWEMap = jose_jwe:to_map(JWE), Encrypted = jose_jwk:box_encrypt_ecdh_es(PlainText, JWEMap, BobPublicJWK, AlicePrivateJWK), CompactEncrypted = jose_jwe:compact(Encrypted), Decrypted = {_, NewJWE} = jose_jwk:box_decrypt_ecdh_es(Encrypted, BobPrivateJWK), {PlainText, NewJWE} =:= Decrypted andalso {PlainText, NewJWE} =:= jose_jwk:block_decrypt(CompactEncrypted, BobPrivateJWK); {{rsa, _, _}, {PrivateJWK, PublicJWK}} -> Encrypted = jose_jwk:block_encrypt(PlainText, JWE, PublicJWK), CompactEncrypted = jose_jwe:compact(Encrypted), Decrypted = {_, NewJWE} = jose_jwk:block_decrypt(Encrypted, PrivateJWK), {PlainText, NewJWE} =:= Decrypted andalso {PlainText, NewJWE} =:= jose_jwk:block_decrypt(CompactEncrypted, PrivateJWK); {_, JWK} -> Encrypted = jose_jwk:block_encrypt(PlainText, JWE, JWK), CompactEncrypted = jose_jwe:compact(Encrypted), Decrypted = {_, NewJWE} = jose_jwk:block_decrypt(Encrypted, JWK), {PlainText, NewJWE} =:= Decrypted andalso {PlainText, NewJWE} =:= jose_jwk:block_decrypt(CompactEncrypted, JWK) end end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwk_kty_rsa_props.erl0000664000232200023220000001252215267717064030344 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwk_kty_rsa_props). -include_lib("public_key/include/public_key.hrl"). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). modulus_size() -> integer(1024, 1280). % integer(256, 8192) | pos_integer(). exponent_size() -> return(65537). % pos_integer(). rsa_keypair(ModulusSize) -> ?LET(ExponentSize, exponent_size(), begin case public_key:generate_key({rsa, ModulusSize, ExponentSize}) of PrivateKey=#'RSAPrivateKey'{modulus=Modulus, publicExponent=PublicExponent} -> {PrivateKey, #'RSAPublicKey'{modulus=Modulus, publicExponent=PublicExponent}} end end). jwk_map() -> ?LET({_ModulusSize, Keys={PrivateKey, _}}, ?LET(ModulusSize, modulus_size(), {ModulusSize, rsa_keypair(ModulusSize)}), begin PrivateJWK = jose_jwk:from_key(PrivateKey), PrivateJWKMap = element(2, jose_jwk:to_map(PrivateJWK)), {Keys, PrivateJWKMap} end). jwk_map_sfm_and_crt() -> ?LET({Keys, PrivateJWKMapCRT}, jwk_map(), begin PrivateJWKMapSFM = maps:without([ <<"dp">>, <<"dq">>, <<"p">>, <<"q">>, <<"qi">> ], PrivateJWKMapCRT), {Keys, PrivateJWKMapCRT, PrivateJWKMapSFM} end). jwk_gen() -> ?LET({Keys, PrivateJWKMap}, jwk_map(), {Keys, jose_jwk:from_map(PrivateJWKMap)}). prop_convert_sfm_to_crt() -> ?FORALL({{PrivateKey, PublicKey}, PrivateJWKMapCRT, PrivateJWKMapSFM}, ?LET({{Keys, JWKMapCRT, JWKMapSFM}, Extras}, {jwk_map_sfm_and_crt(), binary_map()}, {Keys, maps:merge(Extras, JWKMapCRT), maps:merge(Extras, JWKMapSFM)}), begin PrivateJWKCRT = jose_jwk:from_map(PrivateJWKMapCRT), PrivateJWKSFM = jose_jwk:from_map(PrivateJWKMapSFM), ThumbprintCRT = jose_jwk:thumbprint(PrivateJWKMapCRT), ThumbprintSFM = jose_jwk:thumbprint(PrivateJWKMapSFM), PrivateJWKCRT =:= PrivateJWKSFM andalso PrivateKey =:= element(2, jose_jwk:to_key(PrivateJWKCRT)) andalso PrivateKey =:= element(2, jose_jwk:to_key(PrivateJWKSFM)) andalso PublicKey =:= element(2, jose_jwk:to_public_key(PrivateJWKCRT)) andalso PublicKey =:= element(2, jose_jwk:to_public_key(PrivateJWKSFM)) andalso ThumbprintCRT =:= ThumbprintSFM end). prop_from_der_and_to_der() -> ?FORALL({{_, PublicKey}, PrivateJWK, Password}, ?LET({{Keys, PrivateJWK}, Bytes}, {jwk_gen(), binary()}, {Keys, PrivateJWK, jose_jwa_base64url:encode(Bytes)}), begin PublicJWK = jose_jwk:from_key(PublicKey), PublicDER = element(2, jose_jwk:to_der(PublicJWK)), PrivateDER = element(2, jose_jwk:to_der(PrivateJWK)), EncryptedPrivateDER = element(2, jose_jwk:to_der(Password, PrivateJWK)), PrivateJWK =:= jose_jwk:from_der(PrivateDER) andalso PrivateJWK =:= jose_jwk:from_der(Password, EncryptedPrivateDER) andalso PublicJWK =:= jose_jwk:from_der(PublicDER) end). prop_from_map_and_to_map() -> ?FORALL({{PrivateKey, PublicKey}, PrivateJWKMap}, ?LET({{Keys, JWKMap}, Extras}, {jwk_map(), binary_map()}, {Keys, maps:merge(Extras, JWKMap)}), begin PrivateJWK = jose_jwk:from_map(PrivateJWKMap), PublicJWK = jose_jwk:to_public(PrivateJWK), PublicJWKMap = element(2, jose_jwk:to_map(PublicJWK)), PublicThumbprint = jose_jwk:thumbprint(PublicJWK), PrivateJWKMap =:= element(2, jose_jwk:to_map(PrivateJWK)) andalso PrivateKey =:= element(2, jose_jwk:to_key(PrivateJWK)) andalso PublicKey =:= element(2, jose_jwk:to_public_key(PrivateJWK)) andalso PublicJWKMap =:= element(2, jose_jwk:to_public_map(PrivateJWK)) andalso PublicThumbprint =:= jose_jwk:thumbprint(PrivateJWK) end). prop_from_pem_and_to_pem() -> ?FORALL({{_, PublicKey}, PrivateJWK, Password}, ?LET({{Keys, PrivateJWK}, Bytes}, {jwk_gen(), binary()}, {Keys, PrivateJWK, jose_jwa_base64url:encode(Bytes)}), begin PublicJWK = jose_jwk:from_key(PublicKey), PublicPEM = element(2, jose_jwk:to_pem(PublicJWK)), EncryptedPublicPEM = element(2, jose_jwk:to_pem(Password, PublicJWK)), PrivatePEM = element(2, jose_jwk:to_pem(PrivateJWK)), EncryptedPrivatePEM = element(2, jose_jwk:to_pem(Password, PrivateJWK)), PrivateJWK =:= jose_jwk:from_pem(PrivatePEM) andalso PrivateJWK =:= jose_jwk:from_pem(Password, EncryptedPrivatePEM) andalso PublicJWK =:= jose_jwk:from_pem(PublicPEM) andalso PublicJWK =:= jose_jwk:from_pem(Password, EncryptedPublicPEM) end). prop_block_encrypt_and_block_decrypt() -> ?FORALL({{{_, PublicKey}, PrivateJWK}, PlainText}, {jwk_gen(), binary()}, begin PublicJWK = jose_jwk:from_key(PublicKey), Encrypted = jose_jwk:block_encrypt(PlainText, PublicJWK), CompactEncrypted = jose_jwe:compact(Encrypted), Decrypted = {_, JWE} = jose_jwk:box_decrypt(Encrypted, PrivateJWK), {PlainText, JWE} =:= Decrypted andalso {PlainText, JWE} =:= jose_jwk:block_decrypt(CompactEncrypted, PrivateJWK) end). prop_sign_and_verify() -> ?FORALL({_Keys, JWK, Message}, ?LET({Keys, JWK}, jwk_gen(), {Keys, JWK, binary()}), begin Signed = jose_jwk:sign(Message, JWK), CompactSigned = jose_jws:compact(Signed), Verified = {_, _, JWS} = jose_jwk:verify(Signed, JWK), {true, Message, JWS} =:= Verified andalso {true, Message, JWS} =:= jose_jwk:verify(CompactSigned, JWK) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwk_kty_okp_ed25519ph_props.erl0000664000232200023220000000447615267717064031767 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwk_kty_okp_ed25519ph_props). -include_lib("public_key/include/public_key.hrl"). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). ed25519ph_secret() -> binary(32). ed25519ph_keypair(Secret) -> {PK, SK} = jose_curve25519:eddsa_keypair(Secret), {SK, PK}. jwk_map() -> ?LET({AliceSecret, BobSecret}, {ed25519ph_secret(), ed25519ph_secret()}, begin AliceKeys = {AlicePrivateKey, _} = ed25519ph_keypair(AliceSecret), BobKeys = ed25519ph_keypair(BobSecret), AlicePrivateJWK = jose_jwk:from_okp({'Ed25519ph', AlicePrivateKey}), {_, AlicePrivateJWKMap} = jose_jwk:to_map(AlicePrivateJWK), Keys = {AliceKeys, BobKeys}, {Keys, AlicePrivateJWKMap} end). jwk_gen() -> ?LET({Keys, AlicePrivateJWKMap}, jwk_map(), {Keys, jose_jwk:from_map(AlicePrivateJWKMap)}). prop_from_map_and_to_map() -> ?FORALL({{{AlicePrivateKey, AlicePublicKey}, _}, AlicePrivateJWKMap}, ?LET({{Keys, JWKMap}, Extras}, {jwk_map(), binary_map()}, {Keys, maps:merge(Extras, JWKMap)}), begin AlicePrivateJWK = jose_jwk:from_map(AlicePrivateJWKMap), AlicePublicJWK = jose_jwk:to_public(AlicePrivateJWK), AlicePublicJWKMap = element(2, jose_jwk:to_map(AlicePublicJWK)), AlicePublicThumbprint = jose_jwk:thumbprint(AlicePublicJWK), AlicePrivateJWKMap =:= element(2, jose_jwk:to_map(AlicePrivateJWK)) andalso AlicePrivateKey =:= element(2, jose_jwk:to_key(AlicePrivateJWK)) andalso AlicePublicKey =:= element(2, jose_jwk:to_public_key(AlicePrivateJWK)) andalso AlicePublicJWKMap =:= element(2, jose_jwk:to_public_map(AlicePrivateJWK)) andalso AlicePublicThumbprint =:= jose_jwk:thumbprint(AlicePrivateJWK) end). prop_sign_and_verify() -> ?FORALL({_Keys, JWK, Message}, ?LET({Keys, JWK}, jwk_gen(), {Keys, JWK, binary()}), begin Signed = jose_jwk:sign(Message, JWK), CompactSigned = jose_jws:compact(Signed), Verified = {_, _, JWS} = jose_jwk:verify(Signed, JWK), {true, Message, JWS} =:= Verified andalso {true, Message, JWS} =:= jose_jwk:verify(CompactSigned, JWK) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jws_alg_rsa_pkcs1_v1_5_props.erl0000664000232200023220000000406215267717064032243 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jws_alg_rsa_pkcs1_v1_5_props). -include_lib("public_key/include/public_key.hrl"). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). alg() -> oneof([ <<"RS256">>, <<"RS384">>, <<"RS512">> ]). modulus_size() -> integer(1024, 1280). % integer(256, 8192) | pos_integer(). exponent_size() -> return(65537). % pos_integer(). rsa_keypair(ModulusSize) -> ?LET(ExponentSize, exponent_size(), begin case public_key:generate_key({rsa, ModulusSize, ExponentSize}) of PrivateKey=#'RSAPrivateKey'{modulus=Modulus, publicExponent=PublicExponent} -> {PrivateKey, #'RSAPublicKey'{modulus=Modulus, publicExponent=PublicExponent}} end end). jwk_jws_maps() -> ?LET({ModulusSize, ALG, {PrivateKey, PublicKey}}, ?LET(ModulusSize, modulus_size(), {ModulusSize, alg(), rsa_keypair(ModulusSize)}), begin JWKSigner = jose_jwk:from_key(PrivateKey), JWKVerifier = jose_jwk:from_key(PublicKey), JWSMap = #{ <<"alg">> => ALG }, {ModulusSize, {JWKSigner, JWKVerifier}, JWSMap} end). jwk_jws_gen() -> ?LET({ModulusSize, JWKs, JWSMap}, jwk_jws_maps(), {ModulusSize, JWKs, jose_jws:from_map(JWSMap)}). prop_from_map_and_to_map() -> ?FORALL(JWSMap, ?LET({ALG, Extras}, {alg(), binary_map()}, maps:merge(Extras, #{ <<"alg">> => ALG })), begin JWS = jose_jws:from_map(JWSMap), JWSMap =:= element(2, jose_jws:to_map(JWS)) end). prop_sign_and_verify() -> ?FORALL({{_ModulusSize, {JWKSigner, JWKVerifier}, JWS}, Message}, {jwk_jws_gen(), binary()}, begin Signed = jose_jws:sign(JWKSigner, Message, JWS), CompactSigned = jose_jws:compact(Signed), {true, Message, JWS} =:= jose_jws:verify(JWKVerifier, Signed) andalso {true, Message, JWS} =:= jose_jws:verify(JWKVerifier, CompactSigned) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwk_kty_oct_props.erl0000664000232200023220000000421115267717064030340 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwk_kty_oct_props). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). jwk_map() -> ?LET(Key, binary(), begin JWKMap = #{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(Key) }, {Key, JWKMap} end). jwk_map(KeySize) -> ?LET(Key, binary(KeySize), begin JWKMap = #{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(Key) }, {Key, JWKMap} end). jwk_gen() -> ?LET({Key, JWKMap}, jwk_map(32), {Key, jose_jwk:from_map(JWKMap)}). prop_from_map_and_to_map() -> ?FORALL({Key, JWKMap}, ?LET({{Key, JWKMap}, Extras}, {jwk_map(), binary_map()}, {Key, maps:merge(Extras, JWKMap)}), begin JWK = jose_jwk:from_map(JWKMap), Error = try jose_jwk:to_public(JWK) catch error:Reason:_ -> Reason end, PublicThumbprint = jose_jwk:thumbprint(JWK), JWKMap =:= element(2, jose_jwk:to_map(JWK)) andalso Key =:= element(2, jose_jwk:to_key(JWK)) andalso {not_supported, [to_public_map]} =:= Error andalso PublicThumbprint =:= jose_jwk:thumbprint(JWK) end). prop_block_encrypt_and_block_decrypt() -> ?FORALL({{_Key, JWK}, PlainText}, {jwk_gen(), binary()}, begin Encrypted = jose_jwk:block_encrypt(PlainText, JWK), CompactEncrypted = jose_jwe:compact(Encrypted), Decrypted = {_, JWE} = jose_jwk:block_decrypt(Encrypted, JWK), {PlainText, JWE} =:= Decrypted andalso {PlainText, JWE} =:= jose_jwk:block_decrypt(CompactEncrypted, JWK) end). prop_sign_and_verify() -> ?FORALL({_Key, JWK, Message}, ?LET({Key, JWK}, jwk_gen(), {Key, JWK, binary()}), begin Signed = jose_jwk:sign(Message, JWK), CompactSigned = jose_jws:compact(Signed), Verified = {_, _, JWS} = jose_jwk:verify(Signed, JWK), {true, Message, JWS} =:= Verified andalso {true, Message, JWS} =:= jose_jwk:verify(CompactSigned, JWK) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwa_concat_kdf_props.erl0000664000232200023220000000167115267717064030754 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwa_concat_kdf_props). -include_lib("proper/include/proper.hrl"). % -compile(export_all). hash_fun() -> oneof([md5, sha, sha224, sha256, sha384, sha512, {hmac, md5, <<>>}, {hmac, sha, <<>>}, {hmac, sha224, <<>>}, {hmac, sha256, <<>>}, {hmac, sha384, <<>>}, {hmac, sha512, <<>>}]). prop_kdf() -> ?FORALL({Hash, Z, OtherInfo}, {hash_fun(), binary(), {binary(), binary(), binary()}}, begin DerivedKey = jose_jwa_concat_kdf:kdf(Hash, Z, OtherInfo), DerivedKey =:= jose_jwa_concat_kdf:kdf(Hash, Z, OtherInfo) end). prop_kdf_keylen() -> ?FORALL({Hash, Z, OtherInfo, KeyDataLen}, {hash_fun(), binary(), {binary(), binary(), binary()}, integer(0, 1024)}, begin DerivedKey = jose_jwa_concat_kdf:kdf(Hash, Z, OtherInfo, KeyDataLen), DerivedKey =:= jose_jwa_concat_kdf:kdf(Hash, Z, OtherInfo, KeyDataLen) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwa_curve448_props.erl0000664000232200023220000000334715267717064030247 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwa_curve448_props). -include_lib("public_key/include/public_key.hrl"). -include_lib("proper/include/proper.hrl"). % -compile(export_all). eddsa_secret() -> binary(57). eddsa_keypair(Secret) -> {PK, SK} = jose_curve448:eddsa_keypair(Secret), {SK, PK}. x448_secret() -> binary(56). x448_keypair(Secret) -> {PK, SK} = jose_curve448:x448_keypair(Secret), {SK, PK}. eddsa_keypair_gen() -> ?LET(Secret, eddsa_secret(), eddsa_keypair(Secret)). prop_eddsa_secret_to_public() -> ?FORALL({<< Secret:57/binary, _/binary >>, PK}, eddsa_keypair_gen(), begin PK =:= jose_jwa_curve448:eddsa_secret_to_public(Secret) end). prop_ed448_sign_and_verify() -> ?FORALL({{SK, PK}, M}, {eddsa_keypair_gen(), binary()}, begin S = jose_jwa_curve448:ed448_sign(M, SK), jose_jwa_curve448:ed448_verify(S, M, PK) end). prop_ed448ph_sign_and_verify() -> ?FORALL({{SK, PK}, M}, {eddsa_keypair_gen(), binary()}, begin S = jose_jwa_curve448:ed448ph_sign(M, SK), jose_jwa_curve448:ed448ph_verify(S, M, PK) end). x448_keypair_gen() -> ?LET(Secret, x448_secret(), x448_keypair(Secret)). x448_keypairs_gen() -> ?LET({AliceSecret, BobSecret}, {x448_secret(), x448_secret()}, {x448_keypair(AliceSecret), x448_keypair(BobSecret)}). prop_x448_secret_to_public() -> ?FORALL({SK, PK}, x448_keypair_gen(), begin PK =:= jose_jwa_curve448:x448_secret_to_public(SK) end). prop_x448_shared_secret() -> ?FORALL({{AliceSK, AlicePK}, {BobSK, BobPK}}, x448_keypairs_gen(), begin K = jose_jwa_curve448:x448_shared_secret(AliceSK, BobPK), K =:= jose_jwa_curve448:x448_shared_secret(BobSK, AlicePK) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwe_enc_aes_props.erl0000664000232200023220000000330115267717064030252 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwe_enc_aes_props). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). enc() -> oneof([ <<"A128CBC-HS256">>, <<"A192CBC-HS384">>, <<"A256CBC-HS512">>, <<"A128GCM">>, <<"A192GCM">>, <<"A256GCM">> ]). jwk_jwe_maps() -> ?LET(ENC, enc(), begin FakeJWEMap = #{ <<"alg">> => <<"RSA1_5">>, <<"enc">> => ENC }, FakeJWE = jose_jwe:from_map(FakeJWEMap), {Key, _} = jose_jwe:next_cek(undefined, FakeJWE), JWKMap = #{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(Key) }, JWEMap = #{ <<"alg">> => <<"dir">>, <<"enc">> => ENC }, {Key, JWKMap, JWEMap} end). jwk_jwe_gen() -> ?LET({Key, JWKMap, JWEMap}, jwk_jwe_maps(), {Key, jose_jwk:from_map(JWKMap), jose_jwe:from_map(JWEMap)}). prop_from_map_and_to_map() -> ?FORALL(JWEMap, ?LET({{_Key, _JWKMap, JWEMap}, Extras}, {jwk_jwe_maps(), binary_map()}, maps:merge(Extras, JWEMap)), begin JWE = jose_jwe:from_map(JWEMap), JWEMap =:= element(2, jose_jwe:to_map(JWE)) end). prop_block_encrypt_and_block_decrypt() -> ?FORALL({CEK, IV, JWK, JWE, PlainText}, ?LET({CEK, JWK, JWE}, jwk_jwe_gen(), {CEK, jose_jwe:next_iv(JWE), JWK, JWE, binary()}), begin Encrypted = jose_jwe:block_encrypt(JWK, PlainText, CEK, IV, JWE), CompactEncrypted = jose_jwe:compact(Encrypted), PlainText =:= element(1, jose_jwe:block_decrypt(JWK, CompactEncrypted)) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwk_kty_okp_ed448_props.erl0000664000232200023220000000772215267717064031266 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwk_kty_okp_ed448_props). -include("jose_public_key.hrl"). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). ed448_secret() -> binary(57). ed448_keypair(Secret) -> {PK, << S:57/binary, _:57/binary >>} = jose_curve448:eddsa_keypair(Secret), PublicKey = #'jose_EdDSA448PublicKey'{publicKey=PK}, SecretKey = #'jose_EdDSA448PrivateKey'{publicKey=PublicKey, privateKey=S}, {SecretKey, PublicKey}. jwk_map() -> ?LET({AliceSecret, BobSecret}, {ed448_secret(), ed448_secret()}, begin AliceKeys = {AlicePrivateKey, _} = ed448_keypair(AliceSecret), BobKeys = ed448_keypair(BobSecret), AlicePrivateJWK = jose_jwk:from_key(AlicePrivateKey), {_, AlicePrivateJWKMap} = jose_jwk:to_map(AlicePrivateJWK), Keys = {AliceKeys, BobKeys}, {Keys, AlicePrivateJWKMap} end). jwk_gen() -> ?LET({Keys, AlicePrivateJWKMap}, jwk_map(), {Keys, jose_jwk:from_map(AlicePrivateJWKMap)}). prop_from_der_and_to_der() -> ?FORALL({_Keys, AlicePrivateJWK, Password}, ?LET({{Keys, AlicePrivateJWK}, Bytes}, {jwk_gen(), binary()}, {Keys, AlicePrivateJWK, jose_jwa_base64url:encode(Bytes)}), begin AlicePrivateDER = element(2, jose_jwk:to_der(AlicePrivateJWK)), EncryptedAlicePrivateDER = element(2, jose_jwk:to_der(Password, AlicePrivateJWK)), AlicePublicJWK = jose_jwk:to_public(AlicePrivateJWK), AlicePublicDER = element(2, jose_jwk:to_der(AlicePublicJWK)), AlicePrivateJWK =:= jose_jwk:from_der(AlicePrivateDER) andalso AlicePrivateJWK =:= jose_jwk:from_der(Password, EncryptedAlicePrivateDER) andalso AlicePublicJWK =:= jose_jwk:from_der(AlicePublicDER) end). prop_from_map_and_to_map() -> ?FORALL({{{AlicePrivateKey, AlicePublicKey}, _}, AlicePrivateJWKMap}, ?LET({{Keys, JWKMap}, Extras}, {jwk_map(), binary_map()}, {Keys, maps:merge(Extras, JWKMap)}), begin AlicePrivateJWK = jose_jwk:from_map(AlicePrivateJWKMap), AlicePublicJWK = jose_jwk:to_public(AlicePrivateJWK), AlicePublicJWKMap = element(2, jose_jwk:to_map(AlicePublicJWK)), AlicePublicThumbprint = jose_jwk:thumbprint(AlicePublicJWK), AlicePrivateJWKMap =:= element(2, jose_jwk:to_map(AlicePrivateJWK)) andalso AlicePrivateKey =:= element(2, jose_jwk:to_key(AlicePrivateJWK)) andalso AlicePublicKey =:= element(2, jose_jwk:to_public_key(AlicePrivateJWK)) andalso AlicePublicJWKMap =:= element(2, jose_jwk:to_public_map(AlicePrivateJWK)) andalso AlicePublicThumbprint =:= jose_jwk:thumbprint(AlicePrivateJWK) end). prop_from_pem_and_to_pem() -> ?FORALL({_Keys, AlicePrivateJWK, Password}, ?LET({{Keys, AlicePrivateJWK}, Bytes}, {jwk_gen(), binary()}, {Keys, AlicePrivateJWK, jose_jwa_base64url:encode(Bytes)}), begin AlicePrivatePEM = element(2, jose_jwk:to_pem(AlicePrivateJWK)), EncryptedAlicePrivatePEM = element(2, jose_jwk:to_pem(Password, AlicePrivateJWK)), AlicePublicJWK = jose_jwk:to_public(AlicePrivateJWK), AlicePublicPEM = element(2, jose_jwk:to_pem(AlicePublicJWK)), EncryptedAlicePublicPEM = element(2, jose_jwk:to_pem(Password, AlicePublicJWK)), AlicePrivateJWK =:= jose_jwk:from_pem(AlicePrivatePEM) andalso AlicePrivateJWK =:= jose_jwk:from_pem(Password, EncryptedAlicePrivatePEM) andalso AlicePublicJWK =:= jose_jwk:from_pem(AlicePublicPEM) andalso AlicePublicJWK =:= jose_jwk:from_pem(Password, EncryptedAlicePublicPEM) end). prop_sign_and_verify() -> ?FORALL({_Keys, JWK, Message}, ?LET({Keys, JWK}, jwk_gen(), {Keys, JWK, binary()}), begin Signed = jose_jwk:sign(Message, JWK), CompactSigned = jose_jws:compact(Signed), Verified = {_, _, JWS} = jose_jwk:verify(Signed, JWK), {true, Message, JWS} =:= Verified andalso {true, Message, JWS} =:= jose_jwk:verify(CompactSigned, JWK) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jwk_kty_okp_ed25519_props.erl0000664000232200023220000000774615267717064031442 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwk_kty_okp_ed25519_props). -include("jose_public_key.hrl"). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). ed25519_secret() -> binary(32). ed25519_keypair(Secret) -> {PK, << S:32/binary, _:32/binary >>} = jose_curve25519:eddsa_keypair(Secret), PublicKey = #'jose_EdDSA25519PublicKey'{publicKey=PK}, SecretKey = #'jose_EdDSA25519PrivateKey'{publicKey=PublicKey, privateKey=S}, {SecretKey, PublicKey}. jwk_map() -> ?LET({AliceSecret, BobSecret}, {ed25519_secret(), ed25519_secret()}, begin AliceKeys = {AlicePrivateKey, _} = ed25519_keypair(AliceSecret), BobKeys = ed25519_keypair(BobSecret), AlicePrivateJWK = jose_jwk:from_key(AlicePrivateKey), {_, AlicePrivateJWKMap} = jose_jwk:to_map(AlicePrivateJWK), Keys = {AliceKeys, BobKeys}, {Keys, AlicePrivateJWKMap} end). jwk_gen() -> ?LET({Keys, AlicePrivateJWKMap}, jwk_map(), {Keys, jose_jwk:from_map(AlicePrivateJWKMap)}). prop_from_der_and_to_der() -> ?FORALL({_Keys, AlicePrivateJWK, Password}, ?LET({{Keys, AlicePrivateJWK}, Bytes}, {jwk_gen(), binary()}, {Keys, AlicePrivateJWK, jose_jwa_base64url:encode(Bytes)}), begin AlicePrivateDER = element(2, jose_jwk:to_der(AlicePrivateJWK)), EncryptedAlicePrivateDER = element(2, jose_jwk:to_der(Password, AlicePrivateJWK)), AlicePublicJWK = jose_jwk:to_public(AlicePrivateJWK), AlicePublicDER = element(2, jose_jwk:to_der(AlicePublicJWK)), AlicePrivateJWK =:= jose_jwk:from_der(AlicePrivateDER) andalso AlicePrivateJWK =:= jose_jwk:from_der(Password, EncryptedAlicePrivateDER) andalso AlicePublicJWK =:= jose_jwk:from_der(AlicePublicDER) end). prop_from_map_and_to_map() -> ?FORALL({{{AlicePrivateKey, AlicePublicKey}, _}, AlicePrivateJWKMap}, ?LET({{Keys, JWKMap}, Extras}, {jwk_map(), binary_map()}, {Keys, maps:merge(Extras, JWKMap)}), begin AlicePrivateJWK = jose_jwk:from_map(AlicePrivateJWKMap), AlicePublicJWK = jose_jwk:to_public(AlicePrivateJWK), AlicePublicJWKMap = element(2, jose_jwk:to_map(AlicePublicJWK)), AlicePublicThumbprint = jose_jwk:thumbprint(AlicePublicJWK), AlicePrivateJWKMap =:= element(2, jose_jwk:to_map(AlicePrivateJWK)) andalso AlicePrivateKey =:= element(2, jose_jwk:to_key(AlicePrivateJWK)) andalso AlicePublicKey =:= element(2, jose_jwk:to_public_key(AlicePrivateJWK)) andalso AlicePublicJWKMap =:= element(2, jose_jwk:to_public_map(AlicePrivateJWK)) andalso AlicePublicThumbprint =:= jose_jwk:thumbprint(AlicePrivateJWK) end). prop_from_pem_and_to_pem() -> ?FORALL({_Keys, AlicePrivateJWK, Password}, ?LET({{Keys, AlicePrivateJWK}, Bytes}, {jwk_gen(), binary()}, {Keys, AlicePrivateJWK, jose_jwa_base64url:encode(Bytes)}), begin AlicePrivatePEM = element(2, jose_jwk:to_pem(AlicePrivateJWK)), EncryptedAlicePrivatePEM = element(2, jose_jwk:to_pem(Password, AlicePrivateJWK)), AlicePublicJWK = jose_jwk:to_public(AlicePrivateJWK), AlicePublicPEM = element(2, jose_jwk:to_pem(AlicePublicJWK)), EncryptedAlicePublicPEM = element(2, jose_jwk:to_pem(Password, AlicePublicJWK)), AlicePrivateJWK =:= jose_jwk:from_pem(AlicePrivatePEM) andalso AlicePrivateJWK =:= jose_jwk:from_pem(Password, EncryptedAlicePrivatePEM) andalso AlicePublicJWK =:= jose_jwk:from_pem(AlicePublicPEM) andalso AlicePublicJWK =:= jose_jwk:from_pem(Password, EncryptedAlicePublicPEM) end). prop_sign_and_verify() -> ?FORALL({_Keys, JWK, Message}, ?LET({Keys, JWK}, jwk_gen(), {Keys, JWK, binary()}), begin Signed = jose_jwk:sign(Message, JWK), CompactSigned = jose_jws:compact(Signed), Verified = {_, _, JWS} = jose_jwk:verify(Signed, JWK), {true, Message, JWS} =:= Verified andalso {true, Message, JWS} =:= jose_jwk:verify(CompactSigned, JWK) end). potatosalad-erlang-jose-30ac019/test/property_test/jose_jws_alg_poly1305_props.erl0000664000232200023220000000412715267717064031021 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jws_alg_poly1305_props). -include_lib("proper/include/proper.hrl"). % -compile(export_all). base64url_binary() -> ?LET(Binary, binary(), jose_jwa_base64url:encode(Binary)). binary_map() -> ?LET(List, list({base64url_binary(), base64url_binary()}), maps:from_list(List)). alg() -> return(<<"Poly1305">>). jwk_jws_maps() -> ?LET({ALG, Key, Nonce}, {alg(), binary(32), binary(12)}, begin JWKMap = #{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(Key) }, JWSMap = #{ <<"alg">> => ALG }, NonceJWSMap = #{ <<"alg">> => ALG, <<"nonce">> => jose_jwa_base64url:encode(Nonce) }, {Key, JWKMap, JWSMap, NonceJWSMap} end). jwk_jws_gen() -> ?LET({Key, JWKMap, JWSMap, NonceJWSMap}, jwk_jws_maps(), {Key, jose_jwk:from_map(JWKMap), jose_jws:from_map(JWSMap), jose_jws:from_map(NonceJWSMap)}). prop_from_map_and_to_map() -> ?FORALL({JWSMap, NonceJWSMap}, ?LET({{_Key, _JWKMap, JWSMap, NonceJWSMap}, Extras}, {jwk_jws_maps(), binary_map()}, {maps:merge(Extras, JWSMap), maps:merge(Extras, NonceJWSMap)}), begin JWS = jose_jws:from_map(JWSMap), NonceJWS = jose_jws:from_map(NonceJWSMap), JWSMap == element(2, jose_jws:to_map(JWS)) andalso NonceJWSMap == element(2, jose_jws:to_map(NonceJWS)) end). prop_sign_and_verify() -> ?FORALL({{_Key, JWK, JWS, NonceJWS}, Message}, {jwk_jws_gen(), binary()}, begin NonceSigned = jose_jws:sign(JWK, Message, NonceJWS), NonceCompactSigned = jose_jws:compact(NonceSigned), Signed = jose_jws:sign(JWK, Message, JWS), CompactSigned = jose_jws:compact(Signed), {true, Message, NonceJWS} == jose_jws:verify(JWK, NonceSigned) andalso {true, Message, NonceJWS} == jose_jws:verify(JWK, NonceCompactSigned) andalso true == element(1, jose_jws:verify(JWK, Signed)) andalso true == element(1, jose_jws:verify(JWK, CompactSigned)) andalso JWS =/= element(3, jose_jws:verify(JWK, Signed)) andalso NonceJWS =/= element(3, jose_jws:verify(JWK, Signed)) end). potatosalad-erlang-jose-30ac019/test/jose_jwa_SUITE.erl0000664000232200023220000003207515267717064023406 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwa_SUITE). -include_lib("common_test/include/ct.hrl"). -include("jose.hrl"). %% ct. -export([all/0]). -export([groups/0]). -export([init_per_suite/1]). -export([end_per_suite/1]). -export([init_per_group/2]). -export([end_per_group/2]). %% Tests. -export([aes_cbc_block_encrypt_and_cbc_block_decrypt/1]). -export([aes_cbc_block_encrypt_and_jwa_block_decrypt/1]). -export([aes_jwa_block_encrypt_and_cbc_block_decrypt/1]). -export([aes_jwa_block_encrypt_and_ecb_block_decrypt/1]). -export([aes_jwa_block_encrypt_and_gcm_block_decrypt/1]). -export([aes_ecb_block_encrypt_and_ecb_block_decrypt/1]). -export([aes_ecb_block_encrypt_and_jwa_block_decrypt/1]). -export([aes_gcm_block_encrypt_and_gcm_block_decrypt/1]). -export([aes_gcm_block_encrypt_and_jwa_block_decrypt/1]). -export([aes_kw_128_128/1]). -export([aes_kw_128_192/1]). -export([aes_kw_128_256/1]). -export([aes_kw_192_192/1]). -export([aes_kw_192_256/1]). -export([aes_kw_256_256/1]). -export([aes_kw_wrap_and_unwrap/1]). -export([concat_kdf/1]). -export([concat_kdf_keylen/1]). -export([concat_kdf_keylen_bounds/1]). -export([constant_time_compare/1]). -export([curve25519_eddsa_secret_to_public/1]). -export([curve448_eddsa_secret_to_public/1]). -export([ed25519_sign_and_verify/1]). -export([ed25519ph_sign_and_verify/1]). -export([ed448_sign_and_verify/1]). -export([ed448ph_sign_and_verify/1]). -export([pkcs1_rsaes_oaep_encrypt_and_decrypt/1]). -export([pkcs1_rsaes_oaep_encrypt_and_decrypt_with_label/1]). -export([pkcs1_rsaes_pkcs1_encrypt_and_decrypt/1]). -export([pkcs1_rsassa_pkcs1_sign_and_verify/1]). -export([pkcs1_rsassa_pss_sign_and_verify/1]). -export([pkcs1_rsassa_pss_sign_and_verify_with_salt/1]). -export([pkcs5_pbkdf1/1]). -export([pkcs5_pbkdf1_iterations/1]). -export([pkcs5_pbkdf2/1]). -export([pkcs5_pbkdf2_iterations/1]). -export([pkcs5_pbkdf2_iterations_keylen/1]). -export([pkcs7_pad_and_unpad/1]). -export([x25519_secret_to_public/1]). -export([x25519_shared_secret/1]). -export([x448_secret_to_public/1]). -export([x448_shared_secret/1]). all() -> [ constant_time_compare, {group, jose_jwa_aes}, {group, jose_jwa_aes_kw}, {group, jose_jwa_concat_kdf}, {group, jose_jwa_curve25519}, {group, jose_jwa_curve448}, {group, jose_jwa_pkcs1}, % {group, jose_jwa_pkcs5}, {group, jose_jwa_pkcs7} ]. groups() -> [ {jose_jwa_aes, [parallel], [ aes_cbc_block_encrypt_and_cbc_block_decrypt, aes_cbc_block_encrypt_and_jwa_block_decrypt, aes_jwa_block_encrypt_and_cbc_block_decrypt, aes_jwa_block_encrypt_and_ecb_block_decrypt, aes_jwa_block_encrypt_and_gcm_block_decrypt, aes_ecb_block_encrypt_and_ecb_block_decrypt, aes_ecb_block_encrypt_and_jwa_block_decrypt, aes_gcm_block_encrypt_and_gcm_block_decrypt, aes_gcm_block_encrypt_and_jwa_block_decrypt ]}, {jose_jwa_aes_kw, [parallel], [ aes_kw_128_128, aes_kw_128_192, aes_kw_128_256, aes_kw_192_192, aes_kw_192_256, aes_kw_256_256, aes_kw_wrap_and_unwrap ]}, {jose_jwa_concat_kdf, [parallel], [ concat_kdf, concat_kdf_keylen, concat_kdf_keylen_bounds ]}, {jose_jwa_curve25519, [parallel], [ curve25519_eddsa_secret_to_public, ed25519_sign_and_verify, ed25519ph_sign_and_verify, x25519_secret_to_public, x25519_shared_secret ]}, {jose_jwa_curve448, [parallel], [ curve448_eddsa_secret_to_public, ed448_sign_and_verify, ed448ph_sign_and_verify, x448_secret_to_public, x448_shared_secret ]}, {jose_jwa_pkcs1, [parallel], [ pkcs1_rsaes_oaep_encrypt_and_decrypt, pkcs1_rsaes_oaep_encrypt_and_decrypt_with_label, pkcs1_rsaes_pkcs1_encrypt_and_decrypt, pkcs1_rsassa_pkcs1_sign_and_verify, pkcs1_rsassa_pss_sign_and_verify, pkcs1_rsassa_pss_sign_and_verify_with_salt ]}, {jose_jwa_pkcs5, [parallel], [ pkcs5_pbkdf1, pkcs5_pbkdf1_iterations, pkcs5_pbkdf2, pkcs5_pbkdf2_iterations, pkcs5_pbkdf2_iterations_keylen ]}, {jose_jwa_pkcs7, [parallel], [ pkcs7_pad_and_unpad ]} ]. init_per_suite(Config) -> application:set_env(jose, crypto_fallback, true), application:set_env(jose, unsecured_signing, true), _ = application:ensure_all_started(jose), ct_property_test:init_per_suite(Config). end_per_suite(_Config) -> _ = application:stop(jose), ok. init_per_group(Group, Config) -> jose_ct:start(Group, Config). end_per_group(_Group, Config) -> jose_ct:stop(Config), ok. %%==================================================================== %% Tests %%==================================================================== aes_cbc_block_encrypt_and_cbc_block_decrypt(Config) -> ct_property_test:quickcheck( jose_jwa_aes_props:prop_cbc_block_encrypt_and_cbc_block_decrypt(), Config). aes_cbc_block_encrypt_and_jwa_block_decrypt(Config) -> ct_property_test:quickcheck( jose_jwa_aes_props:prop_cbc_block_encrypt_and_jwa_block_decrypt(), Config). aes_jwa_block_encrypt_and_cbc_block_decrypt(Config) -> ct_property_test:quickcheck( jose_jwa_aes_props:prop_jwa_block_encrypt_and_cbc_block_decrypt(), Config). aes_jwa_block_encrypt_and_ecb_block_decrypt(Config) -> ct_property_test:quickcheck( jose_jwa_aes_props:prop_jwa_block_encrypt_and_ecb_block_decrypt(), Config). aes_jwa_block_encrypt_and_gcm_block_decrypt(Config) -> ct_property_test:quickcheck( jose_jwa_aes_props:prop_jwa_block_encrypt_and_gcm_block_decrypt(), Config). aes_ecb_block_encrypt_and_ecb_block_decrypt(Config) -> ct_property_test:quickcheck( jose_jwa_aes_props:prop_ecb_block_encrypt_and_ecb_block_decrypt(), Config). aes_ecb_block_encrypt_and_jwa_block_decrypt(Config) -> ct_property_test:quickcheck( jose_jwa_aes_props:prop_ecb_block_encrypt_and_jwa_block_decrypt(), Config). aes_gcm_block_encrypt_and_gcm_block_decrypt(Config) -> ct_property_test:quickcheck( jose_jwa_aes_props:prop_gcm_block_encrypt_and_gcm_block_decrypt(), Config). aes_gcm_block_encrypt_and_jwa_block_decrypt(Config) -> ct_property_test:quickcheck( jose_jwa_aes_props:prop_gcm_block_encrypt_and_jwa_block_decrypt(), Config). %% See [https://tools.ietf.org/html/rfc3394#section-4.1] aes_kw_128_128(_Config) -> KEK = << 16#000102030405060708090A0B0C0D0E0F:1/unsigned-big-integer-unit:128 >>, KeyData = << 16#00112233445566778899AABBCCDDEEFF:1/unsigned-big-integer-unit:128 >>, CipherText = << 16#1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5:1/unsigned-big-integer-unit:192 >>, CipherText = jose_jwa_aes_kw:wrap(KeyData, KEK), KeyData = jose_jwa_aes_kw:unwrap(CipherText, KEK), true. %% See [https://tools.ietf.org/html/rfc3394#section-4.2] aes_kw_128_192(_Config) -> KEK = << 16#000102030405060708090A0B0C0D0E0F1011121314151617:1/unsigned-big-integer-unit:192 >>, KeyData = << 16#00112233445566778899AABBCCDDEEFF:1/unsigned-big-integer-unit:128 >>, CipherText = << 16#96778B25AE6CA435F92B5B97C050AED2468AB8A17AD84E5D:1/unsigned-big-integer-unit:192 >>, CipherText = jose_jwa_aes_kw:wrap(KeyData, KEK), KeyData = jose_jwa_aes_kw:unwrap(CipherText, KEK), true. %% See [https://tools.ietf.org/html/rfc3394#section-4.3] aes_kw_128_256(_Config) -> KEK = << 16#000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F:1/unsigned-big-integer-unit:256 >>, KeyData = << 16#00112233445566778899AABBCCDDEEFF:1/unsigned-big-integer-unit:128 >>, CipherText = << 16#64E8C3F9CE0F5BA263E9777905818A2A93C8191E7D6E8AE7:1/unsigned-big-integer-unit:192 >>, CipherText = jose_jwa_aes_kw:wrap(KeyData, KEK), KeyData = jose_jwa_aes_kw:unwrap(CipherText, KEK), true. %% See [https://tools.ietf.org/html/rfc3394#section-4.4] aes_kw_192_192(_Config) -> KEK = << 16#000102030405060708090A0B0C0D0E0F1011121314151617:1/unsigned-big-integer-unit:192 >>, KeyData = << 16#00112233445566778899AABBCCDDEEFF0001020304050607:1/unsigned-big-integer-unit:192 >>, CipherText = << 16#031D33264E15D33268F24EC260743EDCE1C6C7DDEE725A936BA814915C6762D2:1/unsigned-big-integer-unit:256 >>, CipherText = jose_jwa_aes_kw:wrap(KeyData, KEK), KeyData = jose_jwa_aes_kw:unwrap(CipherText, KEK), true. %% See [https://tools.ietf.org/html/rfc3394#section-4.5] aes_kw_192_256(_Config) -> KEK = << 16#000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F:1/unsigned-big-integer-unit:256 >>, KeyData = << 16#00112233445566778899AABBCCDDEEFF0001020304050607:1/unsigned-big-integer-unit:192 >>, CipherText = << 16#A8F9BC1612C68B3FF6E6F4FBE30E71E4769C8B80A32CB8958CD5D17D6B254DA1:1/unsigned-big-integer-unit:256 >>, CipherText = jose_jwa_aes_kw:wrap(KeyData, KEK), KeyData = jose_jwa_aes_kw:unwrap(CipherText, KEK), true. %% See [https://tools.ietf.org/html/rfc3394#section-4.6] aes_kw_256_256(_Config) -> KEK = << 16#000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F:1/unsigned-big-integer-unit:256 >>, KeyData = << 16#00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F:1/unsigned-big-integer-unit:256 >>, CipherText = << 16#28C9F404C4B810F4CBCCB35CFB87F8263F5786E2D80ED326CBC7F0E71A99F43BFB988B9B7A02DD21:2/unsigned-big-integer-unit:160 >>, CipherText = jose_jwa_aes_kw:wrap(KeyData, KEK), KeyData = jose_jwa_aes_kw:unwrap(CipherText, KEK), true. aes_kw_wrap_and_unwrap(Config) -> ct_property_test:quickcheck( jose_jwa_aes_kw_props:prop_wrap_and_unwrap(), Config). concat_kdf(Config) -> ct_property_test:quickcheck( jose_jwa_concat_kdf_props:prop_kdf(), Config). concat_kdf_keylen(Config) -> ct_property_test:quickcheck( jose_jwa_concat_kdf_props:prop_kdf_keylen(), Config). %% KeyDataLen = 0 used to loop forever while accumulating hash output. concat_kdf_keylen_bounds(_Config) -> OtherInfo = {<<"A128GCM">>, <<>>, <<>>}, <<>> = jose_jwa_concat_kdf:kdf(sha256, <<"z">>, OtherInfo, 0), <<_:1/bitstring>> = jose_jwa_concat_kdf:kdf(sha256, <<"z">>, OtherInfo, 1), <<_:257/bitstring>> = jose_jwa_concat_kdf:kdf(sha256, <<"z">>, OtherInfo, 257), try jose_jwa_concat_kdf:kdf(sha256, <<"z">>, OtherInfo, -1) of Other -> ct:fail({expected, badarg, got, Other}) catch error:{badarg, _} -> ok end. constant_time_compare(Config) -> ct_property_test:quickcheck( jose_jwa_props:prop_constant_time_compare(), Config). curve25519_eddsa_secret_to_public(Config) -> ct_property_test:quickcheck( jose_jwa_curve25519_props:prop_eddsa_secret_to_public(), Config). curve448_eddsa_secret_to_public(Config) -> ct_property_test:quickcheck( jose_jwa_curve448_props:prop_eddsa_secret_to_public(), Config). ed25519_sign_and_verify(Config) -> ct_property_test:quickcheck( jose_jwa_curve25519_props:prop_ed25519_sign_and_verify(), Config). ed25519ph_sign_and_verify(Config) -> ct_property_test:quickcheck( jose_jwa_curve25519_props:prop_ed25519ph_sign_and_verify(), Config). ed448_sign_and_verify(Config) -> ct_property_test:quickcheck( jose_jwa_curve448_props:prop_ed448_sign_and_verify(), Config). ed448ph_sign_and_verify(Config) -> ct_property_test:quickcheck( jose_jwa_curve448_props:prop_ed448ph_sign_and_verify(), Config). pkcs1_rsaes_oaep_encrypt_and_decrypt(Config) -> ct_property_test:quickcheck( jose_jwa_pkcs1_props:prop_rsaes_oaep_encrypt_and_decrypt(), Config). pkcs1_rsaes_oaep_encrypt_and_decrypt_with_label(Config) -> ct_property_test:quickcheck( jose_jwa_pkcs1_props:prop_rsaes_oaep_encrypt_and_decrypt_with_label(), Config). pkcs1_rsaes_pkcs1_encrypt_and_decrypt(Config) -> ct_property_test:quickcheck( jose_jwa_pkcs1_props:prop_rsaes_pkcs1_encrypt_and_decrypt(), Config). pkcs1_rsassa_pkcs1_sign_and_verify(Config) -> ct_property_test:quickcheck( jose_jwa_pkcs1_props:prop_rsassa_pkcs1_sign_and_verify(), Config). pkcs1_rsassa_pss_sign_and_verify(Config) -> ct_property_test:quickcheck( jose_jwa_pkcs1_props:prop_rsassa_pss_sign_and_verify(), Config). pkcs1_rsassa_pss_sign_and_verify_with_salt(Config) -> ct_property_test:quickcheck( jose_jwa_pkcs1_props:prop_rsassa_pss_sign_and_verify_with_salt(), Config). pkcs5_pbkdf1(Config) -> ct_property_test:quickcheck( jose_jwa_pkcs5_props:prop_pbkdf1(), Config). pkcs5_pbkdf1_iterations(Config) -> ct_property_test:quickcheck( jose_jwa_pkcs5_props:prop_pbkdf1_iterations(), Config). pkcs5_pbkdf2(Config) -> ct_property_test:quickcheck( jose_jwa_pkcs5_props:prop_pbkdf2(), Config). pkcs5_pbkdf2_iterations(Config) -> ct_property_test:quickcheck( jose_jwa_pkcs5_props:prop_pbkdf2_iterations(), Config). pkcs5_pbkdf2_iterations_keylen(Config) -> ct_property_test:quickcheck( jose_jwa_pkcs5_props:prop_pbkdf2_iterations_keylen(), Config). pkcs7_pad_and_unpad(Config) -> ct_property_test:quickcheck( jose_jwa_pkcs7_props:prop_pad_and_unpad(), Config). x25519_secret_to_public(Config) -> ct_property_test:quickcheck( jose_jwa_curve25519_props:prop_x25519_secret_to_public(), Config). x25519_shared_secret(Config) -> ct_property_test:quickcheck( jose_jwa_curve25519_props:prop_x25519_shared_secret(), Config). x448_secret_to_public(Config) -> ct_property_test:quickcheck( jose_jwa_curve448_props:prop_x448_secret_to_public(), Config). x448_shared_secret(Config) -> ct_property_test:quickcheck( jose_jwa_curve448_props:prop_x448_shared_secret(), Config). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/test/Dockerfile0000664000232200023220000000051715267717064022116 0ustar debalancedebalanceARG OTP_VERSION FROM hexpm/erlang:${OTP_VERSION} # Setup environment ENV LANG=C.UTF-8 TERM=xterm # Add dependencies RUN apk upgrade --update musl && \ apk add --no-cache autoconf automake bash bc build-base cmake curl git libtool make openssl python3 rsync unzip && \ rm -rf /var/cache/apk/* RUN mkdir /build WORKDIR /build potatosalad-erlang-jose-30ac019/test/jose_ct.erl0000664000232200023220000000456415267717064022264 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_ct). -include_lib("common_test/include/ct.hrl"). %% API -export([start/2]). -export([stop/1]). -export([progress_start/0]). -export([progress_stop/1]). %% Internal API -export([init/1]). %% Macros -define(RED, "\e[0;31m"). -define(GREEN, "\e[0;32m"). -define(YELLOW, "\e[0;33m"). -define(WHITE, "\e[0;37m"). -define(CYAN, "\e[0;36m"). -define(RESET, "\e[0m"). -define(TIME, 1000). % timer:seconds(1) start(Group, Config) -> Now = os:timestamp(), io:format(user, "~s[ ] ~s :: ~s~s", [?WHITE, format_utc_timestamp(Now), Group, ?RESET]), {ok, Progress} = progress_start(), [{jose_ct, {Progress, Now}} | Config]. stop(Config) -> Now = os:timestamp(), {Progress, Old} = ?config(jose_ct, Config), ok = progress_stop(Progress), Diff = timer:now_diff(Now, Old), io:format(user, "~s[OK] ~s :: ~s elapsed~s~n", [?GREEN, format_utc_timestamp(Now), format_elapsed_time(Diff), ?RESET]), ok. progress_start() -> Ref = erlang:make_ref(), {ok, Pid} = proc_lib:start(?MODULE, init, [{self(), Ref}]), {ok, {Ref, Pid}}. progress_stop({Ref, Pid}) -> Pid ! {stop, self(), Ref}, receive Ref -> ok after 1000 -> ok end. %% @private format_elapsed_time(USec) -> Micro = USec rem 1000000, Second = ((USec - Micro) div 1000000) rem 60, Minute = ((USec - (Second * 1000000)) div 6000000) rem 60, Hour = ((USec - (Minute * 6000000)) div 360000000) rem 24, io_lib:format("~2..0w:~2..0w:~2..0w.~6..0w", [Hour,Minute,Second,Micro]). %% @private format_utc_timestamp(TS = {_, _, Micro}) -> {{Year,Month,Day},{Hour,Minute,Second}} = calendar:now_to_universal_time(TS), io_lib:format("~4..0w-~2..0w-~2..0w ~2..0w:~2..0w:~2..0w.~6..0w", [Year,Month,Day,Hour,Minute,Second,Micro]). %% @private init({Parent, Ref}) -> process_flag(trap_exit, true), ok = proc_lib:init_ack(Parent, {ok, self()}), {ok, TRef} = timer:send_interval(?TIME, {tick, Ref}), loop(Ref, TRef). %% @private loop(Ref, TRef) -> receive {tick, Ref} -> io:format(user, "~s.~s", [?WHITE, ?RESET]), loop(Ref, TRef); {stop, Parent, Ref} when is_pid(Parent) -> io:format(user, "~n", []), _ = try timer:cancel(TRef) catch _:_ -> ok end, Parent ! Ref, exit(normal); Info -> io:format(user, "~n~s[~s] received unhandled message:~n~p~s~n", [?RED, ?MODULE, Info, ?RESET]), loop(Ref, TRef) end. potatosalad-erlang-jose-30ac019/test/cavp_SUITE_data/0000775000232200023220000000000015267717064023014 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/test/cavp_SUITE_data/hex.erl0000664000232200023220000000314415267717064024306 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 12 Aug 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(hex). %% API -export([bin_to_hex/1]). -export([hex_to_bin/1]). -define(HEX_TO_INT(C), case C of $0 -> 16#0; $1 -> 16#1; $2 -> 16#2; $3 -> 16#3; $4 -> 16#4; $5 -> 16#5; $6 -> 16#6; $7 -> 16#7; $8 -> 16#8; $9 -> 16#9; $a -> 16#A; $b -> 16#B; $c -> 16#C; $d -> 16#D; $e -> 16#E; $f -> 16#F; $A -> 16#A; $B -> 16#B; $C -> 16#C; $D -> 16#D; $E -> 16#E; $F -> 16#F end). -define(INT_TO_HEX(C), case C of 16#0 -> $0; 16#1 -> $1; 16#2 -> $2; 16#3 -> $3; 16#4 -> $4; 16#5 -> $5; 16#6 -> $6; 16#7 -> $7; 16#8 -> $8; 16#9 -> $9; 16#A -> $a; 16#B -> $b; 16#C -> $c; 16#D -> $d; 16#E -> $e; 16#F -> $f end). %%==================================================================== %% API functions %%==================================================================== bin_to_hex(Bin) -> << << (?INT_TO_HEX(V bsr 4)), (?INT_TO_HEX(V band 16#F)) >> || << V >> <= Bin >>. hex_to_bin(Hex) -> << << ((?HEX_TO_INT(X) bsl 4) + ?HEX_TO_INT(Y)) >> || << X, Y >> <= Hex >>. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/test/cavp_SUITE_data/fips_testvector.erl0000664000232200023220000001375115267717064026752 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 12 Aug 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(fips_testvector). %% API -export([from_binary/1]). -export([from_file/1]). -export([to_binary/1]). -export([to_file/2]). %%==================================================================== %% API functions %%==================================================================== from_binary(Binary) -> Lines = binary:split(Binary, [<< $\n >>, << $\r >>], [global, trim]), parse_lines(Lines, []). from_file(File) -> case file:read_file(File) of {ok, Binary} -> from_binary(Binary); ReadError -> ReadError end. to_binary(Vectors) when is_list(Vectors) -> << << (case Vector of {flag, Flag} -> << $[, Flag/binary, $], $\n >>; {option, {Key, Val}} -> << $[, Key/binary, $\s, $=, $\s, Val/binary, $], $\n >>; {token, Token} -> << Token/binary, $\n >>; {vector, {Key, Val}, hex} -> << Key/binary, $\s, $=, $\s, (hex:bin_to_hex(Val))/binary, $\n >>; {vector, {Key, Val}, int} -> << Key/binary, $\s, $=, $\s, (integer_to_binary(Val))/binary, $\n >>; {vector, {Key, Val}, raw} -> << Key/binary, $\s, $=, $\s, Val/binary, $\n >> end)/binary >> || Vector <- Vectors >>. to_file(File, State={_, _, _}) -> Binary = to_binary(State), file:write_file(File, Binary). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private parse_lines([], Acc) -> lists:reverse(Acc); parse_lines([Line | Lines], Acc) -> case parse_line(Line) of skip -> parse_lines(Lines, Acc); Term -> parse_lines(Lines, [Term | Acc]) end. %% @private parse_line(<< $#, _/binary >>) -> skip; parse_line(<< $\s, Rest/binary >>) -> parse_line(Rest); parse_line(<< $\t, Rest/binary >>) -> parse_line(Rest); parse_line(<< $[, Rest/binary >>) -> parse_option(Rest); parse_line(<<>>) -> skip; parse_line(Rest) -> parse_vector(Rest). %% @private parse_option(Rest) -> case binary:match(Rest, << $] >>) of {OptionEndPos, 1} -> case binary:match(Rest, << $= >>) of {EqualPos, 1} when EqualPos < OptionEndPos -> Key = parse_option_key(binary:part(Rest, 0, EqualPos), <<>>), Val = parse_option_val(binary:part(Rest, EqualPos + 1, OptionEndPos - EqualPos - 1), <<>>), {option, {Key, Val}}; _ -> Flag = binary:part(Rest, 0, OptionEndPos), {flag, Flag} end; _ -> erlang:error({badarg, [Rest]}) end. %% @private parse_option_key(<< $\s, Rest/binary >>, Key) -> parse_option_key(Rest, Key); parse_option_key(<< $\t, Rest/binary >>, Key) -> parse_option_key(Rest, Key); parse_option_key(<< C, Rest/binary >>, Key) -> parse_option_key(Rest, << Key/binary, C >>); parse_option_key(<<>>, Key) -> Key. %% @private parse_option_val(<< $\s, Rest/binary >>, Val) -> parse_option_val(Rest, Val); parse_option_val(<< $\t, Rest/binary >>, Val) -> parse_option_val(Rest, Val); parse_option_val(<< C, Rest/binary >>, Val) -> parse_option_val(Rest, << Val/binary, C >>); parse_option_val(<<>>, Val) -> Val. %% @private parse_vector(<< C, Rest/binary >>) when (C >= $A andalso C =< $Z) orelse (C >= $a andalso C =< $z) orelse (C >= $0 andalso C =< $9) -> parse_vector_key(Rest, << C >>); parse_vector(Rest) -> erlang:error({badarg, [Rest]}). %% @private parse_vector_key(<< $=, Rest/binary >>, Key) -> parse_vector_val(Rest, Key, <<>>, true); parse_vector_key(<< $\s, Rest/binary >>, Key) -> parse_vector_key(Rest, Key); parse_vector_key(<< $\t, Rest/binary >>, Key) -> parse_vector_key(Rest, Key); parse_vector_key(<< C, Rest/binary >>, Key) when (C >= $A andalso C =< $Z) orelse (C >= $a andalso C =< $z) orelse (C >= $0 andalso C =< $9) -> parse_vector_key(Rest, << Key/binary, C >>); parse_vector_key(<<>>, Key) -> {token, Key}; parse_vector_key(Rest, Key) -> erlang:error({badarg, [Rest, Key]}). %% @private parse_vector_val(<< $#, _/binary >>, Key = << C, O, U, N, T >>, Bin, true) when (C =:= $C orelse C =:= $c) andalso (O =:= $O orelse O =:= $o) andalso (U =:= $U orelse U =:= $u) andalso (N =:= $N orelse N =:= $n) andalso (T =:= $T orelse T =:= $t) -> Val = binary_to_integer(Bin), {vector, {Key, Val}, int}; parse_vector_val(<< $#, _/binary >>, Key, Hex, true) -> Val = hex:hex_to_bin(Hex), {vector, {Key, Val}, hex}; parse_vector_val(<< $#, _/binary >>, Key, Val, false) -> {vector, {Key, Val}, raw}; parse_vector_val(<< $\s, Rest/binary >>, Key, Val, true) -> parse_vector_val(Rest, Key, Val, true); parse_vector_val(<< $\t, Rest/binary >>, Key, Val, true) -> parse_vector_val(Rest, Key, Val, true); parse_vector_val(<< C, Rest/binary >>, Key, Val, true) when (C >= $A andalso C =< $F) orelse (C >= $a andalso C =< $f) orelse (C >= $0 andalso C =< $9) -> parse_vector_val(Rest, Key, << Val/binary, C >>, true); parse_vector_val(<< C, Rest/binary >>, Key, Val, _Hex) -> parse_vector_val(Rest, Key, << Val/binary, C >>, false); parse_vector_val(<<>>, Key = << C, O, U, N, T >>, Bin, true) when (C =:= $C orelse C =:= $c) andalso (O =:= $O orelse O =:= $o) andalso (U =:= $U orelse U =:= $u) andalso (N =:= $N orelse N =:= $n) andalso (T =:= $T orelse T =:= $t) -> Val = binary_to_integer(Bin), {vector, {Key, Val}, int}; parse_vector_val(<<>>, Key = << L, E, N >>, Bin, true) when (L =:= $L orelse L =:= $l) andalso (E =:= $E orelse E =:= $e) andalso (N =:= $N orelse N =:= $n) -> Val = binary_to_integer(Bin), {vector, {Key, Val}, int}; parse_vector_val(<<>>, Key, Hex, true) -> Val = hex:hex_to_bin(Hex), {vector, {Key, Val}, hex}; parse_vector_val(<<>>, Key, Val, false) -> {vector, {Key, Val}, raw}; parse_vector_val(Rest, Key, Val, Hex) -> erlang:error({badarg, [Rest, Key, Val, Hex]}). potatosalad-erlang-jose-30ac019/test/cavp_SUITE_data/emc_testvector.erl0000664000232200023220000001144415267717064026552 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 12 Aug 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(emc_testvector). %% API -export([from_binary/1]). -export([from_file/1]). -export([to_binary/1]). -export([to_file/2]). %%==================================================================== %% API functions %%==================================================================== from_binary(Binary) -> Lines = [Line || Line <- binary:split(Binary, [<< $\n >>, << $\r >>], [global, trim]), Line =/= <<>>], parse_lines(Lines, []). from_file(File) -> case file:read_file(File) of {ok, Binary} -> from_binary(Binary); ReadError -> ReadError end. to_binary(Vectors) when is_list(Vectors) -> << << (case Vector of divider -> << $\n, "# =============================================\n", $\n >>; {example, Example} -> ExampleLen = byte_size(Example), Bar = binary:copy(<<"=">>, ExampleLen), << $\n, "# ", Bar/binary, $\n, "# ", Example/binary, $\n, "# ", Bar/binary, $\n, $\n >>; {component, Component} -> ComponentLen = byte_size(Component), Bar = binary:copy(<<"-">>, ComponentLen), << $\n, "# ", Bar/binary, $\n, "# ", Component/binary, $\n, "# ", Bar/binary, $\n, $\n >>; {vector, {Key, Val}} -> Hex = hex:bin_to_hex(Val), HexLines = to_hex_lines(Hex, <<>>, []), HexBlocks = << << HexLine/binary, $\n >> || HexLine <- HexLines >>, << "# ", Key/binary, $:, $\n, HexBlocks/binary, $\n >> end)/binary >> || Vector <- Vectors >>. to_file(File, State={_, _, _}) -> Binary = to_binary(State), file:write_file(File, Binary). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private parse_lines([], Acc) -> lists:reverse(Acc); parse_lines([ << "# =======", _/binary >>, << "# Example", Example/binary >>, << "# =======", _/binary >> | Lines ], Acc) -> parse_lines(Lines, [{example, << "Example", Example/binary >>} | Acc]); parse_lines([ << "# ----", _/binary >>, << "# ", Component/binary >>, << "# ----", _/binary >> | Lines ], Acc) -> parse_lines(Lines, [{component, Component} | Acc]); parse_lines([ << " ----", _/binary >>, << "# ", Component/binary >>, << " ----", _/binary >> | Lines ], Acc) -> parse_lines(Lines, [{component, Component} | Acc]); parse_lines([<<"# =============================================">> | Lines], Acc) -> parse_lines(Lines, [divider | Acc]); parse_lines([<< "# ", Key/binary >> | Lines], Acc) when length(Acc) > 0 -> case parse_key(Key) of skip -> parse_lines(Lines, Acc); NewKey -> parse_vector(Lines, NewKey, <<>>, Acc) end; parse_lines([_Line | Lines], Acc) -> parse_lines(Lines, Acc). %% @private parse_key(Key) -> case binary:match(Key, << $: >>) of {Pos, 1} -> binary:part(Key, 0, Pos); nomatch -> skip end. %% @private parse_vector(Lines = [<< $#, _/binary >> | _], Key, Hex, Acc) -> Val = hex:hex_to_bin(Hex), parse_lines(Lines, [{vector, {Key, Val}} | Acc]); parse_vector(Lines = [<< "# ----", _/binary >> | _], Key, Hex, Acc) -> Val = hex:hex_to_bin(Hex), parse_lines(Lines, [{vector, {Key, Val}} | Acc]); parse_vector(Lines = [<< " ----", _/binary >> | _], Key, Hex, Acc) -> Val = hex:hex_to_bin(Hex), parse_lines(Lines, [{vector, {Key, Val}} | Acc]); parse_vector([HexLine | Lines], Key, Hex, Acc) -> case parse_vector_hexline(HexLine, Hex) of {ok, NewHex} -> Val = hex:hex_to_bin(NewHex), parse_lines([HexLine | Lines], [{vector, {Key, Val}} | Acc]); {next, NewHex} -> parse_vector(Lines, Key, NewHex, Acc) end. %% @private parse_vector_hexline(<< $\s, Rest/binary >>, Hex) -> parse_vector_hexline(Rest, Hex); parse_vector_hexline(<< C, Rest/binary >>, Hex) when (C >= $A andalso C =< $Z) orelse (C >= $a andalso C =< $z) orelse (C >= $0 andalso C =< $9) -> parse_vector_hexline(Rest, << Hex/binary, C >>); parse_vector_hexline(<<>>, Hex) -> {next, Hex}; parse_vector_hexline(Rest, Hex) -> erlang:error({badarg, [Rest, Hex]}). %% @private to_hex_lines(Rest, Line, Lines) when byte_size(Line) >= 48 -> to_hex_lines(Rest, <<>>, [Line | Lines]); to_hex_lines(<< A, B, Rest/binary >>, Line, Lines) -> to_hex_lines(Rest, << Line/binary, A, B, $\s >>, Lines); to_hex_lines(<<>>, <<>>, Lines) -> lists:reverse(Lines); to_hex_lines(<<>>, Line, Lines) -> lists:reverse([Line | Lines]). potatosalad-erlang-jose-30ac019/test/cavp_SUITE_data/fetch.erl0000664000232200023220000000446215267717064024617 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc Based on core_http_get.erl %%% See [https://github.com/ninenines/erlang.mk/blob/0eb54a71605a955df14c5df793ebe676c86259f9/core/core.mk] %%% @end %%% Created : 13 Aug 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(fetch). %% API -export([fetch/2]). %%==================================================================== %% API functions %%==================================================================== fetch(URL = "ftp" ++ _, OutputFile) -> ssl:start(), inets:start(), << "ftp://", HostPath/binary >> = list_to_binary(URL), [Host | Paths] = binary:split(HostPath, << $/ >>, [global]), [File | RevPath] = lists:reverse(Paths), Path = lists:reverse(RevPath), HostString = binary_to_list(Host), FileString = binary_to_list(File), PathString = lists:flatten([[binary_to_list(P), $/] || P <- Path]), case ftp:open(HostString) of {ok, Pid} -> case ftp:user(Pid, "anonymous", "") of ok -> case ftp:type(Pid, binary) of ok -> case ftp:cd(Pid, PathString) of ok -> case ftp:recv_bin(Pid, FileString) of {ok, Body} -> _ = try ftp:close(Pid) catch _:_ -> ok end, file:write_file(OutputFile, Body); RecvError -> _ = try ftp:close(Pid) catch _:_ -> ok end, RecvError end; CdError -> _ = try ftp:close(Pid) catch _:_ -> ok end, CdError end; TypeError -> _ = try ftp:close(Pid) catch _:_ -> ok end, TypeError end; UserError -> _ = try ftp:close(Pid) catch _:_ -> ok end, UserError end; OpenError -> OpenError end; fetch(URL = "http" ++ _, File) -> ssl:start(), inets:start(), case httpc:request(get, {URL, []}, [{autoredirect, true}], []) of {ok, {{_, 200, _}, _, Body}} -> file:write_file(File, Body); Error -> Error end. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/test/jose_SUITE.erl0000664000232200023220000010261615267717064022544 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_SUITE). -include_lib("common_test/include/ct.hrl"). -include("jose_public_key.hrl"). -include_lib("public_key/include/public_key.hrl"). -include("jose.hrl"). %% ct. -export([all/0]). -export([groups/0]). -export([init_per_suite/1]). -export([end_per_suite/1]). -export([init_per_group/2]). -export([end_per_group/2]). %% Tests. -export([jose_cfrg_curves_a_1/1]). -export([jose_cfrg_curves_a_2/1]). -export([jose_cfrg_curves_a_3/1]). -export([jose_cfrg_curves_a_4/1]). -export([jose_cfrg_curves_a_5/1]). -export([jose_cfrg_curves_a_6/1]). -export([jose_cfrg_curves_a_7/1]). -export([jose_ecdh_1pu_a/1]). -export([jwe_a_1/1]). -export([jwe_a_2/1]). -export([jwe_a_3/1]). -export([jwk_c/1]). -export([jwk_rsa_multi/1]). -export([jws_a_1/1]). -export([jws_a_2/1]). -export([jws_a_3/1]). -export([jws_a_4/1]). -export([jws_a_5/1]). -export([rfc7520_5_9/1]). %% Macros. -define(tv_ok(T, M, F, A, E), case erlang:apply(M, F, A) of E -> ok; T -> ct:fail({{M, F, A}, {expected, E}, {got, T}}) end). all() -> [ {group, jose_cfrg_curves}, {group, jose_ecdh_1pu}, {group, jose_jwe}, {group, jose_jwk}, {group, jose_jws}, {group, rfc7520} ]. groups() -> [ {jose_cfrg_curves, [parallel], [ jose_cfrg_curves_a_1, jose_cfrg_curves_a_2, jose_cfrg_curves_a_3, jose_cfrg_curves_a_4, jose_cfrg_curves_a_5, jose_cfrg_curves_a_6, jose_cfrg_curves_a_7 ]}, {jose_ecdh_1pu, [parallel], [ jose_ecdh_1pu_a ]}, {jose_jwe, [parallel], [ jwe_a_1, jwe_a_2, jwe_a_3 ]}, {jose_jwk, [parallel], [ jwk_c, jwk_rsa_multi ]}, {jose_jws, [parallel], [ jws_a_1, jws_a_2, jws_a_3, jws_a_4, jws_a_5 ]}, {rfc7520, [parallel], [ rfc7520_5_9 ]} ]. init_per_suite(Config) -> application:set_env(jose, crypto_fallback, true), application:set_env(jose, unsecured_signing, true), _ = application:ensure_all_started(jose), Config. end_per_suite(_Config) -> _ = application:stop(jose), ok. init_per_group(G=jose_cfrg_curves, Config) -> {ok, A1} = file:consult(data_file("jose_cfrg_curves/a.1.config", Config)), {ok, A3} = file:consult(data_file("jose_cfrg_curves/a.3.config", Config)), {ok, A4} = file:consult(data_file("jose_cfrg_curves/a.4.config", Config)), {ok, A5} = file:consult(data_file("jose_cfrg_curves/a.5.config", Config)), {ok, A6} = file:consult(data_file("jose_cfrg_curves/a.6.config", Config)), {ok, A7} = file:consult(data_file("jose_cfrg_curves/a.7.config", Config)), [{jose_cfrg_curves_a_1, A1}, {jose_cfrg_curves_a_3, A3}, {jose_cfrg_curves_a_4, A4}, {jose_cfrg_curves_a_5, A5}, {jose_cfrg_curves_a_6, A6}, {jose_cfrg_curves_a_7, A7} | jose_ct:start(G, Config)]; init_per_group(G=jose_ecdh_1pu, Config) -> {ok, A} = file:consult(data_file("jose_ecdh_1pu/a.config", Config)), [{jose_ecdh_1pu_a, A} | jose_ct:start(G, Config)]; init_per_group(G=jose_jwe, Config) -> {ok, A1} = file:consult(data_file("jwe/a.1.config", Config)), {ok, A2} = file:consult(data_file("jwe/a.2.config", Config)), {ok, A3} = file:consult(data_file("jwe/a.3.config", Config)), [{jwe_a_1, A1}, {jwe_a_2, A2}, {jwe_a_3, A3} | jose_ct:start(G, Config)]; init_per_group(G=jose_jwk, Config) -> {ok, C} = file:consult(data_file("jwk/c.config", Config)), [{jwk_c, C} | jose_ct:start(G, Config)]; init_per_group(G=jose_jws, Config) -> {ok, A1} = file:consult(data_file("jws/a.1.config", Config)), {ok, A2} = file:consult(data_file("jws/a.2.config", Config)), {ok, A3} = file:consult(data_file("jws/a.3.config", Config)), {ok, A4} = file:consult(data_file("jws/a.4.config", Config)), {ok, A5} = file:consult(data_file("jws/a.5.config", Config)), [{jws_a_1, A1}, {jws_a_2, A2}, {jws_a_3, A3}, {jws_a_4, A4}, {jws_a_5, A5} | jose_ct:start(G, Config)]; init_per_group(G=rfc7520, Config) -> {ok, V_5_9} = file:consult(data_file("rfc7520/5.9.config", Config)), [{rfc7520_5_9, V_5_9} | jose_ct:start(G, Config)]; init_per_group(Group, Config) -> jose_ct:start(Group, Config). end_per_group(_Group, Config) -> jose_ct:stop(Config), ok. %%==================================================================== %% Tests %%==================================================================== % CFRG ECDH and signatures in JOSE % A.1. Ed25519 private key % [https://tools.ietf.org/html/draft-ietf-jose-cfrg-curves-00#appendix-A.1] jose_cfrg_curves_a_1(Config) -> C = ?config(jose_cfrg_curves_a_1, Config), % A.1 A_1_JWK = jose_jwk:from_file(data_file("jose_cfrg_curves/a.1.jwk+json", Config)), A_1_Secret = hex:hex_to_bin(?config("a.1.secret", C)), A_1_PK = hex:hex_to_bin(?config("a.1.pk", C)), % A_1_SK = << A_1_Secret/binary, A_1_PK/binary >>, {_, #'jose_EdDSA25519PrivateKey'{ publicKey=#'jose_EdDSA25519PublicKey'{publicKey=A_1_PK}, privateKey=A_1_Secret }} = jose_jwk:to_key(A_1_JWK), {_, #'jose_EdDSA25519PublicKey'{publicKey=A_1_PK}} = jose_jwk:to_public_key(A_1_JWK), ok. % CFRG ECDH and signatures in JOSE % A.2. Ed25519 public key % [https://tools.ietf.org/html/draft-ietf-jose-cfrg-curves-00#appendix-A.2] jose_cfrg_curves_a_2(Config) -> % A.1 A_1_JWK = jose_jwk:from_file(data_file("jose_cfrg_curves/a.1.jwk+json", Config)), % A.2 A_2_JWK = jose_jwk:from_file(data_file("jose_cfrg_curves/a.2.jwk+json", Config)), A_2_JWK = jose_jwk:to_public(A_1_JWK), ok. % CFRG ECDH and signatures in JOSE % A.3. JWK thumbprint canonicalization % [https://tools.ietf.org/html/draft-ietf-jose-cfrg-curves-00#appendix-A.3] jose_cfrg_curves_a_3(Config) -> C = ?config(jose_cfrg_curves_a_3, Config), % A.1 A_1_JWK = jose_jwk:from_file(data_file("jose_cfrg_curves/a.1.jwk+json", Config)), % A.2 A_2_JWK = jose_jwk:from_file(data_file("jose_cfrg_curves/a.2.jwk+json", Config)), % A.3 A_3_JWK = jose_jwk:from_binary(?config("a.3.jwk+json", C)), A_3_THUMBPRINT_HEX = ?config("a.3.thumbprint+hex", C), A_3_THUMBPRINT = jose_jwa_base64url:encode(hex:hex_to_bin(A_3_THUMBPRINT_HEX)), A_3_THUMBPRINT = ?config("a.3.thumbprint+b64", C), A_3_THUMBPRINT = jose_jwk:thumbprint(A_1_JWK), A_3_THUMBPRINT = jose_jwk:thumbprint(A_2_JWK), A_3_THUMBPRINT = jose_jwk:thumbprint(A_3_JWK), ok. % CFRG ECDH and signatures in JOSE % A.4. Ed25519 Signing % [https://tools.ietf.org/html/draft-ietf-jose-cfrg-curves-00#appendix-A.4] jose_cfrg_curves_a_4(Config) -> C = ?config(jose_cfrg_curves_a_4, Config), % A.1 A_1_JWK = jose_jwk:from_file(data_file("jose_cfrg_curves/a.1.jwk+json", Config)), % A.4 A_4_PROTECTED = ?config("a.4.jws+json", C), A_4_JWS = jose_jws:from_binary(A_4_PROTECTED), A_4_JWS_B64 = ?config("a.4.jws+b64", C), A_4_TXT = ?config("a.4.txt", C), A_4_TXT_B64 = ?config("a.4.txt+b64", C), A_4_SIGNINGINPUT = ?config("a.4.signing-input", C), A_4_SIG = hex:hex_to_bin(?config("a.4.sig+hex", C)), A_4_SIG_B64 = ?config("a.4.sig+b64", C), A_4_SIG_COMPACT = ?config("a.4.sig+compact", C), A_4_TXT_B64 = jose_jwa_base64url:encode(A_4_TXT), A_4_SIGNINGINPUT = << A_4_JWS_B64/binary, $., A_4_TXT_B64/binary >>, A_4_SIGNINGINPUT = jose_jws:signing_input(A_4_TXT, A_4_JWS), %% Forcing the Protected header to be A_4_PROTECTED A_4_MAP=#{ <<"signature">> := A_4_SIG_B64 } = force_sign(A_1_JWK, A_4_TXT, A_4_PROTECTED, A_4_JWS), A_4_SIG = jose_jwa_base64url:decode(A_4_SIG_B64), {_, A_4_SIG_COMPACT} = jose_jws:compact(A_4_MAP), ok. % CFRG ECDH and signatures in JOSE % A.5. Ed25519 Validation % [https://tools.ietf.org/html/draft-ietf-jose-cfrg-curves-00#appendix-A.5] jose_cfrg_curves_a_5(Config) -> C = ?config(jose_cfrg_curves_a_5, Config), % A.1 A_1_JWK = jose_jwk:from_file(data_file("jose_cfrg_curves/a.1.jwk+json", Config)), % A.2 A_2_JWK = jose_jwk:from_file(data_file("jose_cfrg_curves/a.2.jwk+json", Config)), % A.4 A_5_SIG_COMPACT = ?config("a.5.sig+compact", C), A_5_JWS = jose_jws:from_binary(?config("a.5.jws+json", C)), A_5_PAYLOAD_DATA = ?config("a.5.txt", C), {true, A_5_PAYLOAD_DATA, A_5_JWS} = jose_jws:verify(A_1_JWK, A_5_SIG_COMPACT), {true, A_5_PAYLOAD_DATA, A_5_JWS} = jose_jws:verify(A_2_JWK, A_5_SIG_COMPACT), ok. % CFRG ECDH and signatures in JOSE % A.6. ECDH-ES with X25519 % [https://tools.ietf.org/html/draft-ietf-jose-cfrg-curves-00#appendix-A.6] jose_cfrg_curves_a_6(Config) -> C = ?config(jose_cfrg_curves_a_6, Config), % A.6 A_6_BOB_JWK = jose_jwk:from_binary(?config("a.6.bob-jwk+json", C)), A_6_BOB_Secret = hex:hex_to_bin(?config("a.6.bob-secret+hex", C)), A_6_BOB_PK = hex:hex_to_bin(?config("a.6.bob-pk+hex", C)), A_6_EPK_Secret = hex:hex_to_bin(?config("a.6.epk-secret+hex", C)), A_6_EPK_PK = hex:hex_to_bin(?config("a.6.epk-pk+hex", C)), A_6_EPK_JWK = jose_jwk:from_binary(?config("a.6.epk-jwk+json", C)), A_6_PROTECTED = ?config("a.6.jwe+json", C), A_6_JWE = jose_jwe:from_binary(A_6_PROTECTED), A_6_Z = hex:hex_to_bin(?config("a.6.z+hex", C)), A_6_BOB_SK = << A_6_BOB_Secret/binary, A_6_BOB_PK/binary >>, A_6_EPK_SK = << A_6_EPK_Secret/binary, A_6_EPK_PK/binary >>, A_6_BOB_S_JWK = jose_jwk:from_okp({'X25519', A_6_BOB_SK}), A_6_EPK_S_JWK = jose_jwk:from_okp({'X25519', A_6_EPK_SK}), {_, #'jose_X25519PrivateKey'{ publicKey=#'jose_X25519PublicKey'{publicKey=A_6_BOB_PK}, privateKey=A_6_BOB_Secret }} = jose_jwk:to_key(A_6_BOB_S_JWK), {_, #'jose_X25519PublicKey'{publicKey=A_6_BOB_PK}} = jose_jwk:to_public_key(A_6_BOB_S_JWK), {_, #'jose_X25519PublicKey'{publicKey=A_6_BOB_PK}} = jose_jwk:to_key(A_6_BOB_JWK), {_, #'jose_X25519PrivateKey'{ publicKey=#'jose_X25519PublicKey'{publicKey=A_6_EPK_PK}, privateKey=A_6_EPK_Secret }} = jose_jwk:to_key(A_6_EPK_S_JWK), {_, #'jose_X25519PublicKey'{publicKey=A_6_EPK_PK}} = jose_jwk:to_public_key(A_6_EPK_S_JWK), {_, #'jose_X25519PublicKey'{publicKey=A_6_EPK_PK}} = jose_jwk:to_key(A_6_EPK_JWK), A_6_Z = jose_jwk:shared_secret(A_6_BOB_JWK, A_6_EPK_S_JWK), A_6_Z = jose_jwk:shared_secret(A_6_EPK_JWK, A_6_BOB_S_JWK), A_6_TEXT = <<"Example of X25519 encryption">>, {_, A_6_ENC_MAP} = jose_jwe:block_encrypt({A_6_BOB_JWK, A_6_EPK_S_JWK}, A_6_TEXT, A_6_JWE), {_, A_6_ENC_COMPACT} = jose_jwe:compact(A_6_ENC_MAP), {A_6_TEXT, A_6_JWE} = jose_jwe:block_decrypt(A_6_BOB_S_JWK, A_6_ENC_MAP), {A_6_TEXT, A_6_JWE} = jose_jwe:block_decrypt(A_6_BOB_S_JWK, A_6_ENC_COMPACT), ok. % CFRG ECDH and signatures in JOSE % A.7. ECDH-ES with X448 % [https://tools.ietf.org/html/draft-ietf-jose-cfrg-curves-00#appendix-A.7] jose_cfrg_curves_a_7(Config) -> C = ?config(jose_cfrg_curves_a_7, Config), % A.7 A_7_BOB_JWK = jose_jwk:from_binary(?config("a.7.bob-jwk+json", C)), A_7_BOB_Secret = hex:hex_to_bin(?config("a.7.bob-secret+hex", C)), A_7_BOB_PK = hex:hex_to_bin(?config("a.7.bob-pk+hex", C)), A_7_EPK_Secret = hex:hex_to_bin(?config("a.7.epk-secret+hex", C)), A_7_EPK_PK = hex:hex_to_bin(?config("a.7.epk-pk+hex", C)), A_7_EPK_JWK = jose_jwk:from_binary(?config("a.7.epk-jwk+json", C)), A_7_PROTECTED = ?config("a.7.jwe+json", C), A_7_JWE = jose_jwe:from_binary(A_7_PROTECTED), A_7_Z = hex:hex_to_bin(?config("a.7.z+hex", C)), A_7_BOB_SK = << A_7_BOB_Secret/binary, A_7_BOB_PK/binary >>, A_7_EPK_SK = << A_7_EPK_Secret/binary, A_7_EPK_PK/binary >>, A_7_BOB_S_JWK = jose_jwk:from_okp({'X448', A_7_BOB_SK}), A_7_EPK_S_JWK = jose_jwk:from_okp({'X448', A_7_EPK_SK}), {_, #'jose_X448PrivateKey'{ publicKey=#'jose_X448PublicKey'{publicKey=A_7_BOB_PK}, privateKey=A_7_BOB_Secret }} = jose_jwk:to_key(A_7_BOB_S_JWK), {_, #'jose_X448PublicKey'{publicKey=A_7_BOB_PK}} = jose_jwk:to_public_key(A_7_BOB_S_JWK), {_, #'jose_X448PublicKey'{publicKey=A_7_BOB_PK}} = jose_jwk:to_key(A_7_BOB_JWK), {_, #'jose_X448PrivateKey'{ publicKey=#'jose_X448PublicKey'{publicKey=A_7_EPK_PK}, privateKey=A_7_EPK_Secret }} = jose_jwk:to_key(A_7_EPK_S_JWK), {_, #'jose_X448PublicKey'{publicKey=A_7_EPK_PK}} = jose_jwk:to_public_key(A_7_EPK_S_JWK), {_, #'jose_X448PublicKey'{publicKey=A_7_EPK_PK}} = jose_jwk:to_key(A_7_EPK_JWK), A_7_Z = jose_jwk:shared_secret(A_7_BOB_JWK, A_7_EPK_S_JWK), A_7_Z = jose_jwk:shared_secret(A_7_EPK_JWK, A_7_BOB_S_JWK), A_7_TEXT = <<"Example of X448 encryption">>, {_, A_7_ENC_MAP} = jose_jwe:block_encrypt({A_7_BOB_JWK, A_7_EPK_S_JWK}, A_7_TEXT, A_7_JWE), {_, A_7_ENC_COMPACT} = jose_jwe:compact(A_7_ENC_MAP), {A_7_TEXT, A_7_JWE} = jose_jwe:block_decrypt(A_7_BOB_S_JWK, A_7_ENC_MAP), {A_7_TEXT, A_7_JWE} = jose_jwe:block_decrypt(A_7_BOB_S_JWK, A_7_ENC_COMPACT), ok. % Public Key Authenticated Encryption for JOSE: ECDH-1PU % A. Example ECDH-1PU Key Agreement Computation with A256GCM % [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)), A_VSSK_JWK = jose_jwk:from_binary(?config("a.vssk.jwk+json", C)), A_UESK_JWK = jose_jwk:from_binary(?config("a.uesk.jwk+json", C)), A_JWE = jose_jwe:from_binary(?config("a.jwe+json", C)), A_ZE = hex:hex_to_bin(?config("a.ze+hex", C)), A_ZS = hex:hex_to_bin(?config("a.zs+hex", C)), A_Z = hex:hex_to_bin(?config("a.z+hex", C)), A_CEK = hex:hex_to_bin(?config("a.cek+hex", C)), A_ZE = jose_jwk:shared_secret(A_VSSK_JWK, A_UESK_JWK), A_ZS = jose_jwk:shared_secret(A_VSSK_JWK, A_USSK_JWK), A_ZS = jose_jwk:shared_secret(A_USSK_JWK, A_VSSK_JWK), A_Z = <>, {A_CEK, _} = jose_jwe:next_cek({A_VSSK_JWK, A_USSK_JWK, A_UESK_JWK}, A_JWE), A_CEK = jose_jwe:key_decrypt({A_USSK_JWK, A_VSSK_JWK, A_UESK_JWK}, <<>>, A_JWE), ok. % JSON Web Encryption (JWE) % A.1. Example JWE using RSAES-OAEP and AES GCM % [https://tools.ietf.org/html/rfc7516#appendix-A.1] jwe_a_1(Config) -> C = ?config(jwe_a_1, Config), % A.1 A_1_TXT = ?config("a.1.txt", C), % A.1.1 A_1_1_JWE_DATA = ?config("a.1.1.jwe+json", C), A_1_1_JWE_MAP = jose:decode(A_1_1_JWE_DATA), A_1_1_JWE = jose_jwe:from_binary(A_1_1_JWE_DATA), {_, A_1_1_JWE_MAP} = jose_jwe:to_map(A_1_1_JWE), A_1_1_JWE_DATA_B64 = ?config("a.1.1.jwe+json.b64", C), A_1_1_JWE_DATA_B64 = jose_jwa_base64url:encode(element(2, jose_jwe:to_binary(A_1_1_JWE))), % A.1.2 A_1_2_CEK = ?config("a.1.2.cek", C), % A.1.3 A_1_3_JWK_DATA = ?config("a.1.3.jwk+json", C), A_1_3_JWK_MAP = jose:decode(A_1_3_JWK_DATA), A_1_3_JWK = jose_jwk:from_binary(A_1_3_JWK_DATA), {_, A_1_3_JWK_MAP} = jose_jwk:to_map(A_1_3_JWK), A_1_3_CEK_ENCRYPTED = ?config("a.1.3.cek.encrypted", C), A_1_3_CEK_ENCRYPTED_B64 = ?config("a.1.3.cek.encrypted.b64", C), A_1_3_CEK_ENCRYPTED_B64 = jose_jwa_base64url:encode(A_1_3_CEK_ENCRYPTED), % A.1.4 A_1_4_IV = ?config("a.1.4.iv", C), A_1_4_IV_B64 = ?config("a.1.4.iv.b64", C), A_1_4_IV_B64 = jose_jwa_base64url:encode(A_1_4_IV), % A.1.5 A_1_5_AAD = ?config("a.1.5.aad", C), A_1_1_JWE_DATA_B64 = A_1_5_AAD, % A.1.6 A_1_6_CIPHER = ?config("a.1.6.txt.cipher", C), A_1_6_TAG = ?config("a.1.6.txt.tag", C), A_1_6_CIPHER_B64 = ?config("a.1.6.txt.cipher.b64", C), A_1_6_TAG_B64 = ?config("a.1.6.txt.tag.b64", C), A_1_6_CIPHER = jose_jwa_base64url:decode(A_1_6_CIPHER_B64), A_1_6_TAG = jose_jwa_base64url:decode(A_1_6_TAG_B64), % A.1.7 A_1_7_COMPACT = ?config("a.1.7.jwe+compact", C), {A_1_TXT, A_1_1_JWE} = jose_jwe:block_decrypt(A_1_3_JWK, A_1_7_COMPACT), % Roundtrip test A_1_7_MAP = jose_jwe:block_encrypt(A_1_3_JWK, A_1_TXT, A_1_2_CEK, A_1_4_IV, A_1_1_JWE), {A_1_TXT, A_1_1_JWE} = jose_jwe:block_decrypt(A_1_3_JWK, A_1_7_MAP), ok. % JSON Web Encryption (JWE) % A.2. Example JWE using RSAES-PKCS1-v1_5 and AES_128_CBC_HMAC_SHA_256 % [https://tools.ietf.org/html/rfc7516#appendix-A.2] jwe_a_2(Config) -> C = ?config(jwe_a_2, Config), % A.2 A_2_TXT = ?config("a.2.txt", C), % A.2.1 A_2_1_JWE_DATA = ?config("a.2.1.jwe+json", C), A_2_1_JWE_MAP = jose:decode(A_2_1_JWE_DATA), A_2_1_JWE = jose_jwe:from_binary(A_2_1_JWE_DATA), {_, A_2_1_JWE_MAP} = jose_jwe:to_map(A_2_1_JWE), A_2_1_JWE_DATA_B64 = ?config("a.2.1.jwe+json.b64", C), A_2_1_JWE_DATA_B64 = jose_jwa_base64url:encode(element(2, jose_jwe:to_binary(A_2_1_JWE))), % A.2.2 A_2_2_CEK = ?config("a.2.2.cek", C), % A.2.3 A_2_3_JWK_DATA = ?config("a.2.3.jwk+json", C), A_2_3_JWK_MAP = jose:decode(A_2_3_JWK_DATA), A_2_3_JWK = jose_jwk:from_binary(A_2_3_JWK_DATA), {_, A_2_3_JWK_MAP} = jose_jwk:to_map(A_2_3_JWK), A_2_3_CEK_ENCRYPTED = ?config("a.2.3.cek.encrypted", C), A_2_3_CEK_ENCRYPTED_B64 = ?config("a.2.3.cek.encrypted.b64", C), A_2_3_CEK_ENCRYPTED_B64 = jose_jwa_base64url:encode(A_2_3_CEK_ENCRYPTED), % A.2.4 A_2_4_IV = ?config("a.2.4.iv", C), A_2_4_IV_B64 = ?config("a.2.4.iv.b64", C), A_2_4_IV_B64 = jose_jwa_base64url:encode(A_2_4_IV), % A.2.5 A_2_5_AAD = ?config("a.2.5.aad", C), A_2_1_JWE_DATA_B64 = A_2_5_AAD, % A.2.6 A_2_6_CIPHER = ?config("a.2.6.txt.cipher", C), A_2_6_TAG = ?config("a.2.6.txt.tag", C), A_2_6_CIPHER_B64 = ?config("a.2.6.txt.cipher.b64", C), A_2_6_TAG_B64 = ?config("a.2.6.txt.tag.b64", C), A_2_6_CIPHER = jose_jwa_base64url:decode(A_2_6_CIPHER_B64), A_2_6_TAG = jose_jwa_base64url:decode(A_2_6_TAG_B64), % A.2.7 A_2_7_COMPACT = ?config("a.2.7.jwe+compact", C), {A_2_TXT, A_2_1_JWE} = jose_jwe:block_decrypt(A_2_3_JWK, A_2_7_COMPACT), % Roundtrip test A_2_7_MAP = jose_jwe:block_encrypt(A_2_3_JWK, A_2_TXT, A_2_2_CEK, A_2_4_IV, A_2_1_JWE), {A_2_TXT, A_2_1_JWE} = jose_jwe:block_decrypt(A_2_3_JWK, A_2_7_MAP), ok. % JSON Web Encryption (JWE) % A.3. Example JWE Using AES Key Wrap and AES_128_CBC_HMAC_SHA_256 % [https://tools.ietf.org/html/rfc7516#appendix-A.3] jwe_a_3(Config) -> C = ?config(jwe_a_3, Config), % A.3 A_3_TXT = ?config("a.3.txt", C), % A.3.1 A_3_1_JWE_DATA = ?config("a.3.1.jwe+json", C), A_3_1_JWE_MAP = jose:decode(A_3_1_JWE_DATA), A_3_1_JWE = jose_jwe:from_binary(A_3_1_JWE_DATA), {_, A_3_1_JWE_MAP} = jose_jwe:to_map(A_3_1_JWE), A_3_1_JWE_DATA_B64 = ?config("a.3.1.jwe+json.b64", C), A_3_1_JWE_DATA_B64 = jose_jwa_base64url:encode(element(2, jose_jwe:to_binary(A_3_1_JWE))), % A.3.2 A_3_2_CEK = ?config("a.3.2.cek", C), % A.3.3 A_3_3_JWK_DATA = ?config("a.3.3.jwk+json", C), A_3_3_JWK_MAP = jose:decode(A_3_3_JWK_DATA), A_3_3_JWK = jose_jwk:from_binary(A_3_3_JWK_DATA), {_, A_3_3_JWK_MAP} = jose_jwk:to_map(A_3_3_JWK), A_3_3_CEK_ENCRYPTED = ?config("a.3.3.cek.encrypted", C), A_3_3_CEK_ENCRYPTED_B64 = ?config("a.3.3.cek.encrypted.b64", C), A_3_3_CEK_ENCRYPTED_B64 = jose_jwa_base64url:encode(A_3_3_CEK_ENCRYPTED), % A.3.4 A_3_4_IV = ?config("a.3.4.iv", C), A_3_4_IV_B64 = ?config("a.3.4.iv.b64", C), A_3_4_IV_B64 = jose_jwa_base64url:encode(A_3_4_IV), % A.3.5 A_3_5_AAD = ?config("a.3.5.aad", C), A_3_1_JWE_DATA_B64 = A_3_5_AAD, % A.3.6 A_3_6_CIPHER = ?config("a.3.6.txt.cipher", C), A_3_6_TAG = ?config("a.3.6.txt.tag", C), A_3_6_CIPHER_B64 = ?config("a.3.6.txt.cipher.b64", C), A_3_6_TAG_B64 = ?config("a.3.6.txt.tag.b64", C), A_3_6_CIPHER = jose_jwa_base64url:decode(A_3_6_CIPHER_B64), A_3_6_TAG = jose_jwa_base64url:decode(A_3_6_TAG_B64), % A.3.7 A_3_7_COMPACT = ?config("a.3.7.jwe+compact", C), {A_3_TXT, A_3_1_JWE} = jose_jwe:block_decrypt(A_3_3_JWK, A_3_7_COMPACT), % Roundtrip test A_3_7_MAP = jose_jwe:block_encrypt(A_3_3_JWK, A_3_TXT, A_3_2_CEK, A_3_4_IV, A_3_1_JWE), {A_3_TXT, A_3_1_JWE} = jose_jwe:block_decrypt(A_3_3_JWK, A_3_7_MAP), ok. % JSON Web Key (JWK) % Appendix C. Example Encrypted RSA Private Key % [https://tools.ietf.org/html/rfc7517#appendix-C] jwk_c(Config) -> C = ?config(jwk_c, Config), % C.1 C_1_JSON_DATA = ?config("c.1.jwk+json", C), C_1_JSON = jose:decode(C_1_JSON_DATA), C_1_JWK = jose_jwk:from_file(data_file("jwk/c.1.jwk+json", Config)), {_, C_1_JSON} = jose_jwk:to_map(C_1_JWK), % C.2 C_2_JSON_DATA = ?config("c.2.jwe+json", C), C_2_JSON = jose:decode(C_2_JSON_DATA), C_2_JWE = jose_jwe:from_file(data_file("jwk/c.2.jwe+json", Config)), {_, C_2_JSON} = jose_jwe:to_map(C_2_JWE), C_2_B64_DATA = ?config("c.2.b64", C), C_2_B64_DATA = jose_jwa_base64url:encode(C_2_JSON_DATA), % C.3 C_3_CEK = ?config("c.3.cek", C), % C.4 C_4_TXT = ?config("c.4.txt", C), C_4_SALT = ?config("c.4.salt", C), C_4_SALT = << (maps:get(<<"alg">>, C_2_JSON))/binary, 0, (jose_jwa_base64url:decode(maps:get(<<"p2s">>, C_2_JSON)))/binary >>, C_4_DKEY = ?config("c.4.derivedkey", C), {ok, C_4_DKEY} = jose_jwa_pkcs5:pbkdf2({hmac, sha256}, C_4_TXT, C_4_SALT, maps:get(<<"p2c">>, C_2_JSON), 16), % C.5 C_5_EKEY = ?config("c.5.encryptedkey", C), {C_5_EKEY, _} = jose_jwe:key_encrypt(C_4_TXT, C_3_CEK, C_2_JWE), % C.6 C_6_IV = ?config("c.6.iv", C), % C.7 C_7_AAD = ?config("c.7.aad", C), C_7_AAD = C_2_JSON_DATA, % C.8 C_8_CIPHER_TXT = ?config("c.8.ciphertxt", C), C_8_CIPHER_TAG = ?config("c.8.ciphertag", C), %% Forcing the AAD data to be C_7_AAD C_8_ENC_MAP=#{ <<"ciphertext">> := C_8_CIPHER_TXT_B64, <<"tag">> := C_8_CIPHER_TAG_B64 } = force_block_encrypt(C_4_TXT, C_1_JSON_DATA, C_3_CEK, C_6_IV, C_7_AAD, C_2_JWE), C_8_CIPHER_TXT = jose_jwa_base64url:decode(C_8_CIPHER_TXT_B64), C_8_CIPHER_TAG = jose_jwa_base64url:decode(C_8_CIPHER_TAG_B64), % C.9 C_9_DATA = ?config("c.9.jwe+txt", C), {_, C_9_DATA} = jose_jwe:compact(C_8_ENC_MAP), %% Make sure decryption also works {C_1_JSON_DATA, _} = jose_jwe:block_decrypt(C_4_TXT, C_9_DATA), %% Encrypt and Decrypt {_, C_1_JWK} = jose_jwk:from_map(C_4_TXT, jose_jwk:to_map(C_4_TXT, C_2_JWE, C_1_JWK)), ok. jwk_rsa_multi(Config) -> JWK = jose_jwk:from_pem_file(data_file("rsa-multi.pem", Config)), PlainText = <<"I've Got a Lovely Bunch of Coconuts">>, Encrypted = jose_jwk:block_encrypt(PlainText, JWK), CompactEncrypted = jose_jwe:compact(Encrypted), {PlainText, _} = jose_jwk:block_decrypt(Encrypted, JWK), {PlainText, _} = jose_jwk:block_decrypt(CompactEncrypted, JWK), Message = <<"Secret Message">>, Signed = jose_jwk:sign(Message, JWK), CompactSigned = jose_jws:compact(Signed), {true, Message, _} = jose_jwk:verify(Signed, JWK), {true, Message, _} = jose_jwk:verify(CompactSigned, JWK), {_, Map} = jose_jwk:to_map(JWK), JWK = jose_jwk:from_map(Map), Password = <<"My Passphrase">>, PEM = element(2, jose_jwk:to_pem(JWK)), EncryptedPEM = element(2, jose_jwk:to_pem(Password, JWK)), JWK = jose_jwk:from_pem(PEM), JWK = jose_jwk:from_pem(Password, EncryptedPEM), JWK = jose_jwk:from_pem(jose_jwk:to_pem(JWK)), JWK = jose_jwk:from_pem(Password, jose_jwk:to_pem(Password, JWK)), {_, JWK} = jose_jwk:from_binary(Password, jose_jwk:to_binary(Password, JWK)), {_, JWK} = jose_jwk:from_binary(Password, jose_jwe:compact(jose_jwk:to_map(Password, JWK))), ok. % JSON Web Signature (JWS) % Appendix A.1. Example JWS Using HMAC SHA-256 % [https://tools.ietf.org/html/rfc7515#appendix-A.1] jws_a_1(Config) -> C = ?config(jws_a_1, Config), % A.1.1 A_1_1_JSON_DATA = ?config("a.1.1.jws+json", C), A_1_1_JSON = jose:decode(A_1_1_JSON_DATA), A_1_1_JWS = jose_jws:from_file(data_file("jws/a.1.1.jws+json", Config)), {_, A_1_1_JSON} = jose_jws:to_map(A_1_1_JWS), A_1_1_B64_DATA = ?config("a.1.1.b64", C), A_1_1_B64_DATA = jose_jwa_base64url:encode(A_1_1_JSON_DATA), A_1_1_PAYLOAD_DATA = ?config("a.1.1.payload", C), A_1_1_B64_PAYLOAD_DATA = ?config("a.1.1.payload-b64", C), A_1_1_B64_PAYLOAD_DATA = jose_jwa_base64url:encode(A_1_1_PAYLOAD_DATA), A_1_1_SIGNING_INPUT_DATA = ?config("a.1.1.signing-input", C), A_1_1_SIGNING_INPUT_DATA = << A_1_1_B64_DATA/binary, $., A_1_1_B64_PAYLOAD_DATA/binary >>, A_1_1_JWK = jose_jwk:from_file(data_file("jws/a.1.1.jwk+json", Config)), A_1_1_B64_SIGNATURE_DATA = ?config("a.1.1.signature-b64", C), %% Forcing the Protected header to be A_1_1_JSON_DATA A_1_1_MAP=#{ <<"signature">> := A_1_1_B64_SIGNATURE_DATA } = force_sign(A_1_1_JWK, A_1_1_PAYLOAD_DATA, A_1_1_JSON_DATA, A_1_1_JWS), A_1_1_COMPACT_DATA = ?config("a.1.1.compact", C), {_, A_1_1_COMPACT_DATA} = jose_jws:compact(A_1_1_MAP), % A.1.2 {true, A_1_1_PAYLOAD_DATA, A_1_1_JWS} = jose_jws:verify(A_1_1_JWK, A_1_1_MAP), {true, A_1_1_PAYLOAD_DATA, A_1_1_JWS} = jose_jws:verify(A_1_1_JWK, A_1_1_COMPACT_DATA), %% Sign and Verify {true, A_1_1_PAYLOAD_DATA, A_1_1_JWS} = jose_jwk:verify(jose_jwk:sign(A_1_1_PAYLOAD_DATA, A_1_1_JWS, A_1_1_JWK), A_1_1_JWK), ok. % JSON Web Signature (JWS) % Appendix A.2. Example JWS Using RSASSA-PKCS1-v1_5 SHA-256 % [https://tools.ietf.org/html/rfc7515#appendix-A.2] jws_a_2(Config) -> C = ?config(jws_a_2, Config), % A.2.1 A_2_1_JSON_DATA = ?config("a.2.1.jws+json", C), A_2_1_JSON = jose:decode(A_2_1_JSON_DATA), A_2_1_JWS = jose_jws:from_file(data_file("jws/a.2.1.jws+json", Config)), {_, A_2_1_JSON} = jose_jws:to_map(A_2_1_JWS), A_2_1_B64_DATA = ?config("a.2.1.b64", C), A_2_1_B64_DATA = jose_jwa_base64url:encode(A_2_1_JSON_DATA), A_2_1_PAYLOAD_DATA = ?config("a.2.1.payload", C), A_2_1_B64_PAYLOAD_DATA = ?config("a.2.1.payload-b64", C), A_2_1_B64_PAYLOAD_DATA = jose_jwa_base64url:encode(A_2_1_PAYLOAD_DATA), A_2_1_SIGNING_INPUT_DATA = ?config("a.2.1.signing-input", C), A_2_1_SIGNING_INPUT_DATA = << A_2_1_B64_DATA/binary, $., A_2_1_B64_PAYLOAD_DATA/binary >>, A_2_1_JWK = jose_jwk:from_file(data_file("jws/a.2.1.jwk+json", Config)), A_2_1_B64_SIGNATURE_DATA = ?config("a.2.1.signature-b64", C), %% Forcing the Protected header to be A_2_1_JSON_DATA A_2_1_MAP=#{ <<"signature">> := A_2_1_B64_SIGNATURE_DATA } = force_sign(A_2_1_JWK, A_2_1_PAYLOAD_DATA, A_2_1_JSON_DATA, A_2_1_JWS), A_2_1_COMPACT_DATA = ?config("a.2.1.compact", C), {_, A_2_1_COMPACT_DATA} = jose_jws:compact(A_2_1_MAP), % A.2.2 {true, A_2_1_PAYLOAD_DATA, A_2_1_JWS} = jose_jws:verify(A_2_1_JWK, A_2_1_MAP), {true, A_2_1_PAYLOAD_DATA, A_2_1_JWS} = jose_jws:verify(A_2_1_JWK, A_2_1_COMPACT_DATA), %% Sign and Verify {true, A_2_1_PAYLOAD_DATA, A_2_1_JWS} = jose_jwk:verify(jose_jwk:sign(A_2_1_PAYLOAD_DATA, A_2_1_JWS, A_2_1_JWK), A_2_1_JWK), ok. % JSON Web Signature (JWS) % Appendix A.3. Example JWS Using ECDSA P-256 SHA-256 % https://tools.ietf.org/html/rfc7515#appendix-A.3 jws_a_3(Config) -> C = ?config(jws_a_3, Config), % A.3.1 A_3_1_JSON_DATA = ?config("a.3.1.jws+json", C), A_3_1_JSON = jose:decode(A_3_1_JSON_DATA), A_3_1_JWS = jose_jws:from_file(data_file("jws/a.3.1.jws+json", Config)), {_, A_3_1_JSON} = jose_jws:to_map(A_3_1_JWS), A_3_1_B64_DATA = ?config("a.3.1.b64", C), A_3_1_B64_DATA = jose_jwa_base64url:encode(A_3_1_JSON_DATA), A_3_1_PAYLOAD_DATA = ?config("a.3.1.payload", C), A_3_1_B64_PAYLOAD_DATA = ?config("a.3.1.payload-b64", C), A_3_1_B64_PAYLOAD_DATA = jose_jwa_base64url:encode(A_3_1_PAYLOAD_DATA), A_3_1_SIGNING_INPUT_DATA = ?config("a.3.1.signing-input", C), A_3_1_SIGNING_INPUT_DATA = << A_3_1_B64_DATA/binary, $., A_3_1_B64_PAYLOAD_DATA/binary >>, A_3_1_JWK = jose_jwk:from_file(data_file("jws/a.3.1.jwk+json", Config)), A_3_1_B64_SIGNATURE_DATA = ?config("a.3.1.signature-b64", C), %% Forcing the Protected header to be A_3_1_JSON_DATA A_3_1_MAP=#{ <<"signature">> := A_3_1_B64_SIGNATURE_DATA_ALT } = force_sign(A_3_1_JWK, A_3_1_PAYLOAD_DATA, A_3_1_JSON_DATA, A_3_1_JWS), %% ECDSA produces non-matching signatures true = (A_3_1_B64_SIGNATURE_DATA =/= A_3_1_B64_SIGNATURE_DATA_ALT), A_3_1_COMPACT_DATA = ?config("a.3.1.compact", C), {_, A_3_1_COMPACT_DATA} = jose_jws:compact(A_3_1_MAP#{ <<"signature">> => A_3_1_B64_SIGNATURE_DATA }), % A.3.2 {true, A_3_1_PAYLOAD_DATA, A_3_1_JWS} = jose_jws:verify(A_3_1_JWK, A_3_1_MAP), {true, A_3_1_PAYLOAD_DATA, A_3_1_JWS} = jose_jws:verify(A_3_1_JWK, A_3_1_COMPACT_DATA), %% Sign and Verify {true, A_3_1_PAYLOAD_DATA, A_3_1_JWS} = jose_jwk:verify(jose_jwk:sign(A_3_1_PAYLOAD_DATA, A_3_1_JWS, A_3_1_JWK), A_3_1_JWK), ok. % JSON Web Signature (JWS) % Appendix A.4. Example JWS Using ECDSA P-521 SHA-512 % https://tools.ietf.org/html/rfc7515#appendix-A.4 jws_a_4(Config) -> C = ?config(jws_a_4, Config), % A.4.1 A_4_1_JSON_DATA = ?config("a.4.1.jws+json", C), A_4_1_JSON = jose:decode(A_4_1_JSON_DATA), A_4_1_JWS = jose_jws:from_file(data_file("jws/a.4.1.jws+json", Config)), {_, A_4_1_JSON} = jose_jws:to_map(A_4_1_JWS), A_4_1_B64_DATA = ?config("a.4.1.b64", C), A_4_1_B64_DATA = jose_jwa_base64url:encode(A_4_1_JSON_DATA), A_4_1_PAYLOAD_DATA = ?config("a.4.1.payload", C), A_4_1_B64_PAYLOAD_DATA = ?config("a.4.1.payload-b64", C), A_4_1_B64_PAYLOAD_DATA = jose_jwa_base64url:encode(A_4_1_PAYLOAD_DATA), A_4_1_SIGNING_INPUT_DATA = ?config("a.4.1.signing-input", C), A_4_1_SIGNING_INPUT_DATA = << A_4_1_B64_DATA/binary, $., A_4_1_B64_PAYLOAD_DATA/binary >>, A_4_1_JWK = jose_jwk:from_file(data_file("jws/a.4.1.jwk+json", Config)), A_4_1_B64_SIGNATURE_DATA = ?config("a.4.1.signature-b64", C), %% Forcing the Protected header to be A_4_1_JSON_DATA A_4_1_MAP=#{ <<"signature">> := A_4_1_B64_SIGNATURE_DATA_ALT } = force_sign(A_4_1_JWK, A_4_1_PAYLOAD_DATA, A_4_1_JSON_DATA, A_4_1_JWS), %% ECDSA produces non-matching signatures true = (A_4_1_B64_SIGNATURE_DATA =/= A_4_1_B64_SIGNATURE_DATA_ALT), A_4_1_COMPACT_DATA = ?config("a.4.1.compact", C), {_, A_4_1_COMPACT_DATA} = jose_jws:compact(A_4_1_MAP#{ <<"signature">> => A_4_1_B64_SIGNATURE_DATA }), % A.4.2 {true, A_4_1_PAYLOAD_DATA, A_4_1_JWS} = jose_jws:verify(A_4_1_JWK, A_4_1_MAP), {true, A_4_1_PAYLOAD_DATA, A_4_1_JWS} = jose_jws:verify(A_4_1_JWK, A_4_1_COMPACT_DATA), %% Sign and Verify {true, A_4_1_PAYLOAD_DATA, A_4_1_JWS} = jose_jwk:verify(jose_jwk:sign(A_4_1_PAYLOAD_DATA, A_4_1_JWS, A_4_1_JWK), A_4_1_JWK), ok. % JSON Web Signature (JWS) % Appendix A.5. Example Unsecured JWS % https://tools.ietf.org/html/rfc7515#appendix-A.5 jws_a_5(Config) -> C = ?config(jws_a_5, Config), % A.5 A_5_JSON_DATA = ?config("a.5.jws+json", C), A_5_JSON = jose:decode(A_5_JSON_DATA), A_5_JWS = jose_jws:from_file(data_file("jws/a.5.jws+json", Config)), {_, A_5_JSON} = jose_jws:to_map(A_5_JWS), A_5_B64_DATA = ?config("a.5.b64", C), A_5_B64_DATA = jose_jwa_base64url:encode(A_5_JSON_DATA), A_5_PAYLOAD_DATA = ?config("a.5.payload", C), A_5_B64_PAYLOAD_DATA = ?config("a.5.payload-b64", C), A_5_B64_PAYLOAD_DATA = jose_jwa_base64url:encode(A_5_PAYLOAD_DATA), A_5_SIGNING_INPUT_DATA = ?config("a.5.signing-input", C), A_5_SIGNING_INPUT_DATA = << A_5_B64_DATA/binary, $., A_5_B64_PAYLOAD_DATA/binary >>, %% Forcing the Protected header to be A_5_JSON_DATA A_5_MAP=#{ <<"signature">> := <<>> } = force_sign(none, A_5_PAYLOAD_DATA, A_5_JSON_DATA, A_5_JWS), A_5_COMPACT_DATA = ?config("a.5.compact", C), {_, A_5_COMPACT_DATA} = jose_jws:compact(A_5_MAP), {true, A_5_PAYLOAD_DATA, A_5_JWS} = jose_jws:verify(none, A_5_MAP), {true, A_5_PAYLOAD_DATA, A_5_JWS} = jose_jws:verify(none, A_5_COMPACT_DATA), %% Sign and Verify {true, A_5_PAYLOAD_DATA, A_5_JWS} = jose_jws:verify(none, jose_jws:sign(none, A_5_PAYLOAD_DATA, A_5_JWS)), ok. % Examples of Protecting Content Using JSON Object Signing and Encryption (JOSE) % 5.9. Compressed Content % https://tools.ietf.org/html/rfc7520#section-5.9 rfc7520_5_9(Config) -> C = ?config(rfc7520_5_9, Config), % 5.9.1 V_5_9_1_PLAIN_TEXT = ?config("figure.72", C), V_5_9_1_JWK = jose_jwk:from_binary(?config("figure.151", C)), % 5.9.2 V_5_9_2_COMPRESSED_PLAIN_TEXT = ?config("figure.162", C), V_5_9_1_PLAIN_TEXT = jose_jwe_zip:uncompress(jose_jwa_base64url:decode(V_5_9_2_COMPRESSED_PLAIN_TEXT), zlib), V_5_9_2_COMPRESSED_PLAIN_TEXT = jose_jwa_base64url:encode(jose_jwe_zip:compress(V_5_9_1_PLAIN_TEXT, zlib)), V_5_9_2_CEK = ?config("figure.163", C), V_5_9_2_IV = ?config("figure.164", C), % 5.9.3 V_5_9_3_ENCRYPTED_KEY = ?config("figure.165", C), {ALG, _} = jose_jwe_alg_aes_kw:from_map(#{<<"alg">> => <<"A128KW">>}), V_5_9_3_ENCRYPTED_KEY = jose_jwa_base64url:encode(element(1, jose_jwe_alg_aes_kw:key_encrypt(V_5_9_1_JWK, jose_jwa_base64url:decode(V_5_9_2_CEK), ALG))), V_5_9_2_CEK = jose_jwa_base64url:encode(jose_jwe_alg_aes_kw:key_decrypt(V_5_9_1_JWK, {undefined, undefined, jose_jwa_base64url:decode(V_5_9_3_ENCRYPTED_KEY)}, ALG)), % 5.9.4 V_5_9_4_JWE = jose_jwe:from_binary(?config("figure.166", C)), V_5_9_4_JWE_PROTECTED = ?config("figure.167", C), V_5_9_4_JWE = jose_jwe:from_binary(jose_jwa_base64url:decode(V_5_9_4_JWE_PROTECTED)), V_5_9_4_CIPHER_TEXT = ?config("figure.168", C), V_5_9_4_CIPHER_TAG = ?config("figure.169", C), % 5.9.5 V_5_9_5_JWE_COMPACT = ?config("figure.170", C), V_5_9_5_JWE_MAP = jose:decode(?config("figure.172", C)), V_5_9_4_CIPHER_TEXT = maps:get(<<"ciphertext">>, V_5_9_5_JWE_MAP), V_5_9_4_CIPHER_TAG = maps:get(<<"tag">>, V_5_9_5_JWE_MAP), {V_5_9_1_PLAIN_TEXT, V_5_9_4_JWE} = jose_jwe:block_decrypt(V_5_9_1_JWK, V_5_9_5_JWE_COMPACT), {V_5_9_1_PLAIN_TEXT, V_5_9_4_JWE} = jose_jwe:block_decrypt(V_5_9_1_JWK, V_5_9_5_JWE_MAP), % Roundtrip test {_, CIPHER_TEXT} = jose_jwe:compact(jose_jwe:block_encrypt(V_5_9_1_JWK, V_5_9_1_PLAIN_TEXT, jose_jwa_base64url:decode(V_5_9_2_CEK), jose_jwa_base64url:decode(V_5_9_2_IV), V_5_9_4_JWE)), {V_5_9_1_PLAIN_TEXT, V_5_9_4_JWE} = jose_jwe:block_decrypt(V_5_9_1_JWK, CIPHER_TEXT), ok. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private force_block_encrypt(Key, PlainText, CEK, IV, OverrideProtected, JWE=#jose_jwe{alg={ALGModule, ALG}, enc={ENCModule, ENC}}) -> {EncryptedKey, _} = ALGModule:key_encrypt(Key, CEK, ALG), Protected = jose_jwa_base64url:encode(OverrideProtected), {CipherText, CipherTag} = ENCModule:block_encrypt({Protected, maybe_compress(PlainText, JWE)}, CEK, IV, ENC), #{ <<"protected">> => Protected, <<"encrypted_key">> => jose_jwa_base64url:encode(EncryptedKey), <<"iv">> => jose_jwa_base64url:encode(IV), <<"ciphertext">> => jose_jwa_base64url:encode(CipherText), <<"tag">> => jose_jwa_base64url:encode(CipherTag) }. %% @private force_sign(Key, PlainText, OverrideProtected, #jose_jws{alg={ALGModule, ALG}}) -> Protected = jose_jwa_base64url:encode(OverrideProtected), Payload = jose_jwa_base64url:encode(PlainText), Message = << Protected/binary, $., Payload/binary >>, Signature = jose_jwa_base64url:encode(ALGModule:sign(Key, Message, ALG)), #{ <<"payload">> => Payload, <<"protected">> => Protected, <<"signature">> => Signature }. %% @private data_file(File, Config) -> filename:join([?config(data_dir, Config), File]). %% @private maybe_compress(PlainText, #jose_jwe{zip={Module, ZIP}}) -> Module:compress(PlainText, ZIP); maybe_compress(PlainText, _) -> PlainText. potatosalad-erlang-jose-30ac019/test/jose_jwt_SUITE.erl0000664000232200023220000000277215267717064023432 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwt_SUITE). -include_lib("common_test/include/ct.hrl"). -include("jose.hrl"). %% ct. -export([all/0]). -export([groups/0]). -export([init_per_suite/1]). -export([end_per_suite/1]). -export([init_per_group/2]). -export([end_per_group/2]). %% Tests. -export([from_map_and_to_map/1]). -export([encrypt_and_decrypt/1]). -export([sign_and_verify/1]). all() -> [ {group, jose_jwt} ]. groups() -> [ {jose_jwt, [parallel], [ from_map_and_to_map, encrypt_and_decrypt, sign_and_verify ]} ]. init_per_suite(Config) -> application:set_env(jose, crypto_fallback, true), application:set_env(jose, unsecured_signing, true), _ = application:ensure_all_started(jose), ct_property_test:init_per_suite(Config). end_per_suite(_Config) -> _ = application:stop(jose), ok. init_per_group(Group, Config) -> jose_ct:start(Group, Config). end_per_group(_Group, Config) -> jose_ct:stop(Config), ok. %%==================================================================== %% Tests %%==================================================================== from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jwt_props:prop_from_map_and_to_map(), Config). encrypt_and_decrypt(Config) -> ct_property_test:quickcheck( jose_jwt_props:prop_encrypt_and_decrypt(), Config). sign_and_verify(Config) -> ct_property_test:quickcheck( jose_jwt_props:prop_sign_and_verify(), Config). potatosalad-erlang-jose-30ac019/test/jose_jws_SUITE.erl0000664000232200023220000001145015267717064023422 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jws_SUITE). -include_lib("common_test/include/ct.hrl"). -include("jose.hrl"). %% ct. -export([all/0]). -export([groups/0]). -export([init_per_suite/1]). -export([end_per_suite/1]). -export([init_per_group/2]). -export([end_per_group/2]). %% Tests. -export([alg_ecdsa_from_map_and_to_map/1]). -export([alg_ecdsa_sign_and_verify/1]). -export([alg_ecdsa_verify_rejects_non_canonical_signature_length/1]). -export([alg_eddsa_from_map_and_to_map/1]). -export([alg_eddsa_sign_and_verify/1]). -export([alg_hmac_from_map_and_to_map/1]). -export([alg_hmac_sign_and_verify/1]). -export([alg_none_from_map_and_to_map/1]). -export([alg_none_sign_and_verify/1]). -export([alg_poly1305_from_map_and_to_map/1]). -export([alg_poly1305_sign_and_verify/1]). -export([alg_rsa_pkcs1_v1_5_from_map_and_to_map/1]). -export([alg_rsa_pkcs1_v1_5_sign_and_verify/1]). -export([alg_rsa_pss_from_map_and_to_map/1]). -export([alg_rsa_pss_sign_and_verify/1]). all() -> [ {group, jose_jws_alg_ecdsa}, {group, jose_jws_alg_eddsa}, {group, jose_jws_alg_hmac}, {group, jose_jws_alg_none}, {group, jose_jws_alg_poly1305}, {group, jose_jws_alg_rsa_pkcs1_v1_5}, {group, jose_jws_alg_rsa_pss} ]. groups() -> [ {jose_jws_alg_ecdsa, [parallel], [ alg_ecdsa_from_map_and_to_map, alg_ecdsa_sign_and_verify, alg_ecdsa_verify_rejects_non_canonical_signature_length ]}, {jose_jws_alg_eddsa, [parallel], [ alg_eddsa_from_map_and_to_map, alg_eddsa_sign_and_verify ]}, {jose_jws_alg_hmac, [parallel], [ alg_hmac_from_map_and_to_map, alg_hmac_sign_and_verify ]}, {jose_jws_alg_none, [parallel], [ alg_none_from_map_and_to_map, alg_none_sign_and_verify ]}, {jose_jws_alg_poly1305, [parallel], [ alg_poly1305_from_map_and_to_map, alg_poly1305_sign_and_verify ]}, {jose_jws_alg_rsa_pkcs1_v1_5, [parallel], [ alg_rsa_pkcs1_v1_5_from_map_and_to_map, alg_rsa_pkcs1_v1_5_sign_and_verify ]}, {jose_jws_alg_rsa_pss, [parallel], [ alg_rsa_pss_from_map_and_to_map, alg_rsa_pss_sign_and_verify ]} ]. init_per_suite(Config) -> application:set_env(jose, crypto_fallback, true), application:set_env(jose, unsecured_signing, true), {ok, _} = application:ensure_all_started(jose), ok = jose:crypto_fallback(true), ok = jose:unsecured_signing(true), ct_property_test:init_per_suite(Config). end_per_suite(_Config) -> _ = application:stop(jose), ok. init_per_group(Group, Config) -> jose_ct:start(Group, Config). end_per_group(_Group, Config) -> jose_ct:stop(Config), ok. %%==================================================================== %% Tests %%==================================================================== alg_ecdsa_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jws_alg_ecdsa_props:prop_from_map_and_to_map(), Config). alg_ecdsa_sign_and_verify(Config) -> ct_property_test:quickcheck( jose_jws_alg_ecdsa_props:prop_sign_and_verify(), Config). alg_ecdsa_verify_rejects_non_canonical_signature_length(Config) -> ct_property_test:quickcheck( jose_jws_alg_ecdsa_props:prop_verify_rejects_non_canonical_signature_length(), Config). alg_eddsa_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jws_alg_eddsa_props:prop_from_map_and_to_map(), Config). alg_eddsa_sign_and_verify(Config) -> ct_property_test:quickcheck( jose_jws_alg_eddsa_props:prop_sign_and_verify(), Config). alg_hmac_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jws_alg_hmac_props:prop_from_map_and_to_map(), Config). alg_hmac_sign_and_verify(Config) -> ct_property_test:quickcheck( jose_jws_alg_hmac_props:prop_sign_and_verify(), Config). alg_none_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jws_alg_none_props:prop_from_map_and_to_map(), Config). alg_none_sign_and_verify(Config) -> ct_property_test:quickcheck( jose_jws_alg_none_props:prop_sign_and_verify(), Config). alg_poly1305_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jws_alg_poly1305_props:prop_from_map_and_to_map(), Config). alg_poly1305_sign_and_verify(Config) -> ct_property_test:quickcheck( jose_jws_alg_poly1305_props:prop_sign_and_verify(), Config). alg_rsa_pkcs1_v1_5_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jws_alg_rsa_pkcs1_v1_5_props:prop_from_map_and_to_map(), Config). alg_rsa_pkcs1_v1_5_sign_and_verify(Config) -> ct_property_test:quickcheck( jose_jws_alg_rsa_pkcs1_v1_5_props:prop_sign_and_verify(), Config). alg_rsa_pss_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jws_alg_rsa_pss_props:prop_from_map_and_to_map(), Config). alg_rsa_pss_sign_and_verify(Config) -> ct_property_test:quickcheck( jose_jws_alg_rsa_pss_props:prop_sign_and_verify(), Config). potatosalad-erlang-jose-30ac019/test/test_helper.exs0000664000232200023220000000001715267717064023156 0ustar debalancedebalanceExUnit.start() potatosalad-erlang-jose-30ac019/test/jose_security_SUITE.erl0000664000232200023220000003663015267717064024475 0ustar debalancedebalance%% Security regressions through public APIs and the affected fallback paths. -module(jose_security_SUITE). -include_lib("common_test/include/ct.hrl"). -include_lib("public_key/include/public_key.hrl"). -include("jose.hrl"). -include("jose_public_key.hrl"). -export([all/0, init_per_suite/1, end_per_suite/1]). -export([gcm_tag_lengths/1, gcm_public_forgery/1, ecdsa_lengths/1, zip_public_limit/1, zip_authentication_failures/1, rsa_public_rejection/1, shared_secret_types/1, password_export_and_legacy_import/1, curve_providers/1]). %% Configuration mutations below must run serially and be restored. all() -> [gcm_tag_lengths, gcm_public_forgery, ecdsa_lengths, zip_public_limit, zip_authentication_failures, rsa_public_rejection, shared_secret_types, password_export_and_legacy_import, curve_providers]. init_per_suite(Config) -> {ok, _} = application:ensure_all_started(jose), Config. end_per_suite(_Config) -> ok = application:stop(jose). gcm_tag_lengths(_Config) -> lists:foreach(fun(Bits) -> Cipher = {aes_gcm, Bits}, Key = crypto:strong_rand_bytes(Bits div 8), IV = crypto:strong_rand_bytes(12), Plain = <<"authenticated plaintext">>, AAD = <<"protected">>, {CT, Tag} = jose_jwa_aes:block_encrypt(Cipher, Key, IV, {AAD, Plain}), Plain = jose_jwa_aes:block_decrypt(Cipher, Key, IV, {AAD, CT, Tag}), lists:foreach(fun(Len) -> Invalid = case Len < 16 of true -> binary:part(Tag, 0, Len); false -> <> end, error = jose_jwa_aes:block_decrypt(Cipher, Key, IV, {AAD, CT, Invalid}), error = jose_jwa:block_decrypt(Cipher, Key, IV, {AAD, CT, Invalid}) end, lists:seq(0, 15) ++ lists:seq(17, 32)), <> = Tag, error = jose_jwa_aes:block_decrypt(Cipher, Key, IV, {AAD, CT, <<(First bxor 1), Rest/binary>>}) end, [128, 192, 256]), ok. gcm_public_forgery(_Config) -> Cipher = {aes_gcm, 256}, JWK = jose_jwk:from_oct(crypto:strong_rand_bytes(32)), Plain = <<"victim plaintext">>, {Modules, Encrypted} = jose_jwe:block_encrypt(JWK, Plain, #{<<"alg">> => <<"dir">>, <<"enc">> => <<"A256GCM">>}), Original = ets:lookup_element(jose_jwa, {cipher, Cipher}, 2), try lists:foreach(fun(Provider) -> true = ets:insert(jose_jwa, {{cipher, Cipher}, Provider}), {Plain, _} = jose_jwe:block_decrypt(JWK, {Modules, Encrypted}), {_, Compact} = jose_jwe:compact({Modules, Encrypted}), {Plain, _} = jose_jwe:block_decrypt(JWK, Compact), Forged = Encrypted#{<<"ciphertext">> => b64(<<"attacker chosen ciphertext">>)}, %% Enumerate every possible 8-bit tag, with no key knowledge. lists:foreach(fun(Byte) -> Attack = Forged#{<<"tag">> => b64(<>)}, {error, _} = jose_jwe:block_decrypt(JWK, {Modules, Attack}) end, lists:seq(0, 255)), lists:foreach(fun(Len) -> {error, _} = jose_jwe:block_decrypt(JWK, {Modules, Forged#{<<"tag">> => b64(<<0:(Len * 8)>>)}}) end, [0, 2, 8, 12, 15, 16, 17, 32]) end, [Original, {jose_jwa_aes, Cipher}]) after true = ets:insert(jose_jwa, {{cipher, Cipher}, Original}) end, ok. ecdsa_lengths(_Config) -> lists:foreach(fun({Curve, Alg, Size}) -> Private = jose_jwk:generate_key({ec, Curve}), Public = jose_jwk:to_public(Private), Message = <<"unchanged signed payload">>, {Modules, Signed} = jose_jws:sign(Private, Message, #{<<"alg">> => Alg}), {true, Message, _} = jose_jws:verify(Public, {Modules, Signed}), Signature = jose_jwa_base64url:decode(maps:get(<<"signature">>, Signed)), Size = byte_size(Signature), Half = Size div 2, <> = Signature, lists:foreach(fun(Pad) -> Zeros = binary:copy(<<0>>, Pad), Attack = Signed#{<<"signature">> => b64(<>)}, {false, Message, _} = jose_jws:verify(Public, {Modules, Attack}), {_, Compact} = jose_jws:compact({Modules, Attack}), {false, Message, _} = jose_jws:verify(Public, Compact) end, [1, 2, 8]), lists:foreach(fun(Len) -> Attack = Signed#{<<"signature">> => b64(binary:part(Signature, 0, Len))}, {false, Message, _} = jose_jws:verify(Public, {Modules, Attack}) end, lists:seq(0, Size - 1)) end, [{<<"P-256">>, <<"ES256">>, 64}, {<<"P-384">>, <<"ES384">>, 96}, {<<"P-521">>, <<"ES512">>, 132}, {<<"secp256k1">>, <<"ES256K">>, 64}]), ok. zip_public_limit(_Config) -> Original = jose:zip_size_maximum(), Private = jose_jwk:generate_key({rsa, 2048}), Public = jose_jwk:to_public(Private), Header = #{<<"alg">> => <<"RSA-OAEP">>, <<"enc">> => <<"A256GCM">>, <<"zip">> => <<"DEF">>}, try %% Cross the safeInflate chunk boundary and accept the exact maximum. Limit = 32768, ok = jose:zip_size_maximum(Limit), Plain = binary:copy(<<0>>, Limit), {Plain, _} = jose_jwe:block_decrypt(Private, jose_jwe:block_encrypt(Public, Plain, Header)), Bomb = binary:copy(<<0>>, 16 * 1024 * 1024), {_, Compact} = jose_jwe:compact(jose_jwe:block_encrypt(Public, Bomb, Header)), ct:pal("public-key bomb: ~p bytes expand to ~p; cap ~p", [byte_size(Compact), byte_size(Bomb), Limit]), rejects(fun() -> jose_jwe:block_decrypt(Private, Compact) end), rejects(fun() -> jose_jwe_zip:uncompress(jose_jwe_zip:compress(<>, zlib), zlib) end), ok = jose:zip_size_maximum(0), <<>> = jose_jwe_zip:uncompress(jose_jwe_zip:compress(<<>>, zlib), zlib), rejects(fun() -> jose_jwe_zip:uncompress(jose_jwe_zip:compress(<<0>>, zlib), zlib) end), ok = jose:zip_size_maximum(Limit + 1), Larger = <>, Larger = jose_jwe_zip:uncompress(jose_jwe_zip:compress(Larger, zlib), zlib), %% Malformed and truncated streams must still fail; cleanup permits a next call. Compressed = jose_jwe_zip:compress(Plain, zlib), rejects(fun() -> jose_jwe_zip:uncompress(binary:part(Compressed, 0, byte_size(Compressed) - 2), zlib) end), Plain = jose_jwe_zip:uncompress(Compressed, zlib) after ok = jose:zip_size_maximum(Original) end, ok. %% Exercise both JWE AAD branches and authenticated cipher families. %% Setting the decompression cap below the plaintext makes an accidental %% decompression of any unauthenticated result observable as an exception. zip_authentication_failures(_Config) -> Original = jose:zip_size_maximum(), try lists:foreach(fun({Enc, Bytes}) -> Key = jose_jwk:from_oct(crypto:strong_rand_bytes(Bytes)), Header = #{<<"alg">> => <<"dir">>, <<"enc">> => Enc, <<"zip">> => <<"DEF">>}, Plain = <<"compressed authentication control">>, lists:foreach(fun(Block) -> ok = jose:zip_size_maximum(byte_size(Plain)), {Mods, Map} = jose_jwe:block_encrypt(Key, Block, Header), {Plain, _} = jose_jwe:block_decrypt(Key, {Mods, Map}), <> = jose_jwa_base64url:decode(maps:get(<<"tag">>, Map)), Attack = Map#{<<"tag">> => b64(<<(First bxor 1), Rest/binary>>)}, ok = jose:zip_size_maximum(0), {error, _} = jose_jwe:block_decrypt(Key, {Mods, Attack}), case maps:is_key(<<"aad">>, Map) of false -> {_, Compact} = jose_jwe:compact({Mods, Attack}), {error, _} = jose_jwe:block_decrypt(Key, Compact); true -> ok end end, [Plain, {<<"external aad">>, Plain}]) end, [{<<"A256GCM">>, 32}, {<<"A128CBC-HS256">>, 32}, {<<"A192CBC-HS384">>, 48}, {<<"A256CBC-HS512">>, 64}]) after ok = jose:zip_size_maximum(Original) end, ok. rsa_public_rejection(_Config) -> Private = jose_jwk:generate_key({rsa, 2048}), Public = jose_jwk:to_public(Private), Original = ets:lookup_element(jose_jwa, {rsa_crypt, rsa1_5}, 2), try lists:foreach(fun(Provider) -> true = ets:insert(jose_jwa, {{rsa_crypt, rsa1_5}, Provider}), lists:foreach(fun(Enc) -> Header = #{<<"alg">> => <<"RSA1_5">>, <<"enc">> => Enc}, Plain = <<"valid RSA1_5 plaintext">>, {Modules, Map} = jose_jwe:block_encrypt(Public, Plain, Header), {Plain, _} = jose_jwe:block_decrypt(Private, {Modules, Map}), EK = jose_jwa_base64url:decode(maps:get(<<"encrypted_key">>, Map)), K = byte_size(EK), JWE = jose_jwe:from_map(Header), ValidKey = jose_jwe:key_decrypt(Private, EK, JWE), %% Valid padding with a wrong CEK length must reject too. {_, PublicKey} = jose_jwk:to_key(Public), WrongSize = public_key:encrypt_public(<<"short">>, PublicKey, [{rsa_padding, rsa_pkcs1_padding}]), lists:foreach(fun(BadKey) -> Substitute = jose_jwe:key_decrypt(Private, BadKey, JWE), true = is_binary(Substitute), true = (byte_size(Substitute) =:= byte_size(ValidKey)), true = (Substitute =/= ValidKey), {error, _} = jose_jwe:block_decrypt(Private, {Modules, Map#{<<"encrypted_key">> => b64(BadKey)}}) end, [<<>>, <<0:(K * 8)>>, binary:copy(<<255>>, K), binary:part(EK, 0, K - 1), <>, WrongSize]) end, [<<"A256GCM">>, <<"A128CBC-HS256">>, <<"A192CBC-HS384">>, <<"A256CBC-HS512">>]) end, [Original, {jose_jwa_pkcs1, [{rsa_padding, rsa_pkcs1_padding}]}]) after true = ets:insert(jose_jwa, {{rsa_crypt, rsa1_5}, Original}) end, ok. shared_secret_types(_Config) -> My = jose_jwk:generate_key({ec, <<"P-256">>}), Peer = jose_jwk:generate_key({ec, <<"P-256">>}), Secret = jose_jwk:shared_secret(jose_jwk:to_public(Peer), My), Secret = jose_jwk:shared_secret(jose_jwk:to_public(My), Peer), RSA = jose_jwk:to_public(jose_jwk:generate_key({rsa, 1024})), {_, Map} = jose_jwk:to_map(RSA), lists:foreach(fun(Other) -> rejects(fun() -> jose_jwk:shared_secret(Other, My) end) end, [RSA, Map, jose_jwk:from_oct(<<0:256>>), jose_jwk:from_map(#{<<"keys">> => []})]), ok. password_export_and_legacy_import(_Config) -> Key = public_key:generate_key({namedCurve, secp256r1}), JWK = jose_jwk:from_key(Key), Password = <<"correct-horse">>, {_, DER} = jose_jwk:to_der(Password, JWK), #'EncryptedPrivateKeyInfo'{encryptionAlgorithm = Algorithm} = jose_public_key:der_decode(DER), Params = assert_pbes2(jose_public_key:decrypt_parameters(Algorithm)), JWK = jose_jwk:from_der(Password, DER), {_, PEM} = jose_jwk:to_pem(Password, JWK), [{'PrivateKeyInfo', _, Info}] = jose_public_key:pem_decode(PEM), _ = assert_pbes2(Info), JWK = jose_jwk:from_pem(Password, PEM), %% OTP must import the stronger export independently of our KDF path. Key = public_key:pem_entry_decode(hd(public_key:pem_decode(PEM)), binary_to_list(Password)), %% Make an independent legacy PKCS#8 PEM with OTP's serializer and the %% original 2048-iteration / 8-byte salt parameters. Never weaken exports. KDF = Params#'PBES2-params'.keyDerivationFunc, PBKDF2 = KDF#'PBES2-params_keyDerivationFunc'.parameters, LegacyKDF = KDF#'PBES2-params_keyDerivationFunc'{parameters = PBKDF2#'PBKDF2-params'{ salt = {specified, <<0:64>>}, iterationCount = 2048}}, Legacy = Params#'PBES2-params'{keyDerivationFunc = LegacyKDF}, lists:foreach(fun(OID) -> LegacyParams = LegacyKDF#'PBES2-params_keyDerivationFunc'.parameters, PRF = LegacyParams#'PBKDF2-params'.prf, DigestKDF = LegacyKDF#'PBES2-params_keyDerivationFunc'{parameters = LegacyParams#'PBKDF2-params'{ prf = PRF#'PBKDF2-params_prf'{algorithm = OID}}}, DigestParams = Legacy#'PBES2-params'{keyDerivationFunc = DigestKDF}, Entry = public_key:pem_entry_encode('PrivateKeyInfo', Key, {{"AES-256-CBC", DigestParams}, binary_to_list(Password)}), LegacyPEM = public_key:pem_encode([Entry]), JWK = jose_jwk:from_pem(Password, LegacyPEM) end, [?'id-hmacWithSHA1', ?'id-hmacWithSHA224', ?'id-hmacWithSHA256', ?'id-hmacWithSHA384', ?'id-hmacWithSHA512']), rejects(fun() -> jose_jwk:from_der(<<"wrong-password">>, DER) end), ok. curve_providers(_Config) -> %% Load the actual shipped app resource, independently of test configuration. AppPath = filename:join(filename:dirname(code:which(jose)), "jose.app"), {ok, [{application, jose, Properties}]} = file:consult(AppPath), false = proplists:get_value(crypto_fallback, proplists:get_value(env, Properties)), Previous = jose:crypto_fallback(), try ok = jose:crypto_fallback(false), true = (jose:curve25519_module() =/= jose_curve25519_unsupported), true = (jose:curve448_module() =/= jose_curve448_unsupported), lists:foreach(fun({Module, KeyPair, Shared}) -> {AP, AS} = Module:KeyPair(), {BP, BS} = Module:KeyPair(), AB = Module:Shared(AS, BP), AB = Module:Shared(BS, AP) end, [{jose_curve25519_crypto, x25519_keypair, x25519_shared_secret}, {jose_curve448_crypto, x448_keypair, x448_shared_secret}]), rejects(fun() -> jose_curve25519_fallback:x25519_shared_secret(<<0:256>>, <<9, 0:248>>) end), Scalar448 = <<0:448>>, Native448 = jose_curve448_crypto:x448_secret_to_public(Scalar448), Native448 = jose_curve448:x448_secret_to_public(Scalar448), %% Explicit fallback remains available for compatibility. This is %% a functional check, not evidence that bignum arithmetic is constant time. ok = jose:crypto_fallback(true), Scalar = <<0:256>>, Native = jose_curve25519_crypto:x25519_secret_to_public(Scalar), Native = jose_curve25519_fallback:x25519_secret_to_public(Scalar) after ok = jose:crypto_fallback(Previous) end, ok. assert_pbes2({_Cipher, Params = #'PBES2-params'{keyDerivationFunc = KDF}}) -> #'PBES2-params_keyDerivationFunc'{algorithm = ?'id-PBKDF2', parameters = #'PBKDF2-params'{salt = {specified, Salt}, iterationCount = Count, prf = #'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA256'}}} = KDF, true = (byte_size(Salt) >= 16), true = (Count >= 600000), Params. b64(Binary) -> jose_jwa_base64url:encode(Binary). %% Bound regressions that used to spin forever. Never accept binary plaintext %% or a parsed key as a rejection, even when a negative call returns normally. rejects(Fun) -> Parent = self(), {Pid, Ref} = spawn_monitor(fun() -> Result = try Fun() of {error, _} -> rejected; error -> rejected; Other -> {accepted, Other} catch _:_ -> rejected end, Parent ! {self(), Result} end), receive {Pid, rejected} -> erlang:demonitor(Ref, [flush]), ok; {Pid, Other} -> ct:fail(Other); {'DOWN', Ref, process, Pid, Reason} -> ct:fail({unexpected_exit, Reason}) after 3000 -> exit(Pid, kill), erlang:demonitor(Ref, [flush]), ct:fail(rejection_timeout) end. potatosalad-erlang-jose-30ac019/test/jose_jwe_SUITE.erl0000664000232200023220000002062115267717064023404 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_jwe_SUITE). -include_lib("common_test/include/ct.hrl"). -include("jose.hrl"). %% ct. -export([all/0]). -export([groups/0]). -export([init_per_suite/1]). -export([end_per_suite/1]). -export([init_per_group/2]). -export([end_per_group/2]). %% Tests. -export([alg_aes_kw_from_map_and_to_map/1]). -export([alg_aes_kw_key_encrypt_and_key_decrypt/1]). -export([alg_c20p_kw_from_map_and_to_map/1]). -export([alg_c20p_kw_key_encrypt_and_key_decrypt/1]). -export([alg_dir_from_map_and_to_map/1]). -export([alg_dir_key_decrypt/1]). -export([alg_dir_key_encrypt/1]). -export([alg_dir_next_cek/1]). -export([alg_ecdh_1pu_from_map_and_to_map/1]). -export([alg_ecdh_1pu_key_encrypt_and_key_decrypt/1]). -export([alg_ecdh_es_from_map_and_to_map/1]). -export([alg_ecdh_es_key_encrypt_and_key_decrypt/1]). -export([alg_pbes2_cve_2023_50966/1]). -export([alg_pbes2_from_map_and_to_map/1]). -export([alg_pbes2_key_encrypt_and_key_decrypt/1]). -export([alg_rsa_from_map_and_to_map/1]). -export([alg_rsa_ghsa_vwvr_f994_6j9j/1]). -export([zip_ghsa_wfpv_qxp8_x5gm/1]). -export([alg_rsa_key_encrypt_and_key_decrypt/1]). -export([alg_xc20p_kw_from_map_and_to_map/1]). -export([alg_xc20p_kw_key_encrypt_and_key_decrypt/1]). -export([enc_aes_from_map_and_to_map/1]). -export([enc_aes_block_encrypt_and_block_decrypt/1]). -export([enc_c20p_from_map_and_to_map/1]). -export([enc_c20p_block_encrypt_and_block_decrypt/1]). -export([enc_xc20p_from_map_and_to_map/1]). -export([enc_xc20p_block_encrypt_and_block_decrypt/1]). -export([zip_from_map_and_to_map/1]). -export([zip_block_encrypt_and_block_decrypt/1]). -export([zip_compress_and_uncompress/1]). all() -> [ {group, jose_jwe_alg_aes_kw}, {group, jose_jwe_alg_c20p_kw}, {group, jose_jwe_alg_dir}, {group, jose_jwe_alg_ecdh_1pu}, {group, jose_jwe_alg_ecdh_es}, {group, jose_jwe_alg_pbes2}, {group, jose_jwe_alg_rsa}, {group, jose_jwe_alg_xc20p_kw}, {group, jose_jwe_cve}, {group, jose_jwe_enc_aes}, {group, jose_jwe_enc_c20p}, {group, jose_jwe_enc_xc20p}, {group, jose_jwe_zip} ]. groups() -> [ {jose_jwe_alg_aes_kw, [parallel], [ alg_aes_kw_from_map_and_to_map, alg_aes_kw_key_encrypt_and_key_decrypt ]}, {jose_jwe_alg_c20p_kw, [parallel], [ alg_c20p_kw_from_map_and_to_map, alg_c20p_kw_key_encrypt_and_key_decrypt ]}, {jose_jwe_cve, [shuffle], [ alg_pbes2_cve_2023_50966, alg_rsa_ghsa_vwvr_f994_6j9j, zip_ghsa_wfpv_qxp8_x5gm ]}, {jose_jwe_alg_dir, [parallel], [ alg_dir_from_map_and_to_map, alg_dir_key_decrypt, alg_dir_key_encrypt, alg_dir_next_cek ]}, {jose_jwe_alg_ecdh_1pu, [parallel], [ alg_ecdh_1pu_from_map_and_to_map, alg_ecdh_1pu_key_encrypt_and_key_decrypt ]}, {jose_jwe_alg_ecdh_es, [parallel], [ alg_ecdh_es_from_map_and_to_map, alg_ecdh_es_key_encrypt_and_key_decrypt ]}, {jose_jwe_alg_pbes2, [parallel], [ alg_pbes2_from_map_and_to_map, alg_pbes2_key_encrypt_and_key_decrypt ]}, {jose_jwe_alg_rsa, [parallel], [ alg_rsa_from_map_and_to_map, alg_rsa_key_encrypt_and_key_decrypt ]}, {jose_jwe_alg_xc20p_kw, [parallel], [ alg_xc20p_kw_from_map_and_to_map, alg_xc20p_kw_key_encrypt_and_key_decrypt ]}, {jose_jwe_enc_aes, [parallel], [ enc_aes_from_map_and_to_map, enc_aes_block_encrypt_and_block_decrypt ]}, {jose_jwe_enc_c20p, [parallel], [ enc_c20p_from_map_and_to_map, enc_c20p_block_encrypt_and_block_decrypt ]}, {jose_jwe_enc_xc20p, [parallel], [ enc_xc20p_from_map_and_to_map, enc_xc20p_block_encrypt_and_block_decrypt ]}, {jose_jwe_zip, [parallel], [ zip_from_map_and_to_map, zip_block_encrypt_and_block_decrypt, zip_compress_and_uncompress ]} ]. init_per_suite(Config) -> application:set_env(jose, crypto_fallback, true), application:set_env(jose, unsecured_signing, true), _ = application:ensure_all_started(jose), ct_property_test:init_per_suite(Config). end_per_suite(_Config) -> _ = application:stop(jose), ok. init_per_group(Group, Config) -> jose_ct:start(Group, Config). end_per_group(_Group, Config) -> jose_ct:stop(Config), ok. %%==================================================================== %% Tests %%==================================================================== alg_aes_kw_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jwe_alg_aes_kw_props:prop_from_map_and_to_map(), Config). alg_aes_kw_key_encrypt_and_key_decrypt(Config) -> ct_property_test:quickcheck( jose_jwe_alg_aes_kw_props:prop_key_encrypt_and_key_decrypt(), Config). alg_c20p_kw_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jwe_alg_c20p_kw_props:prop_from_map_and_to_map(), Config). alg_c20p_kw_key_encrypt_and_key_decrypt(Config) -> ct_property_test:quickcheck( jose_jwe_alg_c20p_kw_props:prop_key_encrypt_and_key_decrypt(), Config). alg_dir_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jwe_alg_dir_props:prop_from_map_and_to_map(), Config). alg_dir_key_decrypt(Config) -> ct_property_test:quickcheck( jose_jwe_alg_dir_props:prop_key_decrypt(), Config). alg_dir_key_encrypt(Config) -> ct_property_test:quickcheck( jose_jwe_alg_dir_props:prop_key_encrypt(), Config). alg_dir_next_cek(Config) -> ct_property_test:quickcheck( jose_jwe_alg_dir_props:prop_next_cek(), Config). alg_ecdh_1pu_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jwe_alg_ecdh_1pu_props:prop_from_map_and_to_map(), Config). alg_ecdh_1pu_key_encrypt_and_key_decrypt(Config) -> ct_property_test:quickcheck( jose_jwe_alg_ecdh_1pu_props:prop_key_encrypt_and_key_decrypt(), Config). alg_ecdh_es_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jwe_alg_ecdh_es_props:prop_from_map_and_to_map(), Config). alg_ecdh_es_key_encrypt_and_key_decrypt(Config) -> ct_property_test:quickcheck( jose_jwe_alg_ecdh_es_props:prop_key_encrypt_and_key_decrypt(), Config). alg_pbes2_cve_2023_50966(Config) -> ct_property_test:quickcheck( jose_jwe_alg_pbes2_props:prop_cve_2023_50966(), Config). alg_pbes2_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jwe_alg_pbes2_props:prop_from_map_and_to_map(), Config). alg_pbes2_key_encrypt_and_key_decrypt(Config) -> ct_property_test:quickcheck( jose_jwe_alg_pbes2_props:prop_key_encrypt_and_key_decrypt(), Config). alg_rsa_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jwe_alg_rsa_props:prop_from_map_and_to_map(), Config). alg_rsa_ghsa_vwvr_f994_6j9j(Config) -> ct_property_test:quickcheck( jose_jwe_alg_rsa_props:prop_ghsa_vwvr_f994_6j9j(), Config). zip_ghsa_wfpv_qxp8_x5gm(Config) -> ct_property_test:quickcheck( jose_jwe_zip_props:prop_ghsa_wfpv_qxp8_x5gm(), Config). alg_rsa_key_encrypt_and_key_decrypt(Config) -> ct_property_test:quickcheck( jose_jwe_alg_rsa_props:prop_key_encrypt_and_key_decrypt(), Config). alg_xc20p_kw_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jwe_alg_xc20p_kw_props:prop_from_map_and_to_map(), Config). alg_xc20p_kw_key_encrypt_and_key_decrypt(Config) -> ct_property_test:quickcheck( jose_jwe_alg_xc20p_kw_props:prop_key_encrypt_and_key_decrypt(), Config). enc_aes_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jwe_enc_aes_props:prop_from_map_and_to_map(), Config). enc_aes_block_encrypt_and_block_decrypt(Config) -> ct_property_test:quickcheck( jose_jwe_enc_aes_props:prop_block_encrypt_and_block_decrypt(), Config). enc_c20p_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jwe_enc_c20p_props:prop_from_map_and_to_map(), Config). enc_c20p_block_encrypt_and_block_decrypt(Config) -> ct_property_test:quickcheck( jose_jwe_enc_c20p_props:prop_block_encrypt_and_block_decrypt(), Config). enc_xc20p_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jwe_enc_xc20p_props:prop_from_map_and_to_map(), Config). enc_xc20p_block_encrypt_and_block_decrypt(Config) -> ct_property_test:quickcheck( jose_jwe_enc_xc20p_props:prop_block_encrypt_and_block_decrypt(), Config). zip_from_map_and_to_map(Config) -> ct_property_test:quickcheck( jose_jwe_zip_props:prop_from_map_and_to_map(), Config). zip_block_encrypt_and_block_decrypt(Config) -> ct_property_test:quickcheck( jose_jwe_zip_props:prop_block_encrypt_and_block_decrypt(), Config). zip_compress_and_uncompress(Config) -> ct_property_test:quickcheck( jose_jwe_zip_props:prop_compress_and_uncompress(), Config). potatosalad-erlang-jose-30ac019/test/jose_test.exs0000664000232200023220000006400615267717064022647 0ustar debalancedebalancedefmodule JOSETest do use ExUnit.Case, async: false setup_all do JOSE.crypto_fallback(true) :ok end test "JOSE.JWA 128-bit encrypt and decrypt" do key = <<0::128>> cbc_iv = <<0::128>> gcm_iv = <<0::96>> aad = <<>> plain_text = "my plain text that will be encrypted and decrypted" padded_plain_text = :jose_jwa_pkcs7.pad(plain_text) cbc_cipher_text = <<199, 137, 180, 181, 237, 81, 30, 239, 12, 183, 48, 136, 189, 120, 32, 120, 2, 184, 140, 30, 193, 245, 216, 166, 134, 123, 91, 16, 96, 158, 102, 48, 174, 205, 240, 31, 66, 164, 135, 107, 142, 193, 158, 113, 111, 41, 201, 248, 18, 235, 208, 146, 39, 147, 167, 155, 213, 115, 66, 41, 32, 147, 133, 108>> ecb_cipher_text = <<199, 137, 180, 181, 237, 81, 30, 239, 12, 183, 48, 136, 189, 120, 32, 120, 196, 190, 232, 104, 49, 166, 58, 104, 4, 101, 23, 230, 131, 230, 216, 54, 111, 74, 1, 207, 251, 80, 216, 24, 126, 96, 68, 178, 160, 232, 54, 184, 174, 111, 184, 35, 145, 216, 208, 74, 42, 19, 166, 82, 247, 178, 75, 249>> gcm_cipher_text = <<110, 241, 250, 190, 12, 215, 202, 252, 211, 92, 167, 193, 5, 146, 138, 16, 150, 225, 138, 220, 32, 39, 53, 3, 149, 152, 169, 154, 250, 232, 179, 153, 80, 118, 116, 69, 110, 18, 250, 190, 0, 237, 211, 207, 162, 234, 219, 148, 172, 41, 172, 23, 31, 128, 39, 115, 117, 112, 178, 178, 199, 205, 134, 252>> gcm_cipher_tag = <<51, 155, 66, 18, 126, 201, 118, 185, 242, 41, 175, 75, 96, 213, 29, 68>> assert cbc_cipher_text == JOSE.JWA.block_encrypt({:aes_cbc, 128}, key, cbc_iv, padded_plain_text) assert plain_text == :jose_jwa_pkcs7.unpad(JOSE.JWA.block_decrypt({:aes_cbc, 128}, key, cbc_iv, cbc_cipher_text)) assert ecb_cipher_text == JOSE.JWA.block_encrypt({:aes_ecb, 128}, key, padded_plain_text) assert plain_text == :jose_jwa_pkcs7.unpad(JOSE.JWA.block_decrypt({:aes_ecb, 128}, key, ecb_cipher_text)) assert {gcm_cipher_text, gcm_cipher_tag} == JOSE.JWA.block_encrypt({:aes_gcm, 128}, key, gcm_iv, {aad, padded_plain_text}) assert plain_text == :jose_jwa_pkcs7.unpad(JOSE.JWA.block_decrypt({:aes_gcm, 128}, key, gcm_iv, {aad, gcm_cipher_text, gcm_cipher_tag})) end test "JOSE.JWA 192-bit encrypt and decrypt" do key = <<0::192>> cbc_iv = <<0::128>> gcm_iv = <<0::96>> aad = <<>> plain_text = "my plain text that will be encrypted and decrypted" padded_plain_text = :jose_jwa_pkcs7.pad(plain_text) cbc_cipher_text = <<49, 252, 5, 74, 231, 203, 35, 84, 241, 143, 161, 11, 238, 168, 150, 220, 5, 186, 188, 246, 39, 46, 14, 237, 8, 193, 241, 107, 82, 192, 36, 19, 53, 7, 75, 14, 27, 5, 84, 179, 141, 162, 74, 154, 7, 86, 106, 203, 149, 140, 92, 130, 21, 168, 122, 3, 174, 155, 120, 197, 130, 55, 103, 223>> ecb_cipher_text = <<49, 252, 5, 74, 231, 203, 35, 84, 241, 143, 161, 11, 238, 168, 150, 220, 199, 197, 40, 176, 120, 24, 25, 198, 250, 225, 235, 25, 140, 32, 110, 32, 11, 103, 244, 196, 171, 249, 227, 108, 87, 189, 94, 52, 92, 58, 79, 128, 169, 219, 180, 118, 180, 153, 232, 208, 144, 0, 5, 212, 7, 192, 23, 103>> gcm_cipher_text = <<245, 158, 4, 12, 107, 145, 151, 47, 60, 82, 27, 59, 240, 144, 130, 104, 75, 64, 179, 145, 11, 89, 130, 71, 188, 137, 237, 74, 85, 90, 73, 161, 141, 222, 114, 166, 237, 131, 108, 12, 222, 82, 132, 7, 152, 42, 81, 37, 183, 62, 208, 42, 184, 124, 230, 10, 12, 131, 73, 76, 18, 61, 3, 18>> gcm_cipher_tag = <<35, 252, 154, 36, 245, 70, 214, 141, 72, 99, 106, 35, 226, 195, 77, 212>> assert cbc_cipher_text == JOSE.JWA.block_encrypt({:aes_cbc, 192}, key, cbc_iv, padded_plain_text) assert plain_text == :jose_jwa_pkcs7.unpad(JOSE.JWA.block_decrypt({:aes_cbc, 192}, key, cbc_iv, cbc_cipher_text)) assert ecb_cipher_text == JOSE.JWA.block_encrypt({:aes_ecb, 192}, key, padded_plain_text) assert plain_text == :jose_jwa_pkcs7.unpad(JOSE.JWA.block_decrypt({:aes_ecb, 192}, key, ecb_cipher_text)) assert {gcm_cipher_text, gcm_cipher_tag} == JOSE.JWA.block_encrypt({:aes_gcm, 192}, key, gcm_iv, {aad, padded_plain_text}) assert plain_text == :jose_jwa_pkcs7.unpad(JOSE.JWA.block_decrypt({:aes_gcm, 192}, key, gcm_iv, {aad, gcm_cipher_text, gcm_cipher_tag})) end test "JOSE.JWA 256-bit encrypt and decrypt" do key = <<0::256>> cbc_iv = <<0::128>> gcm_iv = <<0::96>> aad = <<>> plain_text = "my plain text that will be encrypted and decrypted" padded_plain_text = :jose_jwa_pkcs7.pad(plain_text) cbc_cipher_text = <<203, 134, 178, 199, 240, 85, 211, 255, 152, 87, 193, 89, 160, 129, 80, 189, 223, 27, 211, 79, 247, 100, 28, 81, 198, 122, 151, 141, 179, 241, 149, 10, 252, 151, 150, 73, 95, 129, 227, 179, 158, 239, 118, 253, 99, 84, 37, 102, 255, 147, 113, 55, 174, 214, 3, 204, 67, 163, 185, 56, 180, 124, 27, 211>> ecb_cipher_text = <<203, 134, 178, 199, 240, 85, 211, 255, 152, 87, 193, 89, 160, 129, 80, 189, 59, 31, 176, 85, 123, 202, 110, 75, 65, 52, 218, 70, 130, 255, 90, 56, 44, 137, 185, 81, 14, 5, 40, 131, 196, 105, 44, 121, 10, 106, 53, 147, 77, 203, 1, 167, 110, 119, 19, 238, 140, 17, 112, 102, 230, 171, 149, 48>> gcm_cipher_text = <<163, 222, 96, 77, 33, 1, 2, 0, 39, 58, 160, 171, 206, 211, 233, 112, 19, 20, 35, 189, 94, 202, 70, 84, 179, 199, 213, 235, 27, 101, 71, 247, 173, 62, 212, 76, 109, 43, 143, 31, 97, 140, 60, 71, 53, 117, 70, 131, 34, 37, 197, 239, 143, 181, 113, 62, 111, 114, 19, 237, 165, 2, 52, 17>> gcm_cipher_tag = <<62, 158, 38, 76, 169, 81, 223, 217, 172, 83, 155, 21, 226, 51, 65, 230>> assert cbc_cipher_text == JOSE.JWA.block_encrypt({:aes_cbc, 256}, key, cbc_iv, padded_plain_text) assert plain_text == :jose_jwa_pkcs7.unpad(JOSE.JWA.block_decrypt({:aes_cbc, 256}, key, cbc_iv, cbc_cipher_text)) assert ecb_cipher_text == JOSE.JWA.block_encrypt({:aes_ecb, 256}, key, padded_plain_text) assert plain_text == :jose_jwa_pkcs7.unpad(JOSE.JWA.block_decrypt({:aes_ecb, 256}, key, ecb_cipher_text)) assert {gcm_cipher_text, gcm_cipher_tag} == JOSE.JWA.block_encrypt({:aes_gcm, 256}, key, gcm_iv, {aad, padded_plain_text}) assert plain_text == :jose_jwa_pkcs7.unpad(JOSE.JWA.block_decrypt({:aes_gcm, 256}, key, gcm_iv, {aad, gcm_cipher_text, gcm_cipher_tag})) end test "JOSE.JWE decode and encode" do map = %{"alg" => "dir", "enc" => "A128GCM"} binary = JOSE.encode(map) jwe = JOSE.JWE.from_map(map) assert map == :erlang.element(2, JOSE.JWE.to_map(jwe)) assert binary == :erlang.element(2, JOSE.JWE.to_binary(jwe)) assert jwe == JOSE.JWE.from_binary(binary) assert jwe == JOSE.JWE.from(jwe) if System.otp_release() >= "27" do # OTP json JOSE.json_module(:json) assert :jose_json_otp == JOSE.json_module() assert map == :erlang.element(2, JOSE.JWE.to_map(jwe)) assert binary == :erlang.element(2, JOSE.JWE.to_binary(jwe)) assert jwe == JOSE.JWE.from_binary(binary) assert jwe == JOSE.JWE.from(jwe) end # jsx # # jiffy # JOSE.json_module(:jiffy) # assert :jose_json_jiffy == JOSE.json_module() # assert map == :erlang.element(2, JOSE.JWE.to_map(jwe)) # assert binary == :erlang.element(2, JOSE.JWE.to_binary(jwe)) # assert jwe == JOSE.JWE.from_binary(binary) # assert jwe == JOSE.JWE.from(jwe) # jsone JOSE.json_module(:jsone) assert :jose_json_jsone == JOSE.json_module() assert map == :erlang.element(2, JOSE.JWE.to_map(jwe)) assert binary == :erlang.element(2, JOSE.JWE.to_binary(jwe)) assert jwe == JOSE.JWE.from_binary(binary) assert jwe == JOSE.JWE.from(jwe) # jsx JOSE.json_module(:jsx) assert :jose_json_jsx == JOSE.json_module() assert map == :erlang.element(2, JOSE.JWE.to_map(jwe)) assert binary == :erlang.element(2, JOSE.JWE.to_binary(jwe)) assert jwe == JOSE.JWE.from_binary(binary) assert jwe == JOSE.JWE.from(jwe) # thoas JOSE.json_module(:thoas) assert :jose_json_thoas == JOSE.json_module() assert map == :erlang.element(2, JOSE.JWE.to_map(jwe)) assert binary == :erlang.element(2, JOSE.JWE.to_binary(jwe)) assert jwe == JOSE.JWE.from_binary(binary) assert jwe == JOSE.JWE.from(jwe) # ojson JOSE.json_module(:ojson) assert :jose_json_ojson == JOSE.json_module() assert map == :erlang.element(2, JOSE.JWE.to_map(jwe)) assert binary == :erlang.element(2, JOSE.JWE.to_binary(jwe)) assert jwe == JOSE.JWE.from_binary(binary) assert jwe == JOSE.JWE.from(jwe) # Poison JOSE.json_module(Poison) assert :lists.member(JOSE.json_module(), [ :jose_json_poison, :jose_json_poison_compat_encoder, :jose_json_poison_lexical_encoder ]) assert map == :erlang.element(2, JOSE.JWE.to_map(jwe)) assert binary == :erlang.element(2, JOSE.JWE.to_binary(jwe)) assert jwe == JOSE.JWE.from_binary(binary) assert jwe == JOSE.JWE.from(jwe) # Jason JOSE.json_module(Jason) assert :jose_json_jason == JOSE.json_module() assert map == :erlang.element(2, JOSE.JWE.to_map(jwe)) assert binary == :erlang.element(2, JOSE.JWE.to_binary(jwe)) assert jwe == JOSE.JWE.from_binary(binary) assert jwe == JOSE.JWE.from(jwe) end test "JOSE.JWK decode and encode" do map = %{ "crv" => "P-256", "d" => "aJhYDBNS-5yrH97PAExzWNLlJGqJwFGZmv7iJvdG4p0", "kty" => "EC", "x" => "LksdLpZN3ijcn_TBfRK-_tgmvws0c5_V5k0bg14RLhU", "y" => "ukc-JOEAWhW664SY5Q29xHlAVEDlrQwYF3-vQ_cdi1s" } password = "password" binary = JOSE.encode(map) jwk = JOSE.JWK.from_map(map) assert map == :erlang.element(2, JOSE.JWK.to_map(jwk)) assert binary == :erlang.element(2, JOSE.JWK.to_binary(jwk)) assert jwk == JOSE.JWK.from_binary(binary) assert jwk == JOSE.JWK.from(jwk) assert jwk == JOSE.JWK.from_pem(JOSE.JWK.to_pem(jwk)) assert jwk == :erlang.element(2, JOSE.JWK.from_binary(password, JOSE.JWK.to_binary(password, jwk))) assert jwk == :erlang.element(2, JOSE.JWK.from_map(password, JOSE.JWK.to_map(password, jwk))) assert jwk == JOSE.JWK.from_pem(password, JOSE.JWK.to_pem(password, jwk)) # # jiffy # JOSE.json_module(:jiffy) # assert :jose_json_jiffy == JOSE.json_module() # assert map == :erlang.element(2, JOSE.JWK.to_map(jwk)) # assert binary == :erlang.element(2, JOSE.JWK.to_binary(jwk)) # assert jwk == JOSE.JWK.from_binary(binary) # assert jwk == JOSE.JWK.from(jwk) # assert jwk == JOSE.JWK.from_pem(JOSE.JWK.to_pem(jwk)) # assert jwk == :erlang.element(2, JOSE.JWK.from_binary(password, JOSE.JWK.to_binary(password, jwk))) # assert jwk == :erlang.element(2, JOSE.JWK.from_map(password, JOSE.JWK.to_map(password, jwk))) # assert jwk == JOSE.JWK.from_pem(password, JOSE.JWK.to_pem(password, jwk)) # jsone JOSE.json_module(:jsone) assert :jose_json_jsone == JOSE.json_module() assert map == :erlang.element(2, JOSE.JWK.to_map(jwk)) assert binary == :erlang.element(2, JOSE.JWK.to_binary(jwk)) assert jwk == JOSE.JWK.from_binary(binary) assert jwk == JOSE.JWK.from(jwk) assert jwk == JOSE.JWK.from_pem(JOSE.JWK.to_pem(jwk)) assert jwk == :erlang.element(2, JOSE.JWK.from_binary(password, JOSE.JWK.to_binary(password, jwk))) assert jwk == :erlang.element(2, JOSE.JWK.from_map(password, JOSE.JWK.to_map(password, jwk))) assert jwk == JOSE.JWK.from_pem(password, JOSE.JWK.to_pem(password, jwk)) # jsx JOSE.json_module(:jsx) assert :jose_json_jsx == JOSE.json_module() assert map == :erlang.element(2, JOSE.JWK.to_map(jwk)) assert binary == :erlang.element(2, JOSE.JWK.to_binary(jwk)) assert jwk == JOSE.JWK.from_binary(binary) assert jwk == JOSE.JWK.from(jwk) assert jwk == JOSE.JWK.from_pem(JOSE.JWK.to_pem(jwk)) assert jwk == :erlang.element(2, JOSE.JWK.from_binary(password, JOSE.JWK.to_binary(password, jwk))) assert jwk == :erlang.element(2, JOSE.JWK.from_map(password, JOSE.JWK.to_map(password, jwk))) assert jwk == JOSE.JWK.from_pem(password, JOSE.JWK.to_pem(password, jwk)) # thoas JOSE.json_module(:thoas) assert :jose_json_thoas == JOSE.json_module() assert map == :erlang.element(2, JOSE.JWK.to_map(jwk)) assert binary == :erlang.element(2, JOSE.JWK.to_binary(jwk)) assert jwk == JOSE.JWK.from_binary(binary) assert jwk == JOSE.JWK.from(jwk) assert jwk == JOSE.JWK.from_pem(JOSE.JWK.to_pem(jwk)) assert jwk == :erlang.element(2, JOSE.JWK.from_binary(password, JOSE.JWK.to_binary(password, jwk))) assert jwk == :erlang.element(2, JOSE.JWK.from_map(password, JOSE.JWK.to_map(password, jwk))) assert jwk == JOSE.JWK.from_pem(password, JOSE.JWK.to_pem(password, jwk)) # ojson JOSE.json_module(:ojson) assert :jose_json_ojson == JOSE.json_module() assert map == :erlang.element(2, JOSE.JWK.to_map(jwk)) assert binary == :erlang.element(2, JOSE.JWK.to_binary(jwk)) assert jwk == JOSE.JWK.from_binary(binary) assert jwk == JOSE.JWK.from(jwk) assert jwk == JOSE.JWK.from_pem(JOSE.JWK.to_pem(jwk)) assert jwk == :erlang.element(2, JOSE.JWK.from_binary(password, JOSE.JWK.to_binary(password, jwk))) assert jwk == :erlang.element(2, JOSE.JWK.from_map(password, JOSE.JWK.to_map(password, jwk))) assert jwk == JOSE.JWK.from_pem(password, JOSE.JWK.to_pem(password, jwk)) # Poison JOSE.json_module(Poison) assert :lists.member(JOSE.json_module(), [ :jose_json_poison, :jose_json_poison_compat_encoder, :jose_json_poison_lexical_encoder ]) assert map == :erlang.element(2, JOSE.JWK.to_map(jwk)) assert binary == :erlang.element(2, JOSE.JWK.to_binary(jwk)) assert jwk == JOSE.JWK.from_binary(binary) assert jwk == JOSE.JWK.from(jwk) assert jwk == JOSE.JWK.from_pem(JOSE.JWK.to_pem(jwk)) assert jwk == :erlang.element(2, JOSE.JWK.from_binary(password, JOSE.JWK.to_binary(password, jwk))) assert jwk == :erlang.element(2, JOSE.JWK.from_map(password, JOSE.JWK.to_map(password, jwk))) assert jwk == JOSE.JWK.from_pem(password, JOSE.JWK.to_pem(password, jwk)) # Jason JOSE.json_module(Jason) assert :jose_json_jason == JOSE.json_module() assert map == :erlang.element(2, JOSE.JWK.to_map(jwk)) assert binary == :erlang.element(2, JOSE.JWK.to_binary(jwk)) assert jwk == JOSE.JWK.from_binary(binary) assert jwk == JOSE.JWK.from(jwk) assert jwk == JOSE.JWK.from_pem(JOSE.JWK.to_pem(jwk)) assert jwk == :erlang.element(2, JOSE.JWK.from_binary(password, JOSE.JWK.to_binary(password, jwk))) assert jwk == :erlang.element(2, JOSE.JWK.from_map(password, JOSE.JWK.to_map(password, jwk))) assert jwk == JOSE.JWK.from_pem(password, JOSE.JWK.to_pem(password, jwk)) end test "JOSE.JWK display" do map = %{ "crv" => "P-256", "d" => "aJhYDBNS-5yrH97PAExzWNLlJGqJwFGZmv7iJvdG4p0", "kty" => "EC", "x" => "LksdLpZN3ijcn_TBfRK-_tgmvws0c5_V5k0bg14RLhU", "y" => "ukc-JOEAWhW664SY5Q29xHlAVEDlrQwYF3-vQ_cdi1s" } jwk = JOSE.JWK.from_map(map) stdout = inspect(jwk) assert match?("#JOSE.JWK<" <> _, stdout) refute String.contains?(stdout, "kty") end test "JOSE.JWS decode and encode" do map = %{"alg" => "HS256"} binary = JOSE.encode(map) jws = JOSE.JWS.from_map(map) assert map == :erlang.element(2, JOSE.JWS.to_map(jws)) assert binary == :erlang.element(2, JOSE.JWS.to_binary(jws)) assert jws == JOSE.JWS.from_binary(binary) assert jws == JOSE.JWS.from(jws) # # jiffy # JOSE.json_module(:jiffy) # assert :jose_json_jiffy == JOSE.json_module() # assert map == :erlang.element(2, JOSE.JWS.to_map(jws)) # assert binary == :erlang.element(2, JOSE.JWS.to_binary(jws)) # assert jws == JOSE.JWS.from_binary(binary) # assert jws == JOSE.JWS.from(jws) # jsone JOSE.json_module(:jsone) assert :jose_json_jsone == JOSE.json_module() assert map == :erlang.element(2, JOSE.JWS.to_map(jws)) assert binary == :erlang.element(2, JOSE.JWS.to_binary(jws)) assert jws == JOSE.JWS.from_binary(binary) assert jws == JOSE.JWS.from(jws) # jsx JOSE.json_module(:jsx) assert :jose_json_jsx == JOSE.json_module() assert map == :erlang.element(2, JOSE.JWS.to_map(jws)) assert binary == :erlang.element(2, JOSE.JWS.to_binary(jws)) assert jws == JOSE.JWS.from_binary(binary) assert jws == JOSE.JWS.from(jws) # thoas JOSE.json_module(:thoas) assert :jose_json_thoas == JOSE.json_module() assert map == :erlang.element(2, JOSE.JWS.to_map(jws)) assert binary == :erlang.element(2, JOSE.JWS.to_binary(jws)) assert jws == JOSE.JWS.from_binary(binary) assert jws == JOSE.JWS.from(jws) # ojson JOSE.json_module(:ojson) assert :jose_json_ojson == JOSE.json_module() assert map == :erlang.element(2, JOSE.JWS.to_map(jws)) assert binary == :erlang.element(2, JOSE.JWS.to_binary(jws)) assert jws == JOSE.JWS.from_binary(binary) assert jws == JOSE.JWS.from(jws) # Poison JOSE.json_module(Poison) assert :lists.member(JOSE.json_module(), [ :jose_json_poison, :jose_json_poison_compat_encoder, :jose_json_poison_lexical_encoder ]) assert map == :erlang.element(2, JOSE.JWS.to_map(jws)) assert binary == :erlang.element(2, JOSE.JWS.to_binary(jws)) assert jws == JOSE.JWS.from_binary(binary) assert jws == JOSE.JWS.from(jws) # Jason JOSE.json_module(Jason) assert :jose_json_jason == JOSE.json_module() assert map == :erlang.element(2, JOSE.JWS.to_map(jws)) assert binary == :erlang.element(2, JOSE.JWS.to_binary(jws)) assert jws == JOSE.JWS.from_binary(binary) assert jws == JOSE.JWS.from(jws) end test "JOSE.JWT decode and encode" do map = %{"test" => true} binary = JOSE.encode(map) jwt = JOSE.JWT.from_map(map) assert map == :erlang.element(2, JOSE.JWT.to_map(jwt)) assert binary == :erlang.element(2, JOSE.JWT.to_binary(jwt)) assert jwt == JOSE.JWT.from_binary(binary) assert jwt == JOSE.JWT.from(jwt) # # jiffy # JOSE.json_module(:jiffy) # assert :jose_json_jiffy == JOSE.json_module() # assert map == :erlang.element(2, JOSE.JWT.to_map(jwt)) # assert binary == :erlang.element(2, JOSE.JWT.to_binary(jwt)) # assert jwt == JOSE.JWT.from_binary(binary) # assert jwt == JOSE.JWT.from(jwt) # jsone JOSE.json_module(:jsone) assert :jose_json_jsone == JOSE.json_module() assert map == :erlang.element(2, JOSE.JWT.to_map(jwt)) assert binary == :erlang.element(2, JOSE.JWT.to_binary(jwt)) assert jwt == JOSE.JWT.from_binary(binary) assert jwt == JOSE.JWT.from(jwt) # jsx JOSE.json_module(:jsx) assert :jose_json_jsx == JOSE.json_module() assert map == :erlang.element(2, JOSE.JWT.to_map(jwt)) assert binary == :erlang.element(2, JOSE.JWT.to_binary(jwt)) assert jwt == JOSE.JWT.from_binary(binary) assert jwt == JOSE.JWT.from(jwt) # thoas JOSE.json_module(:thoas) assert :jose_json_thoas == JOSE.json_module() assert map == :erlang.element(2, JOSE.JWT.to_map(jwt)) assert binary == :erlang.element(2, JOSE.JWT.to_binary(jwt)) assert jwt == JOSE.JWT.from_binary(binary) assert jwt == JOSE.JWT.from(jwt) # ojson JOSE.json_module(:ojson) assert :jose_json_ojson == JOSE.json_module() assert map == :erlang.element(2, JOSE.JWT.to_map(jwt)) assert binary == :erlang.element(2, JOSE.JWT.to_binary(jwt)) assert jwt == JOSE.JWT.from_binary(binary) assert jwt == JOSE.JWT.from(jwt) # Poison JOSE.json_module(Poison) assert :lists.member(JOSE.json_module(), [ :jose_json_poison, :jose_json_poison_compat_encoder, :jose_json_poison_lexical_encoder ]) assert map == :erlang.element(2, JOSE.JWT.to_map(jwt)) assert binary == :erlang.element(2, JOSE.JWT.to_binary(jwt)) assert jwt == JOSE.JWT.from_binary(binary) assert jwt == JOSE.JWT.from(jwt) # Jason JOSE.json_module(Jason) assert :jose_json_jason == JOSE.json_module() assert map == :erlang.element(2, JOSE.JWT.to_map(jwt)) assert binary == :erlang.element(2, JOSE.JWT.to_binary(jwt)) assert jwt == JOSE.JWT.from_binary(binary) assert jwt == JOSE.JWT.from(jwt) end test "unsecured signing/verifying" do JOSE.unsecured_signing(true) jwk = JOSE.JWK.generate_key(16) jws = %{"alg" => "HS256"} jws_unsecure = %{"alg" => "none"} jwt = JOSE.JWT.from_map(%{"test" => true}) {_, token} = JOSE.JWS.compact(JOSE.JWT.sign(jwk, jws, jwt)) {_, token_unsecure} = JOSE.JWS.compact(JOSE.JWT.sign(jwk, jws_unsecure, jwt)) assert :erlang.element(1, JOSE.JWT.verify(jwk, token)) == true assert :erlang.element(1, JOSE.JWT.verify(jwk, token_unsecure)) == true JOSE.unsecured_signing(false) assert :erlang.element(1, JOSE.JWT.verify(jwk, token)) == true assert :erlang.element(1, JOSE.JWT.verify(jwk, token_unsecure)) == false end test "verify strict" do JOSE.unsecured_signing(true) jwk = JOSE.JWK.generate_key(16) jws = %{"alg" => "HS256"} jws_unsecure = %{"alg" => "none"} jwt = JOSE.JWT.from_map(%{"test" => true}) {_, token} = JOSE.JWS.compact(JOSE.JWT.sign(jwk, jws, jwt)) {_, token_unsecure} = JOSE.JWS.compact(JOSE.JWT.sign(jwk, jws_unsecure, jwt)) assert :erlang.element(1, JOSE.JWT.verify_strict(jwk, ["HS256"], token)) == true assert :erlang.element(1, JOSE.JWT.verify_strict(jwk, ["HS256"], token_unsecure)) == false JOSE.unsecured_signing(false) assert :erlang.element(1, JOSE.JWT.verify_strict(jwk, ["HS256"], token)) == true assert :erlang.element(1, JOSE.JWT.verify_strict(jwk, ["HS256"], token_unsecure)) == false end test "JSON test vectors" do vectors = [ {%{}, "{}"}, {[], "[]"}, {"", "\"\""}, {1, "1"}, {[1, 2, 3], "[1,2,3]"}, {%{"c" => 3, "b" => 2, "a" => [1, 2, 3]}, "{\"a\":[1,2,3],\"b\":2,\"c\":3}"}, {%{"a" => %{"z" => 1, "y" => 2, "x" => 3}, "b" => 4}, "{\"a\":{\"x\":3,\"y\":2,\"z\":1},\"b\":4}"}, {true, "true"}, {false, "false"} ] for {term, json} <- vectors do # assert :jose_json_jiffy.encode(term) == json # assert :jose_json_jiffy.decode(json) == term assert :jose_json_jsone.encode(term) == json assert :jose_json_jsone.decode(json) == term assert :jose_json_jsx.encode(term) == json assert :jose_json_jsx.decode(json) == term assert :jose_json_thoas.encode(term) == json assert :jose_json_thoas.decode(json) == term assert :jose_json_poison_compat_encoder.encode(term) == json assert :jose_json_poison_compat_encoder.decode(json) == term assert :jose_json_poison_lexical_encoder.encode(term) == json assert :jose_json_poison_lexical_encoder.decode(json) == term assert :jose_json_jason.encode(term) == json assert :jose_json_jason.decode(json) == term end end # See https://github.com/ueberauth/guardian/issues/152#issuecomment-221270029 test "sign and verify with incompatible key types is not allowed" do jwk_oct16 = JOSE.JWK.generate_key({:oct, 16}) jwk_ec256 = JOSE.JWK.generate_key({:ec, "P-256"}) jwk_ec521 = JOSE.JWK.generate_key({:ec, "P-521"}) jws_hs256 = JOSE.JWS.from(%{"alg" => "HS256"}) jws_es256 = JOSE.JWS.from(%{"alg" => "ES256"}) jwt = JOSE.JWT.from(%{"test" => true}) assert_raise ErlangError, "Erlang error: {:not_supported, [:ES256]}", fn -> JOSE.JWT.sign(jwk_oct16, jws_es256, jwt) end assert_raise ErlangError, "Erlang error: {:not_supported, [\"P-256\", :HS256]}", fn -> JOSE.JWT.sign(jwk_ec256, jws_hs256, jwt) end assert_raise ErlangError, "Erlang error: {:not_supported, [\"P-521\", :ES256]}", fn -> JOSE.JWT.sign(jwk_ec521, jws_es256, jwt) end signed_hs256 = JOSE.JWT.sign(jwk_oct16, jws_hs256, jwt) |> JOSE.JWS.compact() |> elem(1) signed_es256 = JOSE.JWT.sign(jwk_ec256, jws_es256, jwt) |> JOSE.JWS.compact() |> elem(1) assert(JOSE.JWT.verify_strict(jwk_oct16, ["HS256"], signed_hs256) |> elem(0)) assert(JOSE.JWT.verify_strict(jwk_ec256, ["ES256"], signed_es256) |> elem(0)) refute(JOSE.JWT.verify_strict(jwk_ec256, ["HS256"], signed_hs256) |> elem(0)) refute(JOSE.JWT.verify_strict(jwk_oct16, ["ES256"], signed_es256) |> elem(0)) refute(JOSE.JWT.verify(jwk_ec256, signed_hs256) |> elem(0)) refute(JOSE.JWT.verify(jwk_oct16, signed_es256) |> elem(0)) {kty_module, kty} = jwk_oct16.kty bad_signed_input = JOSE.JWS.signing_input(JOSE.JWT.to_binary(jwt) |> elem(1), jws_es256) bad_signature = kty_module.sign(bad_signed_input, :HS256, kty) |> :jose_base64url.encode() bad_signed_hs256 = bad_signed_input <> "." <> bad_signature refute(JOSE.JWT.verify_strict(jwk_oct16, ["HS256"], bad_signed_hs256) |> elem(0)) refute(JOSE.JWT.verify_strict(jwk_ec256, ["ES256"], bad_signed_hs256) |> elem(0)) refute(JOSE.JWT.verify(jwk_oct16, bad_signed_hs256) |> elem(0)) refute(JOSE.JWT.verify(jwk_ec256, bad_signed_hs256) |> elem(0)) end # See https://github.com/potatosalad/erlang-jose/issues/22 test "handles invalid signed data without raising exception" do jwk = %{ "kty" => "oct", "k" => :jose_base64url.encode("symmetric key") } assert({:error, _} = JOSE.JWT.verify(jwk, "invalid")) assert({:error, _} = JOSE.JWT.verify_strict(jwk, [], "invalid")) end # See https://github.com/potatosalad/erlang-jose/issues/23 test "handles nil signed data" do jwk = %{ "kty" => "oct", "k" => :jose_base64url.encode("symmetric key") } assert({:error, _} = JOSE.JWT.verify(jwk, nil)) assert({:error, _} = JOSE.JWT.verify_strict(jwk, [], nil)) end # See https://github.com/potatosalad/erlang-jose/issues/96 test "reads valid openssh keys" do input = "-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW\nQyNTUxOQAAACAvwUoBPsC8rc4wpR1xbN3Onjuuo+2gqLNECWvmUlDEpQAAAKA6FC8aOhQv\nGgAAAAtzc2gtZWQyNTUxOQAAACAvwUoBPsC8rc4wpR1xbN3Onjuuo+2gqLNECWvmUlDEpQ\nAAAEBXwZ8kolKDzWxBdcPJT0KXilyZEfbF/GjyGa4AOf2d5C/BSgE+wLytzjClHXFs3c6e\nO66j7aCos0QJa+ZSUMSlAAAAFmRtb3JuQHNpbHZlci5mcml0ei5ib3gBAgMEBQYH\n-----END OPENSSH PRIVATE KEY-----\n" assert %JOSE.JWK{kty: {:jose_jwk_kty_okp_ed25519, _}} = JOSE.JWK.from_openssh_key(input) end end potatosalad-erlang-jose-30ac019/mix.exs0000664000232200023220000000551215267717064020463 0ustar debalancedebalancedefmodule JOSE.Mixfile do use Mix.Project def project() do [ app: :jose, version: "1.11.13", elixir: "~> 1.13", erlc_options: erlc_options(), build_embedded: Mix.env() == :prod, start_permanent: Mix.env() == :prod, deps: deps(), name: "JOSE", source_url: "https://github.com/potatosalad/erlang-jose", docs: fn -> {ref, 0} = System.cmd("git", ["rev-parse", "--verify", "--quiet", "HEAD"]) [ source_ref: ref, main: "readme", extras: ["README.md", "CHANGELOG.md", "examples/KEY-GENERATION.md", "ALGORITHMS.md"], groups_for_modules: ["Elixir": [~r/JOSE/], Erlang: [~r/jose/]] ] end, description: description(), package: package(), aliases: [docs: ["compile", &edoc_chunks/1, "docs"]], dialyzer: [list_unused_filters: true] ] end def application() do [mod: {:jose_app, []}, extra_applications: extra_applications(Mix.env())] end defp extra_applications(env) defp extra_applications(:dev), do: [:crypto, :asn1, :public_key, :edoc, :xmerl] defp extra_applications(_env), do: [:crypto, :asn1, :public_key] defp deps() do [ # {:cutkey, github: "potatosalad/cutkey", only: [:test]}, {:jason, "~> 1.4.5", only: [:test]}, {:jsone, "~> 1.9", only: [:test]}, {:jsx, "~> 3.1", only: [:test]}, # {:keccakf1600, "~> 2.0.0", only: [:test]}, {:libdecaf, "~> 3.0.0", only: [:test]}, {:libsodium, "~> 3.0.1", only: [:test]}, {:ojson, "~> 1.0", only: [:test]}, # Optionally used by JOSE.Poison. {:poison, "~> 3.0 or ~> 4.0 or ~> 5.0 or ~> 6.0", only: [:test], optional: true}, {:thoas, "~> 1.2", only: [:test]}, {:ex_doc, "~> 0.40.4", only: :dev}, {:dialyxir, "~> 1.4.8", only: [:test], runtime: false} ] end defp description() do "JSON Object Signing and Encryption (JOSE) for Erlang and Elixir." end def erlc_options() do extra_options = [] [:debug_info | if(Mix.env() == :prod, do: [], else: [:warnings_as_errors]) ++ extra_options] end defp package() do [ maintainers: ["Andrew Bennett"], files: [ "CHANGELOG*", "include", "lib", "LICENSE*", "priv", "mix.exs", "README*", "rebar.config", "src" ], licenses: ["MIT"], links: %{"Github" => "https://github.com/potatosalad/erlang-jose", "Docs" => "https://hexdocs.pm/jose"} ] end defp edoc_chunks(_args) do base_path = Path.dirname(__ENV__.file) doc_chunk_path = Application.app_dir(:jose, "doc") :ok = :edoc.application(:jose, String.to_charlist(base_path), doclet: :edoc_doclet_chunks, layout: :edoc_layout_chunks, preprocess: true, dir: String.to_charlist(doc_chunk_path) ) end end potatosalad-erlang-jose-30ac019/Makefile0000664000232200023220000001417715267717064020614 0ustar debalancedebalancePROJECT = jose PROJECT_DESCRIPTION = JSON Object Signing and Encryption (JOSE) for Erlang and Elixir. PROJECT_VERSION = 1.11.13 # The Elixir sources under lib/ are built by Mix, not erlang.mk. ELIXIR = disable TEST_DEPS = jiffy jsone jsx libdecaf libsodium ojson proper thoas dep_jiffy = hex 2.0.2 dep_jsone = hex 1.9.0 dep_jsx = hex 3.1.0 # dep_keccakf1600 = hex 3.0.0 dep_libdecaf = hex 3.0.0 dep_libsodium = hex 3.0.1 dep_ojson = hex 1.0.0 dep_thoas = hex 1.2.1 dep_proper = git https://github.com/proper-testing/proper.git 21de022438cb57a04cbcf0f3e5b40bb846598daa include erlang.mk .PHONY: docker-build docker-load docker-setup docker-save docker-shell docker-test docker-test-build DOCKER_OTP_VERSION ?= 27.3-alpine-3.19.7 CT_SUITES ?= docker-build:: $(gen_verbose) docker build \ --tag ${DOCKER_OTP_VERSION} \ --file test/Dockerfile \ --build-arg OTP_VERSION=${DOCKER_OTP_VERSION} \ test docker-load:: $(gen_verbose) docker load \ --input "${DOCKER_OTP_VERSION}/image.tar" docker-save:: $(verbose) mkdir -p "${DOCKER_OTP_VERSION}" $(gen_verbose) docker save \ --output "${DOCKER_OTP_VERSION}/image.tar" \ ${DOCKER_OTP_VERSION} docker-setup:: $(verbose) if [ -f "${DOCKER_OTP_VERSION}/image.tar" ]; then \ $(MAKE) docker-load; \ else \ $(MAKE) docker-build; \ $(MAKE) docker-save; \ fi docker-shell:: $(verbose) docker run \ --volume "$(shell pwd)":"/build/jose" --rm --interactive --tty "${DOCKER_OTP_VERSION}" \ /bin/bash -l ifeq ($(CT_SUITES),) docker-test:: $(gen_verbose) docker run \ --volume "$(shell pwd)":"/build/jose" \ "${DOCKER_OTP_VERSION}" \ sh -c 'cd jose && make ct' else docker-test:: $(gen_verbose) docker run \ --volume "$(shell pwd)":"/build/jose" \ -e CT_SUITES="${CT_SUITES}" \ "${DOCKER_OTP_VERSION}" \ sh -c 'cd jose && make ct' endif docker-test-build:: $(gen_verbose) docker run \ --volume "$(shell pwd)":"/build/jose" \ "${DOCKER_OTP_VERSION}" \ sh -c 'cd jose && make test-build' .PHONY: eqwalize eqwalize-all distclean-elp # Arch detection. ifeq ($(ARCH),) UNAME_M := $(shell uname -m) ifeq ($(UNAME_M),amd64) ARCH = x86_64 else ifeq ($(UNAME_M),x86_64) ARCH = x86_64 else ifeq ($(UNAME_M),arm64) ARCH = aarch64 else ifeq ($(UNAME_M),aarch64) ARCH = aarch64 else $(error Unable to detect architecture. Please open a ticket with the output of uname -s.) endif export ARCH endif # Configuration. ELP_VERSION ?= 2025-09-01 ELP_OTP_VERSION ?= 28 ELP ?= $(CURDIR)/elp export ELP ifeq ($(PLATFORM),darwin) ELP_URL ?= https://github.com/WhatsApp/erlang-language-platform/releases/download/${ELP_VERSION}/elp-macos-${ARCH}-apple-darwin-otp-${ELP_OTP_VERSION}.tar.gz else ELP_URL ?= https://github.com/WhatsApp/erlang-language-platform/releases/download/${ELP_VERSION}/elp-linux-${ARCH}-unknown-linux-gnu-otp-${ELP_OTP_VERSION}.tar.gz endif ELP_OPTS ?= ELP_BUILD_DIR ?= $(CURDIR)/_elp_build ELP_ARCHIVE = elp-$(ELP_VERSION).tar.gz # Core targets. help:: $(verbose) printf "%s\n" "" \ "elp targets:" \ " eqwalize Run 'elp eqwalize-app argo' on the current project" \ " eqwalize-all Run 'elp eqwalize-all' on the current project" distclean:: distclean-elp # Plugin-specific targets. $(ELP): $(verbose) mkdir -p $(ELP_BUILD_DIR) $(verbose) echo "Downloading eqwalizer from: "$(ELP_URL) $(verbose) $(call core_http_get,$(ELP_BUILD_DIR)/$(ELP_ARCHIVE),$(ELP_URL)) $(verbose) cd $(ELP_BUILD_DIR) && \ tar -xzf $(ELP_ARCHIVE) $(gen_verbose) cp $(ELP_BUILD_DIR)/elp $(ELP) $(verbose) chmod +x $(ELP) $(verbose) rm -rf $(ELP_BUILD_DIR) eqwalize: $(ELP) $(verbose) $(ELP) eqwalize $(PROJECT) eqwalize-all: $(ELP) $(verbose) $(ELP) eqwalize-all distclean-elp: $(gen_verbose) rm -rf $(ELP) .PHONY: erlfmt erlfmt-check distclean-erlfmt format # Configuration. ERLFMT_VERSION ?= 1.7.0 ERLFMT ?= $(CURDIR)/erlfmt export ERLFMT ERLFMT_URL ?= https://github.com/WhatsApp/erlfmt/archive/refs/tags/v$(ERLFMT_VERSION).tar.gz ERLFMT_OPTS ?= ERLFMT_BUILD_DIR ?= $(CURDIR)/_erlfmt_build ERLFMT_CODE_ARCHIVE = $(ERLFMT_VERSION).tar.gz ERLFMT_REBAR3_URL ?= https://s3.amazonaws.com/rebar3/rebar3 ERLFMT_REBAR3 ?= rebar3 # Core targets. help:: $(verbose) printf "%s\n" "" \ "erlfmt targets:" \ " erlfmt Run erlfmt or download the default otherwise" \ " elrfmt-check Run erlfmt --check" distclean:: distclean-erlfmt # Plugin-specific targets. $(ERLFMT): $(verbose) mkdir -p $(ERLFMT_BUILD_DIR) ifeq ($(shell command -v $(ERLFMT_REBAR3)),) $(verbose) echo "Downloading Rebar3 from: "$(ERLFMT_REBAR3_URL) $(verbose) $(call core_http_get,$(ERLFMT_BUILD_DIR)/rebar3,$(ERLFMT_REBAR3_URL)) $(verbose) chmod +x $(ERLFMT_BUILD_DIR)/rebar3 $(eval ERLFMT_REBAR3 := $(ERLFMT_BUILD_DIR)/rebar3) else $(verbose) echo "Using Rebar3: "$(ERLFMT_REBAR3) endif $(verbose) echo "Downloading erlfmt from: "$(ERLFMT_URL) $(verbose) $(call core_http_get,$(ERLFMT_BUILD_DIR)/$(ERLFMT_CODE_ARCHIVE),$(ERLFMT_URL)) $(verbose) cd $(ERLFMT_BUILD_DIR) && \ tar -xzf $(ERLFMT_CODE_ARCHIVE) && \ cd erlfmt-$(ERLFMT_VERSION) && \ $(ERLFMT_REBAR3) as release escriptize $(gen_verbose) cp $(ERLFMT_BUILD_DIR)/erlfmt-$(ERLFMT_VERSION)/_build/release/bin/erlfmt $(ERLFMT) $(verbose) chmod +x $(ERLFMT) $(verbose) rm -rf $(ERLFMT_BUILD_DIR)/erlfmt-$(ERLFMT_VERSION) $(verbose) rm $(ERLFMT_BUILD_DIR)/$(ERLFMT_CODE_ARCHIVE) $(verbose) rm -f $(ERLFMT_BUILD_DIR)/rebar3 $(verbose) rm -rf $(ERLFMT_BUILD_DIR) erlfmt: $(ERLFMT) $(verbose) $(ERLFMT) --verbose --write --require-pragma --print-width=120 \ '{src,codegen,include,test}/**/*.{hrl,erl,app.src,app.src.script}' \ 'test/**/*.config' \ '{rebar.config,rebar.config.script}' erlfmt-check: $(ERLFMT) $(verbose) $(ERLFMT) --check --require-pragma --print-width=120 \ '{src,codegen,include,test}/**/*.{hrl,erl,app.src,app.src.script}' \ 'test/**/*.config' \ '{rebar.config,rebar.config.script}' distclean-erlfmt: $(gen_verbose) rm -rf $(ERLFMT) format: $(ERLFMT) $(verbose) $(MAKE) erlfmt $(verbose) mix format --migrate .PHONY: lint lint-dialyzer lint-eqwalizer lint-format lint-xref lint:: lint-format lint-eqwalizer lint-xref lint-dialyzer lint-dialyzer: $(verbose) rebar3 dialyzer lint-eqwalizer: eqwalize-all lint-format: erlfmt-check lint-xref: $(verbose) rebar3 xref potatosalad-erlang-jose-30ac019/.formatter.exs0000664000232200023220000000021315267717064021740 0ustar debalancedebalance# Used by "mix format" [ inputs: ["{mix,.formatter,.dialyzer_ignore}.exs", "{config,lib,priv,test}/**/*.{ex,exs}"], line_length: 132 ] potatosalad-erlang-jose-30ac019/examples/0000775000232200023220000000000015267717064020760 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/examples/KEY-GENERATION.md0000664000232200023220000002224315267717064023366 0ustar debalancedebalance# Examples: Key Generation There are four key generation methods described below for each key type: * Method 1: OpenSSL * Method 2: `jose_jwk:generate_key/1` or `JOSE.JWK.generate_key/1` * Method 3: `jose_jwe:generate_key/1` or `JOSE.JWE.generate_key/1` * Method 4: `jose_jws:generate_key/1` or `JOSE.JWS.generate_key/1` ## EC The three curve types defined in the [JWA RFC 7518](https://tools.ietf.org/html/rfc7518#section-6.2.1.1) for the `EC` key type are: 1. `"P-256"` (openssl curve `secp256r1`) 2. `"P-384"` (openssl curve `secp384r1`) 3. `"P-521"` (openssl curve `secp521r1`) ### Method 1 The basic formula for key generation is `openssl ecparam -name CURVE -genkey -noout -out FILE`, for example: ```bash openssl ecparam -name secp256r1 -genkey -noout -out ec-secp256r1.pem openssl ecparam -name secp384r1 -genkey -noout -out ec-secp384r1.pem openssl ecparam -name secp521r1 -genkey -noout -out ec-secp521r1.pem ``` The PEM files can then be read using `jose_jwk:from_pem_file/1` or `JOSE.JWK.from_pem_file/1`: ```elixir jwk = JOSE.JWK.from_pem_file("ec-secp256r1.pem") ``` ### Method 2 The curve names are the same as the ones for OpenSSL. ```elixir jwk = JOSE.JWK.generate_key(:secp256r1) jwk = JOSE.JWK.generate_key(:secp384r1) jwk = JOSE.JWK.generate_key(:secp521r1) # Alternative explicit syntax: jwk = JOSE.JWK.generate_key({:ec, :secp256r1}) jwk = JOSE.JWK.generate_key({:ec, :secp384r1}) jwk = JOSE.JWK.generate_key({:ec, :secp521r1}) # Alternative curve alias syntax: jwk = JOSE.JWK.generate_key({:ec, "P-256"}) jwk = JOSE.JWK.generate_key({:ec, "P-384"}) jwk = JOSE.JWK.generate_key({:ec, "P-521"}) ``` Keys may also be generated based on other keys. The new key will use the same curve as the supplied key. ```elixir old_jwk = JOSE.JWK.from_pem_file("ec-secp256r1.pem") new_jwk = JOSE.JWK.generate_key(old_jwk) ``` ### Method 3 If you have a JWE header with an `"epk"` field, a new key will be generated based on the same key type of the `"epk"`. Otherwise, the `P-521` curve will be used. ```elixir # Based on the "epk" field. epk = JOSE.JWK.generate_key({:ec, "P-256"}) jwe = JOSE.JWE.from_map(%{"alg" => "ECDH-ES", "enc" => "A128GCM", "epk" => epk |> JOSE.JWK.to_map |> elem(1)}) jwk = JOSE.JWE.generate_key(jwe) # Otherwise, defaults to "P-521". jwk = JOSE.JWE.generate_key(%{"alg" => "ECDH-ES", "enc" => "A128GCM"}) ``` ### Method 4 If you have a JWS header with one of the ECDSA signature algorithms specified, a corresponding EC key will be generated with the correct curve for the signature type. ```elixir jwk_ec256 = JOSE.JWS.generate_key(%{"alg" => "ES256"}) jwk_ec384 = JOSE.JWS.generate_key(%{"alg" => "ES384"}) jwk_ec521 = JOSE.JWS.generate_key(%{"alg" => "ES512"}) ``` ## oct This key type is simply an octet or byte sequence (see [RFC 7518 Section 6.4](https://tools.ietf.org/html/rfc7518#section-6.4)). ### Method 1 The basic formula for generating a random octet sequence is `openssl rand -out FILE BYTE_SIZE`, for example: ```bash openssl rand -out oct-128-bit.bin 16 ``` The binary file can then be read using `jose_jwk:from_oct_file/1` or `JOSE.JWK.from_oct_file/1`: ```elixir jwk = JOSE.JWK.from_oct_file("oct-128-bit.bin") ``` ### Method 2 Calling either of these functions with an integer will generate a random octet sequence. ```elixir jwk = JOSE.JWK.generate_key(16) # Alternative explicit syntax: jwk = JOSE.JWK.generate_key({:oct, 16}) ``` Keys may also be generated based on other keys. The new key will use the same byte size as the supplied key. ```elixir old_jwk = JOSE.JWK.from_oct_file("oct-128-bit.bin") new_jwk = JOSE.JWK.generate_key(old_jwk) ``` ### Method 3 If you have a JWE header with an `"alg"` field that requires a symmetric key, a new `oct` key will be generated based on the byte size required of `"alg"` and/or `"enc"`. ```elixir jwk_oct16 = JOSE.JWE.generate_key(%{"alg" => "dir", "enc" => "A128GCM"}) jwk_oct24 = JOSE.JWE.generate_key(%{"alg" => "dir", "enc" => "A192GCM"}) jwk_oct32 = JOSE.JWE.generate_key(%{"alg" => "dir", "enc" => "A256GCM"}) jwk_oct32 = JOSE.JWE.generate_key(%{"alg" => "dir", "enc" => "A128CBC-HS256"}) jwk_oct48 = JOSE.JWE.generate_key(%{"alg" => "dir", "enc" => "A192CBC-HS384"}) jwk_oct64 = JOSE.JWE.generate_key(%{"alg" => "dir", "enc" => "A256CBC-HS512"}) ``` ### Method 4 If you have a JWS header with an `"alg"` field that requires a symmetric key, a new `oct` key will be generated based on the byte size recommended for `"alg"`. ```elixir jwk_oct32 = JOSE.JWS.generate_key(%{"alg" => "HS256"}) jwk_oct48 = JOSE.JWS.generate_key(%{"alg" => "HS384"}) jwk_oct64 = JOSE.JWS.generate_key(%{"alg" => "HS512"}) ``` ## OKP This key type is an octet key pair with an associated curve (see [draft-ietf-jose-cfrg-curves](https://tools.ietf.org/html/draft-ietf-jose-cfrg-curves)). ### Method 1 *NOTE:* Only `Ed25519` is currently supported by `ssh-keygen`. The basic formula for generating a octet key pair is `ssh-keygen -t TYPE -f FILE`, for example: ```bash ssh-keygen -t ed25519 -f ed25519 ``` The private key file can then be read using `jose_jwk:from_openssh_key_file/1` or `JOSE.JWK.from_openssh_key_file/1`: ```elixir jwk = JOSE.JWK.from_openssh_key_file("ed25519") ``` ### Method 2 Calling either of these functions with a specified curve will generate an octet key pair. You may also specify the secret portion of the key after the curve. ```elixir % Curve25519 jwk_Ed25519 = JOSE.JWK.generate_key({:okp, :Ed25519}) jwk_Ed25519ph = JOSE.JWK.generate_key({:okp, :Ed25519ph}) jwk_X25519 = JOSE.JWK.generate_key({:okp, :X25519}) % Curve448 jwk_Ed448 = JOSE.JWK.generate_key({:okp, :Ed448}) jwk_Ed448ph = JOSE.JWK.generate_key({:okp, :Ed448ph}) jwk_X448 = JOSE.JWK.generate_key({:okp, :X448}) ``` Keys may also be generated based on other keys. The new key will use the same curve as the supplied key. ```elixir old_jwk = JOSE.JWK.from_openssh_key_file("ed25519") new_jwk = JOSE.JWK.generate_key(old_jwk) ``` ### Method 3 If you have a JWE header with an `"epk"` field, a new key will be generated based on the same key type of the `"epk"`. ```elixir # Based on the "epk" field. epk = JOSE.JWK.generate_key({:okp, :X25519}) jwe = JOSE.JWE.from_map(%{"alg" => "ECDH-ES", "enc" => "A128GCM", "epk" => epk |> JOSE.JWK.to_map |> elem(1)}) jwk = JOSE.JWE.generate_key(jwe) ``` ### Method 4 If you have a JWS header with one of the EdDSA signature algorithms specified, a corresponding OKP key will be generated with the correct curve for the signature type. ```elixir jwk_Ed25519 = JOSE.JWS.generate_key(%{"alg" => "Ed25519"}) jwk_Ed25519ph = JOSE.JWS.generate_key(%{"alg" => "Ed25519ph"}) jwk_Ed448 = JOSE.JWS.generate_key(%{"alg" => "Ed448"}) jwk_Ed448ph = JOSE.JWS.generate_key(%{"alg" => "Ed448ph"}) # EdDSA defaults to Ed25519 jwk_EdDSA = JOSE.JWS.generate_key(%{"alg" => "EdDSA"}) ``` ## RSA Both two-prime and multi-prime RSA keys are supported by [RFC 7518 Section 6.3](https://tools.ietf.org/html/rfc7518#section-6.3), but currently only two-prime RSA keys can be generated by OpenSSL-based generators. See [`test/jose_SUITE_data/rsa-multi.pem`](https://github.com/potatosalad/erlang-jose/blob/main/test/jose_SUITE_data/rsa-multi.pem) for an example multi-prime RSA key. ### Method 1 The basic formula for generating a RSA key is `openssl genrsa -out FILE BIT_SIZE`, for example: ```bash openssl genrsa -out rsa-2048.pem 2048 ``` The PEM file can then be read using `jose_jwk:from_pem_file/1` or `JOSE.JWK.from_pem_file/1`: ```elixir jwk = JOSE.JWK.from_pem_file("rsa-2048.pem") ``` ### Method 2 _Note:_ RSA key generation is dependent on [`cutkey`](https://github.com/potatosalad/cutkey), which must be included as a dependency. An error will be thrown if `cutkey` is not found. The modulus bit size is the only required argument. Optionally, you may specify the public exponent as the second argument (default is `65537`). ```elixir jwk = JOSE.JWK.generate_key({:rsa, 2048}) # Alternative explicit syntax with public exponent: jwk = JOSE.JWK.generate_key({:rsa, 4096, 65537}) ``` Keys may also be generated based on other keys. The new key will use the same modulus size and public exponent as the supplied key. ```elixir old_jwk = JOSE.JWK.from_pem_file("rsa-2048.pem") new_jwk = JOSE.JWK.generate_key(old_jwk) ``` ### Method 3 If you have a JWE header with an `"alg"` field that requires an asymmetric RSA key, a new `RSA` key will be generated. 2048-bit keys are generated in these cases. ```elixir jwk_rsa1_5 = JOSE.JWE.generate_key(%{"alg" => "RSA1_5", "enc" => "A128GCM"}) jwk_rsa_oaep = JOSE.JWE.generate_key(%{"alg" => "RSA-OAEP", "enc" => "A128GCM"}) jwk_rsa_oaep256 = JOSE.JWE.generate_key(%{"alg" => "RSA-OAEP-256", "enc" => "A128GCM"}) ``` ### Method 4 If you have a JWS header with one of the RSA PKCS1 or PSS signature algorithms specified, a corresponding RSA key will be generated with a recommended modulus size based on the digest type. ```elixir # RS256, RS384, RS512 jwk_rsa2048 = JOSE.JWS.generate_key(%{"alg" => "RS256"}) jwk_rsa3072 = JOSE.JWS.generate_key(%{"alg" => "RS384"}) jwk_rsa4096 = JOSE.JWS.generate_key(%{"alg" => "RS512"}) # PS256, PS384, PS512 jwk_rsa2048 = JOSE.JWS.generate_key(%{"alg" => "PS256"}) jwk_rsa3072 = JOSE.JWS.generate_key(%{"alg" => "PS384"}) jwk_rsa4096 = JOSE.JWS.generate_key(%{"alg" => "PS512"}) ``` potatosalad-erlang-jose-30ac019/src/0000775000232200023220000000000015267717064017731 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/src/jose_block_encryptor.erl0000664000232200023220000000267715267717064024670 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 10 Aug 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_block_encryptor). -callback block_decrypt(Cipher, Key, CipherText) -> PlainText | error when Cipher :: {atom(), pos_integer()}, Key :: bitstring(), CipherText :: binary(), PlainText :: binary(). -callback block_encrypt(Cipher, Key, PlainText) -> CipherText when Cipher :: {atom(), pos_integer()}, Key :: bitstring(), PlainText :: binary(), CipherText :: binary(). -optional_callbacks([block_decrypt/3]). -optional_callbacks([block_encrypt/3]). -callback block_decrypt(Cipher, Key, IV, CipherText) -> PlainText | error when Cipher :: {atom(), pos_integer()}, Key :: bitstring(), IV :: bitstring(), CipherText :: binary() | {binary(), binary(), binary()}, PlainText :: binary(). -callback block_encrypt(Cipher, Key, IV, PlainText) -> CipherText when Cipher :: {atom(), pos_integer()}, Key :: bitstring(), IV :: bitstring(), PlainText :: binary() | {binary(), binary()}, CipherText :: binary() | {binary(), binary()}. potatosalad-erlang-jose-30ac019/src/jwe/0000775000232200023220000000000015267717064020516 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/src/jwe/jose_jwe_enc_aes.erl0000664000232200023220000001467115267717064024515 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 22 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwe_enc_aes). -behaviour(jose_jwe). -behaviour(jose_jwe_enc). %% jose_jwe callbacks -export([from_map/1]). -export([to_map/2]). %% jose_jwe_enc callbacks -export([algorithm/1]). -export([bits/1]). -export([block_decrypt/4]). -export([block_encrypt/4]). -export([next_cek/1]). -export([next_iv/1]). %% API -export([cipher_supported/0]). -export([hmac_supported/0]). %% Types -type cipher() :: aes_cbc | aes_gcm. -type key_size() :: 128 | 192 | 256. -record(jose_jwe_enc_aes, { cipher = undefined :: undefined | {cipher(), key_size()}, bits = undefined :: undefined | pos_integer(), cek_len = undefined :: undefined | pos_integer(), iv_len = undefined :: undefined | pos_integer(), enc_len = undefined :: undefined | pos_integer(), mac_len = undefined :: undefined | pos_integer(), tag_len = undefined :: undefined | pos_integer(), hmac = undefined :: undefined | sha256 | sha384 | sha512 }). -type enc() :: #jose_jwe_enc_aes{}. -export_type([enc/0]). -define(AES_128_CBC_HMAC_SHA_256, #jose_jwe_enc_aes{ cipher = {aes_cbc, 128}, bits = 256, cek_len = 32, iv_len = 16, enc_len = 16, mac_len = 16, tag_len = 16, hmac = sha256 }). -define(AES_192_CBC_HMAC_SHA_384, #jose_jwe_enc_aes{ cipher = {aes_cbc, 192}, bits = 384, cek_len = 48, iv_len = 16, enc_len = 24, mac_len = 24, tag_len = 24, hmac = sha384 }). -define(AES_256_CBC_HMAC_SHA_512, #jose_jwe_enc_aes{ cipher = {aes_cbc, 256}, bits = 512, cek_len = 64, iv_len = 16, enc_len = 32, mac_len = 32, tag_len = 32, hmac = sha512 }). -define(AES_128_GCM, #jose_jwe_enc_aes{ cipher = {aes_gcm, 128}, bits = 128, cek_len = 16, iv_len = 12 }). -define(AES_192_GCM, #jose_jwe_enc_aes{ cipher = {aes_gcm, 192}, bits = 192, cek_len = 24, iv_len = 12 }). -define(AES_256_GCM, #jose_jwe_enc_aes{ cipher = {aes_gcm, 256}, bits = 256, cek_len = 32, iv_len = 12 }). %%==================================================================== %% jose_jwe callbacks %%==================================================================== from_map(F = #{ <<"enc">> := <<"A128CBC-HS256">> }) -> {?AES_128_CBC_HMAC_SHA_256, maps:remove(<<"enc">>, F)}; from_map(F = #{ <<"enc">> := <<"A192CBC-HS384">> }) -> {?AES_192_CBC_HMAC_SHA_384, maps:remove(<<"enc">>, F)}; from_map(F = #{ <<"enc">> := <<"A256CBC-HS512">> }) -> {?AES_256_CBC_HMAC_SHA_512, maps:remove(<<"enc">>, F)}; from_map(F = #{ <<"enc">> := <<"A128GCM">> }) -> {?AES_128_GCM, maps:remove(<<"enc">>, F)}; from_map(F = #{ <<"enc">> := <<"A192GCM">> }) -> {?AES_192_GCM, maps:remove(<<"enc">>, F)}; from_map(F = #{ <<"enc">> := <<"A256GCM">> }) -> {?AES_256_GCM, maps:remove(<<"enc">>, F)}. to_map(?AES_128_CBC_HMAC_SHA_256, F) -> F#{ <<"enc">> => <<"A128CBC-HS256">> }; to_map(?AES_192_CBC_HMAC_SHA_384, F) -> F#{ <<"enc">> => <<"A192CBC-HS384">> }; to_map(?AES_256_CBC_HMAC_SHA_512, F) -> F#{ <<"enc">> => <<"A256CBC-HS512">> }; to_map(?AES_128_GCM, F) -> F#{ <<"enc">> => <<"A128GCM">> }; to_map(?AES_192_GCM, F) -> F#{ <<"enc">> => <<"A192GCM">> }; to_map(?AES_256_GCM, F) -> F#{ <<"enc">> => <<"A256GCM">> }. %%==================================================================== %% jose_jwe_enc callbacks %%==================================================================== algorithm(?AES_128_CBC_HMAC_SHA_256) -> <<"A128CBC-HS256">>; algorithm(?AES_192_CBC_HMAC_SHA_384) -> <<"A192CBC-HS384">>; algorithm(?AES_256_CBC_HMAC_SHA_512) -> <<"A256CBC-HS512">>; algorithm(?AES_128_GCM) -> <<"A128GCM">>; algorithm(?AES_192_GCM) -> <<"A192GCM">>; algorithm(?AES_256_GCM) -> <<"A256GCM">>. bits(#jose_jwe_enc_aes{bits=Bits}) -> Bits. block_decrypt({AAD, CipherText, CipherTag}, CEK, IV, #jose_jwe_enc_aes{ cipher=Cipher, cek_len=CEKLen, hmac=undefined}) when byte_size(CEK) =:= CEKLen andalso bit_size(IV) > 0 -> jose_jwa:block_decrypt(Cipher, CEK, IV, {AAD, CipherText, CipherTag}); block_decrypt({AAD, CipherText, CipherTag}, CEK, IV, #jose_jwe_enc_aes{ cipher=Cipher, cek_len=CEKLen, iv_len=IVLen, enc_len=EncLen, mac_len=MacLen, tag_len=TagLen, hmac=HMAC}) when byte_size(CEK) =:= CEKLen andalso byte_size(IV) =:= IVLen -> << MacKey:MacLen/binary, EncKey:EncLen/binary >> = CEK, AADLength = << (bit_size(AAD)):1/unsigned-big-integer-unit:64 >>, MacData = << AAD/binary, IV/binary, CipherText/binary, AADLength/binary >>, case jose_crypto_compat:mac(hmac, HMAC, MacKey, MacData) of << CipherTag:TagLen/binary, _/binary >> -> PlainText = jose_jwa_pkcs7:unpad(jose_jwa:block_decrypt(Cipher, EncKey, IV, CipherText)), PlainText; _ -> error end. block_encrypt({AAD, PlainText}, CEK, IV, #jose_jwe_enc_aes{ cipher=Cipher, cek_len=CEKLen, hmac=undefined}) when byte_size(CEK) =:= CEKLen andalso bit_size(IV) > 0 -> jose_jwa:block_encrypt(Cipher, CEK, IV, {AAD, PlainText}); block_encrypt({AAD, PlainText}, CEK, IV, #jose_jwe_enc_aes{ cipher=Cipher, cek_len=CEKLen, iv_len=IVLen, enc_len=EncLen, mac_len=MacLen, tag_len=TagLen, hmac=HMAC}) when byte_size(CEK) =:= CEKLen andalso byte_size(IV) =:= IVLen -> << MacKey:MacLen/binary, EncKey:EncLen/binary, _/binary >> = CEK, CipherText = jose_jwa:block_encrypt(Cipher, EncKey, IV, jose_jwa_pkcs7:pad(PlainText)), AADLength = << (bit_size(AAD)):1/unsigned-big-integer-unit:64 >>, MacData = << AAD/binary, IV/binary, CipherText/binary, AADLength/binary >>, << CipherTag:TagLen/binary, _/binary >> = jose_crypto_compat:mac(hmac, HMAC, MacKey, MacData), {CipherText, CipherTag}. next_cek(#jose_jwe_enc_aes{cek_len=CEKLen}) -> crypto:strong_rand_bytes(CEKLen). next_iv(#jose_jwe_enc_aes{iv_len=IVLen}) -> crypto:strong_rand_bytes(IVLen). %%==================================================================== %% API functions %%==================================================================== cipher_supported() -> [aes_cbc, aes_gcm]. hmac_supported() -> [sha256, sha384, sha512]. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jwe/jose_jwe_alg_ecdh_ss.erl0000664000232200023220000003721215267717064025347 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc Use of Static-Static ECDH in JSON Object Signing and Encryption (JOSE) %%% See https://datatracker.ietf.org/doc/html/draft-amringer-jose-ecdh-ss-00 %%% %%% @end %%% Created : 01 Sep 2022 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwe_alg_ecdh_ss). -behaviour(jose_jwe). -behaviour(jose_jwe_alg). -include_lib("public_key/include/public_key.hrl"). -include("jose_jwk.hrl"). %% jose_jwe callbacks -export([from_map/1]). -export([to_map/2]). %% jose_jwe_alg callbacks -export([generate_key/3]). -export([key_decrypt/3]). -export([key_encrypt/3]). -export([next_cek/3]). %% API -export([algorithm/1]). %% Types -type ec_public_key() :: {#'ECPoint'{},{namedCurve, Oid::tuple()} | #'ECParameters'{}} | term(). -record(jose_jwe_alg_ecdh_ss, { spk = undefined :: undefined | {ec_public_key(), map()}, apu = undefined :: undefined | binary(), apv = undefined :: undefined | binary(), wrap = undefined :: undefined | aes_gcm_kw | aes_kw | c20p_kw | xc20p_kw, bits = undefined :: undefined | 128 | 192 | 256, iv = undefined :: undefined | binary(), tag = undefined :: undefined | binary() }). -type alg() :: #jose_jwe_alg_ecdh_ss{}. -export_type([alg/0]). -define(ECDH_SS, #jose_jwe_alg_ecdh_ss{}). -define(ECDH_SS_A128GCMKW, #jose_jwe_alg_ecdh_ss{wrap=aes_gcm_kw, bits=128}). -define(ECDH_SS_A192GCMKW, #jose_jwe_alg_ecdh_ss{wrap=aes_gcm_kw, bits=192}). -define(ECDH_SS_A256GCMKW, #jose_jwe_alg_ecdh_ss{wrap=aes_gcm_kw, bits=256}). -define(ECDH_SS_A128KW, #jose_jwe_alg_ecdh_ss{wrap=aes_kw, bits=128}). -define(ECDH_SS_A192KW, #jose_jwe_alg_ecdh_ss{wrap=aes_kw, bits=192}). -define(ECDH_SS_A256KW, #jose_jwe_alg_ecdh_ss{wrap=aes_kw, bits=256}). -define(ECDH_SS_C20PKW, #jose_jwe_alg_ecdh_ss{wrap=c20p_kw, bits=256}). -define(ECDH_SS_XC20PKW, #jose_jwe_alg_ecdh_ss{wrap=xc20p_kw, bits=256}). %%==================================================================== %% jose_jwe callbacks %%==================================================================== from_map(F = #{ <<"alg">> := <<"ECDH-SS">> }) -> from_map_ecdh_ss(maps:remove(<<"alg">>, F), ?ECDH_SS); from_map(F = #{ <<"alg">> := <<"ECDH-SS+A128GCMKW">> }) -> from_map_ecdh_ss(maps:remove(<<"alg">>, F), ?ECDH_SS_A128GCMKW); from_map(F = #{ <<"alg">> := <<"ECDH-SS+A192GCMKW">> }) -> from_map_ecdh_ss(maps:remove(<<"alg">>, F), ?ECDH_SS_A192GCMKW); from_map(F = #{ <<"alg">> := <<"ECDH-SS+A256GCMKW">> }) -> from_map_ecdh_ss(maps:remove(<<"alg">>, F), ?ECDH_SS_A256GCMKW); from_map(F = #{ <<"alg">> := <<"ECDH-SS+A128KW">> }) -> from_map_ecdh_ss(maps:remove(<<"alg">>, F), ?ECDH_SS_A128KW); from_map(F = #{ <<"alg">> := <<"ECDH-SS+A192KW">> }) -> from_map_ecdh_ss(maps:remove(<<"alg">>, F), ?ECDH_SS_A192KW); from_map(F = #{ <<"alg">> := <<"ECDH-SS+A256KW">> }) -> from_map_ecdh_ss(maps:remove(<<"alg">>, F), ?ECDH_SS_A256KW); from_map(F = #{ <<"alg">> := <<"ECDH-SS+C20PKW">> }) -> from_map_ecdh_ss(maps:remove(<<"alg">>, F), ?ECDH_SS_C20PKW); from_map(F = #{ <<"alg">> := <<"ECDH-SS+XC20PKW">> }) -> from_map_ecdh_ss(maps:remove(<<"alg">>, F), ?ECDH_SS_XC20PKW). to_map(A = ?ECDH_SS_A128GCMKW, F) -> to_map_ecdh_ss(F#{ <<"alg">> => <<"ECDH-SS+A128GCMKW">> }, A); to_map(A = ?ECDH_SS_A192GCMKW, F) -> to_map_ecdh_ss(F#{ <<"alg">> => <<"ECDH-SS+A192GCMKW">> }, A); to_map(A = ?ECDH_SS_A256GCMKW, F) -> to_map_ecdh_ss(F#{ <<"alg">> => <<"ECDH-SS+A256GCMKW">> }, A); to_map(A = ?ECDH_SS_A128KW, F) -> to_map_ecdh_ss(F#{ <<"alg">> => <<"ECDH-SS+A128KW">> }, A); to_map(A = ?ECDH_SS_A192KW, F) -> to_map_ecdh_ss(F#{ <<"alg">> => <<"ECDH-SS+A192KW">> }, A); to_map(A = ?ECDH_SS_A256KW, F) -> to_map_ecdh_ss(F#{ <<"alg">> => <<"ECDH-SS+A256KW">> }, A); to_map(A = ?ECDH_SS_C20PKW, F) -> to_map_ecdh_ss(F#{ <<"alg">> => <<"ECDH-SS+C20PKW">> }, A); to_map(A = ?ECDH_SS_XC20PKW, F) -> to_map_ecdh_ss(F#{ <<"alg">> => <<"ECDH-SS+XC20PKW">> }, A); to_map(A = ?ECDH_SS, F) -> to_map_ecdh_ss(F#{ <<"alg">> => <<"ECDH-SS">> }, A). %%==================================================================== %% jose_jwe_alg callbacks %%==================================================================== generate_key(_Fields, {ENCModule, ENC}, ALG=#jose_jwe_alg_ecdh_ss{spk=USenderPublicKey=#jose_jwk{}}) -> jose_jwe_alg:generate_key(USenderPublicKey, maps:get(<<"alg">>, to_map(ALG, #{})), ENCModule:algorithm(ENC)); generate_key(_Fields, {ENCModule, ENC}, ALG=#jose_jwe_alg_ecdh_ss{}) -> jose_jwe_alg:generate_key({ec, <<"X25519">>}, maps:get(<<"alg">>, to_map(ALG, #{})), ENCModule:algorithm(ENC)). key_decrypt({UStaticPublicKey=#jose_jwk{}, VStaticSecretKey=#jose_jwk{}}, EncryptedKey, JWEECDHSS=#jose_jwe_alg_ecdh_ss{spk=USenderPublicKey=#jose_jwk{}}) -> case jose_jwk:thumbprint(UStaticPublicKey) =:= jose_jwk:thumbprint(USenderPublicKey) of true -> Z = jose_jwk:shared_secret(UStaticPublicKey, VStaticSecretKey), key_decrypt(Z, EncryptedKey, JWEECDHSS); false -> error end; key_decrypt({UStaticPublicKey=#jose_jwk{}, VStaticSecretKey=#jose_jwk{}}, EncryptedKey, JWEECDHSS0=#jose_jwe_alg_ecdh_ss{spk=undefined}) -> JWEECDHSS = JWEECDHSS0#jose_jwe_alg_ecdh_ss{spk=UStaticPublicKey}, key_decrypt({UStaticPublicKey, VStaticSecretKey}, EncryptedKey, JWEECDHSS); key_decrypt(VStaticSecretKey=#jose_jwk{}, EncryptedKey, JWEECDHSS=#jose_jwe_alg_ecdh_ss{spk=USenderPublicKey=#jose_jwk{}}) -> Z = jose_jwk:shared_secret(USenderPublicKey, VStaticSecretKey), key_decrypt(Z, EncryptedKey, JWEECDHSS); key_decrypt(Z, {ENCModule, ENC, <<>>}, #jose_jwe_alg_ecdh_ss{apu=APU, apv=APV, wrap=undefined, bits=undefined}) when is_binary(Z) -> Algorithm = ENCModule:algorithm(ENC), KeyDataLen = ENCModule:bits(ENC), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), DerivedKey; key_decrypt(Z, {_ENCModule, _ENC, EncryptedKey}, JWEECDHSS=#jose_jwe_alg_ecdh_ss{apu=APU, apv=APV, wrap=aes_gcm_kw, bits=KeyDataLen, iv=IV, tag=TAG}) when is_binary(Z) andalso is_binary(IV) andalso is_binary(TAG) -> Algorithm = algorithm(JWEECDHSS), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), jose_jwa:block_decrypt({aes_gcm, KeyDataLen}, DerivedKey, IV, {<<>>, EncryptedKey, TAG}); key_decrypt(Z, {_ENCModule, _ENC, EncryptedKey}, JWEECDHSS=#jose_jwe_alg_ecdh_ss{apu=APU, apv=APV, wrap=aes_kw, bits=KeyDataLen}) when is_binary(Z) -> Algorithm = algorithm(JWEECDHSS), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), jose_jwa_aes_kw:unwrap(EncryptedKey, DerivedKey); key_decrypt(Z, {_ENCModule, _ENC, EncryptedKey}, JWEECDHSS=#jose_jwe_alg_ecdh_ss{apu=APU, apv=APV, wrap=c20p_kw, bits=KeyDataLen, iv=IV, tag=TAG}) when is_binary(Z) andalso is_binary(IV) andalso is_binary(TAG) -> Algorithm = algorithm(JWEECDHSS), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), jose_jwa:block_decrypt({chacha20_poly1305, KeyDataLen}, DerivedKey, IV, {<<>>, EncryptedKey, TAG}); key_decrypt(Z, {_ENCModule, _ENC, EncryptedKey}, JWEECDHSS=#jose_jwe_alg_ecdh_ss{apu=APU, apv=APV, wrap=xc20p_kw, bits=KeyDataLen, iv=IV, tag=TAG}) when is_binary(Z) andalso is_binary(IV) andalso is_binary(TAG) -> Algorithm = algorithm(JWEECDHSS), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), jose_jwa:block_decrypt({xchacha20_poly1305, KeyDataLen}, DerivedKey, IV, {<<>>, EncryptedKey, TAG}). key_encrypt(_Key, _DecryptedKey, JWEECDHSS=#jose_jwe_alg_ecdh_ss{wrap=undefined, bits=undefined}) -> {<<>>, JWEECDHSS}; key_encrypt({VStaticPublicKey=#jose_jwk{}, UStaticSecretKey=#jose_jwk{}}, DecryptedKey, JWEECDHSS=#jose_jwe_alg_ecdh_ss{spk=USenderPublicKey=#jose_jwk{}}) -> case jose_jwk:thumbprint(UStaticSecretKey) =:= jose_jwk:thumbprint(USenderPublicKey) of true -> Z = jose_jwk:shared_secret(VStaticPublicKey, UStaticSecretKey), key_encrypt(Z, DecryptedKey, JWEECDHSS); false -> error end; key_encrypt({VStaticPublicKey=#jose_jwk{}, UStaticSecretKey=#jose_jwk{}}, DecryptedKey, JWEECDHSS0=#jose_jwe_alg_ecdh_ss{wrap=undefined, bits=undefined, spk=undefined}) -> USenderPublicKey = jose_jwk:to_public(UStaticSecretKey), JWEECDHSS1 = JWEECDHSS0#jose_jwe_alg_ecdh_ss{spk=USenderPublicKey}, key_encrypt({VStaticPublicKey, UStaticSecretKey}, DecryptedKey, JWEECDHSS1); key_encrypt(#jose_jwk{kty={KTYModule, KTY}}, DecryptedKey, JWEECDHSS) -> DerivedKey = KTYModule:derive_key(KTY), key_encrypt(DerivedKey, DecryptedKey, JWEECDHSS); key_encrypt(Z, DecryptedKey, JWEECDHSS=#jose_jwe_alg_ecdh_ss{apu=APU, apv=APV, wrap=aes_gcm_kw, bits=KeyDataLen, iv=IV}) when is_binary(Z) andalso is_binary(IV) -> Algorithm = algorithm(JWEECDHSS), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), {CipherText, CipherTag} = jose_jwa:block_encrypt({aes_gcm, KeyDataLen}, DerivedKey, IV, {<<>>, DecryptedKey}), {CipherText, JWEECDHSS#jose_jwe_alg_ecdh_ss{ tag = CipherTag }}; key_encrypt(Z, DecryptedKey, JWEECDHSS=#jose_jwe_alg_ecdh_ss{apu=APU, apv=APV, wrap=aes_kw, bits=KeyDataLen}) when is_binary(Z) -> Algorithm = algorithm(JWEECDHSS), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), {jose_jwa_aes_kw:wrap(DecryptedKey, DerivedKey), JWEECDHSS}; key_encrypt(Z, DecryptedKey, JWEECDHSS=#jose_jwe_alg_ecdh_ss{apu=APU, apv=APV, wrap=c20p_kw, bits=KeyDataLen, iv=IV}) when is_binary(Z) andalso is_binary(IV) -> Algorithm = algorithm(JWEECDHSS), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), {CipherText, CipherTag} = jose_jwa:block_encrypt({chacha20_poly1305, KeyDataLen}, DerivedKey, IV, {<<>>, DecryptedKey}), {CipherText, JWEECDHSS#jose_jwe_alg_ecdh_ss{ tag = CipherTag }}; key_encrypt(Z, DecryptedKey, JWEECDHSS=#jose_jwe_alg_ecdh_ss{apu=APU, apv=APV, wrap=xc20p_kw, bits=KeyDataLen, iv=IV}) when is_binary(Z) andalso is_binary(IV) -> Algorithm = algorithm(JWEECDHSS), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), {CipherText, CipherTag} = jose_jwa:block_encrypt({xchacha20_poly1305, KeyDataLen}, DerivedKey, IV, {<<>>, DecryptedKey}), {CipherText, JWEECDHSS#jose_jwe_alg_ecdh_ss{ tag = CipherTag }}; key_encrypt(Z, DecryptedKey, JWEECDHSS=#jose_jwe_alg_ecdh_ss{wrap=aes_gcm_kw, iv=undefined}) when is_binary(Z) -> key_encrypt(Z, DecryptedKey, JWEECDHSS#jose_jwe_alg_ecdh_ss{ iv = crypto:strong_rand_bytes(12) }); key_encrypt(Z, DecryptedKey, JWEECDHSS=#jose_jwe_alg_ecdh_ss{wrap=c20p_kw, iv=undefined}) when is_binary(Z) -> key_encrypt(Z, DecryptedKey, JWEECDHSS#jose_jwe_alg_ecdh_ss{ iv = crypto:strong_rand_bytes(12) }); key_encrypt(Z, DecryptedKey, JWEECDHSS=#jose_jwe_alg_ecdh_ss{wrap=xc20p_kw, iv=undefined}) when is_binary(Z) -> key_encrypt(Z, DecryptedKey, JWEECDHSS#jose_jwe_alg_ecdh_ss{ iv = crypto:strong_rand_bytes(24) }). next_cek({VStaticPublicKey=#jose_jwk{}, UStaticSecretKey=#jose_jwk{}}, {ENCModule, ENC}, JWEECDHSS=#jose_jwe_alg_ecdh_ss{bits=undefined, spk=USenderPublicKey=#jose_jwk{}}) -> case jose_jwk:thumbprint(UStaticSecretKey) =:= jose_jwk:thumbprint(USenderPublicKey) of true -> Z = jose_jwk:shared_secret(VStaticPublicKey, UStaticSecretKey), next_cek(Z, {ENCModule, ENC}, JWEECDHSS); false -> error end; next_cek({VStaticPublicKey=#jose_jwk{}, UStaticSecretKey=#jose_jwk{}}, {ENCModule, ENC}, JWEECDHSS0=#jose_jwe_alg_ecdh_ss{wrap=undefined, bits=undefined, spk=undefined}) -> USenderPublicKey = jose_jwk:to_public(UStaticSecretKey), JWEECDHSS1 = JWEECDHSS0#jose_jwe_alg_ecdh_ss{spk=USenderPublicKey}, next_cek({VStaticPublicKey, UStaticSecretKey}, {ENCModule, ENC}, JWEECDHSS1); next_cek(#jose_jwk{kty={KTYModule, KTY}}, {ENC, ENCModule}, JWEECDHSS=#jose_jwe_alg_ecdh_ss{wrap=undefined, bits=undefined}) -> DerivedKey = KTYModule:derive_key(KTY), next_cek(DerivedKey, {ENCModule, ENC}, JWEECDHSS); next_cek(Z, {ENCModule, ENC}, JWEECDHSS=#jose_jwe_alg_ecdh_ss{apu=APU, apv=APV, wrap=undefined, bits=undefined}) when is_binary(Z) -> Algorithm = ENCModule:algorithm(ENC), KeyDataLen = ENCModule:bits(ENC), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), {DerivedKey, JWEECDHSS}; next_cek(_Key, {ENCModule, ENC}, JWEECDHSS=#jose_jwe_alg_ecdh_ss{}) -> {ENCModule:next_cek(ENC), JWEECDHSS}. %%==================================================================== %% API functions %%==================================================================== algorithm(?ECDH_SS_A128GCMKW) -> <<"ECDH-SS+A128GCMKW">>; algorithm(?ECDH_SS_A192GCMKW) -> <<"ECDH-SS+A192GCMKW">>; algorithm(?ECDH_SS_A256GCMKW) -> <<"ECDH-SS+A256GCMKW">>; algorithm(?ECDH_SS_A128KW) -> <<"ECDH-SS+A128KW">>; algorithm(?ECDH_SS_A192KW) -> <<"ECDH-SS+A192KW">>; algorithm(?ECDH_SS_A256KW) -> <<"ECDH-SS+A256KW">>; algorithm(?ECDH_SS_C20PKW) -> <<"ECDH-SS+C20PKW">>; algorithm(?ECDH_SS_XC20PKW) -> <<"ECDH-SS+XC20PKW">>; algorithm(?ECDH_SS) -> <<"ECDH-SS">>. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private from_map_ecdh_ss(F = #{ <<"spk">> := SPK }, H) -> from_map_ecdh_ss(maps:remove(<<"spk">>, F), H#jose_jwe_alg_ecdh_ss{ spk = jose_jwk:from_map(SPK) }); from_map_ecdh_ss(F = #{ <<"apu">> := APU }, H) -> from_map_ecdh_ss(maps:remove(<<"apu">>, F), H#jose_jwe_alg_ecdh_ss{ apu = jose_jwa_base64url:decode(APU) }); from_map_ecdh_ss(F = #{ <<"apv">> := APV }, H) -> from_map_ecdh_ss(maps:remove(<<"apv">>, F), H#jose_jwe_alg_ecdh_ss{ apv = jose_jwa_base64url:decode(APV) }); from_map_ecdh_ss(F=#{ <<"iv">> := IV }, H) -> from_map_ecdh_ss(maps:remove(<<"iv">>, F), H#jose_jwe_alg_ecdh_ss{ iv = jose_jwa_base64url:decode(IV) }); from_map_ecdh_ss(F=#{ <<"tag">> := TAG }, H) -> from_map_ecdh_ss(maps:remove(<<"tag">>, F), H#jose_jwe_alg_ecdh_ss{ tag = jose_jwa_base64url:decode(TAG) }); from_map_ecdh_ss(F, H) -> {H, F}. %% @private to_map_ecdh_ss(F, H=#jose_jwe_alg_ecdh_ss{ spk = SPK = #jose_jwk{} }) -> to_map_ecdh_ss(F#{ <<"spk">> => element(2, jose_jwk:to_public_map(SPK)) }, H#jose_jwe_alg_ecdh_ss{ spk = undefined }); to_map_ecdh_ss(F, H=#jose_jwe_alg_ecdh_ss{ apu = APU }) when is_binary(APU) -> to_map_ecdh_ss(F#{ <<"apu">> => jose_jwa_base64url:encode(APU) }, H#jose_jwe_alg_ecdh_ss{ apu = undefined }); to_map_ecdh_ss(F, H=#jose_jwe_alg_ecdh_ss{ apv = APV }) when is_binary(APV) -> to_map_ecdh_ss(F#{ <<"apv">> => jose_jwa_base64url:encode(APV) }, H#jose_jwe_alg_ecdh_ss{ apv = undefined }); to_map_ecdh_ss(F, H=#jose_jwe_alg_ecdh_ss{ iv = IV }) when is_binary(IV) -> to_map_ecdh_ss(F#{ <<"iv">> => jose_jwa_base64url:encode(IV) }, H#jose_jwe_alg_ecdh_ss{ iv = undefined }); to_map_ecdh_ss(F, H=#jose_jwe_alg_ecdh_ss{ tag = TAG }) when is_binary(TAG) -> to_map_ecdh_ss(F#{ <<"tag">> => jose_jwa_base64url:encode(TAG) }, H#jose_jwe_alg_ecdh_ss{ tag = undefined }); to_map_ecdh_ss(F, _) -> F. potatosalad-erlang-jose-30ac019/src/jwe/jose_jwe_alg.erl0000664000232200023220000000277515267717064023665 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 23 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwe_alg). -callback key_decrypt(Key, {ENCModule, ENC, EncryptedKey}, ALG) -> DecryptedKey when Key :: any(), ENCModule :: module(), ENC :: any(), EncryptedKey :: iodata(), ALG :: any(), DecryptedKey :: iodata(). -callback key_encrypt(Key, DecryptedKey, ALG) -> {EncryptedKey, NewALG} when Key :: any(), DecryptedKey :: iodata(), ALG :: any(), EncryptedKey :: iodata(), NewALG :: any(). -callback next_cek(Key, {ENCModule, ENC}, ALG) -> {DecryptedKey, NewALG} when Key :: any(), ENCModule :: module(), ENC :: any(), ALG :: any(), DecryptedKey :: iodata(), NewALG :: any(). %% API -export([generate_key/3]). %%==================================================================== %% API functions %%==================================================================== generate_key(Parameters, Algorithm, Encryption) -> jose_jwk:merge(jose_jwk:generate_key(Parameters), #{ <<"alg">> => Algorithm, <<"enc">> => Encryption, <<"use">> => <<"enc">> }). potatosalad-erlang-jose-30ac019/src/jwe/jose_jwe_alg_dir.erl0000664000232200023220000000415715267717064024517 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 23 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwe_alg_dir). -behaviour(jose_jwe). -behaviour(jose_jwe_alg). -include("jose_jwk.hrl"). %% jose_jwe callbacks -export([from_map/1]). -export([to_map/2]). %% jose_jwe_alg callbacks -export([generate_key/3]). -export([key_decrypt/3]). -export([key_encrypt/3]). -export([next_cek/3]). %% API %% Types -type alg() :: dir. -export_type([alg/0]). %%==================================================================== %% jose_jwe callbacks %%==================================================================== from_map(F = #{ <<"alg">> := <<"dir">> }) -> {dir, maps:remove(<<"alg">>, F)}. to_map(dir, F) -> F#{ <<"alg">> => <<"dir">> }. %%==================================================================== %% jose_jwe_alg callbacks %%==================================================================== generate_key(_Fields, {ENCModule, ENC}, dir) -> jose_jwe_alg:generate_key({oct, (ENCModule:bits(ENC) div 8)}, <<"dir">>, ENCModule:algorithm(ENC)). key_decrypt(Key, _EncryptedKey, dir) when is_binary(Key) -> Key; key_decrypt(#jose_jwk{kty={KTYModule, KTY}}, _EncryptedKey, dir) -> KTYModule:derive_key(KTY). key_encrypt(_Key, _DecryptedKey, dir) -> {<<>>, dir}. next_cek(Key, {_ENCModule, _ENC}, dir) when is_binary(Key) -> {Key, dir}; next_cek(#jose_jwk{kty={KTYModule, KTY}}, {ENCModule, ENC}, dir) -> next_cek(KTYModule:derive_key(KTY), {ENCModule, ENC}, dir). %%==================================================================== %% API functions %%==================================================================== %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jwe/jose_jwe_enc_c20p.erl0000664000232200023220000000437415267717064024510 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 31 May 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwe_enc_c20p). -behaviour(jose_jwe). -behaviour(jose_jwe_enc). %% jose_jwe callbacks -export([from_map/1]). -export([to_map/2]). %% jose_jwe_enc callbacks -export([algorithm/1]). -export([bits/1]). -export([block_decrypt/4]). -export([block_encrypt/4]). -export([next_cek/1]). -export([next_iv/1]). %% API -export([cipher_supported/0]). %% Types -type enc() :: {chacha20_poly1305, 256}. -export_type([enc/0]). %% Macros -define(CHACHA20_POLY1305, {chacha20_poly1305, 256}). %%==================================================================== %% jose_jwe callbacks %%==================================================================== from_map(F = #{ <<"enc">> := <<"C20P">> }) -> {?CHACHA20_POLY1305, maps:remove(<<"enc">>, F)}. to_map(?CHACHA20_POLY1305, F) -> F#{ <<"enc">> => <<"C20P">> }. %%==================================================================== %% jose_jwe_enc callbacks %%==================================================================== algorithm(?CHACHA20_POLY1305) -> <<"C20P">>. bits(?CHACHA20_POLY1305) -> 256. block_decrypt({AAD, CipherText, CipherTag}, CEK, IV, ?CHACHA20_POLY1305) -> jose_jwa:block_decrypt(?CHACHA20_POLY1305, CEK, IV, {AAD, CipherText, CipherTag}). block_encrypt({AAD, PlainText}, CEK, IV, ?CHACHA20_POLY1305) -> jose_jwa:block_encrypt(?CHACHA20_POLY1305, CEK, IV, {AAD, PlainText}). next_cek(?CHACHA20_POLY1305) -> crypto:strong_rand_bytes(32). next_iv(?CHACHA20_POLY1305) -> crypto:strong_rand_bytes(12). %%==================================================================== %% API functions %%==================================================================== cipher_supported() -> [chacha20_poly1305]. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jwe/jose_jwe.erl0000664000232200023220000003533215267717064023035 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 21 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwe). -include("jose_jwe.hrl"). -include("jose_jwk.hrl"). -callback from_map(Fields) -> State when Fields :: map(), State :: any(). -callback to_map(State, Fields) -> Map when State :: any(), Fields :: map(), Map :: map(). %% Decode API -export([from/1]). -export([from_binary/1]). -export([from_file/1]). -export([from_map/1]). %% Encode API -export([to_binary/1]). -export([to_file/2]). -export([to_map/1]). %% API -export([block_decrypt/2]). -export([block_encrypt/3]). -export([block_encrypt/4]). -export([block_encrypt/5]). -export([compact/1]). -export([compress/2]). -export([expand/1]). -export([generate_key/1]). -export([key_decrypt/3]). -export([key_encrypt/3]). -export([merge/2]). -export([next_cek/2]). -export([next_iv/1]). -export([uncompress/2]). -define(ALG_AES_KW_MODULE, jose_jwe_alg_aes_kw). -define(ALG_C20P_KW_MODULE, jose_jwe_alg_c20p_kw). -define(ALG_XC20P_KW_MODULE, jose_jwe_alg_xc20p_kw). -define(ALG_DIR_MODULE, jose_jwe_alg_dir). -define(ALG_ECDH_1PU_MODULE, jose_jwe_alg_ecdh_1pu). -define(ALG_ECDH_ES_MODULE, jose_jwe_alg_ecdh_es). -define(ALG_ECDH_SS_MODULE, jose_jwe_alg_ecdh_ss). -define(ALG_PBES2_MODULE, jose_jwe_alg_pbes2). -define(ALG_RSA_MODULE, jose_jwe_alg_rsa). -define(ENC_AES_MODULE, jose_jwe_enc_aes). -define(ENC_C20P_MODULE, jose_jwe_enc_c20p). -define(ENC_XC20P_MODULE, jose_jwe_enc_xc20p). -define(ZIP_MODULE, jose_jwe_zip). %%==================================================================== %% Decode API functions %%==================================================================== from({Modules, Map}) when is_map(Modules) andalso is_map(Map) -> from_map({Modules, Map}); from({Modules, Binary}) when is_map(Modules) andalso is_binary(Binary) -> from_binary({Modules, Binary}); from(JWE=#jose_jwe{}) -> JWE; from(Other) when is_map(Other) orelse is_binary(Other) -> from({#{}, Other}). from_binary({Modules, Binary}) when is_map(Modules) andalso is_binary(Binary) -> from_map({Modules, jose:decode(Binary)}); from_binary(Binary) when is_binary(Binary) -> from_binary({#{}, Binary}). from_file({Modules, File}) when is_map(Modules) andalso (is_binary(File) orelse is_list(File)) -> case file:read_file(File) of {ok, Binary} -> from_binary({Modules, Binary}); ReadError -> ReadError end; from_file(File) when is_binary(File) orelse is_list(File) -> from_file({#{}, File}). from_map(Map) when is_map(Map) -> from_map({#{}, Map}); from_map({Modules, Map}) when is_map(Modules) andalso is_map(Map) -> from_map({#jose_jwe{}, Modules, Map}); from_map({JWE, Modules = #{ alg := Module }, Map=#{ <<"alg">> := _ }}) -> {ALG, Fields} = Module:from_map(Map), from_map({JWE#jose_jwe{ alg = {Module, ALG} }, maps:remove(alg, Modules), Fields}); from_map({JWE, Modules = #{ enc := Module }, Map=#{ <<"enc">> := _ }}) -> {ENC, Fields} = Module:from_map(Map), from_map({JWE#jose_jwe{ enc = {Module, ENC} }, maps:remove(enc, Modules), Fields}); from_map({JWE, Modules = #{ zip := Module }, Map=#{ <<"zip">> := _ }}) -> {ZIP, Fields} = Module:from_map(Map), from_map({JWE#jose_jwe{ zip = {Module, ZIP} }, maps:remove(zip, Modules), Fields}); from_map({JWE, Modules, Map=#{ <<"alg">> := << "A", _, _, _, "GCMKW", _/binary >> }}) -> from_map({JWE, Modules#{ alg => ?ALG_AES_KW_MODULE }, Map}); from_map({JWE, Modules, Map=#{ <<"alg">> := << "A", _, _, _, "KW", _/binary >> }}) -> from_map({JWE, Modules#{ alg => ?ALG_AES_KW_MODULE }, Map}); from_map({JWE, Modules, Map=#{ <<"alg">> := << "C20PKW" >> }}) -> from_map({JWE, Modules#{ alg => ?ALG_C20P_KW_MODULE }, Map}); from_map({JWE, Modules, Map=#{ <<"alg">> := << "dir", _/binary >> }}) -> from_map({JWE, Modules#{ alg => ?ALG_DIR_MODULE }, Map}); from_map({JWE, Modules, Map=#{ <<"alg">> := << "ECDH-1PU", _/binary >> }}) -> from_map({JWE, Modules#{ alg => ?ALG_ECDH_1PU_MODULE }, Map}); from_map({JWE, Modules, Map=#{ <<"alg">> := << "ECDH-ES", _/binary >> }}) -> from_map({JWE, Modules#{ alg => ?ALG_ECDH_ES_MODULE }, Map}); from_map({JWE, Modules, Map=#{ <<"alg">> := << "ECDH-SS", _/binary >> }}) -> from_map({JWE, Modules#{ alg => ?ALG_ECDH_SS_MODULE }, Map}); from_map({JWE, Modules, Map=#{ <<"alg">> := << "PBES2", _/binary >> }}) -> from_map({JWE, Modules#{ alg => ?ALG_PBES2_MODULE }, Map}); from_map({JWE, Modules, Map=#{ <<"alg">> := << "RSA", _/binary >> }}) -> from_map({JWE, Modules#{ alg => ?ALG_RSA_MODULE }, Map}); from_map({JWE, Modules, Map=#{ <<"alg">> := << "XC20PKW" >> }}) -> from_map({JWE, Modules#{ alg => ?ALG_XC20P_KW_MODULE }, Map}); from_map({JWE, Modules, Map=#{ <<"enc">> := << "A", _/binary >> }}) -> from_map({JWE, Modules#{ enc => ?ENC_AES_MODULE }, Map}); from_map({JWE, Modules, Map=#{ <<"enc">> := << "C20P" >> }}) -> from_map({JWE, Modules#{ enc => ?ENC_C20P_MODULE }, Map}); from_map({JWE, Modules, Map=#{ <<"enc">> := << "XC20P" >> }}) -> from_map({JWE, Modules#{ enc => ?ENC_XC20P_MODULE }, Map}); from_map({JWE, Modules, Map=#{ <<"zip">> := << "DEF" >> }}) -> from_map({JWE, Modules#{ zip => ?ZIP_MODULE }, Map}); from_map({#jose_jwe{ alg = undefined, enc = undefined }, _Modules, _Map}) -> {error, {missing_required_keys, [<<"alg">>, <<"enc">>]}}; from_map({JWE, _Modules, Fields}) -> case {maps:is_key(<<"alg">>, Fields), maps:is_key(<<"enc">>, Fields)} of {true, true} -> {error, {invalid_fields, [{<<"alg">>, maps:get(<<"alg">>, Fields)}, {<<"enc">>, maps:get(<<"enc">>, Fields)}]}}; {true, false} -> {error, {invalid_fields, [{<<"alg">>, maps:get(<<"alg">>, Fields)}]}}; {false, true} -> {error, {invalid_fields, [{<<"enc">>, maps:get(<<"enc">>, Fields)}]}}; {false, false} -> JWE#jose_jwe{ fields = Fields } end. %%==================================================================== %% Encode API functions %%==================================================================== to_binary(JWE=#jose_jwe{}) -> {Modules, Map} = to_map(JWE), {Modules, jose:encode(Map)}; to_binary(Other) -> to_binary(from(Other)). to_file(File, JWE=#jose_jwe{}) when is_binary(File) orelse is_list(File) -> {Modules, Binary} = to_binary(JWE), case file:write_file(File, Binary) of ok -> {Modules, File}; WriteError -> WriteError end; to_file(File, Other) when is_binary(File) orelse is_list(File) -> to_file(File, from(Other)). to_map(JWE=#jose_jwe{fields=Fields}) -> record_to_map(JWE, #{}, Fields); to_map(Other) -> to_map(from(Other)). %%==================================================================== %% API functions %%==================================================================== block_decrypt(Key, EncryptedMap) when is_map(EncryptedMap) -> block_decrypt(Key, {#{}, EncryptedMap}); block_decrypt(Key, EncryptedBinary) when is_binary(EncryptedBinary) -> block_decrypt(Key, expand(EncryptedBinary)); block_decrypt(Key, {Modules, EncryptedBinary}) when is_binary(EncryptedBinary) -> block_decrypt(Key, expand({Modules, EncryptedBinary})); block_decrypt(Key, {Modules, EncryptedMap=#{ <<"protected">> := Protected, <<"encrypted_key">> := EncodedEncryptedKey, <<"iv">> := EncodedIV, <<"ciphertext">> := EncodedCipherText, <<"tag">> := EncodedCipherTag}}) -> case maps:is_key(<<"aad">>, EncryptedMap) of false -> JWE = #jose_jwe{enc={ENCModule, ENC}} = from_binary({Modules, jose_jwa_base64url:decode(Protected)}), EncryptedKey = jose_jwa_base64url:decode(EncodedEncryptedKey), IV = jose_jwa_base64url:decode(EncodedIV), CipherText = jose_jwa_base64url:decode(EncodedCipherText), CipherTag = jose_jwa_base64url:decode(EncodedCipherTag), case key_decrypt(Key, EncryptedKey, JWE) of error -> {error, JWE}; CEK when is_binary(CEK) -> PlainText = uncompress(ENCModule:block_decrypt({Protected, CipherText, CipherTag}, CEK, IV, ENC), JWE), {PlainText, JWE} end; true -> EncodedAAD = maps:get(<<"aad">>, EncryptedMap), ConcatAAD = << Protected/binary, $., EncodedAAD/binary >>, JWE = #jose_jwe{enc={ENCModule, ENC}} = from_binary({Modules, jose_jwa_base64url:decode(Protected)}), EncryptedKey = jose_jwa_base64url:decode(EncodedEncryptedKey), IV = jose_jwa_base64url:decode(EncodedIV), CipherText = jose_jwa_base64url:decode(EncodedCipherText), CipherTag = jose_jwa_base64url:decode(EncodedCipherTag), case key_decrypt(Key, EncryptedKey, JWE) of error -> {error, JWE}; CEK when is_binary(CEK) -> PlainText = uncompress(ENCModule:block_decrypt({ConcatAAD, CipherText, CipherTag}, CEK, IV, ENC), JWE), {PlainText, JWE} end end. block_encrypt(Key, Block, JWE0=#jose_jwe{}) -> {CEK, JWE1} = next_cek(Key, JWE0), block_encrypt(Key, Block, CEK, JWE1); block_encrypt(Key, PlainText, Other) -> block_encrypt(Key, PlainText, from(Other)). block_encrypt(Key, Block, CEK, JWE=#jose_jwe{}) -> IV = next_iv(JWE), block_encrypt(Key, Block, CEK, IV, JWE); block_encrypt(Key, Block, CEK, Other) -> block_encrypt(Key, Block, CEK, from(Other)). block_encrypt(Key, PlainText, CEK, IV, JWE0=#jose_jwe{enc={ENCModule, ENC}}) when is_binary(PlainText) -> {EncryptedKey, JWE1} = key_encrypt(Key, CEK, JWE0), {Modules, ProtectedBinary} = to_binary(JWE1), Protected = jose_jwa_base64url:encode(ProtectedBinary), {CipherText, CipherTag} = ENCModule:block_encrypt({Protected, compress(PlainText, JWE1)}, CEK, IV, ENC), {Modules, #{ <<"protected">> => Protected, <<"encrypted_key">> => jose_jwa_base64url:encode(EncryptedKey), <<"iv">> => jose_jwa_base64url:encode(IV), <<"ciphertext">> => jose_jwa_base64url:encode(CipherText), <<"tag">> => jose_jwa_base64url:encode(CipherTag) }}; block_encrypt(Key, {AAD0, PlainText}, CEK, IV, JWE0=#jose_jwe{enc={ENCModule, ENC}}) when is_binary(AAD0) andalso is_binary(PlainText) -> {EncryptedKey, JWE1} = key_encrypt(Key, CEK, JWE0), {Modules, ProtectedBinary} = to_binary(JWE1), Protected = jose_jwa_base64url:encode(ProtectedBinary), AAD1 = jose_jwa_base64url:encode(AAD0), ConcatAAD = << Protected/binary, $., AAD1/binary >>, {CipherText, CipherTag} = ENCModule:block_encrypt({ConcatAAD, compress(PlainText, JWE1)}, CEK, IV, ENC), {Modules, #{ <<"protected">> => Protected, <<"encrypted_key">> => jose_jwa_base64url:encode(EncryptedKey), <<"iv">> => jose_jwa_base64url:encode(IV), <<"ciphertext">> => jose_jwa_base64url:encode(CipherText), <<"tag">> => jose_jwa_base64url:encode(CipherTag), <<"aad">> => AAD1 }}; block_encrypt(Key, Block, CEK, IV, Other) when is_binary(Block) orelse (is_tuple(Block) andalso tuple_size(Block) =:= 2) -> block_encrypt(Key, Block, CEK, IV, from(Other)). compact({Modules, EncryptedMap=#{ <<"protected">> := Protected, <<"encrypted_key">> := EncryptedKey, <<"iv">> := InitializationVector, <<"ciphertext">> := CipherText, <<"tag">> := AuthenticationTag}}) -> case maps:is_key(<<"aad">>, EncryptedMap) of false -> {Modules, << Protected/binary, $., EncryptedKey/binary, $., InitializationVector/binary, $., CipherText/binary, $., AuthenticationTag/binary >>}; true -> erlang:error({badarg, [{Modules, EncryptedMap}]}) end; compact(Map) when is_map(Map) -> compact({#{}, Map}). compress(PlainText, #jose_jwe{zip={Module, ZIP}}) -> Module:compress(PlainText, ZIP); compress(PlainText, #jose_jwe{}) -> PlainText; compress(PlainText, Other) -> compress(PlainText, from(Other)). expand({Modules, Binary}) when is_binary(Binary) -> case binary:split(Binary, <<".">>, [global]) of [Protected, EncryptedKey, InitializationVector, CipherText, AuthenticationTag] -> {Modules, #{ <<"protected">> => Protected, <<"encrypted_key">> => EncryptedKey, <<"iv">> => InitializationVector, <<"ciphertext">> => CipherText, <<"tag">> => AuthenticationTag }}; _ -> erlang:error({badarg, [{Modules, Binary}]}) end; expand(Binary) when is_binary(Binary) -> expand({#{}, Binary}). generate_key(List) when is_list(List) -> [generate_key(Element) || Element <- List]; generate_key(#jose_jwe{alg={ALGModule, ALG}, enc={ENCModule, ENC}, fields=Fields}) -> ALGModule:generate_key(Fields, {ENCModule, ENC}, ALG); generate_key(Other) -> generate_key(from(Other)). key_decrypt(Key, EncryptedKey, #jose_jwe{alg={ALGModule, ALG}, enc={ENCModule, ENC}}) -> ALGModule:key_decrypt(Key, {ENCModule, ENC, EncryptedKey}, ALG); key_decrypt(Key, EncryptedKey, JWE=#jose_jwe{}) -> erlang:error({badarg, [Key, EncryptedKey, JWE]}); key_decrypt(Key, EncryptedKey, Other) -> key_decrypt(Key, EncryptedKey, from(Other)). key_encrypt(Key, DecryptedKey, JWE0=#jose_jwe{alg={ALGModule, ALG0}}) -> {EncryptedKey, ALG1} = ALGModule:key_encrypt(Key, DecryptedKey, ALG0), JWE1 = JWE0#jose_jwe{alg={ALGModule, ALG1}}, {EncryptedKey, JWE1}; key_encrypt(Key, EncryptedKey, Other) -> key_encrypt(Key, EncryptedKey, from(Other)). merge(LeftJWE=#jose_jwe{}, RightMap) when is_map(RightMap) -> {Modules, LeftMap} = to_map(LeftJWE), from_map({Modules, maps:merge(LeftMap, RightMap)}); merge(LeftOther, RightJWE=#jose_jwe{}) -> merge(LeftOther, element(2, to_map(RightJWE))); merge(LeftOther, RightMap) when is_map(RightMap) -> merge(from(LeftOther), RightMap). next_cek(Key, JWE0=#jose_jwe{alg={ALGModule, ALG0}, enc={ENCModule, ENC}}) -> {DecryptedKey, ALG1} = ALGModule:next_cek(Key, {ENCModule, ENC}, ALG0), JWE1 = JWE0#jose_jwe{alg={ALGModule, ALG1}}, {DecryptedKey, JWE1}; next_cek(Key, Other) -> next_cek(Key, from(Other)). next_iv(#jose_jwe{enc={ENCModule, ENC}}) -> ENCModule:next_iv(ENC); next_iv(Other) -> next_iv(from(Other)). %% Authentication failure must never be passed to a decompressor. uncompress(error, #jose_jwe{}) -> error; uncompress(CipherText, #jose_jwe{zip={Module, ZIP}}) -> Module:uncompress(CipherText, ZIP); uncompress(CipherText, #jose_jwe{}) -> CipherText; uncompress(CipherText, Other) -> uncompress(CipherText, from(Other)). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private record_to_map(JWE=#jose_jwe{alg={Module, ALG}}, Modules, Fields0) -> Fields1 = Module:to_map(ALG, Fields0), record_to_map(JWE#jose_jwe{alg=undefined}, Modules#{ alg => Module }, Fields1); record_to_map(JWE=#jose_jwe{enc={Module, ENC}}, Modules, Fields0) -> Fields1 = Module:to_map(ENC, Fields0), record_to_map(JWE#jose_jwe{enc=undefined}, Modules#{ enc => Module }, Fields1); record_to_map(JWE=#jose_jwe{zip={Module, ZIP}}, Modules, Fields0) -> Fields1 = Module:to_map(ZIP, Fields0), record_to_map(JWE#jose_jwe{zip=undefined}, Modules#{ zip => Module }, Fields1); record_to_map(_JWE, Modules, Fields) -> {Modules, Fields}. potatosalad-erlang-jose-30ac019/src/jwe/jose_jwe_alg_xc20p_kw.erl0000664000232200023220000001012415267717064025365 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 14 Sep 2019 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwe_alg_xc20p_kw). -behaviour(jose_jwe). -behaviour(jose_jwe_alg). -include("jose_jwk.hrl"). %% jose_jwe callbacks -export([from_map/1]). -export([to_map/2]). %% jose_jwe_alg callbacks -export([generate_key/3]). -export([key_decrypt/3]). -export([key_encrypt/3]). -export([next_cek/3]). %% API %% Types -record(jose_jwe_alg_xc20p_kw, { iv = undefined :: undefined | binary(), tag = undefined :: undefined | binary() }). -type alg() :: #jose_jwe_alg_xc20p_kw{}. -export_type([alg/0]). %% Macros -define(BITS, 256). -define(XC20PKW, #jose_jwe_alg_xc20p_kw{}). %%==================================================================== %% jose_jwe callbacks %%==================================================================== from_map(F = #{ <<"alg">> := <<"XC20PKW">> }) -> from_map_xc20p(?XC20PKW, maps:remove(<<"alg">>, F)). to_map(A = ?XC20PKW, F) -> to_map_xc20p(A, F#{ <<"alg">> => <<"XC20PKW">> }). %%==================================================================== %% jose_jwe_alg callbacks %%==================================================================== generate_key(_Fields, {ENCModule, ENC}, ALG=#jose_jwe_alg_xc20p_kw{}) -> jose_jwe_alg:generate_key({oct, (?BITS div 8)}, maps:get(<<"alg">>, to_map(ALG, #{})), ENCModule:algorithm(ENC)). key_decrypt(DerivedKey, {_ENCModule, _ENC, EncryptedKey}, #jose_jwe_alg_xc20p_kw{iv=IV, tag=TAG}) when is_binary(DerivedKey) andalso bit_size(DerivedKey) =:= ?BITS andalso is_binary(IV) andalso is_binary(TAG) -> jose_jwa:block_decrypt({xchacha20_poly1305, ?BITS}, DerivedKey, IV, {<<>>, EncryptedKey, TAG}); key_decrypt(#jose_jwk{kty={KTYModule, KTY}}, EncryptedKey, JWEXC20PKW=#jose_jwe_alg_xc20p_kw{}) -> key_decrypt(KTYModule:derive_key(KTY), EncryptedKey, JWEXC20PKW). key_encrypt(DerivedKey, DecryptedKey, JWEXC20PKW=#jose_jwe_alg_xc20p_kw{iv=IV}) when is_binary(DerivedKey) andalso bit_size(DerivedKey) =:= ?BITS andalso is_binary(IV) -> {CipherText, CipherTag} = jose_jwa:block_encrypt({xchacha20_poly1305, ?BITS}, DerivedKey, IV, {<<>>, DecryptedKey}), {CipherText, JWEXC20PKW#jose_jwe_alg_xc20p_kw{ tag = CipherTag }}; key_encrypt(DerivedKey, DecryptedKey, JWEXC20PKW=#jose_jwe_alg_xc20p_kw{iv=undefined}) -> key_encrypt(DerivedKey, DecryptedKey, JWEXC20PKW#jose_jwe_alg_xc20p_kw{ iv = crypto:strong_rand_bytes(24) }); key_encrypt(#jose_jwk{kty={KTYModule, KTY}}, DecryptedKey, JWEXC20PKW=#jose_jwe_alg_xc20p_kw{}) -> key_encrypt(KTYModule:derive_key(KTY), DecryptedKey, JWEXC20PKW). next_cek(_Key, {ENCModule, ENC}, ALG=#jose_jwe_alg_xc20p_kw{}) -> {ENCModule:next_cek(ENC), ALG}. %%==================================================================== %% API functions %%==================================================================== %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private from_map_xc20p(A, F=#{ <<"iv">> := IV }) -> from_map_xc20p(A#jose_jwe_alg_xc20p_kw{ iv = jose_jwa_base64url:decode(IV) }, maps:remove(<<"iv">>, F)); from_map_xc20p(A, F=#{ <<"tag">> := TAG }) -> from_map_xc20p(A#jose_jwe_alg_xc20p_kw{ tag = jose_jwa_base64url:decode(TAG) }, maps:remove(<<"tag">>, F)); from_map_xc20p(A, F) -> {A, F}. %% @private to_map_xc20p(#jose_jwe_alg_xc20p_kw{ iv = undefined, tag = undefined }, F) -> F; to_map_xc20p(A=#jose_jwe_alg_xc20p_kw{ iv = IV }, F) when is_binary(IV) -> to_map_xc20p(A#jose_jwe_alg_xc20p_kw{ iv = undefined }, F#{ <<"iv">> => jose_jwa_base64url:encode(IV) }); to_map_xc20p(A=#jose_jwe_alg_xc20p_kw{ tag = TAG }, F) when is_binary(TAG) -> to_map_xc20p(A#jose_jwe_alg_xc20p_kw{ tag = undefined }, F#{ <<"tag">> => jose_jwa_base64url:encode(TAG) }). potatosalad-erlang-jose-30ac019/src/jwe/jose_jwe_enc.erl0000664000232200023220000000255015267717064023656 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 29 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwe_enc). -callback algorithm(ENC) -> Algorithm when ENC :: any(), Algorithm :: iodata(). -callback bits(ENC) -> Bits when ENC :: any(), Bits :: non_neg_integer(). -optional_callbacks([algorithm/1]). -optional_callbacks([bits/1]). -callback block_decrypt({AAD, CipherText, CipherTag}, CEK, IV, ENC) -> PlainText | error when AAD :: iodata(), CipherText :: iodata(), CipherTag :: iodata(), CEK :: iodata(), IV :: iodata(), ENC :: any(), PlainText :: iodata(). -callback block_encrypt({AAD, PlainText}, CEK, IV, ENC) -> {CipherText, CipherTag} when AAD :: iodata(), PlainText :: iodata(), CEK :: iodata(), IV :: iodata(), ENC :: any(), CipherText :: iodata(), CipherTag :: iodata(). -callback next_cek(ENC) -> CEK when ENC :: any(), CEK :: iodata(). -callback next_iv(ENC) -> IV when ENC :: any(), IV :: iodata(). potatosalad-erlang-jose-30ac019/src/jwe/jose_jwe_zip.erl0000664000232200023220000000731415267717064023716 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 22 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwe_zip). -behaviour(jose_jwe). -callback compress(Uncompressed, ZIP) -> Compressed when Uncompressed :: iodata(), ZIP :: any(), Compressed :: iodata(). -callback uncompress(Compressed, ZIP) -> Uncompressed when Compressed :: iodata(), ZIP :: any(), Uncompressed :: iodata(). %% jose_jwe callbacks -export([from_map/1]). -export([to_map/2]). %% jose_jwe_zip callbacks -export([compress/2]). -export([uncompress/2]). %% API -export([format_error/2]). -export([zip_supported/0]). %% Types -type zip() :: zlib. -export_type([zip/0]). -define(DEF, zlib). %%==================================================================== %% jose_jwe callbacks %%==================================================================== from_map(Fields = #{ <<"zip">> := <<"DEF">> }) -> {?DEF, maps:remove(<<"zip">>, Fields)}. to_map(?DEF, Fields) -> Fields#{ <<"zip">> => <<"DEF">> }. %%==================================================================== %% jose_jwe_zip callbacks %%==================================================================== compress(Uncompressed, zlib) -> Z = zlib:open(), ok = zlib:deflateInit(Z, default, deflated, -15, 8, default), Compressed = zlib:deflate(Z, Uncompressed, finish), ok = zlib:deflateEnd(Z), ok = zlib:close(Z), iolist_to_binary(Compressed). uncompress(Compressed, zlib) -> ZipSizeMaximum = jose:zip_size_maximum(), Z = zlib:open(), try ok = zlib:inflateInit(Z, -15), Uncompressed = safe_inflate(Z, Compressed, ZipSizeMaximum), ok = zlib:inflateEnd(Z), Uncompressed after ok = zlib:close(Z) end. %%==================================================================== %% API functions %%==================================================================== -spec format_error(term(), term()) -> term(). format_error(_Reason, [{_M, _F, _As, Info} | _]) -> ErrorInfo = proplists:get_value(error_info, Info, #{}), ErrorDescription1 = maps:get(cause, ErrorInfo), ErrorDescription1. zip_supported() -> [zlib]. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private %% Incrementally inflate so that a decompression bomb is rejected before the %% whole payload has been materialized, rather than after. safe_inflate(Z, Compressed, ZipSizeMaximum) -> safe_inflate(zlib:safeInflate(Z, Compressed), Z, ZipSizeMaximum, 0, []). %% @private safe_inflate({continue, Output}, Z, ZipSizeMaximum, Size0, Acc) -> Size1 = Size0 + erlang:iolist_size(Output), ok = safe_inflate_check(Size1, ZipSizeMaximum), safe_inflate(zlib:safeInflate(Z, []), Z, ZipSizeMaximum, Size1, [Output | Acc]); safe_inflate({finished, Output}, _Z, ZipSizeMaximum, Size0, Acc) -> Size1 = Size0 + erlang:iolist_size(Output), ok = safe_inflate_check(Size1, ZipSizeMaximum), erlang:iolist_to_binary(lists:reverse([Output | Acc])). %% @private safe_inflate_check(Size, ZipSizeMaximum) when Size =< ZipSizeMaximum -> ok; safe_inflate_check(Size, ZipSizeMaximum) -> erlang:error(badarg, [<<"REDACTED">>, zlib], [ {error_info, #{ module => ?MODULE, cause => #{1 => lists:flatten(io_lib:format("maximum uncompressed size is set to ~w bytes, but at least ~w bytes was attempted (see jose:zip_size_maximum/0)", [ZipSizeMaximum, Size]))} }} ]). potatosalad-erlang-jose-30ac019/src/jwe/jose_jwe_alg_aes_kw.erl0000664000232200023220000001332315267717064025205 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 23 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwe_alg_aes_kw). -behaviour(jose_jwe). -behaviour(jose_jwe_alg). -include("jose_jwk.hrl"). %% jose_jwe callbacks -export([from_map/1]). -export([to_map/2]). %% jose_jwe_alg callbacks -export([generate_key/3]). -export([key_decrypt/3]). -export([key_encrypt/3]). -export([next_cek/3]). %% API %% Types -record(jose_jwe_alg_aes_kw, { bits = undefined :: undefined | 128 | 192 | 256, gcm = false :: boolean(), iv = undefined :: undefined | binary(), tag = undefined :: undefined | binary() }). -type alg() :: #jose_jwe_alg_aes_kw{}. -export_type([alg/0]). -define(A128KW, #jose_jwe_alg_aes_kw{bits=128, gcm=false}). -define(A192KW, #jose_jwe_alg_aes_kw{bits=192, gcm=false}). -define(A256KW, #jose_jwe_alg_aes_kw{bits=256, gcm=false}). -define(A128GCMKW, #jose_jwe_alg_aes_kw{bits=128, gcm=true}). -define(A192GCMKW, #jose_jwe_alg_aes_kw{bits=192, gcm=true}). -define(A256GCMKW, #jose_jwe_alg_aes_kw{bits=256, gcm=true}). %%==================================================================== %% jose_jwe callbacks %%==================================================================== from_map(F = #{ <<"alg">> := <<"A128KW">> }) -> {?A128KW, maps:remove(<<"alg">>, F)}; from_map(F = #{ <<"alg">> := <<"A192KW">> }) -> {?A192KW, maps:remove(<<"alg">>, F)}; from_map(F = #{ <<"alg">> := <<"A256KW">> }) -> {?A256KW, maps:remove(<<"alg">>, F)}; from_map(F = #{ <<"alg">> := <<"A128GCMKW">> }) -> from_map_aes_gcm(?A128GCMKW, maps:remove(<<"alg">>, F)); from_map(F = #{ <<"alg">> := <<"A192GCMKW">> }) -> from_map_aes_gcm(?A192GCMKW, maps:remove(<<"alg">>, F)); from_map(F = #{ <<"alg">> := <<"A256GCMKW">> }) -> from_map_aes_gcm(?A256GCMKW, maps:remove(<<"alg">>, F)). to_map(?A128KW, F) -> F#{ <<"alg">> => <<"A128KW">> }; to_map(?A192KW, F) -> F#{ <<"alg">> => <<"A192KW">> }; to_map(?A256KW, F) -> F#{ <<"alg">> => <<"A256KW">> }; to_map(A = ?A128GCMKW, F) -> to_map_aes_gcm(A, F#{ <<"alg">> => <<"A128GCMKW">> }); to_map(A = ?A192GCMKW, F) -> to_map_aes_gcm(A, F#{ <<"alg">> => <<"A192GCMKW">> }); to_map(A = ?A256GCMKW, F) -> to_map_aes_gcm(A, F#{ <<"alg">> => <<"A256GCMKW">> }). %%==================================================================== %% jose_jwe_alg callbacks %%==================================================================== generate_key(_Fields, {ENCModule, ENC}, ALG=#jose_jwe_alg_aes_kw{bits=Bits}) -> jose_jwe_alg:generate_key({oct, (Bits div 8)}, maps:get(<<"alg">>, to_map(ALG, #{})), ENCModule:algorithm(ENC)). key_decrypt(DerivedKey, {_ENCModule, _ENC, EncryptedKey}, #jose_jwe_alg_aes_kw{bits=Bits, gcm=false}) when is_binary(DerivedKey) andalso bit_size(DerivedKey) =:= Bits -> jose_jwa_aes_kw:unwrap(EncryptedKey, DerivedKey); key_decrypt(DerivedKey, {_ENCModule, _ENC, EncryptedKey}, #jose_jwe_alg_aes_kw{bits=Bits, gcm=true, iv=IV, tag=TAG}) when is_binary(DerivedKey) andalso bit_size(DerivedKey) =:= Bits andalso is_binary(IV) andalso is_binary(TAG) -> jose_jwa:block_decrypt({aes_gcm, Bits}, DerivedKey, IV, {<<>>, EncryptedKey, TAG}); key_decrypt(#jose_jwk{kty={KTYModule, KTY}}, EncryptedKey, JWEAESKW=#jose_jwe_alg_aes_kw{}) -> key_decrypt(KTYModule:derive_key(KTY), EncryptedKey, JWEAESKW). key_encrypt(DerivedKey, DecryptedKey, JWEAESKW=#jose_jwe_alg_aes_kw{bits=Bits, gcm=false}) when is_binary(DerivedKey) andalso bit_size(DerivedKey) =:= Bits -> {jose_jwa_aes_kw:wrap(DecryptedKey, DerivedKey), JWEAESKW}; key_encrypt(DerivedKey, DecryptedKey, JWEAESKW=#jose_jwe_alg_aes_kw{bits=Bits, gcm=true, iv=IV}) when is_binary(DerivedKey) andalso bit_size(DerivedKey) =:= Bits andalso is_binary(IV) -> {CipherText, CipherTag} = jose_jwa:block_encrypt({aes_gcm, Bits}, DerivedKey, IV, {<<>>, DecryptedKey}), {CipherText, JWEAESKW#jose_jwe_alg_aes_kw{ tag = CipherTag }}; key_encrypt(DerivedKey, DecryptedKey, JWEAESKW=#jose_jwe_alg_aes_kw{gcm=true, iv=undefined}) -> key_encrypt(DerivedKey, DecryptedKey, JWEAESKW#jose_jwe_alg_aes_kw{ iv = crypto:strong_rand_bytes(12) }); key_encrypt(#jose_jwk{kty={KTYModule, KTY}}, DecryptedKey, JWEAESKW=#jose_jwe_alg_aes_kw{}) -> key_encrypt(KTYModule:derive_key(KTY), DecryptedKey, JWEAESKW). next_cek(_Key, {ENCModule, ENC}, ALG=#jose_jwe_alg_aes_kw{}) -> {ENCModule:next_cek(ENC), ALG}. %%==================================================================== %% API functions %%==================================================================== %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private from_map_aes_gcm(A, F=#{ <<"iv">> := IV }) -> from_map_aes_gcm(A#jose_jwe_alg_aes_kw{ iv = jose_jwa_base64url:decode(IV) }, maps:remove(<<"iv">>, F)); from_map_aes_gcm(A, F=#{ <<"tag">> := TAG }) -> from_map_aes_gcm(A#jose_jwe_alg_aes_kw{ tag = jose_jwa_base64url:decode(TAG) }, maps:remove(<<"tag">>, F)); from_map_aes_gcm(A, F) -> {A, F}. %% @private to_map_aes_gcm(#jose_jwe_alg_aes_kw{ iv = undefined, tag = undefined }, F) -> F; to_map_aes_gcm(A=#jose_jwe_alg_aes_kw{ iv = IV }, F) when is_binary(IV) -> to_map_aes_gcm(A#jose_jwe_alg_aes_kw{ iv = undefined }, F#{ <<"iv">> => jose_jwa_base64url:encode(IV) }); to_map_aes_gcm(A=#jose_jwe_alg_aes_kw{ tag = TAG }, F) when is_binary(TAG) -> to_map_aes_gcm(A#jose_jwe_alg_aes_kw{ tag = undefined }, F#{ <<"tag">> => jose_jwa_base64url:encode(TAG) }). potatosalad-erlang-jose-30ac019/src/jwe/jose_jwe_alg_ecdh_1pu.erl0000664000232200023220000004067215267717064025433 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc Key Agreement with Elliptic Curve Diffie-Hellman One-Pass Unified Model (ECDH-1PU) %%% See https://datatracker.ietf.org/doc/html/draft-madden-jose-ecdh-1pu-04 %%% %%% @end %%% Created : 29 Dec 2019 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwe_alg_ecdh_1pu). -behaviour(jose_jwe). -behaviour(jose_jwe_alg). -include_lib("public_key/include/public_key.hrl"). -include("jose_jwk.hrl"). %% jose_jwe callbacks -export([from_map/1]). -export([to_map/2]). %% jose_jwe_alg callbacks -export([generate_key/3]). -export([key_decrypt/3]). -export([key_encrypt/3]). -export([next_cek/3]). %% API -export([algorithm/1]). %% Types -type ec_public_key() :: {#'ECPoint'{},{namedCurve, Oid::tuple()} | #'ECParameters'{}} | term(). -record(jose_jwe_alg_ecdh_1pu, { epk = undefined :: undefined | {ec_public_key(), map()}, apu = undefined :: undefined | binary(), apv = undefined :: undefined | binary(), wrap = undefined :: undefined | aes_gcm_kw | aes_kw | c20p_kw | xc20p_kw, bits = undefined :: undefined | 128 | 192 | 256, iv = undefined :: undefined | binary(), tag = undefined :: undefined | binary() }). -type alg() :: #jose_jwe_alg_ecdh_1pu{}. -export_type([alg/0]). -define(ECDH_1PU, #jose_jwe_alg_ecdh_1pu{}). -define(ECDH_1PU_A128GCMKW, #jose_jwe_alg_ecdh_1pu{wrap=aes_gcm_kw, bits=128}). -define(ECDH_1PU_A192GCMKW, #jose_jwe_alg_ecdh_1pu{wrap=aes_gcm_kw, bits=192}). -define(ECDH_1PU_A256GCMKW, #jose_jwe_alg_ecdh_1pu{wrap=aes_gcm_kw, bits=256}). -define(ECDH_1PU_A128KW, #jose_jwe_alg_ecdh_1pu{wrap=aes_kw, bits=128}). -define(ECDH_1PU_A192KW, #jose_jwe_alg_ecdh_1pu{wrap=aes_kw, bits=192}). -define(ECDH_1PU_A256KW, #jose_jwe_alg_ecdh_1pu{wrap=aes_kw, bits=256}). -define(ECDH_1PU_C20PKW, #jose_jwe_alg_ecdh_1pu{wrap=c20p_kw, bits=256}). -define(ECDH_1PU_XC20PKW, #jose_jwe_alg_ecdh_1pu{wrap=xc20p_kw, bits=256}). %%==================================================================== %% jose_jwe callbacks %%==================================================================== from_map(F = #{ <<"alg">> := <<"ECDH-1PU">> }) -> from_map_ecdh_1pu(maps:remove(<<"alg">>, F), ?ECDH_1PU); from_map(F = #{ <<"alg">> := <<"ECDH-1PU+A128GCMKW">> }) -> from_map_ecdh_1pu(maps:remove(<<"alg">>, F), ?ECDH_1PU_A128GCMKW); from_map(F = #{ <<"alg">> := <<"ECDH-1PU+A192GCMKW">> }) -> from_map_ecdh_1pu(maps:remove(<<"alg">>, F), ?ECDH_1PU_A192GCMKW); from_map(F = #{ <<"alg">> := <<"ECDH-1PU+A256GCMKW">> }) -> from_map_ecdh_1pu(maps:remove(<<"alg">>, F), ?ECDH_1PU_A256GCMKW); from_map(F = #{ <<"alg">> := <<"ECDH-1PU+A128KW">> }) -> from_map_ecdh_1pu(maps:remove(<<"alg">>, F), ?ECDH_1PU_A128KW); from_map(F = #{ <<"alg">> := <<"ECDH-1PU+A192KW">> }) -> from_map_ecdh_1pu(maps:remove(<<"alg">>, F), ?ECDH_1PU_A192KW); from_map(F = #{ <<"alg">> := <<"ECDH-1PU+A256KW">> }) -> from_map_ecdh_1pu(maps:remove(<<"alg">>, F), ?ECDH_1PU_A256KW); from_map(F = #{ <<"alg">> := <<"ECDH-1PU+C20PKW">> }) -> from_map_ecdh_1pu(maps:remove(<<"alg">>, F), ?ECDH_1PU_C20PKW); from_map(F = #{ <<"alg">> := <<"ECDH-1PU+XC20PKW">> }) -> from_map_ecdh_1pu(maps:remove(<<"alg">>, F), ?ECDH_1PU_XC20PKW). to_map(A = ?ECDH_1PU_A128GCMKW, F) -> to_map_ecdh_1pu(F#{ <<"alg">> => <<"ECDH-1PU+A128GCMKW">> }, A); to_map(A = ?ECDH_1PU_A192GCMKW, F) -> to_map_ecdh_1pu(F#{ <<"alg">> => <<"ECDH-1PU+A192GCMKW">> }, A); to_map(A = ?ECDH_1PU_A256GCMKW, F) -> to_map_ecdh_1pu(F#{ <<"alg">> => <<"ECDH-1PU+A256GCMKW">> }, A); to_map(A = ?ECDH_1PU_A128KW, F) -> to_map_ecdh_1pu(F#{ <<"alg">> => <<"ECDH-1PU+A128KW">> }, A); to_map(A = ?ECDH_1PU_A192KW, F) -> to_map_ecdh_1pu(F#{ <<"alg">> => <<"ECDH-1PU+A192KW">> }, A); to_map(A = ?ECDH_1PU_A256KW, F) -> to_map_ecdh_1pu(F#{ <<"alg">> => <<"ECDH-1PU+A256KW">> }, A); to_map(A = ?ECDH_1PU_C20PKW, F) -> to_map_ecdh_1pu(F#{ <<"alg">> => <<"ECDH-1PU+C20PKW">> }, A); to_map(A = ?ECDH_1PU_XC20PKW, F) -> to_map_ecdh_1pu(F#{ <<"alg">> => <<"ECDH-1PU+XC20PKW">> }, A); to_map(A = ?ECDH_1PU, F) -> to_map_ecdh_1pu(F#{ <<"alg">> => <<"ECDH-1PU">> }, A). %%==================================================================== %% jose_jwe_alg callbacks %%==================================================================== generate_key(_Fields, {ENCModule, ENC}, ALG=#jose_jwe_alg_ecdh_1pu{epk=EphemeralPublicJWK=#jose_jwk{}}) -> jose_jwe_alg:generate_key(EphemeralPublicJWK, maps:get(<<"alg">>, to_map(ALG, #{})), ENCModule:algorithm(ENC)); generate_key(_Fields, {ENCModule, ENC}, ALG=#jose_jwe_alg_ecdh_1pu{}) -> jose_jwe_alg:generate_key({ec, <<"X25519">>}, maps:get(<<"alg">>, to_map(ALG, #{})), ENCModule:algorithm(ENC)). key_decrypt({UStaticPublicKey=#jose_jwk{}, VStaticSecretKey=#jose_jwk{}, UEphemeralKey=#jose_jwk{}}, EncryptedKey, JWEECDH1PU=#jose_jwe_alg_ecdh_1pu{epk=UEphemeralPublicKey=#jose_jwk{}}) -> case jose_jwk:thumbprint(UEphemeralKey) =:= jose_jwk:thumbprint(UEphemeralPublicKey) of true -> key_decrypt({UStaticPublicKey, VStaticSecretKey}, EncryptedKey, JWEECDH1PU); false -> error end; key_decrypt({UStaticPublicKey=#jose_jwk{}, VStaticSecretKey=#jose_jwk{}, UEphemeralPublicKey=#jose_jwk{}}, EncryptedKey, JWEECDH1PU0=#jose_jwe_alg_ecdh_1pu{epk=undefined}) -> JWEECDH1PU = JWEECDH1PU0#jose_jwe_alg_ecdh_1pu{epk=UEphemeralPublicKey}, key_decrypt({UStaticPublicKey, VStaticSecretKey}, EncryptedKey, JWEECDH1PU); key_decrypt({UStaticPublicKey=#jose_jwk{}, VStaticSecretKey=#jose_jwk{}}, EncryptedKey, JWEECDH1PU=#jose_jwe_alg_ecdh_1pu{epk=UEphemeralPublicKey=#jose_jwk{}}) -> Ze = jose_jwk:shared_secret(UEphemeralPublicKey, VStaticSecretKey), Zs = jose_jwk:shared_secret(UStaticPublicKey, VStaticSecretKey), Z = <>, key_decrypt(Z, EncryptedKey, JWEECDH1PU); key_decrypt(Z, {ENCModule, ENC, <<>>}, #jose_jwe_alg_ecdh_1pu{apu=APU, apv=APV, wrap=undefined, bits=undefined}) when is_binary(Z) -> Algorithm = ENCModule:algorithm(ENC), KeyDataLen = ENCModule:bits(ENC), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), DerivedKey; key_decrypt(Z, {_ENCModule, _ENC, EncryptedKey}, JWEECDH1PU=#jose_jwe_alg_ecdh_1pu{apu=APU, apv=APV, wrap=aes_gcm_kw, bits=KeyDataLen, iv=IV, tag=TAG}) when is_binary(Z) andalso is_binary(IV) andalso is_binary(TAG) -> Algorithm = algorithm(JWEECDH1PU), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), jose_jwa:block_decrypt({aes_gcm, KeyDataLen}, DerivedKey, IV, {<<>>, EncryptedKey, TAG}); key_decrypt(Z, {_ENCModule, _ENC, EncryptedKey}, JWEECDH1PU=#jose_jwe_alg_ecdh_1pu{apu=APU, apv=APV, wrap=aes_kw, bits=KeyDataLen}) when is_binary(Z) -> Algorithm = algorithm(JWEECDH1PU), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), jose_jwa_aes_kw:unwrap(EncryptedKey, DerivedKey); key_decrypt(Z, {_ENCModule, _ENC, EncryptedKey}, JWEECDH1PU=#jose_jwe_alg_ecdh_1pu{apu=APU, apv=APV, wrap=c20p_kw, bits=KeyDataLen, iv=IV, tag=TAG}) when is_binary(Z) andalso is_binary(IV) andalso is_binary(TAG) -> Algorithm = algorithm(JWEECDH1PU), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), jose_jwa:block_decrypt({chacha20_poly1305, KeyDataLen}, DerivedKey, IV, {<<>>, EncryptedKey, TAG}); key_decrypt(Z, {_ENCModule, _ENC, EncryptedKey}, JWEECDH1PU=#jose_jwe_alg_ecdh_1pu{apu=APU, apv=APV, wrap=xc20p_kw, bits=KeyDataLen, iv=IV, tag=TAG}) when is_binary(Z) andalso is_binary(IV) andalso is_binary(TAG) -> Algorithm = algorithm(JWEECDH1PU), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), jose_jwa:block_decrypt({xchacha20_poly1305, KeyDataLen}, DerivedKey, IV, {<<>>, EncryptedKey, TAG}). key_encrypt(_Key, _DecryptedKey, JWEECDH1PU=#jose_jwe_alg_ecdh_1pu{wrap=undefined, bits=undefined}) -> {<<>>, JWEECDH1PU}; key_encrypt({VStaticPublicKey=#jose_jwk{}, UStaticSecretKey=#jose_jwk{}, UEphemeralSecretKey=#jose_jwk{}}, DecryptedKey, JWEECDH1PU=#jose_jwe_alg_ecdh_1pu{epk=UEphemeralPublicKey=#jose_jwk{}}) -> case jose_jwk:thumbprint(UEphemeralSecretKey) =:= jose_jwk:thumbprint(UEphemeralPublicKey) of true -> Ze = jose_jwk:shared_secret(VStaticPublicKey, UEphemeralSecretKey), Zs = jose_jwk:shared_secret(VStaticPublicKey, UStaticSecretKey), Z = <>, key_encrypt(Z, DecryptedKey, JWEECDH1PU); false -> error end; key_encrypt({VStaticPublicKey=#jose_jwk{}, UStaticSecretKey=#jose_jwk{}, UEphemeralSecretKey=#jose_jwk{}}, DecryptedKey, JWEECDH1PU0=#jose_jwe_alg_ecdh_1pu{wrap=undefined, bits=undefined, epk=undefined}) -> UEphemeralPublicKey = jose_jwk:to_public(UEphemeralSecretKey), JWEECDH1PU1 = JWEECDH1PU0#jose_jwe_alg_ecdh_1pu{epk=UEphemeralPublicKey}, key_encrypt({VStaticPublicKey, UStaticSecretKey, UEphemeralSecretKey}, DecryptedKey, JWEECDH1PU1); key_encrypt(#jose_jwk{kty={KTYModule, KTY}}, DecryptedKey, JWEECDH1PU) -> DerivedKey = KTYModule:derive_key(KTY), key_encrypt(DerivedKey, DecryptedKey, JWEECDH1PU); key_encrypt(Z, DecryptedKey, JWEECDH1PU=#jose_jwe_alg_ecdh_1pu{apu=APU, apv=APV, wrap=aes_gcm_kw, bits=KeyDataLen, iv=IV}) when is_binary(Z) andalso is_binary(IV) -> Algorithm = algorithm(JWEECDH1PU), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), {CipherText, CipherTag} = jose_jwa:block_encrypt({aes_gcm, KeyDataLen}, DerivedKey, IV, {<<>>, DecryptedKey}), {CipherText, JWEECDH1PU#jose_jwe_alg_ecdh_1pu{ tag = CipherTag }}; key_encrypt(Z, DecryptedKey, JWEECDH1PU=#jose_jwe_alg_ecdh_1pu{apu=APU, apv=APV, wrap=aes_kw, bits=KeyDataLen}) when is_binary(Z) -> Algorithm = algorithm(JWEECDH1PU), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), {jose_jwa_aes_kw:wrap(DecryptedKey, DerivedKey), JWEECDH1PU}; key_encrypt(Z, DecryptedKey, JWEECDH1PU=#jose_jwe_alg_ecdh_1pu{apu=APU, apv=APV, wrap=c20p_kw, bits=KeyDataLen, iv=IV}) when is_binary(Z) andalso is_binary(IV) -> Algorithm = algorithm(JWEECDH1PU), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), {CipherText, CipherTag} = jose_jwa:block_encrypt({chacha20_poly1305, KeyDataLen}, DerivedKey, IV, {<<>>, DecryptedKey}), {CipherText, JWEECDH1PU#jose_jwe_alg_ecdh_1pu{ tag = CipherTag }}; key_encrypt(Z, DecryptedKey, JWEECDH1PU=#jose_jwe_alg_ecdh_1pu{apu=APU, apv=APV, wrap=xc20p_kw, bits=KeyDataLen, iv=IV}) when is_binary(Z) andalso is_binary(IV) -> Algorithm = algorithm(JWEECDH1PU), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), {CipherText, CipherTag} = jose_jwa:block_encrypt({xchacha20_poly1305, KeyDataLen}, DerivedKey, IV, {<<>>, DecryptedKey}), {CipherText, JWEECDH1PU#jose_jwe_alg_ecdh_1pu{ tag = CipherTag }}; key_encrypt(Z, DecryptedKey, JWEECDH1PU=#jose_jwe_alg_ecdh_1pu{wrap=aes_gcm_kw, iv=undefined}) when is_binary(Z) -> key_encrypt(Z, DecryptedKey, JWEECDH1PU#jose_jwe_alg_ecdh_1pu{ iv = crypto:strong_rand_bytes(12) }); key_encrypt(Z, DecryptedKey, JWEECDH1PU=#jose_jwe_alg_ecdh_1pu{wrap=c20p_kw, iv=undefined}) when is_binary(Z) -> key_encrypt(Z, DecryptedKey, JWEECDH1PU#jose_jwe_alg_ecdh_1pu{ iv = crypto:strong_rand_bytes(12) }); key_encrypt(Z, DecryptedKey, JWEECDH1PU=#jose_jwe_alg_ecdh_1pu{wrap=xc20p_kw, iv=undefined}) when is_binary(Z) -> key_encrypt(Z, DecryptedKey, JWEECDH1PU#jose_jwe_alg_ecdh_1pu{ iv = crypto:strong_rand_bytes(24) }). next_cek({VStaticPublicKey=#jose_jwk{}, UStaticSecretKey=#jose_jwk{}, UEphemeralSecretKey=#jose_jwk{}}, {ENCModule, ENC}, JWEECDH1PU=#jose_jwe_alg_ecdh_1pu{bits=undefined, epk=UEphemeralPublicKey=#jose_jwk{}}) -> case jose_jwk:thumbprint(UEphemeralSecretKey) =:= jose_jwk:thumbprint(UEphemeralPublicKey) of true -> Ze = jose_jwk:shared_secret(VStaticPublicKey, UEphemeralSecretKey), Zs = jose_jwk:shared_secret(VStaticPublicKey, UStaticSecretKey), Z = <>, next_cek(Z, {ENCModule, ENC}, JWEECDH1PU); false -> error end; next_cek({VStaticPublicKey=#jose_jwk{}, UStaticSecretKey=#jose_jwk{}, UEphemeralSecretKey=#jose_jwk{}}, {ENCModule, ENC}, JWEECDH1PU0=#jose_jwe_alg_ecdh_1pu{wrap=undefined, bits=undefined, epk=undefined}) -> UEphemeralPublicKey = jose_jwk:to_public(UEphemeralSecretKey), JWEECDH1PU1 = JWEECDH1PU0#jose_jwe_alg_ecdh_1pu{epk=UEphemeralPublicKey}, next_cek({VStaticPublicKey, UStaticSecretKey, UEphemeralSecretKey}, {ENCModule, ENC}, JWEECDH1PU1); next_cek(#jose_jwk{kty={KTYModule, KTY}}, {ENC, ENCModule}, JWEECDH1PU=#jose_jwe_alg_ecdh_1pu{wrap=undefined, bits=undefined}) -> DerivedKey = KTYModule:derive_key(KTY), next_cek(DerivedKey, {ENCModule, ENC}, JWEECDH1PU); next_cek(Z, {ENCModule, ENC}, JWEECDH1PU=#jose_jwe_alg_ecdh_1pu{apu=APU, apv=APV, wrap=undefined, bits=undefined}) when is_binary(Z) -> Algorithm = ENCModule:algorithm(ENC), KeyDataLen = ENCModule:bits(ENC), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), {DerivedKey, JWEECDH1PU}; next_cek(_Key, {ENCModule, ENC}, JWEECDH1PU=#jose_jwe_alg_ecdh_1pu{}) -> {ENCModule:next_cek(ENC), JWEECDH1PU}. %%==================================================================== %% API functions %%==================================================================== algorithm(?ECDH_1PU_A128GCMKW) -> <<"ECDH-1PU+A128GCMKW">>; algorithm(?ECDH_1PU_A192GCMKW) -> <<"ECDH-1PU+A192GCMKW">>; algorithm(?ECDH_1PU_A256GCMKW) -> <<"ECDH-1PU+A256GCMKW">>; algorithm(?ECDH_1PU_A128KW) -> <<"ECDH-1PU+A128KW">>; algorithm(?ECDH_1PU_A192KW) -> <<"ECDH-1PU+A192KW">>; algorithm(?ECDH_1PU_A256KW) -> <<"ECDH-1PU+A256KW">>; algorithm(?ECDH_1PU_C20PKW) -> <<"ECDH-1PU+C20PKW">>; algorithm(?ECDH_1PU_XC20PKW) -> <<"ECDH-1PU+XC20PKW">>; algorithm(?ECDH_1PU) -> <<"ECDH-1PU">>. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private from_map_ecdh_1pu(F = #{ <<"epk">> := EPK }, H) -> from_map_ecdh_1pu(maps:remove(<<"epk">>, F), H#jose_jwe_alg_ecdh_1pu{ epk = jose_jwk:from_map(EPK) }); from_map_ecdh_1pu(F = #{ <<"apu">> := APU }, H) -> from_map_ecdh_1pu(maps:remove(<<"apu">>, F), H#jose_jwe_alg_ecdh_1pu{ apu = jose_jwa_base64url:decode(APU) }); from_map_ecdh_1pu(F = #{ <<"apv">> := APV }, H) -> from_map_ecdh_1pu(maps:remove(<<"apv">>, F), H#jose_jwe_alg_ecdh_1pu{ apv = jose_jwa_base64url:decode(APV) }); from_map_ecdh_1pu(F=#{ <<"iv">> := IV }, H) -> from_map_ecdh_1pu(maps:remove(<<"iv">>, F), H#jose_jwe_alg_ecdh_1pu{ iv = jose_jwa_base64url:decode(IV) }); from_map_ecdh_1pu(F=#{ <<"tag">> := TAG }, H) -> from_map_ecdh_1pu(maps:remove(<<"tag">>, F), H#jose_jwe_alg_ecdh_1pu{ tag = jose_jwa_base64url:decode(TAG) }); from_map_ecdh_1pu(F, H) -> {H, F}. %% @private to_map_ecdh_1pu(F, H=#jose_jwe_alg_ecdh_1pu{ epk = EPK = #jose_jwk{} }) -> to_map_ecdh_1pu(F#{ <<"epk">> => element(2, jose_jwk:to_public_map(EPK)) }, H#jose_jwe_alg_ecdh_1pu{ epk = undefined }); to_map_ecdh_1pu(F, H=#jose_jwe_alg_ecdh_1pu{ apu = APU }) when is_binary(APU) -> to_map_ecdh_1pu(F#{ <<"apu">> => jose_jwa_base64url:encode(APU) }, H#jose_jwe_alg_ecdh_1pu{ apu = undefined }); to_map_ecdh_1pu(F, H=#jose_jwe_alg_ecdh_1pu{ apv = APV }) when is_binary(APV) -> to_map_ecdh_1pu(F#{ <<"apv">> => jose_jwa_base64url:encode(APV) }, H#jose_jwe_alg_ecdh_1pu{ apv = undefined }); to_map_ecdh_1pu(F, H=#jose_jwe_alg_ecdh_1pu{ iv = IV }) when is_binary(IV) -> to_map_ecdh_1pu(F#{ <<"iv">> => jose_jwa_base64url:encode(IV) }, H#jose_jwe_alg_ecdh_1pu{ iv = undefined }); to_map_ecdh_1pu(F, H=#jose_jwe_alg_ecdh_1pu{ tag = TAG }) when is_binary(TAG) -> to_map_ecdh_1pu(F#{ <<"tag">> => jose_jwa_base64url:encode(TAG) }, H#jose_jwe_alg_ecdh_1pu{ tag = undefined }); to_map_ecdh_1pu(F, _) -> F. potatosalad-erlang-jose-30ac019/src/jwe/jose_jwe_enc_xc20p.erl0000664000232200023220000000441615267717064024675 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 14 Sep 2019 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwe_enc_xc20p). -behaviour(jose_jwe). -behaviour(jose_jwe_enc). %% jose_jwe callbacks -export([from_map/1]). -export([to_map/2]). %% jose_jwe_enc callbacks -export([algorithm/1]). -export([bits/1]). -export([block_decrypt/4]). -export([block_encrypt/4]). -export([next_cek/1]). -export([next_iv/1]). %% API -export([cipher_supported/0]). %% Types -type enc() :: {xchacha20_poly1305, 256}. -export_type([enc/0]). %% Macros -define(XCHACHA20_POLY1305, {xchacha20_poly1305, 256}). %%==================================================================== %% jose_jwe callbacks %%==================================================================== from_map(F = #{ <<"enc">> := <<"XC20P">> }) -> {?XCHACHA20_POLY1305, maps:remove(<<"enc">>, F)}. to_map(?XCHACHA20_POLY1305, F) -> F#{ <<"enc">> => <<"XC20P">> }. %%==================================================================== %% jose_jwe_enc callbacks %%==================================================================== algorithm(?XCHACHA20_POLY1305) -> <<"XC20P">>. bits(?XCHACHA20_POLY1305) -> 256. block_decrypt({AAD, CipherText, CipherTag}, CEK, IV, ?XCHACHA20_POLY1305) -> jose_jwa:block_decrypt(?XCHACHA20_POLY1305, CEK, IV, {AAD, CipherText, CipherTag}). block_encrypt({AAD, PlainText}, CEK, IV, ?XCHACHA20_POLY1305) -> jose_jwa:block_encrypt(?XCHACHA20_POLY1305, CEK, IV, {AAD, PlainText}). next_cek(?XCHACHA20_POLY1305) -> crypto:strong_rand_bytes(32). next_iv(?XCHACHA20_POLY1305) -> crypto:strong_rand_bytes(24). %%==================================================================== %% API functions %%==================================================================== cipher_supported() -> [xchacha20_poly1305]. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jwe/jose_jwe_alg_c20p_kw.erl0000664000232200023220000001004515267717064025177 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 14 Sep 2019 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwe_alg_c20p_kw). -behaviour(jose_jwe). -behaviour(jose_jwe_alg). -include("jose_jwk.hrl"). %% jose_jwe callbacks -export([from_map/1]). -export([to_map/2]). %% jose_jwe_alg callbacks -export([generate_key/3]). -export([key_decrypt/3]). -export([key_encrypt/3]). -export([next_cek/3]). %% API %% Types -record(jose_jwe_alg_c20p_kw, { iv = undefined :: undefined | binary(), tag = undefined :: undefined | binary() }). -type alg() :: #jose_jwe_alg_c20p_kw{}. -export_type([alg/0]). %% Macros -define(BITS, 256). -define(C20PKW, #jose_jwe_alg_c20p_kw{}). %%==================================================================== %% jose_jwe callbacks %%==================================================================== from_map(F = #{ <<"alg">> := <<"C20PKW">> }) -> from_map_c20p(?C20PKW, maps:remove(<<"alg">>, F)). to_map(A = ?C20PKW, F) -> to_map_c20p(A, F#{ <<"alg">> => <<"C20PKW">> }). %%==================================================================== %% jose_jwe_alg callbacks %%==================================================================== generate_key(_Fields, {ENCModule, ENC}, ALG=#jose_jwe_alg_c20p_kw{}) -> jose_jwe_alg:generate_key({oct, (?BITS div 8)}, maps:get(<<"alg">>, to_map(ALG, #{})), ENCModule:algorithm(ENC)). key_decrypt(DerivedKey, {_ENCModule, _ENC, EncryptedKey}, #jose_jwe_alg_c20p_kw{iv=IV, tag=TAG}) when is_binary(DerivedKey) andalso bit_size(DerivedKey) =:= ?BITS andalso is_binary(IV) andalso is_binary(TAG) -> jose_jwa:block_decrypt({chacha20_poly1305, ?BITS}, DerivedKey, IV, {<<>>, EncryptedKey, TAG}); key_decrypt(#jose_jwk{kty={KTYModule, KTY}}, EncryptedKey, JWEC20PKW=#jose_jwe_alg_c20p_kw{}) -> key_decrypt(KTYModule:derive_key(KTY), EncryptedKey, JWEC20PKW). key_encrypt(DerivedKey, DecryptedKey, JWEC20PKW=#jose_jwe_alg_c20p_kw{iv=IV}) when is_binary(DerivedKey) andalso bit_size(DerivedKey) =:= ?BITS andalso is_binary(IV) -> {CipherText, CipherTag} = jose_jwa:block_encrypt({chacha20_poly1305, ?BITS}, DerivedKey, IV, {<<>>, DecryptedKey}), {CipherText, JWEC20PKW#jose_jwe_alg_c20p_kw{ tag = CipherTag }}; key_encrypt(DerivedKey, DecryptedKey, JWEC20PKW=#jose_jwe_alg_c20p_kw{iv=undefined}) -> key_encrypt(DerivedKey, DecryptedKey, JWEC20PKW#jose_jwe_alg_c20p_kw{ iv = crypto:strong_rand_bytes(12) }); key_encrypt(#jose_jwk{kty={KTYModule, KTY}}, DecryptedKey, JWEC20PKW=#jose_jwe_alg_c20p_kw{}) -> key_encrypt(KTYModule:derive_key(KTY), DecryptedKey, JWEC20PKW). next_cek(_Key, {ENCModule, ENC}, ALG=#jose_jwe_alg_c20p_kw{}) -> {ENCModule:next_cek(ENC), ALG}. %%==================================================================== %% API functions %%==================================================================== %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private from_map_c20p(A, F=#{ <<"iv">> := IV }) -> from_map_c20p(A#jose_jwe_alg_c20p_kw{ iv = jose_jwa_base64url:decode(IV) }, maps:remove(<<"iv">>, F)); from_map_c20p(A, F=#{ <<"tag">> := TAG }) -> from_map_c20p(A#jose_jwe_alg_c20p_kw{ tag = jose_jwa_base64url:decode(TAG) }, maps:remove(<<"tag">>, F)); from_map_c20p(A, F) -> {A, F}. %% @private to_map_c20p(#jose_jwe_alg_c20p_kw{ iv = undefined, tag = undefined }, F) -> F; to_map_c20p(A=#jose_jwe_alg_c20p_kw{ iv = IV }, F) when is_binary(IV) -> to_map_c20p(A#jose_jwe_alg_c20p_kw{ iv = undefined }, F#{ <<"iv">> => jose_jwa_base64url:encode(IV) }); to_map_c20p(A=#jose_jwe_alg_c20p_kw{ tag = TAG }, F) when is_binary(TAG) -> to_map_c20p(A#jose_jwe_alg_c20p_kw{ tag = undefined }, F#{ <<"tag">> => jose_jwa_base64url:encode(TAG) }). potatosalad-erlang-jose-30ac019/src/jwe/jose_jwe_alg_pbes2.erl0000664000232200023220000003277215267717064024760 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 22 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwe_alg_pbes2). -behaviour(jose_jwe). -behaviour(jose_jwe_alg). -include("jose_jwk.hrl"). %% jose_jwe callbacks -export([from_map/1]). -export([to_map/2]). %% jose_jwe_alg callbacks -export([generate_key/3]). -export([key_decrypt/3]). -export([key_encrypt/3]). -export([next_cek/3]). %% API -export([format_error/2]). -export([hmac_supported/0]). -export([wrap_supported/0]). %% Types -record(jose_jwe_alg_pbes2, { hmac = undefined :: undefined | sha256 | sha384 | sha512, salt = undefined :: undefined | binary(), iter = undefined :: undefined | pos_integer(), wrap = undefined :: undefined | aes_gcm_kw | aes_kw | c20p_kw | xc20p_kw, bits = undefined :: undefined | 128 | 192 | 256, iv = undefined :: undefined | binary(), tag = undefined :: undefined | binary() }). -type alg() :: #jose_jwe_alg_pbes2{}. -export_type([alg/0]). -define(PBES2_HS256_A128GCMKW, #jose_jwe_alg_pbes2{hmac=sha256, wrap=aes_gcm_kw, bits=128}). -define(PBES2_HS384_A192GCMKW, #jose_jwe_alg_pbes2{hmac=sha384, wrap=aes_gcm_kw, bits=192}). -define(PBES2_HS512_A256GCMKW, #jose_jwe_alg_pbes2{hmac=sha512, wrap=aes_gcm_kw, bits=256}). -define(PBES2_HS256_A128KW, #jose_jwe_alg_pbes2{hmac=sha256, wrap=aes_kw, bits=128}). -define(PBES2_HS384_A192KW, #jose_jwe_alg_pbes2{hmac=sha384, wrap=aes_kw, bits=192}). -define(PBES2_HS512_A256KW, #jose_jwe_alg_pbes2{hmac=sha512, wrap=aes_kw, bits=256}). -define(PBES2_HS512_C20PKW, #jose_jwe_alg_pbes2{hmac=sha512, wrap=c20p_kw, bits=256}). -define(PBES2_HS512_XC20PKW, #jose_jwe_alg_pbes2{hmac=sha512, wrap=xc20p_kw, bits=256}). %%==================================================================== %% jose_jwe callbacks %%==================================================================== from_map(F = #{ <<"alg">> := <<"PBES2-HS256+A128GCMKW">> }) -> from_map_pbes2(maps:remove(<<"alg">>, F), ?PBES2_HS256_A128GCMKW); from_map(F = #{ <<"alg">> := <<"PBES2-HS384+A192GCMKW">> }) -> from_map_pbes2(maps:remove(<<"alg">>, F), ?PBES2_HS384_A192GCMKW); from_map(F = #{ <<"alg">> := <<"PBES2-HS512+A256GCMKW">> }) -> from_map_pbes2(maps:remove(<<"alg">>, F), ?PBES2_HS512_A256GCMKW); from_map(F = #{ <<"alg">> := <<"PBES2-HS256+A128KW">> }) -> from_map_pbes2(maps:remove(<<"alg">>, F), ?PBES2_HS256_A128KW); from_map(F = #{ <<"alg">> := <<"PBES2-HS384+A192KW">> }) -> from_map_pbes2(maps:remove(<<"alg">>, F), ?PBES2_HS384_A192KW); from_map(F = #{ <<"alg">> := <<"PBES2-HS512+A256KW">> }) -> from_map_pbes2(maps:remove(<<"alg">>, F), ?PBES2_HS512_A256KW); from_map(F = #{ <<"alg">> := <<"PBES2-HS512+C20PKW">> }) -> from_map_pbes2(maps:remove(<<"alg">>, F), ?PBES2_HS512_C20PKW); from_map(F = #{ <<"alg">> := <<"PBES2-HS512+XC20PKW">> }) -> from_map_pbes2(maps:remove(<<"alg">>, F), ?PBES2_HS512_XC20PKW). to_map(A = ?PBES2_HS256_A128GCMKW, F) -> to_map_pbes2(F#{ <<"alg">> => <<"PBES2-HS256+A128GCMKW">> }, A); to_map(A = ?PBES2_HS384_A192GCMKW, F) -> to_map_pbes2(F#{ <<"alg">> => <<"PBES2-HS384+A192GCMKW">> }, A); to_map(A = ?PBES2_HS512_A256GCMKW, F) -> to_map_pbes2(F#{ <<"alg">> => <<"PBES2-HS512+A256GCMKW">> }, A); to_map(A = ?PBES2_HS256_A128KW, F) -> to_map_pbes2(F#{ <<"alg">> => <<"PBES2-HS256+A128KW">> }, A); to_map(A = ?PBES2_HS384_A192KW, F) -> to_map_pbes2(F#{ <<"alg">> => <<"PBES2-HS384+A192KW">> }, A); to_map(A = ?PBES2_HS512_A256KW, F) -> to_map_pbes2(F#{ <<"alg">> => <<"PBES2-HS512+A256KW">> }, A); to_map(A = ?PBES2_HS512_C20PKW, F) -> to_map_pbes2(F#{ <<"alg">> => <<"PBES2-HS512+C20PKW">> }, A); to_map(A = ?PBES2_HS512_XC20PKW, F) -> to_map_pbes2(F#{ <<"alg">> => <<"PBES2-HS512+XC20PKW">> }, A). %%==================================================================== %% jose_jwe_alg callbacks %%==================================================================== generate_key(_Fields, {ENCModule, ENC}, ALG=#jose_jwe_alg_pbes2{}) -> jose_jwe_alg:generate_key({oct, 16}, maps:get(<<"alg">>, to_map(ALG, #{})), ENCModule:algorithm(ENC)). key_decrypt(Password, {_ENCModule, _ENC, EncryptedKey}, #jose_jwe_alg_pbes2{hmac=HMAC, salt=Salt, iter=Iterations, wrap=aes_gcm_kw, bits=Bits, iv=IV, tag=TAG}) when is_binary(Password) andalso is_binary(IV) andalso is_binary(TAG) -> {ok, DerivedKey} = pbkdf2({hmac, HMAC}, Password, Salt, Iterations, (Bits div 8) + (Bits rem 8)), jose_jwa:block_decrypt({aes_gcm, Bits}, DerivedKey, IV, {<<>>, EncryptedKey, TAG}); key_decrypt(Password, {_ENCModule, _ENC, EncryptedKey}, #jose_jwe_alg_pbes2{hmac=HMAC, salt=Salt, iter=Iterations, wrap=aes_kw, bits=Bits}) when is_binary(Password) -> {ok, DerivedKey} = pbkdf2({hmac, HMAC}, Password, Salt, Iterations, (Bits div 8) + (Bits rem 8)), jose_jwa_aes_kw:unwrap(EncryptedKey, DerivedKey); key_decrypt(Password, {_ENCModule, _ENC, EncryptedKey}, #jose_jwe_alg_pbes2{hmac=HMAC, salt=Salt, iter=Iterations, wrap=c20p_kw, bits=Bits, iv=IV, tag=TAG}) when is_binary(Password) andalso is_binary(IV) andalso is_binary(TAG) -> {ok, DerivedKey} = pbkdf2({hmac, HMAC}, Password, Salt, Iterations, (Bits div 8) + (Bits rem 8)), jose_jwa:block_decrypt({chacha20_poly1305, Bits}, DerivedKey, IV, {<<>>, EncryptedKey, TAG}); key_decrypt(Password, {_ENCModule, _ENC, EncryptedKey}, #jose_jwe_alg_pbes2{hmac=HMAC, salt=Salt, iter=Iterations, wrap=xc20p_kw, bits=Bits, iv=IV, tag=TAG}) when is_binary(Password) andalso is_binary(IV) andalso is_binary(TAG) -> {ok, DerivedKey} = pbkdf2({hmac, HMAC}, Password, Salt, Iterations, (Bits div 8) + (Bits rem 8)), jose_jwa:block_decrypt({xchacha20_poly1305, Bits}, DerivedKey, IV, {<<>>, EncryptedKey, TAG}); key_decrypt(#jose_jwk{kty={KTYModule, KTY}}, EncryptedKey, JWEPBES2=#jose_jwe_alg_pbes2{}) -> key_decrypt(KTYModule:derive_key(KTY), EncryptedKey, JWEPBES2). key_encrypt(Password, DecryptedKey, ALG0=#jose_jwe_alg_pbes2{bits=Bits, salt=undefined}) -> ALG1 = ALG0#jose_jwe_alg_pbes2{salt=wrap_salt(crypto:strong_rand_bytes(Bits div 8), ALG0)}, key_encrypt(Password, DecryptedKey, ALG1); key_encrypt(Password, DecryptedKey, ALG0=#jose_jwe_alg_pbes2{bits=Bits, iter=undefined}) -> ALG1 = ALG0#jose_jwe_alg_pbes2{iter=(Bits * 32)}, key_encrypt(Password, DecryptedKey, ALG1); key_encrypt(Password, DecryptedKey, JWEPBES2=#jose_jwe_alg_pbes2{hmac=HMAC, salt=Salt, iter=Iterations, wrap=aes_gcm_kw, bits=Bits, iv=IV}) when is_binary(Password) andalso is_binary(DecryptedKey) andalso is_binary(Salt) andalso is_integer(Iterations) andalso is_binary(IV) -> {ok, DerivedKey} = pbkdf2({hmac, HMAC}, Password, Salt, Iterations, (Bits div 8) + (Bits rem 8)), {CipherText, CipherTag} = jose_jwa:block_encrypt({aes_gcm, Bits}, DerivedKey, IV, {<<>>, DecryptedKey}), {CipherText, JWEPBES2#jose_jwe_alg_pbes2{ tag = CipherTag }}; key_encrypt(Password, DecryptedKey, JWEPBES2=#jose_jwe_alg_pbes2{hmac=HMAC, salt=Salt, iter=Iterations, wrap=aes_kw, bits=Bits}) when is_binary(Password) andalso is_binary(DecryptedKey) andalso is_binary(Salt) andalso is_integer(Iterations) -> {ok, DerivedKey} = pbkdf2({hmac, HMAC}, Password, Salt, Iterations, (Bits div 8) + (Bits rem 8)), {jose_jwa_aes_kw:wrap(DecryptedKey, DerivedKey), JWEPBES2}; key_encrypt(Password, DecryptedKey, JWEPBES2=#jose_jwe_alg_pbes2{hmac=HMAC, salt=Salt, iter=Iterations, wrap=c20p_kw, bits=Bits, iv=IV}) when is_binary(Password) andalso is_binary(DecryptedKey) andalso is_binary(Salt) andalso is_integer(Iterations) andalso is_binary(IV) -> {ok, DerivedKey} = pbkdf2({hmac, HMAC}, Password, Salt, Iterations, (Bits div 8) + (Bits rem 8)), {CipherText, CipherTag} = jose_jwa:block_encrypt({chacha20_poly1305, Bits}, DerivedKey, IV, {<<>>, DecryptedKey}), {CipherText, JWEPBES2#jose_jwe_alg_pbes2{ tag = CipherTag }}; key_encrypt(Password, DecryptedKey, JWEPBES2=#jose_jwe_alg_pbes2{hmac=HMAC, salt=Salt, iter=Iterations, wrap=xc20p_kw, bits=Bits, iv=IV}) when is_binary(Password) andalso is_binary(DecryptedKey) andalso is_binary(Salt) andalso is_integer(Iterations) andalso is_binary(IV) -> {ok, DerivedKey} = pbkdf2({hmac, HMAC}, Password, Salt, Iterations, (Bits div 8) + (Bits rem 8)), {CipherText, CipherTag} = jose_jwa:block_encrypt({xchacha20_poly1305, Bits}, DerivedKey, IV, {<<>>, DecryptedKey}), {CipherText, JWEPBES2#jose_jwe_alg_pbes2{ tag = CipherTag }}; key_encrypt(Password, DecryptedKey, JWEPBES2=#jose_jwe_alg_pbes2{wrap=aes_gcm_kw, iv=undefined}) when is_binary(Password) -> key_encrypt(Password, DecryptedKey, JWEPBES2#jose_jwe_alg_pbes2{ iv = crypto:strong_rand_bytes(12) }); key_encrypt(Password, DecryptedKey, JWEPBES2=#jose_jwe_alg_pbes2{wrap=c20p_kw, iv=undefined}) when is_binary(Password) -> key_encrypt(Password, DecryptedKey, JWEPBES2#jose_jwe_alg_pbes2{ iv = crypto:strong_rand_bytes(12) }); key_encrypt(Password, DecryptedKey, JWEPBES2=#jose_jwe_alg_pbes2{wrap=xc20p_kw, iv=undefined}) when is_binary(Password) -> key_encrypt(Password, DecryptedKey, JWEPBES2#jose_jwe_alg_pbes2{ iv = crypto:strong_rand_bytes(24) }); key_encrypt(#jose_jwk{kty={KTYModule, KTY}}, DecryptedKey, JWEPBES2=#jose_jwe_alg_pbes2{}) -> key_encrypt(KTYModule:derive_key(KTY), DecryptedKey, JWEPBES2). next_cek(_Key, {ENCModule, ENC}, ALG=#jose_jwe_alg_pbes2{}) -> {ENCModule:next_cek(ENC), ALG}. %%==================================================================== %% API functions %%==================================================================== -spec format_error(term(), term()) -> term(). format_error(_Reason, [{_M, _F, _As, Info} | _]) -> ErrorInfo = proplists:get_value(error_info, Info, #{}), ErrorDescription1 = maps:get(cause, ErrorInfo), ErrorDescription1. hmac_supported() -> [sha256, sha384, sha512]. wrap_supported() -> [128, 192, 256]. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private from_map_pbes2(F = #{ <<"p2c">> := P2C }, H) -> from_map_pbes2(maps:remove(<<"p2c">>, F), H#jose_jwe_alg_pbes2{ iter = P2C }); from_map_pbes2(F = #{ <<"p2s">> := P2S }, H) -> from_map_pbes2(maps:remove(<<"p2s">>, F), H#jose_jwe_alg_pbes2{ salt = wrap_salt(jose_jwa_base64url:decode(P2S), H) }); from_map_pbes2(F=#{ <<"iv">> := IV }, H) -> from_map_pbes2(maps:remove(<<"iv">>, F), H#jose_jwe_alg_pbes2{ iv = jose_jwa_base64url:decode(IV) }); from_map_pbes2(F=#{ <<"tag">> := TAG }, H) -> from_map_pbes2(maps:remove(<<"tag">>, F), H#jose_jwe_alg_pbes2{ tag = jose_jwa_base64url:decode(TAG) }); from_map_pbes2(F, H) -> {H, F}. %% @private pbkdf2(Mac, Password, Salt, Iterations, DerivedKeyLen) -> PBES2CountMaximum = jose:pbes2_count_maximum(), case PBES2CountMaximum < Iterations of false -> jose_jwa_pkcs5:pbkdf2(Mac, Password, Salt, Iterations, DerivedKeyLen); true -> erlang:error(badarg, [Mac, <<"REDACTED">>, Salt, Iterations, DerivedKeyLen], [ {error_info, #{ module => ?MODULE, cause => #{4 => lists:flatten(io_lib:format("maximum PBES2 iterations is set to ~w, but ~w was attempted (see jose:pbes2_count_maximum/0)", [PBES2CountMaximum, Iterations]))} }} ]) end. %% @private to_map_pbes2(F, H=#jose_jwe_alg_pbes2{ iter = P2C }) when is_integer(P2C) -> to_map_pbes2(F#{ <<"p2c">> => P2C }, H#jose_jwe_alg_pbes2{ iter = undefined }); to_map_pbes2(F, H=#jose_jwe_alg_pbes2{ salt = P2S }) when is_binary(P2S) -> to_map_pbes2(F#{ <<"p2s">> => jose_jwa_base64url:encode(unwrap_salt(P2S, H)) }, H#jose_jwe_alg_pbes2{ salt = undefined }); to_map_pbes2(F, H=#jose_jwe_alg_pbes2{ iv = IV }) when is_binary(IV) -> to_map_pbes2(F#{ <<"iv">> => jose_jwa_base64url:encode(IV) }, H#jose_jwe_alg_pbes2{ iv = undefined }); to_map_pbes2(F, H=#jose_jwe_alg_pbes2{ tag = TAG }) when is_binary(TAG) -> to_map_pbes2(F#{ <<"tag">> => jose_jwa_base64url:encode(TAG) }, H#jose_jwe_alg_pbes2{ tag = undefined }); to_map_pbes2(F, _) -> F. %% @private wrap_salt(SaltInput, ?PBES2_HS256_A128GCMKW) -> << "PBES2-HS256+A128GCMKW", 0, SaltInput/binary >>; wrap_salt(SaltInput, ?PBES2_HS384_A192GCMKW) -> << "PBES2-HS384+A192GCMKW", 0, SaltInput/binary >>; wrap_salt(SaltInput, ?PBES2_HS512_A256GCMKW) -> << "PBES2-HS512+A256GCMKW", 0, SaltInput/binary >>; wrap_salt(SaltInput, ?PBES2_HS256_A128KW) -> << "PBES2-HS256+A128KW", 0, SaltInput/binary >>; wrap_salt(SaltInput, ?PBES2_HS384_A192KW) -> << "PBES2-HS384+A192KW", 0, SaltInput/binary >>; wrap_salt(SaltInput, ?PBES2_HS512_A256KW) -> << "PBES2-HS512+A256KW", 0, SaltInput/binary >>; wrap_salt(SaltInput, ?PBES2_HS512_C20PKW) -> << "PBES2-HS512+C20PKW", 0, SaltInput/binary >>; wrap_salt(SaltInput, ?PBES2_HS512_XC20PKW) -> << "PBES2-HS512+XC20PKW", 0, SaltInput/binary >>. %% @private unwrap_salt(<< "PBES2-HS256+A128GCMKW", 0, SaltInput/binary >>, ?PBES2_HS256_A128GCMKW) -> SaltInput; unwrap_salt(<< "PBES2-HS384+A192GCMKW", 0, SaltInput/binary >>, ?PBES2_HS384_A192GCMKW) -> SaltInput; unwrap_salt(<< "PBES2-HS512+A256GCMKW", 0, SaltInput/binary >>, ?PBES2_HS512_A256GCMKW) -> SaltInput; unwrap_salt(<< "PBES2-HS256+A128KW", 0, SaltInput/binary >>, ?PBES2_HS256_A128KW) -> SaltInput; unwrap_salt(<< "PBES2-HS384+A192KW", 0, SaltInput/binary >>, ?PBES2_HS384_A192KW) -> SaltInput; unwrap_salt(<< "PBES2-HS512+A256KW", 0, SaltInput/binary >>, ?PBES2_HS512_A256KW) -> SaltInput; unwrap_salt(<< "PBES2-HS512+C20PKW", 0, SaltInput/binary >>, ?PBES2_HS512_C20PKW) -> SaltInput; unwrap_salt(<< "PBES2-HS512+XC20PKW", 0, SaltInput/binary >>, ?PBES2_HS512_XC20PKW) -> SaltInput. potatosalad-erlang-jose-30ac019/src/jwe/jose_jwe_alg_ecdh_es.erl0000664000232200023220000003672115267717064025335 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 23 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwe_alg_ecdh_es). -behaviour(jose_jwe). -behaviour(jose_jwe_alg). -include_lib("public_key/include/public_key.hrl"). -include("jose_jwk.hrl"). %% jose_jwe callbacks -export([from_map/1]). -export([to_map/2]). %% jose_jwe_alg callbacks -export([generate_key/3]). -export([key_decrypt/3]). -export([key_encrypt/3]). -export([next_cek/3]). %% API -export([algorithm/1]). %% Types -type ec_public_key() :: {#'ECPoint'{},{namedCurve, Oid::tuple()} | #'ECParameters'{}} | term(). -record(jose_jwe_alg_ecdh_es, { epk = undefined :: undefined | {ec_public_key(), map()}, apu = undefined :: undefined | binary(), apv = undefined :: undefined | binary(), wrap = undefined :: undefined | aes_gcm_kw | aes_kw | c20p_kw | xc20p_kw, bits = undefined :: undefined | 128 | 192 | 256, iv = undefined :: undefined | binary(), tag = undefined :: undefined | binary() }). -type alg() :: #jose_jwe_alg_ecdh_es{}. -export_type([alg/0]). -define(ECDH_ES, #jose_jwe_alg_ecdh_es{}). -define(ECDH_ES_A128GCMKW, #jose_jwe_alg_ecdh_es{wrap=aes_gcm_kw, bits=128}). -define(ECDH_ES_A192GCMKW, #jose_jwe_alg_ecdh_es{wrap=aes_gcm_kw, bits=192}). -define(ECDH_ES_A256GCMKW, #jose_jwe_alg_ecdh_es{wrap=aes_gcm_kw, bits=256}). -define(ECDH_ES_A128KW, #jose_jwe_alg_ecdh_es{wrap=aes_kw, bits=128}). -define(ECDH_ES_A192KW, #jose_jwe_alg_ecdh_es{wrap=aes_kw, bits=192}). -define(ECDH_ES_A256KW, #jose_jwe_alg_ecdh_es{wrap=aes_kw, bits=256}). -define(ECDH_ES_C20PKW, #jose_jwe_alg_ecdh_es{wrap=c20p_kw, bits=256}). -define(ECDH_ES_XC20PKW, #jose_jwe_alg_ecdh_es{wrap=xc20p_kw, bits=256}). %%==================================================================== %% jose_jwe callbacks %%==================================================================== from_map(F = #{ <<"alg">> := <<"ECDH-ES">> }) -> from_map_ecdh_es(maps:remove(<<"alg">>, F), ?ECDH_ES); from_map(F = #{ <<"alg">> := <<"ECDH-ES+A128GCMKW">> }) -> from_map_ecdh_es(maps:remove(<<"alg">>, F), ?ECDH_ES_A128GCMKW); from_map(F = #{ <<"alg">> := <<"ECDH-ES+A192GCMKW">> }) -> from_map_ecdh_es(maps:remove(<<"alg">>, F), ?ECDH_ES_A192GCMKW); from_map(F = #{ <<"alg">> := <<"ECDH-ES+A256GCMKW">> }) -> from_map_ecdh_es(maps:remove(<<"alg">>, F), ?ECDH_ES_A256GCMKW); from_map(F = #{ <<"alg">> := <<"ECDH-ES+A128KW">> }) -> from_map_ecdh_es(maps:remove(<<"alg">>, F), ?ECDH_ES_A128KW); from_map(F = #{ <<"alg">> := <<"ECDH-ES+A192KW">> }) -> from_map_ecdh_es(maps:remove(<<"alg">>, F), ?ECDH_ES_A192KW); from_map(F = #{ <<"alg">> := <<"ECDH-ES+A256KW">> }) -> from_map_ecdh_es(maps:remove(<<"alg">>, F), ?ECDH_ES_A256KW); from_map(F = #{ <<"alg">> := <<"ECDH-ES+C20PKW">> }) -> from_map_ecdh_es(maps:remove(<<"alg">>, F), ?ECDH_ES_C20PKW); from_map(F = #{ <<"alg">> := <<"ECDH-ES+XC20PKW">> }) -> from_map_ecdh_es(maps:remove(<<"alg">>, F), ?ECDH_ES_XC20PKW). to_map(A = ?ECDH_ES_A128GCMKW, F) -> to_map_ecdh_es(F#{ <<"alg">> => <<"ECDH-ES+A128GCMKW">> }, A); to_map(A = ?ECDH_ES_A192GCMKW, F) -> to_map_ecdh_es(F#{ <<"alg">> => <<"ECDH-ES+A192GCMKW">> }, A); to_map(A = ?ECDH_ES_A256GCMKW, F) -> to_map_ecdh_es(F#{ <<"alg">> => <<"ECDH-ES+A256GCMKW">> }, A); to_map(A = ?ECDH_ES_A128KW, F) -> to_map_ecdh_es(F#{ <<"alg">> => <<"ECDH-ES+A128KW">> }, A); to_map(A = ?ECDH_ES_A192KW, F) -> to_map_ecdh_es(F#{ <<"alg">> => <<"ECDH-ES+A192KW">> }, A); to_map(A = ?ECDH_ES_A256KW, F) -> to_map_ecdh_es(F#{ <<"alg">> => <<"ECDH-ES+A256KW">> }, A); to_map(A = ?ECDH_ES_C20PKW, F) -> to_map_ecdh_es(F#{ <<"alg">> => <<"ECDH-ES+C20PKW">> }, A); to_map(A = ?ECDH_ES_XC20PKW, F) -> to_map_ecdh_es(F#{ <<"alg">> => <<"ECDH-ES+XC20PKW">> }, A); to_map(A = ?ECDH_ES, F) -> to_map_ecdh_es(F#{ <<"alg">> => <<"ECDH-ES">> }, A). %%==================================================================== %% jose_jwe_alg callbacks %%==================================================================== generate_key(_Fields, {ENCModule, ENC}, ALG=#jose_jwe_alg_ecdh_es{epk=EphemeralPublicJWK=#jose_jwk{}}) -> jose_jwe_alg:generate_key(EphemeralPublicJWK, maps:get(<<"alg">>, to_map(ALG, #{})), ENCModule:algorithm(ENC)); generate_key(_Fields, {ENCModule, ENC}, ALG=#jose_jwe_alg_ecdh_es{}) -> jose_jwe_alg:generate_key({ec, <<"X25519">>}, maps:get(<<"alg">>, to_map(ALG, #{})), ENCModule:algorithm(ENC)). key_decrypt({UEphemeralKey=#jose_jwk{}, VStaticSecretKey=#jose_jwk{}}, EncryptedKey, JWEECDHES=#jose_jwe_alg_ecdh_es{epk=UEphemeralPublicKey=#jose_jwk{}}) -> case jose_jwk:thumbprint(UEphemeralKey) =:= jose_jwk:thumbprint(UEphemeralPublicKey) of true -> key_decrypt(VStaticSecretKey, EncryptedKey, JWEECDHES); false -> error end; key_decrypt({UEphemeralPublicKey=#jose_jwk{}, VStaticSecretKey=#jose_jwk{}}, EncryptedKey, JWEECDHES0=#jose_jwe_alg_ecdh_es{epk=undefined}) -> JWEECDHES = JWEECDHES0#jose_jwe_alg_ecdh_es{epk=UEphemeralPublicKey}, key_decrypt(VStaticSecretKey, EncryptedKey, JWEECDHES); key_decrypt(VStaticSecretKey=#jose_jwk{}, EncryptedKey, JWEECDHES=#jose_jwe_alg_ecdh_es{epk=UEphemeralPublicKey=#jose_jwk{}}) -> Z = jose_jwk:shared_secret(UEphemeralPublicKey, VStaticSecretKey), key_decrypt(Z, EncryptedKey, JWEECDHES); key_decrypt(Z, {ENCModule, ENC, <<>>}, #jose_jwe_alg_ecdh_es{apu=APU, apv=APV, wrap=undefined, bits=undefined}) when is_binary(Z) -> Algorithm = ENCModule:algorithm(ENC), KeyDataLen = ENCModule:bits(ENC), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), DerivedKey; key_decrypt(Z, {_ENCModule, _ENC, EncryptedKey}, JWEECDHES=#jose_jwe_alg_ecdh_es{apu=APU, apv=APV, wrap=aes_gcm_kw, bits=KeyDataLen, iv=IV, tag=TAG}) when is_binary(Z) andalso is_binary(IV) andalso is_binary(TAG) -> Algorithm = algorithm(JWEECDHES), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), jose_jwa:block_decrypt({aes_gcm, KeyDataLen}, DerivedKey, IV, {<<>>, EncryptedKey, TAG}); key_decrypt(Z, {_ENCModule, _ENC, EncryptedKey}, JWEECDHES=#jose_jwe_alg_ecdh_es{apu=APU, apv=APV, wrap=aes_kw, bits=KeyDataLen}) when is_binary(Z) -> Algorithm = algorithm(JWEECDHES), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), jose_jwa_aes_kw:unwrap(EncryptedKey, DerivedKey); key_decrypt(Z, {_ENCModule, _ENC, EncryptedKey}, JWEECDHES=#jose_jwe_alg_ecdh_es{apu=APU, apv=APV, wrap=c20p_kw, bits=KeyDataLen, iv=IV, tag=TAG}) when is_binary(Z) andalso is_binary(IV) andalso is_binary(TAG) -> Algorithm = algorithm(JWEECDHES), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), jose_jwa:block_decrypt({chacha20_poly1305, KeyDataLen}, DerivedKey, IV, {<<>>, EncryptedKey, TAG}); key_decrypt(Z, {_ENCModule, _ENC, EncryptedKey}, JWEECDHES=#jose_jwe_alg_ecdh_es{apu=APU, apv=APV, wrap=xc20p_kw, bits=KeyDataLen, iv=IV, tag=TAG}) when is_binary(Z) andalso is_binary(IV) andalso is_binary(TAG) -> Algorithm = algorithm(JWEECDHES), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), jose_jwa:block_decrypt({xchacha20_poly1305, KeyDataLen}, DerivedKey, IV, {<<>>, EncryptedKey, TAG}). key_encrypt(_Key, _DecryptedKey, JWEECDHES=#jose_jwe_alg_ecdh_es{wrap=undefined, bits=undefined}) -> {<<>>, JWEECDHES}; key_encrypt({VStaticPublicKey=#jose_jwk{}, UEphemeralSecretKey=#jose_jwk{}}, DecryptedKey, JWEECDHES=#jose_jwe_alg_ecdh_es{epk=UEphemeralPublicKey=#jose_jwk{}}) -> case jose_jwk:thumbprint(UEphemeralSecretKey) =:= jose_jwk:thumbprint(UEphemeralPublicKey) of true -> Z = jose_jwk:shared_secret(VStaticPublicKey, UEphemeralSecretKey), key_encrypt(Z, DecryptedKey, JWEECDHES); false -> error end; key_encrypt({VStaticPublicKey=#jose_jwk{}, UEphemeralSecretKey=#jose_jwk{}}, DecryptedKey, JWEECDHES0=#jose_jwe_alg_ecdh_es{epk=undefined}) -> UEphemeralPublicKey = jose_jwk:to_public(UEphemeralSecretKey), JWEECDHES1 = JWEECDHES0#jose_jwe_alg_ecdh_es{epk=UEphemeralPublicKey}, key_encrypt({VStaticPublicKey, UEphemeralSecretKey}, DecryptedKey, JWEECDHES1); key_encrypt(#jose_jwk{kty={KTYModule, KTY}}, DecryptedKey, JWEECDHES) -> DerivedKey = KTYModule:derive_key(KTY), key_encrypt(DerivedKey, DecryptedKey, JWEECDHES); key_encrypt(Z, DecryptedKey, JWEECDHES=#jose_jwe_alg_ecdh_es{apu=APU, apv=APV, wrap=aes_gcm_kw, bits=KeyDataLen, iv=IV}) when is_binary(Z) andalso is_binary(IV) -> Algorithm = algorithm(JWEECDHES), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), {CipherText, CipherTag} = jose_jwa:block_encrypt({aes_gcm, KeyDataLen}, DerivedKey, IV, {<<>>, DecryptedKey}), {CipherText, JWEECDHES#jose_jwe_alg_ecdh_es{ tag = CipherTag }}; key_encrypt(Z, DecryptedKey, JWEECDHES=#jose_jwe_alg_ecdh_es{apu=APU, apv=APV, wrap=aes_kw, bits=KeyDataLen}) when is_binary(Z) -> Algorithm = algorithm(JWEECDHES), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), {jose_jwa_aes_kw:wrap(DecryptedKey, DerivedKey), JWEECDHES}; key_encrypt(Z, DecryptedKey, JWEECDHES=#jose_jwe_alg_ecdh_es{apu=APU, apv=APV, wrap=c20p_kw, bits=KeyDataLen, iv=IV}) when is_binary(Z) andalso is_binary(IV) -> Algorithm = algorithm(JWEECDHES), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), {CipherText, CipherTag} = jose_jwa:block_encrypt({chacha20_poly1305, KeyDataLen}, DerivedKey, IV, {<<>>, DecryptedKey}), {CipherText, JWEECDHES#jose_jwe_alg_ecdh_es{ tag = CipherTag }}; key_encrypt(Z, DecryptedKey, JWEECDHES=#jose_jwe_alg_ecdh_es{apu=APU, apv=APV, wrap=xc20p_kw, bits=KeyDataLen, iv=IV}) when is_binary(Z) andalso is_binary(IV) -> Algorithm = algorithm(JWEECDHES), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), {CipherText, CipherTag} = jose_jwa:block_encrypt({xchacha20_poly1305, KeyDataLen}, DerivedKey, IV, {<<>>, DecryptedKey}), {CipherText, JWEECDHES#jose_jwe_alg_ecdh_es{ tag = CipherTag }}; key_encrypt(Z, DecryptedKey, JWEECDHES=#jose_jwe_alg_ecdh_es{wrap=aes_gcm_kw, iv=undefined}) when is_binary(Z) -> key_encrypt(Z, DecryptedKey, JWEECDHES#jose_jwe_alg_ecdh_es{ iv = crypto:strong_rand_bytes(12) }); key_encrypt(Z, DecryptedKey, JWEECDHES=#jose_jwe_alg_ecdh_es{wrap=c20p_kw, iv=undefined}) when is_binary(Z) -> key_encrypt(Z, DecryptedKey, JWEECDHES#jose_jwe_alg_ecdh_es{ iv = crypto:strong_rand_bytes(12) }); key_encrypt(Z, DecryptedKey, JWEECDHES=#jose_jwe_alg_ecdh_es{wrap=xc20p_kw, iv=undefined}) when is_binary(Z) -> key_encrypt(Z, DecryptedKey, JWEECDHES#jose_jwe_alg_ecdh_es{ iv = crypto:strong_rand_bytes(24) }). next_cek({VStaticPublicKey=#jose_jwk{}, UEphemeralSecretKey=#jose_jwk{}}, {ENCModule, ENC}, JWEECDHES=#jose_jwe_alg_ecdh_es{bits=undefined, epk=UEphemeralPublicKey=#jose_jwk{}}) -> case jose_jwk:thumbprint(UEphemeralSecretKey) =:= jose_jwk:thumbprint(UEphemeralPublicKey) of true -> Z = jose_jwk:shared_secret(VStaticPublicKey, UEphemeralSecretKey), next_cek(Z, {ENCModule, ENC}, JWEECDHES); false -> error end; next_cek({VStaticPublicKey=#jose_jwk{}, UEphemeralSecretKey=#jose_jwk{}}, {ENCModule, ENC}, JWEECDHES0=#jose_jwe_alg_ecdh_es{wrap=undefined, bits=undefined, epk=undefined}) -> UEphemeralPublicKey = jose_jwk:to_public(UEphemeralSecretKey), JWEECDHES1 = JWEECDHES0#jose_jwe_alg_ecdh_es{epk=UEphemeralPublicKey}, next_cek({VStaticPublicKey, UEphemeralSecretKey}, {ENCModule, ENC}, JWEECDHES1); next_cek(#jose_jwk{kty={KTYModule, KTY}}, {ENC, ENCModule}, JWEECDHES=#jose_jwe_alg_ecdh_es{wrap=undefined, bits=undefined}) -> DerivedKey = KTYModule:derive_key(KTY), next_cek(DerivedKey, {ENCModule, ENC}, JWEECDHES); next_cek(Z, {ENCModule, ENC}, JWEECDHES=#jose_jwe_alg_ecdh_es{apu=APU, apv=APV, wrap=undefined, bits=undefined}) when is_binary(Z) -> Algorithm = ENCModule:algorithm(ENC), KeyDataLen = ENCModule:bits(ENC), SuppPubInfo = << KeyDataLen:1/unsigned-big-integer-unit:32 >>, DerivedKey = jose_jwa_concat_kdf:kdf(sha256, Z, {Algorithm, APU, APV, SuppPubInfo}, KeyDataLen), {DerivedKey, JWEECDHES}; next_cek(_Key, {ENCModule, ENC}, JWEECDHES=#jose_jwe_alg_ecdh_es{}) -> {ENCModule:next_cek(ENC), JWEECDHES}. %%==================================================================== %% API functions %%==================================================================== algorithm(?ECDH_ES_A128GCMKW) -> <<"ECDH-ES+A128GCMKW">>; algorithm(?ECDH_ES_A192GCMKW) -> <<"ECDH-ES+A192GCMKW">>; algorithm(?ECDH_ES_A256GCMKW) -> <<"ECDH-ES+A256GCMKW">>; algorithm(?ECDH_ES_A128KW) -> <<"ECDH-ES+A128KW">>; algorithm(?ECDH_ES_A192KW) -> <<"ECDH-ES+A192KW">>; algorithm(?ECDH_ES_A256KW) -> <<"ECDH-ES+A256KW">>; algorithm(?ECDH_ES_C20PKW) -> <<"ECDH-ES+C20PKW">>; algorithm(?ECDH_ES_XC20PKW) -> <<"ECDH-ES+XC20PKW">>; algorithm(?ECDH_ES) -> <<"ECDH-ES">>. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private from_map_ecdh_es(F = #{ <<"epk">> := EPK }, H) -> from_map_ecdh_es(maps:remove(<<"epk">>, F), H#jose_jwe_alg_ecdh_es{ epk = jose_jwk:from_map(EPK) }); from_map_ecdh_es(F = #{ <<"apu">> := APU }, H) -> from_map_ecdh_es(maps:remove(<<"apu">>, F), H#jose_jwe_alg_ecdh_es{ apu = jose_jwa_base64url:decode(APU) }); from_map_ecdh_es(F = #{ <<"apv">> := APV }, H) -> from_map_ecdh_es(maps:remove(<<"apv">>, F), H#jose_jwe_alg_ecdh_es{ apv = jose_jwa_base64url:decode(APV) }); from_map_ecdh_es(F=#{ <<"iv">> := IV }, H) -> from_map_ecdh_es(maps:remove(<<"iv">>, F), H#jose_jwe_alg_ecdh_es{ iv = jose_jwa_base64url:decode(IV) }); from_map_ecdh_es(F=#{ <<"tag">> := TAG }, H) -> from_map_ecdh_es(maps:remove(<<"tag">>, F), H#jose_jwe_alg_ecdh_es{ tag = jose_jwa_base64url:decode(TAG) }); from_map_ecdh_es(F, H) -> {H, F}. %% @private to_map_ecdh_es(F, H=#jose_jwe_alg_ecdh_es{ epk = EPK = #jose_jwk{} }) -> to_map_ecdh_es(F#{ <<"epk">> => element(2, jose_jwk:to_public_map(EPK)) }, H#jose_jwe_alg_ecdh_es{ epk = undefined }); to_map_ecdh_es(F, H=#jose_jwe_alg_ecdh_es{ apu = APU }) when is_binary(APU) -> to_map_ecdh_es(F#{ <<"apu">> => jose_jwa_base64url:encode(APU) }, H#jose_jwe_alg_ecdh_es{ apu = undefined }); to_map_ecdh_es(F, H=#jose_jwe_alg_ecdh_es{ apv = APV }) when is_binary(APV) -> to_map_ecdh_es(F#{ <<"apv">> => jose_jwa_base64url:encode(APV) }, H#jose_jwe_alg_ecdh_es{ apv = undefined }); to_map_ecdh_es(F, H=#jose_jwe_alg_ecdh_es{ iv = IV }) when is_binary(IV) -> to_map_ecdh_es(F#{ <<"iv">> => jose_jwa_base64url:encode(IV) }, H#jose_jwe_alg_ecdh_es{ iv = undefined }); to_map_ecdh_es(F, H=#jose_jwe_alg_ecdh_es{ tag = TAG }) when is_binary(TAG) -> to_map_ecdh_es(F#{ <<"tag">> => jose_jwa_base64url:encode(TAG) }, H#jose_jwe_alg_ecdh_es{ tag = undefined }); to_map_ecdh_es(F, _) -> F. potatosalad-erlang-jose-30ac019/src/jwe/jose_jwe_alg_rsa.erl0000664000232200023220000000775015267717064024530 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 22 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwe_alg_rsa). -behaviour(jose_jwe). -behaviour(jose_jwe_alg). -include("jose_jwk.hrl"). %% jose_jwe callbacks -export([from_map/1]). -export([to_map/2]). %% jose_jwe_alg callbacks -export([generate_key/3]). -export([key_decrypt/3]). -export([key_encrypt/3]). -export([next_cek/3]). %% API %% Types -record(jose_jwe_alg_rsa, { algorithm = undefined :: undefined | rsa1_5 | rsa_oaep | rsa_oaep_256 }). -type alg() :: #jose_jwe_alg_rsa{}. -export_type([alg/0]). -define(RSA1_5, #jose_jwe_alg_rsa{algorithm=rsa1_5}). -define(RSA_OAEP, #jose_jwe_alg_rsa{algorithm=rsa_oaep}). -define(RSA_OAEP_256, #jose_jwe_alg_rsa{algorithm=rsa_oaep_256}). %%==================================================================== %% jose_jwe callbacks %%==================================================================== from_map(F = #{ <<"alg">> := <<"RSA1_5">> }) -> {?RSA1_5, maps:remove(<<"alg">>, F)}; from_map(F = #{ <<"alg">> := <<"RSA-OAEP">> }) -> {?RSA_OAEP, maps:remove(<<"alg">>, F)}; from_map(F = #{ <<"alg">> := <<"RSA-OAEP-256">> }) -> {?RSA_OAEP_256, maps:remove(<<"alg">>, F)}. to_map(?RSA1_5, F) -> F#{ <<"alg">> => <<"RSA1_5">> }; to_map(?RSA_OAEP, F) -> F#{ <<"alg">> => <<"RSA-OAEP">> }; to_map(?RSA_OAEP_256, F) -> F#{ <<"alg">> => <<"RSA-OAEP-256">> }. %%==================================================================== %% jose_jwe_alg callbacks %%==================================================================== generate_key(_Fields, {ENCModule, ENC}, ?RSA1_5) -> jose_jwe_alg:generate_key({rsa, 2048}, <<"RSA1_5">>, ENCModule:algorithm(ENC)); generate_key(_Fields, {ENCModule, ENC}, ?RSA_OAEP) -> jose_jwe_alg:generate_key({rsa, 2048}, <<"RSA-OAEP">>, ENCModule:algorithm(ENC)); generate_key(_Fields, {ENCModule, ENC}, ?RSA_OAEP_256) -> jose_jwe_alg:generate_key({rsa, 2048}, <<"RSA-OAEP-256">>, ENCModule:algorithm(ENC)). %% RFC 7516, Section 11.5 (and RFC 3218): the recipient MUST NOT distinguish %% between format, padding, and length errors of the JWE Encrypted Key, and %% SHOULD substitute a randomly generated CEK when the encrypted key is %% improperly formatted. Without this, RSAES-PKCS1-v1_5 ("RSA1_5") unwrapping %% hands the caller a one-bit padding-conformance signal, which is exactly the %% oracle a Bleichenbacher adaptive chosen-ciphertext attack consumes. %% %% Generate the substitute CEK unconditionally and funnel malformed encrypted %% keys into the content authentication check. This hides the padding error %% result, but does not make the underlying RSA operation constant-time. %% Prefer RSA-OAEP or RSA-OAEP-256 for new applications. key_decrypt(#jose_jwk{kty={KTYModule, KTY}}, {ENCModule, ENC, EncryptedKey}, #jose_jwe_alg_rsa{algorithm=Algorithm}) -> RandomKey = ENCModule:next_cek(ENC), try KTYModule:decrypt_private(EncryptedKey, Algorithm, KTY) of DecryptedKey when is_binary(DecryptedKey) andalso byte_size(DecryptedKey) =:= byte_size(RandomKey) -> DecryptedKey; _BadDecryptedKey -> RandomKey catch _:_ -> RandomKey end. key_encrypt(#jose_jwk{kty={KTYModule, KTY}}, DecryptedKey, JWERSA=#jose_jwe_alg_rsa{algorithm=Algorithm}) -> {KTYModule:encrypt_public(DecryptedKey, Algorithm, KTY), JWERSA}. next_cek(_Key, {ENCModule, ENC}, ALG=#jose_jwe_alg_rsa{}) -> {ENCModule:next_cek(ENC), ALG}. %%==================================================================== %% API functions %%==================================================================== %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jose.erl0000664000232200023220000001121015267717064021370 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 20 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose). %% API -export([chacha20_poly1305_module/0]). -export([chacha20_poly1305_module/1]). -export([crypto_fallback/0]). -export([crypto_fallback/1]). -export([curve25519_module/0]). -export([curve25519_module/1]). -export([curve448_module/0]). -export([curve448_module/1]). -export([decode/1]). -export([encode/1]). -export([json_module/0]). -export([json_module/1]). -export([pbes2_count_maximum/0]). -export([pbes2_count_maximum/1]). -export([sha3_module/0]). -export([sha3_module/1]). -export([unsecured_signing/0]). -export([unsecured_signing/1]). -export([xchacha20_poly1305_module/0]). -export([xchacha20_poly1305_module/1]). -export([zip_size_maximum/0]). -export([zip_size_maximum/1]). %% Private API -export([start/0]). -define(TAB, jose_jwa). -define(MAYBE_START_JOSE(F), try F catch _:_ -> _ = jose:start(), F end). %%==================================================================== %% API functions %%==================================================================== chacha20_poly1305_module() -> ?MAYBE_START_JOSE(ets:lookup_element(?TAB, chacha20_poly1305_module, 2)). chacha20_poly1305_module(ChaCha20Poly1305Module) when is_atom(ChaCha20Poly1305Module) -> ?MAYBE_START_JOSE(jose_server:chacha20_poly1305_module(ChaCha20Poly1305Module)). crypto_fallback() -> jose_jwa:crypto_fallback(). crypto_fallback(Boolean) when is_boolean(Boolean) -> jose_jwa:crypto_fallback(Boolean). curve25519_module() -> ?MAYBE_START_JOSE(ets:lookup_element(?TAB, curve25519_module, 2)). curve25519_module(Curve25519Module) when is_atom(Curve25519Module) -> ?MAYBE_START_JOSE(jose_server:curve25519_module(Curve25519Module)). curve448_module() -> ?MAYBE_START_JOSE(ets:lookup_element(?TAB, curve448_module, 2)). curve448_module(Curve448Module) when is_atom(Curve448Module) -> ?MAYBE_START_JOSE(jose_server:curve448_module(Curve448Module)). decode(Binary) -> JSONModule = json_module(), JSONModule:decode(Binary). encode(Term) -> JSONModule = json_module(), JSONModule:encode(Term). json_module() -> ?MAYBE_START_JOSE(ets:lookup_element(?TAB, json_module, 2)). json_module(JSONModule) when is_atom(JSONModule) -> ?MAYBE_START_JOSE(jose_server:json_module(JSONModule)). -spec pbes2_count_maximum() -> non_neg_integer(). pbes2_count_maximum() -> ?MAYBE_START_JOSE(ets:lookup_element(?TAB, pbes2_count_maximum, 2)). -spec pbes2_count_maximum(PBES2CountMaximum) -> ok when PBES2CountMaximum :: non_neg_integer(). pbes2_count_maximum(PBES2CountMaximum) when is_integer(PBES2CountMaximum) andalso PBES2CountMaximum >= 0 -> ?MAYBE_START_JOSE(jose_server:pbes2_count_maximum(PBES2CountMaximum)). sha3_module() -> ?MAYBE_START_JOSE(ets:lookup_element(?TAB, sha3_module, 2)). sha3_module(SHA3Module) when is_atom(SHA3Module) -> ?MAYBE_START_JOSE(jose_server:sha3_module(SHA3Module)). -spec unsecured_signing() -> boolean(). unsecured_signing() -> ?MAYBE_START_JOSE(ets:lookup_element(?TAB, unsecured_signing, 2)). -spec unsecured_signing(UnsecuredSigning) -> ok when UnsecuredSigning :: boolean(). unsecured_signing(UnsecuredSigning) when is_boolean(UnsecuredSigning) -> ?MAYBE_START_JOSE(jose_server:unsecured_signing(UnsecuredSigning)). xchacha20_poly1305_module() -> ?MAYBE_START_JOSE(ets:lookup_element(?TAB, xchacha20_poly1305_module, 2)). xchacha20_poly1305_module(XChaCha20Poly1305Module) when is_atom(XChaCha20Poly1305Module) -> ?MAYBE_START_JOSE(jose_server:xchacha20_poly1305_module(XChaCha20Poly1305Module)). -spec zip_size_maximum() -> non_neg_integer(). zip_size_maximum() -> ?MAYBE_START_JOSE(ets:lookup_element(?TAB, zip_size_maximum, 2)). -spec zip_size_maximum(ZipSizeMaximum) -> ok when ZipSizeMaximum :: non_neg_integer(). zip_size_maximum(ZipSizeMaximum) when is_integer(ZipSizeMaximum) andalso ZipSizeMaximum >= 0 -> ?MAYBE_START_JOSE(jose_server:zip_size_maximum(ZipSizeMaximum)). %%==================================================================== %% Private API functions %%==================================================================== start() -> case application:ensure_all_started(?MODULE) of {ok, _} -> ok; StartError -> StartError end. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jwk/0000775000232200023220000000000015267717064020524 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/src/jwk/jose_jwk_kty_okp_x448.erl0000664000232200023220000002147315267717064025401 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 15 Jan 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwk_kty_okp_x448). -behaviour(jose_jwk). -behaviour(jose_jwk_kty). -behaviour(jose_jwk_use_enc). -include("jose_public_key.hrl"). %% jose_jwk callbacks -export([from_map/1]). -export([to_key/1]). -export([to_map/2]). -export([to_public_map/2]). -export([to_thumbprint_map/2]). %% jose_jwk_kty callbacks -export([generate_key/1]). -export([generate_key/2]). -export([key_encryptor/3]). %% jose_jwk_use_enc callbacks -export([block_encryptor/2]). -export([derive_key/2]). %% API -export([from_der/1]). -export([from_der/2]). -export([from_key/1]). -export([from_okp/1]). -export([from_openssh_key/1]). -export([from_pem/1]). -export([from_pem/2]). -export([to_der/1]). -export([to_der/2]). -export([to_okp/1]). -export([to_openssh_key/2]). -export([to_pem/1]). -export([to_pem/2]). %% Macros -define(crv, <<"X448">>). -define(secretbytes, 56). -define(publickeybytes, 56). -define(secretkeybytes, 112). %% Types -type publickey() :: << _:448 >>. -type secretkey() :: << _:896 >>. -type key() :: publickey() | secretkey(). -export_type([key/0]). %%==================================================================== %% jose_jwk callbacks %%==================================================================== from_map(F = #{ <<"kty">> := <<"OKP">>, <<"crv">> := ?crv, <<"d">> := D, <<"x">> := X }) -> << Secret:?secretbytes/binary >> = jose_jwa_base64url:decode(D), << PK:?publickeybytes/binary >> = jose_jwa_base64url:decode(X), SK = << Secret/binary, PK/binary >>, {SK, maps:without([<<"crv">>, <<"d">>, <<"kty">>, <<"x">>], F)}; from_map(F = #{ <<"kty">> := <<"OKP">>, <<"crv">> := ?crv, <<"x">> := X }) -> << PK:?publickeybytes/binary >> = jose_jwa_base64url:decode(X), {PK, maps:without([<<"crv">>, <<"kty">>, <<"x">>], F)}. to_key(<< PublicKey:?publickeybytes/binary >>) -> #'jose_X448PublicKey'{ publicKey = PublicKey }; to_key(<< PrivateKey:?secretbytes/binary, PublicKey:?publickeybytes/binary >>) -> #'jose_X448PrivateKey'{ publicKey = #'jose_X448PublicKey'{ publicKey = PublicKey }, privateKey = PrivateKey }. to_map(PK = << _:?publickeybytes/binary >>, F) -> F#{ <<"crv">> => ?crv, <<"kty">> => <<"OKP">>, <<"x">> => jose_jwa_base64url:encode(PK) }; to_map(<< Secret:?secretbytes/binary, PK:?publickeybytes/binary >>, F) -> F#{ <<"crv">> => ?crv, <<"d">> => jose_jwa_base64url:encode(Secret), <<"kty">> => <<"OKP">>, <<"x">> => jose_jwa_base64url:encode(PK) }. to_public_map(PK = << _:?publickeybytes/binary >>, F) -> to_map(PK, F); to_public_map(<< _:?secretbytes/binary, PK:?publickeybytes/binary >>, F) -> to_public_map(PK, F). to_thumbprint_map(K, F) -> maps:with([<<"crv">>, <<"kty">>, <<"x">>], to_public_map(K, F)). %%==================================================================== %% jose_jwk_kty callbacks %%==================================================================== generate_key(Seed = << _:?secretbytes/binary >>) -> {PK, SK} = jose_curve448:x448_keypair(Seed), {<< SK/binary, PK/binary >>, #{}}; generate_key({okp, 'X448', Seed = << _:?secretbytes/binary >>}) -> generate_key(Seed); generate_key({okp, 'X448'}) -> {PK, SK} = jose_curve448:x448_keypair(), {<< SK/binary, PK/binary >>, #{}}. generate_key(KTY, Fields) when is_binary(KTY) andalso (byte_size(KTY) =:= ?publickeybytes orelse byte_size(KTY) =:= ?secretkeybytes) -> {NewKTY, OtherFields} = generate_key({okp, 'X448'}), {NewKTY, maps:merge(maps:remove(<<"kid">>, Fields), OtherFields)}. key_encryptor(KTY, Fields, Key) -> jose_jwk_kty:key_encryptor(KTY, Fields, Key). %%==================================================================== %% jose_jwk_use_enc callbacks %%==================================================================== block_encryptor(_KTY, Fields=#{ <<"alg">> := ALG, <<"enc">> := ENC, <<"use">> := <<"enc">> }) -> Folder = fun (K, V, F) when K =:= <<"apu">> orelse K =:= <<"apv">> orelse K =:= <<"epk">> orelse K =:= <<"skid">> -> maps:put(K, V, F); (_K, _V, F) -> F end, maps:fold(Folder, #{ <<"alg">> => ALG, <<"enc">> => ENC }, Fields); block_encryptor(KTY, Fields=#{ <<"alg">> := <<"ECDH-1PU", _/binary>> }) -> block_encryptor(KTY, maps:merge(Fields, #{ <<"enc">> => case jose_jwa:is_block_cipher_supported({aes_gcm, 128}) of false -> <<"A128CBC-HS256">>; true -> <<"A128GCM">> end, <<"use">> => <<"enc">> })); block_encryptor(KTY, Fields=#{ <<"alg">> := <<"ECDH-SS", _/binary>> }) -> block_encryptor(KTY, maps:merge(Fields, #{ <<"enc">> => case jose_jwa:is_block_cipher_supported({aes_gcm, 128}) of false -> <<"A128CBC-HS256">>; true -> <<"A128GCM">> end, <<"use">> => <<"enc">> })); block_encryptor(KTY, Fields) -> block_encryptor(KTY, maps:merge(Fields, #{ <<"alg">> => <<"ECDH-ES">>, <<"enc">> => case jose_jwa:is_block_cipher_supported({aes_gcm, 128}) of false -> <<"A128CBC-HS256">>; true -> <<"A128GCM">> end, <<"use">> => <<"enc">> })). derive_key(<< _:?secretbytes/binary, PK:?publickeybytes/binary >>, SK = << _:?secretkeybytes/binary >>) -> derive_key(PK, SK); derive_key(PK = << _:?publickeybytes/binary >>, << Secret:?secretbytes/binary, _:?publickeybytes/binary >>) -> jose_curve448:x448_shared_secret(Secret, PK). %%==================================================================== %% API functions %%==================================================================== from_der(DERBinary) when is_binary(DERBinary) -> case jose_jwk_der:from_binary(DERBinary) of {?MODULE, {Key, Fields}} -> {Key, Fields} end. from_der(Password, DERBinary) when is_binary(DERBinary) -> case jose_jwk_der:from_binary(Password, DERBinary) of {?MODULE, {Key, Fields}} -> {Key, Fields} end. from_key(#'jose_X448PrivateKey'{publicKey=#'jose_X448PublicKey'{publicKey=Public}, privateKey=Secret}) -> {<< Secret/binary, Public/binary >>, #{}}; from_key(#'jose_X448PublicKey'{publicKey=Public}) -> {Public, #{}}. from_okp({'X448', SK = << Secret:?secretbytes/binary, PK:?publickeybytes/binary >>}) -> case jose_curve448:x448_secret_to_public(Secret) of PK -> {SK, #{}}; _ -> erlang:error(badarg) end; from_okp({'X448', PK = << _:?publickeybytes/binary >>}) -> {PK, #{}}. from_openssh_key({<<"ssh-x448">>, _PK, SK, Comment}) -> {KTY, OtherFields} = from_okp({'X448', SK}), case Comment of <<>> -> {KTY, OtherFields}; _ -> {KTY, maps:merge(#{ <<"kid">> => Comment }, OtherFields)} end. from_pem(PEMBinary) when is_binary(PEMBinary) -> case jose_jwk_pem:from_binary(PEMBinary) of {?MODULE, {Key, Fields}} -> {Key, Fields}; PEMError -> PEMError end. from_pem(Password, PEMBinary) when is_binary(PEMBinary) -> case jose_jwk_pem:from_binary(Password, PEMBinary) of {?MODULE, {Key, Fields}} -> {Key, Fields}; PEMError -> PEMError end. to_der(SK = << _:?secretkeybytes/binary >>) -> X448PrivateKey = to_key(SK), jose_public_key:der_encode('X448PrivateKey', X448PrivateKey); to_der(PK = << _:?publickeybytes/binary >>) -> X448PublicKey = to_key(PK), jose_public_key:der_encode('X448PublicKey', X448PublicKey). to_der(Password, SK = << _:?secretkeybytes/binary >>) -> X448PrivateKey = to_key(SK), jose_jwk_der:to_binary(Password, 'X448PrivateKey', X448PrivateKey); to_der(Password, PK = << _:?publickeybytes/binary >>) -> X448PublicKey = to_key(PK), jose_jwk_der:to_binary(Password, 'X448PublicKey', X448PublicKey). to_okp(SK = << _:?secretkeybytes/binary >>) -> {'X448', SK}; to_okp(PK = << _:?publickeybytes/binary >>) -> {'X448', PK}. to_openssh_key(SK = << _:?secretbytes/binary, PK:?publickeybytes/binary >>, F) -> Comment = maps:get(<<"kid">>, F, <<>>), jose_jwk_openssh_key:to_binary([[{{<<"ssh-x448">>, PK}, {<<"ssh-x448">>, PK, SK, Comment}}]]). to_pem(SK = << _:?secretkeybytes/binary >>) -> X448PrivateKey = to_key(SK), PEMEntry = jose_public_key:pem_entry_encode('X448PrivateKey', X448PrivateKey), jose_public_key:pem_encode([PEMEntry]); to_pem(PK = << _:?publickeybytes/binary >>) -> X448PublicKey = to_key(PK), PEMEntry = jose_public_key:pem_entry_encode('X448PublicKey', X448PublicKey), jose_public_key:pem_encode([PEMEntry]). to_pem(Password, SK = << _:?secretkeybytes/binary >>) -> X448PrivateKey = to_key(SK), jose_jwk_pem:to_binary(Password, 'PrivateKeyInfo', X448PrivateKey); to_pem(Password, PK = << _:?publickeybytes/binary >>) -> X448PublicKey = to_key(PK), jose_jwk_pem:to_binary(Password, 'X448PublicKey', X448PublicKey). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jwk/jose_jwk.erl0000664000232200023220000012665715267717064023064 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 21 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwk). -include("jose_jwe.hrl"). -include("jose_jwk.hrl"). -include("jose_jws.hrl"). -callback from_map(Fields) -> KTY when Fields :: map(), KTY :: any(). -callback to_key(KTY) -> Key when KTY :: any(), Key :: any(). -callback to_map(KTY, Fields) -> Map when KTY :: any(), Fields :: map(), Map :: map(). -callback to_public_map(KTY, Fields) -> Map when KTY :: any(), Fields :: map(), Map :: map(). -callback to_thumbprint_map(KTY, Fields) -> Map when KTY :: any(), Fields :: map(), Map :: map(). %% Decode API -export([from/1]). -export([from/2]). -export([from_binary/1]). -export([from_binary/2]). -export([from_der/1]). -export([from_der/2]). -export([from_der_file/1]). -export([from_der_file/2]). -export([from_file/1]). -export([from_file/2]). -export([from_firebase/1]). -export([from_key/1]). -export([from_map/1]). -export([from_map/2]). -export([from_oct/1]). -export([from_oct/2]). -export([from_oct_file/1]). -export([from_oct_file/2]). -export([from_okp/1]). -export([from_openssh_key/1]). -export([from_openssh_key_file/1]). -export([from_pem/1]). -export([from_pem/2]). -export([from_pem_file/1]). -export([from_pem_file/2]). %% Encode API -export([to_binary/1]). -export([to_binary/2]). -export([to_binary/3]). -export([to_der/1]). -export([to_der/2]). -export([to_der_file/2]). -export([to_der_file/3]). -export([to_file/2]). -export([to_file/3]). -export([to_file/4]). -export([to_key/1]). -export([to_map/1]). -export([to_map/2]). -export([to_map/3]). -export([to_oct/1]). -export([to_oct/2]). -export([to_oct/3]). -export([to_oct_file/2]). -export([to_oct_file/3]). -export([to_oct_file/4]). -export([to_okp/1]). -export([to_openssh_key/1]). -export([to_openssh_key_file/2]). -export([to_pem/1]). -export([to_pem/2]). -export([to_pem_file/2]). -export([to_pem_file/3]). -export([to_public/1]). -export([to_public_file/2]). -export([to_public_key/1]). -export([to_public_map/1]). -export([to_thumbprint_map/1]). %% API -export([block_decrypt/2]). -export([block_encrypt/2]). -export([block_encrypt/3]). -export([block_encryptor/1]). -export([box_decrypt/2]). -export([box_encrypt/2]). -export([box_encrypt/3]). -export([box_encrypt/4]). -deprecated([{box_decrypt, 2, next_major_release}]). -deprecated([{box_encrypt, 2, next_major_release}]). -deprecated([{box_encrypt, 3, next_major_release}]). -deprecated([{box_encrypt, 4, next_major_release}]). -export([box_decrypt_ecdh_1pu/3]). -export([box_encrypt_ecdh_1pu/3]). -export([box_encrypt_ecdh_1pu/4]). -export([box_encrypt_ecdh_1pu/5]). -export([box_decrypt_ecdh_es/2]). -export([box_encrypt_ecdh_es/2]). -export([box_encrypt_ecdh_es/3]). -export([box_encrypt_ecdh_es/4]). -export([box_decrypt_ecdh_ss/2]). -export([box_encrypt_ecdh_ss/2]). -export([box_encrypt_ecdh_ss/3]). -export([box_encrypt_ecdh_ss/4]). -export([generate_key/1]). -export([merge/2]). -export([shared_secret/2]). -export([sign/2]). -export([sign/3]). -export([signer/1]). -export([thumbprint/1]). -export([thumbprint/2]). -export([thumbprint_concat/1]). -export([thumbprint_concat/2]). -export([verifier/1]). -export([verify/2]). -export([verify_strict/3]). %% Types -type key() :: #jose_jwk{}. -export_type([key/0]). -define(KEYS_MODULE, jose_jwk_set). -define(KTY_EC_MODULE, jose_jwk_kty_ec). -define(KTY_OCT_MODULE, jose_jwk_kty_oct). -define(KTY_RSA_MODULE, jose_jwk_kty_rsa). -define(KTY_OKP_Ed25519_MODULE, jose_jwk_kty_okp_ed25519). -define(KTY_OKP_Ed25519ph_MODULE, jose_jwk_kty_okp_ed25519ph). -define(KTY_OKP_X25519_MODULE, jose_jwk_kty_okp_x25519). -define(KTY_OKP_Ed448_MODULE, jose_jwk_kty_okp_ed448). -define(KTY_OKP_Ed448ph_MODULE, jose_jwk_kty_okp_ed448ph). -define(KTY_OKP_X448_MODULE, jose_jwk_kty_okp_x448). %%==================================================================== %% Decode API functions %%==================================================================== from(List) when is_list(List) -> [from(Element) || Element <- List]; from({Modules, Map}) when is_map(Modules) andalso is_map(Map) -> from_map({Modules, Map}); from({Modules, Binary}) when is_map(Modules) andalso is_binary(Binary) -> from_binary({Modules, Binary}); from(JWK=#jose_jwk{}) -> JWK; from(Other) when is_map(Other) orelse is_binary(Other) -> from({#{}, Other}). from(Key, List) when is_list(List) -> [from(Key, Element) || Element <- List]; from(Key, {Modules, EncryptedMap}) when is_map(Modules) andalso is_map(EncryptedMap) -> from_map(Key, {Modules, EncryptedMap}); from(Key, {Modules, EncryptedBinary}) when is_map(Modules) andalso is_binary(EncryptedBinary) -> from_binary(Key, {Modules, EncryptedBinary}); from(_Key, JWK=#jose_jwk{}) -> JWK; from(Key, Other) when is_map(Other) orelse is_binary(Other) -> from(Key, {#{}, Other}). from_binary(List) when is_list(List) -> [from_binary(Element) || Element <- List]; from_binary({Modules, Binary}) when is_map(Modules) andalso is_binary(Binary) -> from_map({Modules, jose:decode(Binary)}); from_binary(Binary) when is_binary(Binary) -> from_binary({#{}, Binary}). from_binary(Key, List) when is_list(List) -> [from_binary(Key, Element) || Element <- List]; from_binary(Key, {Modules, Encrypted = << ${, _/binary >>}) when is_map(Modules) andalso is_binary(Encrypted) -> EncrypedMap = jose:decode(Encrypted), from_map(Key, {Modules, EncrypedMap}); from_binary(Key, {Modules, Encrypted}) when is_map(Modules) andalso is_binary(Encrypted) -> {JWKBinary, JWE=#jose_jwe{}} = jose_jwe:block_decrypt(Key, {Modules, Encrypted}), {JWE, from_binary({Modules, JWKBinary})}; from_binary(Key, Encrypted) when is_binary(Encrypted) -> from_binary(Key, {#{}, Encrypted}). from_der(List) when is_list(List) -> [from_der(Element) || Element <- List]; from_der({#{ kty := Module }, Binary}) when is_binary(Binary) -> {KTY, Fields} = Module:from_der(Binary), #jose_jwk{ kty = {Module, KTY}, fields = Fields }; from_der({#{}, Binary}) when is_binary(Binary) -> case jose_jwk_der:from_binary(Binary) of {Module, {KTY, Fields}} when Module =/= error -> #jose_jwk{ kty = {Module, KTY}, fields = Fields }; PEMError -> PEMError end; from_der(Binary) when is_binary(Binary) -> from_der({#{}, Binary}). from_der(Key, List) when is_list(List) -> [from_der(Key, Element) || Element <- List]; from_der(Key, {#{ kty := Module }, Binary}) when is_binary(Binary) -> {KTY, Fields} = Module:from_der(Key, Binary), #jose_jwk{ kty = {Module, KTY}, fields = Fields }; from_der(Key, {#{}, Binary}) when is_binary(Binary) -> case jose_jwk_der:from_binary(Key, Binary) of {Module, {KTY, Fields}} when Module =/= error -> #jose_jwk{ kty = {Module, KTY}, fields = Fields }; PEMError -> PEMError end; from_der(Key, Binary) when is_binary(Binary) -> from_der(Key, {#{}, Binary}). from_der_file({Modules, File}) when is_map(Modules) andalso (is_binary(File) orelse is_list(File)) -> case file:read_file(File) of {ok, Binary} -> from_der({Modules, Binary}); ReadError -> ReadError end; from_der_file(File) when is_binary(File) orelse is_list(File) -> from_der_file({#{}, File}). from_der_file(Key, {Modules, File}) when is_map(Modules) andalso (is_binary(File) orelse is_list(File)) -> case file:read_file(File) of {ok, Binary} -> from_der(Key, {Modules, Binary}); ReadError -> ReadError end; from_der_file(Key, File) when is_binary(File) orelse is_list(File) -> from_der_file(Key, {#{}, File}). from_file({Modules, File}) when is_map(Modules) andalso (is_binary(File) orelse is_list(File)) -> case file:read_file(File) of {ok, Binary} -> from_binary({Modules, Binary}); ReadError -> ReadError end; from_file(File) when is_binary(File) orelse is_list(File) -> from_file({#{}, File}). from_file(Key, {Modules, File}) when is_map(Modules) andalso (is_binary(File) orelse is_list(File)) -> case file:read_file(File) of {ok, Binary} -> from_binary(Key, {Modules, Binary}); ReadError -> ReadError end; from_file(Key, File) when is_binary(File) orelse is_list(File) -> from_file(Key, {#{}, File}). from_firebase(Binary) when is_binary(Binary) -> from_firebase({#{}, Binary}); from_firebase({Modules, Binary}) when is_map(Modules) andalso is_binary(Binary) -> Map = jose:decode(Binary), from_firebase({Modules, Map}); from_firebase(Map) when is_map(Map) -> from_firebase({#{}, Map}); from_firebase({Modules, Map}) when is_map(Modules) andalso is_map(Map) -> Folder = fun (Key, Val, Acc) -> JWK = jose_jwk:merge(jose_jwk:from_pem({Modules, Val}), #{ <<"kid">> => Key }), maps:put(Key, JWK, Acc) end, maps:fold(Folder, #{}, Map). from_key(List) when is_list(List) -> [from_key(Element) || Element <- List]; from_key(Key) -> case jose_jwk_kty:from_key(Key) of {KTYModule, {KTY, Fields}} when KTYModule =/= error -> #jose_jwk{ kty = {KTYModule, KTY}, fields = Fields }; FromKeyError -> FromKeyError end. from_map(List) when is_list(List) -> [from_map(Element) || Element <- List]; from_map(Map) when is_map(Map) -> from_map({#{}, Map}); from_map({Modules, Map}) when is_map(Modules) andalso is_map(Map) -> from_map({#jose_jwk{}, Modules, Map}); from_map({JWK, Modules=#{ keys := Module }, Map=#{ <<"keys">> := _ }}) -> {KEYS, Fields} = Module:from_map(Map), from_map({JWK#jose_jwk{ keys = {Module, KEYS} }, maps:remove(keys, Modules), Fields}); from_map({JWK, Modules=#{ kty := Module }, Map=#{ <<"kty">> := _ }}) -> {KTY, Fields} = Module:from_map(Map), from_map({JWK#jose_jwk{ kty = {Module, KTY} }, maps:remove(kty, Modules), Fields}); from_map({JWK, Modules, Map=#{ <<"keys">> := _ }}) -> from_map({JWK, Modules#{ keys => ?KEYS_MODULE }, Map}); from_map({JWK, Modules, Map=#{ <<"kty">> := <<"EC">> }}) -> from_map({JWK, Modules#{ kty => ?KTY_EC_MODULE }, Map}); from_map({JWK, Modules, Map=#{ <<"kty">> := <<"oct">> }}) -> from_map({JWK, Modules#{ kty => ?KTY_OCT_MODULE }, Map}); from_map({JWK, Modules, Map=#{ <<"kty">> := <<"OKP">>, <<"crv">> := <<"Ed25519">> }}) -> from_map({JWK, Modules#{ kty => ?KTY_OKP_Ed25519_MODULE }, Map}); from_map({JWK, Modules, Map=#{ <<"kty">> := <<"OKP">>, <<"crv">> := <<"Ed25519ph">> }}) -> from_map({JWK, Modules#{ kty => ?KTY_OKP_Ed25519ph_MODULE }, Map}); from_map({JWK, Modules, Map=#{ <<"kty">> := <<"OKP">>, <<"crv">> := <<"X25519">> }}) -> from_map({JWK, Modules#{ kty => ?KTY_OKP_X25519_MODULE }, Map}); from_map({JWK, Modules, Map=#{ <<"kty">> := <<"OKP">>, <<"crv">> := <<"Ed448">> }}) -> from_map({JWK, Modules#{ kty => ?KTY_OKP_Ed448_MODULE }, Map}); from_map({JWK, Modules, Map=#{ <<"kty">> := <<"OKP">>, <<"crv">> := <<"Ed448ph">> }}) -> from_map({JWK, Modules#{ kty => ?KTY_OKP_Ed448ph_MODULE }, Map}); from_map({JWK, Modules, Map=#{ <<"kty">> := <<"OKP">>, <<"crv">> := <<"X448">> }}) -> from_map({JWK, Modules#{ kty => ?KTY_OKP_X448_MODULE }, Map}); from_map({JWK, Modules, Map=#{ <<"kty">> := <<"RSA">> }}) -> from_map({JWK, Modules#{ kty => ?KTY_RSA_MODULE }, Map}); from_map({#jose_jwk{ keys = undefined, kty = undefined }, _Modules, _Map}) -> {error, {missing_required_keys, [<<"keys">>, <<"kty">>]}}; from_map({JWK, _Modules, Fields}) -> JWK#jose_jwk{ fields = Fields }. from_map(Key, List) when is_list(List) -> [from_map(Key, Element) || Element <- List]; from_map(Key, {Modules, Encrypted}) when is_map(Modules) andalso is_map(Encrypted) -> {JWKBinary, JWE=#jose_jwe{}} = jose_jwe:block_decrypt(Key, {Modules, Encrypted}), {JWE, from_binary({Modules, JWKBinary})}; from_map(Key, Encrypted) when is_map(Encrypted) -> from_map(Key, {#{}, Encrypted}). from_oct(List) when is_list(List) -> [from_oct(Element) || Element <- List]; from_oct({#{ kty := Module }, Binary}) when is_binary(Binary) -> {KTY, Fields} = Module:from_oct(Binary), #jose_jwk{ kty = {Module, KTY}, fields = Fields }; from_oct({#{}, Binary}) when is_binary(Binary) -> case jose_jwk_oct:from_binary(Binary) of {Module, {KTY, Fields}} when Module =/= error -> #jose_jwk{ kty = {Module, KTY}, fields = Fields }; OCTError -> OCTError end; from_oct(Binary) when is_binary(Binary) -> from_oct({#{}, Binary}). from_oct(Key, List) when is_list(List) -> [from_oct(Key, Element) || Element <- List]; from_oct(Key, {Modules, Encrypted}) when is_map(Modules) andalso is_binary(Encrypted) -> {OCTBinary, JWE=#jose_jwe{}} = jose_jwe:block_decrypt(Key, {Modules, Encrypted}), {JWE, from_oct({Modules, OCTBinary})}; from_oct(Key, Encrypted) when is_binary(Encrypted) -> from_oct(Key, {#{}, Encrypted}). from_oct_file({Modules, File}) when is_map(Modules) andalso (is_binary(File) orelse is_list(File)) -> case file:read_file(File) of {ok, Binary} -> from_oct({Modules, Binary}); ReadError -> ReadError end; from_oct_file(File) when is_binary(File) orelse is_list(File) -> from_oct_file({#{}, File}). from_oct_file(Key, {Modules, File}) when is_map(Modules) andalso (is_binary(File) orelse is_list(File)) -> case file:read_file(File) of {ok, Binary} -> from_oct(Key, {Modules, Binary}); ReadError -> ReadError end; from_oct_file(Key, File) when is_binary(File) orelse is_list(File) -> from_oct_file(Key, {#{}, File}). from_okp(List) when is_list(List) -> [from_okp(Element) || Element <- List]; from_okp({#{ kty := Module }, OKP}) -> {KTY, Fields} = Module:from_okp(OKP), #jose_jwk{ kty = {Module, KTY}, fields = Fields }; from_okp(P={'Ed25519', Binary}) when is_binary(Binary) -> from_okp({#{ kty => ?KTY_OKP_Ed25519_MODULE }, P}); from_okp(P={'Ed25519ph', Binary}) when is_binary(Binary) -> from_okp({#{ kty => ?KTY_OKP_Ed25519ph_MODULE }, P}); from_okp(P={'X25519', Binary}) when is_binary(Binary) -> from_okp({#{ kty => ?KTY_OKP_X25519_MODULE }, P}); from_okp(P={'Ed448', Binary}) when is_binary(Binary) -> from_okp({#{ kty => ?KTY_OKP_Ed448_MODULE }, P}); from_okp(P={'Ed448ph', Binary}) when is_binary(Binary) -> from_okp({#{ kty => ?KTY_OKP_Ed448ph_MODULE }, P}); from_okp(P={'X448', Binary}) when is_binary(Binary) -> from_okp({#{ kty => ?KTY_OKP_X448_MODULE }, P}). from_openssh_key(List) when is_list(List) -> [from_openssh_key(Element) || Element <- List]; from_openssh_key({#{ kty := Module }, Binary}) when is_binary(Binary) -> {KTY, Fields} = Module:from_openssh_key(Binary), #jose_jwk{ kty = {Module, KTY}, fields = Fields }; from_openssh_key({#{}, Binary}) when is_binary(Binary) -> case jose_jwk_openssh_key:from_binary(Binary) of CurrentKey = [[{{Type, PK}, Key = {Type, PK, _SK, _Comment}} | _] | _] -> Module = case Type of <<"ssh-ed25519">> -> ?KTY_OKP_Ed25519_MODULE; <<"ssh-ed25519ph">> -> ?KTY_OKP_Ed25519ph_MODULE; <<"ssh-x25519">> -> ?KTY_OKP_X25519_MODULE; <<"ssh-ed448">> -> ?KTY_OKP_Ed448_MODULE; <<"ssh-ed448ph">> -> ?KTY_OKP_Ed448ph_MODULE; <<"ssh-x448">> -> ?KTY_OKP_X448_MODULE; _ -> {error, {unknown_key, CurrentKey}} end, case is_atom(Module) of true -> {KTY, Fields} = Module:from_openssh_key(Key), #jose_jwk{ kty = {Module, KTY}, fields = Fields }; false -> Module end; UnknownKey -> {error, {unknown_key, UnknownKey}} end; from_openssh_key(Binary) when is_binary(Binary) -> from_openssh_key({#{}, Binary}). from_openssh_key_file({Modules, File}) when is_map(Modules) andalso (is_binary(File) orelse is_list(File)) -> case file:read_file(File) of {ok, Binary} -> from_openssh_key({Modules, Binary}); ReadError -> ReadError end; from_openssh_key_file(File) when is_binary(File) orelse is_list(File) -> from_openssh_key_file({#{}, File}). from_pem(List) when is_list(List) -> [from_pem(Element) || Element <- List]; from_pem({#{ kty := Module }, Binary}) when is_binary(Binary) -> {KTY, Fields} = Module:from_pem(Binary), #jose_jwk{ kty = {Module, KTY}, fields = Fields }; from_pem({#{}, Binary}) when is_binary(Binary) -> case jose_jwk_pem:from_binary(Binary) of {Module, {KTY, Fields}} when Module =/= error -> #jose_jwk{ kty = {Module, KTY}, fields = Fields }; PEMError -> PEMError end; from_pem(Binary) when is_binary(Binary) -> from_pem({#{}, Binary}). from_pem(Key, List) when is_list(List) -> [from_pem(Key, Element) || Element <- List]; from_pem(Key, {#{ kty := Module }, Binary}) when is_binary(Binary) -> {KTY, Fields} = Module:from_pem(Key, Binary), #jose_jwk{ kty = {Module, KTY}, fields = Fields }; from_pem(Key, {#{}, Binary}) when is_binary(Binary) -> case jose_jwk_pem:from_binary(Key, Binary) of {Module, {KTY, Fields}} when Module =/= error -> #jose_jwk{ kty = {Module, KTY}, fields = Fields }; PEMError -> PEMError end; from_pem(Key, Binary) when is_binary(Binary) -> from_pem(Key, {#{}, Binary}). from_pem_file({Modules, File}) when is_map(Modules) andalso (is_binary(File) orelse is_list(File)) -> case file:read_file(File) of {ok, Binary} -> from_pem({Modules, Binary}); ReadError -> ReadError end; from_pem_file(File) when is_binary(File) orelse is_list(File) -> from_pem_file({#{}, File}). from_pem_file(Key, {Modules, File}) when is_map(Modules) andalso (is_binary(File) orelse is_list(File)) -> case file:read_file(File) of {ok, Binary} -> from_pem(Key, {Modules, Binary}); ReadError -> ReadError end; from_pem_file(Key, File) when is_binary(File) orelse is_list(File) -> from_pem_file(Key, {#{}, File}). %%==================================================================== %% Encode API functions %%==================================================================== to_binary(List) when is_list(List) -> [to_binary(Element) || Element <- List]; to_binary(JWK=#jose_jwk{}) -> {Modules, Map} = to_map(JWK), {Modules, jose:encode(Map)}; to_binary(Other) -> to_binary(from(Other)). to_binary(Key, List) when is_list(List) -> [to_binary(Key, Element) || Element <- List]; to_binary(Key, JWK=#jose_jwk{kty={Module, KTY}, fields=Fields}) -> to_binary(Key, Module:key_encryptor(KTY, Fields, Key), JWK); to_binary(Key, Other) -> to_binary(Key, from(Other)). to_binary(Key, JWE=#jose_jwe{}, JWK=#jose_jwk{}) -> {Modules, EncryptedMap} = to_map(Key, JWE, JWK), {Modules, jose:encode(EncryptedMap)}; to_binary(Key, JWEOther, JWKOther) -> to_binary(Key, jose_jwe:from(JWEOther), from(JWKOther)). to_der(List) when is_list(List) -> [to_der(Element) || Element <- List]; to_der(#jose_jwk{kty={Module, KTY}}) -> {#{ kty => Module }, Module:to_der(KTY)}; to_der(Other) -> to_der(from(Other)). to_der(Key, #jose_jwk{kty={Module, KTY}}) -> {#{ kty => Module }, Module:to_der(Key, KTY)}; to_der(Key, Other) -> to_der(Key, from(Other)). to_der_file(File, JWK=#jose_jwk{}) when is_binary(File) orelse is_list(File) -> {Modules, Binary} = to_der(JWK), case file:write_file(File, Binary) of ok -> {Modules, File}; WriteError -> WriteError end; to_der_file(File, Other) when is_binary(File) orelse is_list(File) -> to_der_file(File, from(Other)). to_der_file(Key, File, JWK=#jose_jwk{}) when is_binary(File) orelse is_list(File) -> {Modules, Binary} = to_der(Key, JWK), case file:write_file(File, Binary) of ok -> {Modules, File}; WriteError -> WriteError end; to_der_file(Key, File, Other) when is_binary(File) orelse is_list(File) -> to_der_file(Key, File, from(Other)). to_file(File, JWK=#jose_jwk{}) when is_binary(File) orelse is_list(File) -> {Modules, Binary} = to_binary(JWK), case file:write_file(File, Binary) of ok -> {Modules, File}; WriteError -> WriteError end; to_file(File, Other) when is_binary(File) orelse is_list(File) -> to_file(File, from(Other)). to_file(Key, File, JWK=#jose_jwk{kty={Module, KTY}, fields=Fields}) when is_binary(File) orelse is_list(File) -> to_file(Key, File, Module:key_encryptor(KTY, Fields, Key), JWK); to_file(Key, File, Other) when is_binary(File) orelse is_list(File) -> to_file(Key, File, from(Other)). to_file(Key, File, JWE=#jose_jwe{}, JWK=#jose_jwk{}) when is_binary(File) orelse is_list(File) -> {Modules, EncryptedBinary} = to_binary(Key, JWE, JWK), case file:write_file(File, EncryptedBinary) of ok -> {Modules, File}; WriteError -> WriteError end; to_file(Key, File, JWEOther, JWKOther) when is_binary(File) orelse is_list(File) -> to_file(Key, File, jose_jwe:from(JWEOther), from(JWKOther)). to_key(List) when is_list(List) -> [to_key(Element) || Element <- List]; to_key(#jose_jwk{kty={Module, KTY}}) -> {#{ kty => Module }, Module:to_key(KTY)}; to_key(Other) -> to_key(from(Other)). to_map(List) when is_list(List) -> [to_map(Element) || Element <- List]; to_map(JWK=#jose_jwk{fields=Fields}) -> record_to_map(JWK, #{}, Fields); to_map(Other) -> to_map(from(Other)). to_map(Key, JWK=#jose_jwk{kty={Module, KTY}, fields=Fields}) -> to_map(Key, Module:key_encryptor(KTY, Fields, Key), JWK); to_map(Key, Other) -> to_map(Key, from(Other)). to_map(Key, JWE=#jose_jwe{}, JWK=#jose_jwk{}) -> {Modules0, JWKBinary} = to_binary(JWK), {Modules1, Encrypted} = jose_jwe:block_encrypt(Key, JWKBinary, JWE), {maps:merge(Modules0, Modules1), Encrypted}; to_map(Key, JWEOther, JWKOther) -> to_map(Key, jose_jwe:from(JWEOther), from(JWKOther)). to_oct(List) when is_list(List) -> [to_oct(Element) || Element <- List]; to_oct(#jose_jwk{kty={Module, KTY}}) -> {#{ kty => Module }, Module:to_oct(KTY)}; to_oct(Other) -> to_oct(from(Other)). to_oct(Key, JWK=#jose_jwk{kty={Module, KTY}, fields=Fields}) -> to_oct(Key, Module:key_encryptor(KTY, Fields, Key), JWK); to_oct(Key, Other) -> to_oct(Key, from(Other)). to_oct(Key, JWE=#jose_jwe{}, JWK=#jose_jwk{}) -> {Modules0, OCTBinary} = to_oct(JWK), {Modules1, EncryptedMap} = jose_jwe:block_encrypt(Key, OCTBinary, JWE), jose_jwe:compact({maps:merge(Modules0, Modules1), EncryptedMap}); to_oct(Key, JWEOther, JWKOther) -> to_oct(Key, jose_jwe:from(JWEOther), from(JWKOther)). to_oct_file(File, JWK=#jose_jwk{}) when is_binary(File) orelse is_list(File) -> {Modules, Binary} = to_oct(JWK), case file:write_file(File, Binary) of ok -> {Modules, File}; WriteError -> WriteError end; to_oct_file(File, Other) when is_binary(File) orelse is_list(File) -> to_oct_file(File, from(Other)). to_oct_file(Key, File, JWK=#jose_jwk{kty={Module, KTY}, fields=Fields}) when is_binary(File) orelse is_list(File) -> to_oct_file(Key, File, Module:key_encryptor(KTY, Fields, Key), JWK); to_oct_file(Key, File, Other) when is_binary(File) orelse is_list(File) -> to_oct_file(Key, File, from(Other)). to_oct_file(Key, File, JWE=#jose_jwe{}, JWK=#jose_jwk{}) when is_binary(File) orelse is_list(File) -> {Modules, EncryptedBinary} = to_oct(Key, JWE, JWK), case file:write_file(File, EncryptedBinary) of ok -> {Modules, File}; WriteError -> WriteError end; to_oct_file(Key, File, JWEOther, JWKOther) when is_binary(File) orelse is_list(File) -> to_oct_file(Key, File, jose_jwe:from(JWEOther), from(JWKOther)). to_okp(List) when is_list(List) -> [to_okp(Element) || Element <- List]; to_okp(#jose_jwk{kty={Module, KTY}}) -> {#{ kty => Module }, Module:to_okp(KTY)}; to_okp(Other) -> to_okp(from(Other)). to_openssh_key(List) when is_list(List) -> [to_openssh_key(Element) || Element <- List]; to_openssh_key(#jose_jwk{kty={Module, KTY}, fields=Fields}) -> {#{ kty => Module }, Module:to_openssh_key(KTY, Fields)}; to_openssh_key(Other) -> to_openssh_key(from(Other)). to_openssh_key_file(File, JWK=#jose_jwk{}) when is_binary(File) orelse is_list(File) -> {Modules, Binary} = to_openssh_key(JWK), case file:write_file(File, Binary) of ok -> {Modules, File}; WriteError -> WriteError end; to_openssh_key_file(File, Other) when is_binary(File) orelse is_list(File) -> to_openssh_key_file(File, from(Other)). to_pem(List) when is_list(List) -> [to_pem(Element) || Element <- List]; to_pem(#jose_jwk{kty={Module, KTY}}) -> {#{ kty => Module }, Module:to_pem(KTY)}; to_pem(Other) -> to_pem(from(Other)). to_pem(Key, #jose_jwk{kty={Module, KTY}}) -> {#{ kty => Module }, Module:to_pem(Key, KTY)}; to_pem(Key, Other) -> to_pem(Key, from(Other)). to_pem_file(File, JWK=#jose_jwk{}) when is_binary(File) orelse is_list(File) -> {Modules, Binary} = to_pem(JWK), case file:write_file(File, Binary) of ok -> {Modules, File}; WriteError -> WriteError end; to_pem_file(File, Other) when is_binary(File) orelse is_list(File) -> to_pem_file(File, from(Other)). to_pem_file(Key, File, JWK=#jose_jwk{}) when is_binary(File) orelse is_list(File) -> {Modules, Binary} = to_pem(Key, JWK), case file:write_file(File, Binary) of ok -> {Modules, File}; WriteError -> WriteError end; to_pem_file(Key, File, Other) when is_binary(File) orelse is_list(File) -> to_pem_file(Key, File, from(Other)). to_public(List) when is_list(List) -> [to_public(Element) || Element <- List]; to_public(JWK=#jose_jwk{}) -> from_map(to_public_map(JWK)); to_public(Other) -> to_public(from(Other)). to_public_file(File, JWK=#jose_jwk{}) when is_binary(File) orelse is_list(File) -> to_file(File, to_public(JWK)); to_public_file(File, Other) when is_binary(File) orelse is_list(File) -> to_public_file(File, from(Other)). to_public_key(List) when is_list(List) -> [to_public_key(Element) || Element <- List]; to_public_key(JWT=#jose_jwk{}) -> to_key(to_public(JWT)); to_public_key(Other) -> to_public_key(from(Other)). to_public_map(List) when is_list(List) -> [to_public_map(Element) || Element <- List]; to_public_map(#jose_jwk{kty={Module, KTY}, fields=Fields}) -> {#{ kty => Module }, Module:to_public_map(KTY, Fields)}; to_public_map(Other) -> to_public_map(from(Other)). to_thumbprint_map(List) when is_list(List) -> [to_thumbprint_map(Element) || Element <- List]; to_thumbprint_map(#jose_jwk{kty={Module, KTY}, fields=Fields}) -> {#{ kty => Module }, Module:to_thumbprint_map(KTY, Fields)}; to_thumbprint_map(Other) -> to_thumbprint_map(from(Other)). %%==================================================================== %% API functions %%==================================================================== block_decrypt(Encrypted, JWK=#jose_jwk{}) -> jose_jwe:block_decrypt(JWK, Encrypted); block_decrypt(Encrypted, Other) -> block_decrypt(Encrypted, from(Other)). block_encrypt(PlainText, JWK=#jose_jwk{}) -> block_encrypt(PlainText, block_encryptor(JWK), JWK); block_encrypt(PlainText, Other) -> block_encrypt(PlainText, from(Other)). block_encrypt(PlainText, JWE=#jose_jwe{}, JWK=#jose_jwk{}) -> jose_jwe:block_encrypt(JWK, PlainText, JWE); block_encrypt(PlainText, JWEOther, JWKOther) -> block_encrypt(PlainText, jose_jwe:from(JWEOther), from(JWKOther)). block_encryptor(List) when is_list(List) -> [block_encryptor(Element) || Element <- List]; block_encryptor(#jose_jwk{kty={Module, KTY}, fields=Fields}) -> Module:block_encryptor(KTY, Fields); block_encryptor(Other) -> block_encryptor(from(Other)). box_decrypt(Encrypted, VStaticSecretKey) -> box_decrypt_ecdh_es(Encrypted, VStaticSecretKey). %% @doc Generates an ephemeral private key based on receiver public key curve. box_encrypt(PlainText, VStaticPublicKey=#jose_jwk{}) -> box_encrypt_ecdh_es(PlainText, VStaticPublicKey). box_encrypt(PlainText, VStaticPublicKey, UEphemeralSecretKey) -> box_encrypt_ecdh_es(PlainText, VStaticPublicKey, UEphemeralSecretKey). box_encrypt(PlainText, JWE, VStaticPublicKey, UEphemeralSecretKey) -> box_encrypt_ecdh_es(PlainText, JWE, VStaticPublicKey, UEphemeralSecretKey). box_decrypt_ecdh_1pu(Encrypted, UStaticPublicKey=#jose_jwk{}, VStaticSecretKey=#jose_jwk{}) -> jose_jwe:block_decrypt({UStaticPublicKey, VStaticSecretKey}, Encrypted); box_decrypt_ecdh_1pu(Encrypted, UStaticPublicKey, VStaticSecretKey) -> box_decrypt_ecdh_1pu(Encrypted, from(UStaticPublicKey), from(VStaticSecretKey)). %% @doc Generates an ephemeral private key based on receiver public key curve. box_encrypt_ecdh_1pu(PlainText, VStaticPublicKey=#jose_jwk{}, UStaticSecretKey=#jose_jwk{}) -> UEphemeralSecretKey = generate_key(VStaticPublicKey), {box_encrypt_ecdh_1pu(PlainText, VStaticPublicKey, UStaticSecretKey, UEphemeralSecretKey), UEphemeralSecretKey}; box_encrypt_ecdh_1pu(PlainText, VStaticPublicKey, UStaticSecretKey) -> box_encrypt_ecdh_1pu(PlainText, from(VStaticPublicKey), from(UStaticSecretKey)). box_encrypt_ecdh_1pu(PlainText, VStaticPublicKey=#jose_jwk{}, UStaticSecretKey=#jose_jwk{}, UEphemeralSecretKey=#jose_jwk{}) -> UEphemeralPublicKey0 = #jose_jwk{fields=Fields0} = to_public(UEphemeralSecretKey), Fields1 = maps:put(<<"epk">>, element(2, to_map(UEphemeralPublicKey0)), Fields0), Fields2 = case Fields1 of #{ <<"alg">> := _ } -> Fields1; _ -> maps:put(<<"alg">>, <<"ECDH-1PU">>, Fields1) end, UEphemeralPublicKey = UEphemeralPublicKey0#jose_jwk{fields=Fields2}, JWEFields = block_encryptor(UEphemeralPublicKey), box_encrypt_ecdh_1pu(PlainText, JWEFields, VStaticPublicKey, UStaticSecretKey, UEphemeralSecretKey); box_encrypt_ecdh_1pu(PlainText, VStaticPublicKey, UStaticSecretKey, UEphemeralSecretKey) -> box_encrypt_ecdh_1pu(PlainText, from(VStaticPublicKey), from(UStaticSecretKey), from(UEphemeralSecretKey)). box_encrypt_ecdh_1pu(PlainText, JWE=#jose_jwe{}, VStaticPublicKey=#jose_jwk{}, UStaticSecretKey=#jose_jwk{}, UEphemeralSecretKey=#jose_jwk{}) -> jose_jwe:block_encrypt({VStaticPublicKey, UStaticSecretKey, UEphemeralSecretKey}, PlainText, JWE); box_encrypt_ecdh_1pu(PlainText, {JWEModules, JWEMap=#{ <<"apu">> := _, <<"apv">> := _, <<"epk">> := _, <<"skid">> := _ }}, VStaticPublicKey=#jose_jwk{}, UStaticSecretKey=#jose_jwk{}, UEphemeralSecretKey=#jose_jwk{}) -> box_encrypt_ecdh_1pu(PlainText, jose_jwe:from({JWEModules, JWEMap}), VStaticPublicKey, UStaticSecretKey, UEphemeralSecretKey); box_encrypt_ecdh_1pu(PlainText, {JWEModules, JWEMap0}, VStaticPublicKey=#jose_jwk{}, UStaticSecretKey=#jose_jwk{}, UEphemeralSecretKey=#jose_jwk{}) -> Keys = [<<"apu">>, <<"apv">>, <<"epk">>, <<"skid">>] -- maps:keys(JWEMap0), JWEMap1 = normalize_ecdh_1pu(Keys, JWEMap0, VStaticPublicKey, UStaticSecretKey, UEphemeralSecretKey), box_encrypt_ecdh_1pu(PlainText, {JWEModules, JWEMap1}, VStaticPublicKey, UStaticSecretKey, UEphemeralSecretKey); box_encrypt_ecdh_1pu(PlainText, JWEMap, VStaticPublicKey, UStaticSecretKey, UEphemeralSecretKey) when is_map(JWEMap) -> box_encrypt_ecdh_1pu(PlainText, {#{}, JWEMap}, VStaticPublicKey, UStaticSecretKey, UEphemeralSecretKey); box_encrypt_ecdh_1pu(PlainText, JWE, VStaticPublicKey, UStaticSecretKey, UEphemeralSecretKey) -> box_encrypt_ecdh_1pu(PlainText, JWE, from(VStaticPublicKey), from(UStaticSecretKey), from(UEphemeralSecretKey)). box_decrypt_ecdh_es(Encrypted, VStaticSecretKey=#jose_jwk{}) -> jose_jwe:block_decrypt(VStaticSecretKey, Encrypted); box_decrypt_ecdh_es(Encrypted, VStaticSecretKey) -> box_decrypt_ecdh_es(Encrypted, from(VStaticSecretKey)). %% @doc Generates an ephemeral private key based on receiver public key curve. box_encrypt_ecdh_es(PlainText, VStaticPublicKey=#jose_jwk{}) -> UEphemeralSecretKey = generate_key(VStaticPublicKey), {box_encrypt_ecdh_es(PlainText, VStaticPublicKey, UEphemeralSecretKey), UEphemeralSecretKey}; box_encrypt_ecdh_es(PlainText, VStaticPublicKey) -> box_encrypt_ecdh_es(PlainText, from(VStaticPublicKey)). box_encrypt_ecdh_es(PlainText, VStaticPublicKey=#jose_jwk{}, UEphemeralSecretKey=#jose_jwk{}) -> UEphemeralPublicKey0 = #jose_jwk{fields=Fields0} = to_public(UEphemeralSecretKey), Fields1 = maps:put(<<"epk">>, element(2, to_map(UEphemeralPublicKey0)), Fields0), UEphemeralPublicKey = UEphemeralPublicKey0#jose_jwk{fields=Fields1}, JWEFields = block_encryptor(UEphemeralPublicKey), box_encrypt_ecdh_es(PlainText, JWEFields, VStaticPublicKey, UEphemeralSecretKey); box_encrypt_ecdh_es(PlainText, VStaticPublicKey, UEphemeralSecretKey) -> box_encrypt_ecdh_es(PlainText, from(VStaticPublicKey), from(UEphemeralSecretKey)). box_encrypt_ecdh_es(PlainText, JWE=#jose_jwe{}, VStaticPublicKey=#jose_jwk{}, UEphemeralSecretKey=#jose_jwk{}) -> jose_jwe:block_encrypt({VStaticPublicKey, UEphemeralSecretKey}, PlainText, JWE); box_encrypt_ecdh_es(PlainText, {JWEModules, JWEMap=#{ <<"apu">> := _, <<"apv">> := _, <<"epk">> := _ }}, VStaticPublicKey=#jose_jwk{}, UEphemeralSecretKey=#jose_jwk{}) -> box_encrypt_ecdh_es(PlainText, jose_jwe:from({JWEModules, JWEMap}), VStaticPublicKey, UEphemeralSecretKey); box_encrypt_ecdh_es(PlainText, {JWEModules, JWEMap0}, VStaticPublicKey=#jose_jwk{}, UEphemeralSecretKey=#jose_jwk{}) -> Keys = [<<"apu">>, <<"apv">>, <<"epk">>] -- maps:keys(JWEMap0), JWEMap1 = normalize_ecdh_es(Keys, JWEMap0, VStaticPublicKey, UEphemeralSecretKey), box_encrypt_ecdh_es(PlainText, {JWEModules, JWEMap1}, VStaticPublicKey, UEphemeralSecretKey); box_encrypt_ecdh_es(PlainText, JWEMap, VStaticPublicKey, UEphemeralSecretKey) when is_map(JWEMap) -> box_encrypt_ecdh_es(PlainText, {#{}, JWEMap}, VStaticPublicKey, UEphemeralSecretKey); box_encrypt_ecdh_es(PlainText, JWE, VStaticPublicKey, UEphemeralSecretKey) -> box_encrypt_ecdh_es(PlainText, JWE, from(VStaticPublicKey), from(UEphemeralSecretKey)). box_decrypt_ecdh_ss(Encrypted, VStaticSecretKey0) -> VStaticSecretKey = case VStaticSecretKey0 of #jose_jwk{} -> VStaticSecretKey0; _ -> from(VStaticSecretKey0) end, jose_jwe:block_decrypt(VStaticSecretKey, Encrypted). %% @doc Generates a new static secret key based on receiver public key curve. box_encrypt_ecdh_ss(PlainText, VStaticPublicKey0) -> VStaticPublicKey = case VStaticPublicKey0 of #jose_jwk{} -> VStaticPublicKey0; _ -> from(VStaticPublicKey0) end, UStaticSecretKey = generate_key(VStaticPublicKey), {box_encrypt_ecdh_ss(PlainText, VStaticPublicKey, UStaticSecretKey), UStaticSecretKey}. box_encrypt_ecdh_ss(PlainText, VStaticPublicKey0, UStaticSecretKey0) -> VStaticPublicKey = case VStaticPublicKey0 of #jose_jwk{} -> VStaticPublicKey0; _ -> from(VStaticPublicKey0) end, UStaticSecretKey = case UStaticSecretKey0 of #jose_jwk{} -> UStaticSecretKey0; _ -> from(UStaticSecretKey0) end, UStaticPublicKey0 = #jose_jwk{fields=Fields0} = to_public(UStaticSecretKey), Fields1 = maps:put(<<"spk">>, element(2, to_map(UStaticPublicKey0)), Fields0), Fields2 = case Fields1 of #{ <<"alg">> := _ } -> Fields1; _ -> maps:put(<<"alg">>, <<"ECDH-SS">>, Fields1) end, UStaticPublicKey = UStaticPublicKey0#jose_jwk{fields=Fields2}, JWEFields = block_encryptor(UStaticPublicKey), box_encrypt_ecdh_ss(PlainText, JWEFields, VStaticPublicKey, UStaticSecretKey). box_encrypt_ecdh_ss(PlainText, JWE0, VStaticPublicKey0, UStaticSecretKey0) -> VStaticPublicKey = case VStaticPublicKey0 of #jose_jwk{} -> VStaticPublicKey0; _ -> from(VStaticPublicKey0) end, UStaticSecretKey = case UStaticSecretKey0 of #jose_jwk{} -> UStaticSecretKey0; _ -> from(UStaticSecretKey0) end, JWE = case JWE0 of #jose_jwe{} -> JWE0; {JWEModules, JWEMap=#{ <<"apu">> := _, <<"spk">> := _ }} -> jose_jwe:from({JWEModules, JWEMap}); {JWEModules, JWEMap0} when is_map(JWEMap0) -> Keys = [<<"apu">>, <<"apv">>, <<"spk">>] -- maps:keys(JWEMap0), JWEMap1 = normalize_ecdh_ss(Keys, JWEMap0, VStaticPublicKey, UStaticSecretKey), jose_jwe:from({JWEModules, JWEMap1}); JWEMap0 when is_map(JWEMap0) -> Keys = [<<"apu">>, <<"apv">>, <<"spk">>] -- maps:keys(JWEMap0), JWEMap1 = normalize_ecdh_ss(Keys, JWEMap0, VStaticPublicKey, UStaticSecretKey), jose_jwe:from_map(JWEMap1); _ -> jose_jwe:from(JWE0) end, jose_jwe:block_encrypt({VStaticPublicKey, UStaticSecretKey}, PlainText, JWE). generate_key(#jose_jwk{kty={Module, KTY}, fields=Fields}) -> {NewKTY, NewFields} = Module:generate_key(KTY, Fields), #jose_jwk{kty={Module, NewKTY}, fields=NewFields}; generate_key({#{ kty := Module }, Parameters}) -> {KTY, Fields} = Module:generate_key(Parameters), #jose_jwk{kty={Module, KTY}, fields=Fields}; generate_key(Parameters) -> jose_jwk_kty:generate_key(Parameters). merge(LeftJWK=#jose_jwk{}, RightMap) when is_map(RightMap) -> {Modules, LeftMap} = to_map(LeftJWK), from_map({Modules, maps:merge(LeftMap, RightMap)}); merge(LeftOther, RightJWK=#jose_jwk{}) -> merge(LeftOther, element(2, to_map(RightJWK))); merge(LeftOther, RightMap) when is_map(RightMap) -> merge(from(LeftOther), RightMap). shared_secret(#jose_jwk{kty={Module, YourKTY}}, #jose_jwk{kty={Module, MyKTY}}) -> Module:derive_key(YourKTY, MyKTY); %% Both arguments are already decoded, but their "kty" modules differ (or one %% is absent), so no shared secret can be derived. Fail closed here: the %% fallback clause below would call from/1 on two #jose_jwk{} records, which %% returns them unchanged, and re-enter shared_secret/2 with identical %% arguments -- an unbounded tail-recursive loop. Only the module names are %% reported, because a #jose_jwk{} may carry private key material. shared_secret(#jose_jwk{kty=YourKTY}, #jose_jwk{kty=MyKTY}) -> erlang:error({badarg, [kty_module(YourKTY), kty_module(MyKTY)]}); shared_secret(YourJWK, MyJWK) -> shared_secret(from(YourJWK), from(MyJWK)). sign(PlainText, JWK=#jose_jwk{}) -> sign(PlainText, signer(JWK), JWK); sign(PlainText, KeyList) when is_list(KeyList) -> Folder = fun Folder(Key=#jose_jwk{}, {Signers, Keys}) -> Signer = signer(Key), {[Signer | Signers], [Key | Keys]}; Folder(Other, Acc) -> Folder(from(Other), Acc) end, {Signers, Keys} = lists:foldr(Folder, {[], []}, KeyList), sign(PlainText, Signers, Keys); sign(PlainText, Other) -> sign(PlainText, from(Other)). sign(PlainText, JWS=#jose_jws{}, JWK=#jose_jwk{}) -> jose_jws:sign(JWK, PlainText, JWS); sign(PlainText, SignerList, KeyList) when is_list(SignerList) andalso is_list(KeyList) andalso length(SignerList) =:= length(KeyList) -> Signers = jose_jws:from(SignerList), Keys = from(KeyList), jose_jws:sign(Keys, PlainText, Signers); sign(PlainText, JWS=#jose_jws{}, KeyList) when is_list(KeyList) -> jose_jws:sign(from(KeyList), PlainText, JWS); sign(PlainText, SignerList, KeyList) when is_list(SignerList) andalso is_list(KeyList) andalso length(SignerList) =/= length(KeyList) -> erlang:error({badarg, [PlainText, SignerList, KeyList]}); sign(PlainText, JWSOther, KeyList) when is_list(KeyList) -> sign(PlainText, jose_jws:from(JWSOther), KeyList); sign(PlainText, JWSOther, JWKOther) -> sign(PlainText, jose_jws:from(JWSOther), from(JWKOther)). signer(List) when is_list(List) -> [signer(Element) || Element <- List]; signer(#jose_jwk{kty={Module, KTY}, fields=Fields}) -> Module:signer(KTY, Fields); signer(Other) -> signer(from(Other)). %% See https://tools.ietf.org/html/rfc7638 thumbprint(List) when is_list(List) -> [thumbprint(Element) || Element <- List]; thumbprint(JWK=#jose_jwk{}) -> thumbprint(sha256, JWK); thumbprint(Other) -> thumbprint(from(Other)). thumbprint(DigestType, List) when is_list(List) -> [thumbprint(DigestType, Element) || Element <- List]; thumbprint(DigestType, JWK=#jose_jwk{}) -> {_, ThumbprintMap} = to_thumbprint_map(JWK), ThumbprintBinary = jose:encode(ThumbprintMap), jose_jwa_base64url:encode(crypto:hash(DigestType, ThumbprintBinary)); thumbprint(DigestType, Other) -> thumbprint(DigestType, from(Other)). thumbprint_concat(List) when is_list(List) -> thumbprint_concat(sha256, List). thumbprint_concat(DigestType, List) when is_list(List) -> jose_jwa_base64url:encode(crypto:hash(DigestType, do_thumbprint_concat(List))). verifier(List) when is_list(List) -> [verifier(Element) || Element <- List]; verifier(#jose_jwk{kty={Module, KTY}, fields=Fields}) -> Module:verifier(KTY, Fields); verifier(Other) -> verifier(from(Other)). verify(Signed, JWK=#jose_jwk{}) -> jose_jws:verify(JWK, Signed); verify(Signed, Other) -> verify(Signed, from(Other)). verify_strict(Signed, Allow, JWK=#jose_jwk{}) -> jose_jws:verify_strict(JWK, Allow, Signed); verify_strict(Signed, Allow, Other) -> verify_strict(Signed, Allow, from(Other)). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private do_thumbprint_concat([]) -> []; do_thumbprint_concat([H | T]) -> {_, ThumbprintMap} = to_thumbprint_map(H), ThumbprintBinary = jose:encode(ThumbprintMap), [ThumbprintBinary | do_thumbprint_concat(T)]. %% @private kty_module({Module, _KTY}) when is_atom(Module) -> Module; kty_module(_) -> undefined. %% @private normalize_ecdh_1pu([<<"apu">> | Keys], Map, VStaticPublicKey, UStaticSecretKey, UEphemeralSecretKey) -> APU = thumbprint_concat([UStaticSecretKey, UEphemeralSecretKey]), normalize_ecdh_1pu(Keys, Map#{ <<"apu">> => APU }, VStaticPublicKey, UStaticSecretKey, UEphemeralSecretKey); normalize_ecdh_1pu([<<"apv">> | Keys], Map, VStaticPublicKey, UStaticSecretKey, UEphemeralSecretKey) -> APV = thumbprint(VStaticPublicKey), normalize_ecdh_1pu(Keys, Map#{ <<"apv">> => APV }, VStaticPublicKey, UStaticSecretKey, UEphemeralSecretKey); normalize_ecdh_1pu([<<"epk">> | Keys], Map, VStaticPublicKey, UStaticSecretKey, UEphemeralSecretKey) -> {_, UEphemeralPublicKeyMap} = to_public_map(UEphemeralSecretKey), normalize_ecdh_1pu(Keys, Map#{ <<"epk">> => UEphemeralPublicKeyMap }, VStaticPublicKey, UStaticSecretKey, UEphemeralSecretKey); normalize_ecdh_1pu([<<"skid">> | Keys], Map, VStaticPublicKey, UStaticSecretKey, UEphemeralSecretKey) -> SenderKeyId = case UStaticSecretKey of #jose_jwk{fields=#{ <<"kid">> := KID }} -> KID; _ -> thumbprint(UStaticSecretKey) end, normalize_ecdh_1pu(Keys, Map#{ <<"skid">> => SenderKeyId }, VStaticPublicKey, UStaticSecretKey, UEphemeralSecretKey); normalize_ecdh_1pu([], Map, _, _, _) -> Map. %% @private normalize_ecdh_es([<<"apu">> | Keys], Map, VStaticPublicKey, UEphemeralSecretKey=#jose_jwk{fields=#{ <<"kid">> := KID }}) -> normalize_ecdh_es(Keys, Map#{ <<"apu">> => KID }, VStaticPublicKey, UEphemeralSecretKey); normalize_ecdh_es([<<"apu">> | Keys], Map, VStaticPublicKey, UEphemeralSecretKey) -> normalize_ecdh_es(Keys, Map#{ <<"apu">> => thumbprint(UEphemeralSecretKey) }, VStaticPublicKey, UEphemeralSecretKey); normalize_ecdh_es([<<"apv">> | Keys], Map, VStaticPublicKey=#jose_jwk{fields=#{ <<"kid">> := KID }}, UEphemeralSecretKey) -> normalize_ecdh_es(Keys, Map#{ <<"apv">> => KID }, VStaticPublicKey, UEphemeralSecretKey); normalize_ecdh_es([<<"apv">> | Keys], Map, VStaticPublicKey, UEphemeralSecretKey) -> normalize_ecdh_es(Keys, Map#{ <<"apv">> => thumbprint(VStaticPublicKey) }, VStaticPublicKey, UEphemeralSecretKey); normalize_ecdh_es([<<"epk">> | Keys], Map, VStaticPublicKey, UEphemeralSecretKey) -> {_, UEphemeralPublicKeyMap} = to_public_map(UEphemeralSecretKey), normalize_ecdh_es(Keys, Map#{ <<"epk">> => UEphemeralPublicKeyMap }, VStaticPublicKey, UEphemeralSecretKey); normalize_ecdh_es([], Map, _, _) -> Map. %% @private normalize_ecdh_ss([<<"apu">> | Keys], Map, VStaticPublicKey, UStaticSecretKey) -> normalize_ecdh_ss(Keys, Map#{ <<"apu">> => jose_jwa_base64url:encode(crypto:strong_rand_bytes(64)) }, VStaticPublicKey, UStaticSecretKey); normalize_ecdh_ss([<<"apv">> | Keys], Map, VStaticPublicKey=#jose_jwk{fields=#{ <<"kid">> := KID }}, UStaticSecretKey) -> normalize_ecdh_ss(Keys, Map#{ <<"apv">> => KID }, VStaticPublicKey, UStaticSecretKey); normalize_ecdh_ss([<<"apv">> | Keys], Map, VStaticPublicKey, UStaticSecretKey) -> normalize_ecdh_ss(Keys, Map#{ <<"apv">> => thumbprint(VStaticPublicKey) }, VStaticPublicKey, UStaticSecretKey); normalize_ecdh_ss([<<"spk">> | Keys], Map, VStaticPublicKey, UStaticSecretKey) -> {_, UStaticPublicKeyMap} = to_public_map(UStaticSecretKey), normalize_ecdh_ss(Keys, Map#{ <<"spk">> => UStaticPublicKeyMap }, VStaticPublicKey, UStaticSecretKey); normalize_ecdh_ss([], Map, _, _) -> Map. %% @private record_to_map(JWK=#jose_jwk{keys={Module, KEYS}}, Modules, Fields0) -> Fields1 = Module:to_map(KEYS, Fields0), record_to_map(JWK#jose_jwk{keys=undefined}, Modules#{ keys => Module }, Fields1); record_to_map(JWK=#jose_jwk{kty={Module, KTY}}, Modules, Fields0) -> Fields1 = Module:to_map(KTY, Fields0), record_to_map(JWK#jose_jwk{kty=undefined}, Modules#{ kty => Module }, Fields1); record_to_map(_JWK, Modules, Fields) -> {Modules, Fields}. potatosalad-erlang-jose-30ac019/src/jwk/jose_jwk_kty_oct.erl0000664000232200023220000001230115267717064024574 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 21 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwk_kty_oct). -behaviour(jose_jwk). -behaviour(jose_jwk_kty). -behaviour(jose_jwk_oct). -behaviour(jose_jwk_use_enc). -behaviour(jose_jwk_use_sig). %% jose_jwk callbacks -export([from_map/1]). -export([to_key/1]). -export([to_map/2]). -export([to_public_map/2]). -export([to_thumbprint_map/2]). %% jose_jwk_kty callbacks -export([generate_key/1]). -export([generate_key/2]). -export([key_encryptor/3]). %% jose_jwk_use_enc callbacks -export([block_encryptor/2]). -export([derive_key/1]). %% jose_jwk_use_sig callbacks -export([sign/3]). -export([signer/2]). -export([verifier/2]). -export([verify/4]). %% jose_jwk_oct callbacks -export([from_oct/1]). -export([to_oct/1]). %% Types -type key() :: binary(). -export_type([key/0]). %%==================================================================== %% jose_jwk callbacks %%==================================================================== from_map(F = #{ <<"kty">> := <<"oct">>, <<"k">> := K }) -> {jose_jwa_base64url:decode(K), maps:without([<<"k">>, <<"kty">>], F)}. to_key(Key) -> Key. to_map(K, F) -> F#{ <<"kty">> => <<"oct">>, <<"k">> => jose_jwa_base64url:encode(K) }. to_public_map(_K, _F) -> erlang:error({not_supported, [to_public_map]}). to_thumbprint_map(K, F) -> maps:with([<<"k">>, <<"kty">>], to_map(K, F)). %%==================================================================== %% jose_jwk_kty callbacks %%==================================================================== generate_key(Size) when is_integer(Size) -> {crypto:strong_rand_bytes(Size), #{}}; generate_key({oct, Size}) when is_integer(Size) -> generate_key(Size). generate_key(KTY, Fields) -> {NewKTY, OtherFields} = generate_key(byte_size(KTY)), {NewKTY, maps:merge(maps:remove(<<"kid">>, Fields), OtherFields)}. key_encryptor(KTY, Fields, Key) -> jose_jwk_kty:key_encryptor(KTY, Fields, Key). %%==================================================================== %% jose_jwk_use_enc callbacks %%==================================================================== block_encryptor(_KTY, #{ <<"alg">> := ALG, <<"enc">> := ENC, <<"use">> := <<"enc">> }) -> #{ <<"alg">> => ALG, <<"enc">> => ENC }; block_encryptor(KTY, Fields) -> ENC = case bit_size(KTY) of 128 -> <<"A128GCM">>; 192 -> <<"A192GCM">>; 256 -> case jose_jwa:is_block_cipher_supported({aes_gcm, 256}) of false -> <<"A128CBC-HS256">>; true -> <<"A256GCM">> end; 384 -> <<"A192CBC-HS384">>; 512 -> <<"A256CBC-HS512">>; _ -> erlang:error({badarg, [KTY, Fields]}) end, #{ <<"alg">> => <<"dir">>, <<"enc">> => ENC }. derive_key(Key) -> Key. %%==================================================================== %% jose_jwk_use_sig callbacks %%==================================================================== sign(Message, JWSALG, Key) when is_atom(JWSALG) -> DigestType = jws_alg_to_digest_type(JWSALG), jose_crypto_compat:mac(hmac, DigestType, Key, Message); sign(Message, {'Poly1305', Nonce}, Key) -> jose_chacha20_poly1305:authenticate(Message, Key, Nonce); sign(_Message, JWSALG, _Key) -> erlang:error({not_supported, [JWSALG]}). signer(_KTY, #{ <<"alg">> := ALG, <<"use">> := <<"sig">> }) -> #{ <<"alg">> => ALG }; signer(Key, _Fields) -> #{ <<"alg">> => case bit_size(Key) of KeySize when KeySize < 384 -> <<"HS256">>; KeySize when KeySize < 512 -> <<"HS384">>; _ -> <<"HS512">> end }. verifier(_KTY, #{ <<"alg">> := ALG, <<"use">> := <<"sig">> }) -> [ALG]; verifier(Key, _Fields) -> case bit_size(Key) of 256 -> case jose_jwa:is_chacha20_poly1305_supported() of true -> [<<"HS256">>, <<"Poly1305">>]; false -> [<<"HS256">>] end; KeySize when KeySize < 384 -> [<<"HS256">>]; KeySize when KeySize < 512 -> [<<"HS256">>, <<"HS384">>]; _ -> [<<"HS256">>, <<"HS384">>, <<"HS512">>] end. verify(Message, JWSALG, Signature, Key) when is_atom(JWSALG) -> try sign(Message, JWSALG, Key) of Challenge -> jose_jwa:constant_time_compare(Signature, Challenge) catch error:{not_supported, _} -> false end; verify(Message, {'Poly1305', Nonce}, Signature, Key) -> jose_chacha20_poly1305:verify(Signature, Message, Key, Nonce). %%==================================================================== %% jose_jwk_oct callbacks %%==================================================================== from_oct(OCTBinary) when is_binary(OCTBinary) -> {OCTBinary, #{}}. to_oct(OCTBinary) when is_binary(OCTBinary) -> OCTBinary. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private jws_alg_to_digest_type('HS256') -> sha256; jws_alg_to_digest_type('HS384') -> sha384; jws_alg_to_digest_type('HS512') -> sha512; jws_alg_to_digest_type(ALG) -> erlang:error({not_supported, [ALG]}). potatosalad-erlang-jose-30ac019/src/jwk/jose_jwk_kty_okp_ed448.erl0000664000232200023220000002063315267717064025517 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 15 Jan 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwk_kty_okp_ed448). -behaviour(jose_jwk). -behaviour(jose_jwk_kty). -behaviour(jose_jwk_use_sig). -include("jose_public_key.hrl"). %% jose_jwk callbacks -export([from_map/1]). -export([to_key/1]). -export([to_map/2]). -export([to_public_map/2]). -export([to_thumbprint_map/2]). %% jose_jwk_kty callbacks -export([generate_key/1]). -export([generate_key/2]). -export([key_encryptor/3]). %% jose_jwk_use_sig callbacks -export([sign/3]). -export([signer/2]). -export([verifier/2]). -export([verify/4]). %% API -export([from_der/1]). -export([from_der/2]). -export([from_key/1]). -export([from_okp/1]). -export([from_openssh_key/1]). -export([from_pem/1]). -export([from_pem/2]). -export([to_der/1]). -export([to_der/2]). -export([to_okp/1]). -export([to_openssh_key/2]). -export([to_pem/1]). -export([to_pem/2]). %% Macros -define(crv, <<"Ed448">>). -define(secretbytes, 57). -define(publickeybytes, 57). -define(secretkeybytes, 114). %% Types -type publickey() :: << _:456 >>. -type secretkey() :: << _:912 >>. -type key() :: publickey() | secretkey(). -export_type([key/0]). %%==================================================================== %% jose_jwk callbacks %%==================================================================== from_map(F = #{ <<"kty">> := <<"OKP">>, <<"crv">> := ?crv, <<"d">> := D, <<"x">> := X }) -> << Secret:?secretbytes/binary >> = jose_jwa_base64url:decode(D), << PK:?publickeybytes/binary >> = jose_jwa_base64url:decode(X), SK = << Secret/binary, PK/binary >>, {SK, maps:without([<<"crv">>, <<"d">>, <<"kty">>, <<"x">>], F)}; from_map(F = #{ <<"kty">> := <<"OKP">>, <<"crv">> := ?crv, <<"x">> := X }) -> << PK:?publickeybytes/binary >> = jose_jwa_base64url:decode(X), {PK, maps:without([<<"crv">>, <<"kty">>, <<"x">>], F)}. to_key(<< PublicKey:?publickeybytes/binary >>) -> #'jose_EdDSA448PublicKey'{ publicKey = PublicKey }; to_key(<< PrivateKey:?secretbytes/binary, PublicKey:?publickeybytes/binary >>) -> #'jose_EdDSA448PrivateKey'{ publicKey = #'jose_EdDSA448PublicKey'{ publicKey = PublicKey }, privateKey = PrivateKey }. to_map(PK = << _:?publickeybytes/binary >>, F) -> F#{ <<"crv">> => ?crv, <<"kty">> => <<"OKP">>, <<"x">> => jose_jwa_base64url:encode(PK) }; to_map(<< Secret:?secretbytes/binary, PK:?publickeybytes/binary >>, F) -> F#{ <<"crv">> => ?crv, <<"d">> => jose_jwa_base64url:encode(Secret), <<"kty">> => <<"OKP">>, <<"x">> => jose_jwa_base64url:encode(PK) }. to_public_map(PK = << _:?publickeybytes/binary >>, F) -> to_map(PK, F); to_public_map(<< _:?secretbytes/binary, PK:?publickeybytes/binary >>, F) -> to_public_map(PK, F). to_thumbprint_map(K, F) -> maps:with([<<"crv">>, <<"kty">>, <<"x">>], to_public_map(K, F)). %%==================================================================== %% jose_jwk_kty callbacks %%==================================================================== generate_key(Seed = << _:?secretbytes/binary >>) -> {_PK, SK} = jose_curve448:eddsa_keypair(Seed), {SK, #{}}; generate_key({okp, 'Ed448', Seed = << _:?secretbytes/binary >>}) -> generate_key(Seed); generate_key({okp, 'Ed448'}) -> {_PK, SK} = jose_curve448:eddsa_keypair(), {SK, #{}}. generate_key(KTY, Fields) when is_binary(KTY) andalso (byte_size(KTY) =:= ?publickeybytes orelse byte_size(KTY) =:= ?secretkeybytes) -> {NewKTY, OtherFields} = generate_key({okp, 'Ed448'}), {NewKTY, maps:merge(maps:remove(<<"kid">>, Fields), OtherFields)}. key_encryptor(KTY, Fields, Key) -> jose_jwk_kty:key_encryptor(KTY, Fields, Key). %%==================================================================== %% jose_jwk_use_sig callbacks %%==================================================================== sign(Message, ALG, SK = << _:?secretkeybytes/binary >>) when ALG =:= 'Ed448' orelse ALG =:= 'EdDSA' -> jose_curve448:ed448_sign(Message, SK). signer(<< _:?secretkeybytes/binary >>, #{ <<"alg">> := ALG, <<"use">> := <<"sig">> }) -> #{ <<"alg">> => ALG }; signer(<< _:?secretkeybytes/binary >>, _Fields) -> #{ <<"alg">> => <<"EdDSA">> }. verifier(<< _:?publickeybytes/binary >>, #{ <<"alg">> := ALG, <<"use">> := <<"sig">> }) -> [ALG]; verifier(<< _:?secretbytes/binary, PK:?publickeybytes/binary >>, Fields) -> verifier(PK, Fields); verifier(<< _:?publickeybytes/binary >>, _Fields) -> [?crv, <<"EdDSA">>]. verify(Message, ALG, Signature, << _:?secretbytes/binary, PK:?publickeybytes/binary >>) when ALG =:= 'Ed448' orelse ALG =:= 'EdDSA' -> verify(Message, ALG, Signature, PK); verify(Message, ALG, Signature, PK = << _:?publickeybytes/binary >>) when ALG =:= 'Ed448' orelse ALG =:= 'EdDSA' -> jose_curve448:ed448_verify(Signature, Message, PK). %%==================================================================== %% API functions %%==================================================================== from_der(DERBinary) when is_binary(DERBinary) -> case jose_jwk_der:from_binary(DERBinary) of {?MODULE, {Key, Fields}} -> {Key, Fields} end. from_der(Password, DERBinary) when is_binary(DERBinary) -> case jose_jwk_der:from_binary(Password, DERBinary) of {?MODULE, {Key, Fields}} -> {Key, Fields} end. from_key(#'jose_EdDSA448PrivateKey'{publicKey=#'jose_EdDSA448PublicKey'{publicKey=Public}, privateKey=Secret}) -> {<< Secret/binary, Public/binary >>, #{}}; from_key(#'jose_EdDSA448PublicKey'{publicKey=Public}) -> {Public, #{}}. from_okp({'Ed448', SK = << Secret:?secretbytes/binary, PK:?publickeybytes/binary >>}) -> case jose_curve448:eddsa_secret_to_public(Secret) of PK -> {SK, #{}}; _ -> erlang:error(badarg) end; from_okp({'Ed448', PK = << _:?publickeybytes/binary >>}) -> {PK, #{}}. from_openssh_key({<<"ssh-ed448">>, _PK, SK, Comment}) -> {KTY, OtherFields} = from_okp({'Ed448', SK}), case Comment of <<>> -> {KTY, OtherFields}; _ -> {KTY, maps:merge(#{ <<"kid">> => Comment }, OtherFields)} end. from_pem(PEMBinary) when is_binary(PEMBinary) -> case jose_jwk_pem:from_binary(PEMBinary) of {?MODULE, {Key, Fields}} -> {Key, Fields}; PEMError -> PEMError end. from_pem(Password, PEMBinary) when is_binary(PEMBinary) -> case jose_jwk_pem:from_binary(Password, PEMBinary) of {?MODULE, {Key, Fields}} -> {Key, Fields}; PEMError -> PEMError end. to_der(SK = << _:?secretkeybytes/binary >>) -> EdDSA448PrivateKey = to_key(SK), jose_public_key:der_encode('EdDSA448PrivateKey', EdDSA448PrivateKey); to_der(PK = << _:?publickeybytes/binary >>) -> EdDSA448PublicKey = to_key(PK), jose_public_key:der_encode('EdDSA448PublicKey', EdDSA448PublicKey). to_der(Password, SK = << _:?secretkeybytes/binary >>) -> EdDSA448PrivateKey = to_key(SK), jose_jwk_der:to_binary(Password, 'EdDSA448PrivateKey', EdDSA448PrivateKey); to_der(Password, PK = << _:?publickeybytes/binary >>) -> EdDSA448PublicKey = to_key(PK), jose_jwk_der:to_binary(Password, 'EdDSA448PublicKey', EdDSA448PublicKey). to_okp(SK = << _:?secretkeybytes/binary >>) -> {'Ed448', SK}; to_okp(PK = << _:?publickeybytes/binary >>) -> {'Ed448', PK}. to_openssh_key(SK = << _:?secretbytes/binary, PK:?publickeybytes/binary >>, F) -> Comment = maps:get(<<"kid">>, F, <<>>), jose_jwk_openssh_key:to_binary([[{{<<"ssh-ed448">>, PK}, {<<"ssh-ed448">>, PK, SK, Comment}}]]). to_pem(SK = << _:?secretkeybytes/binary >>) -> EdDSA448PrivateKey = to_key(SK), PEMEntry = jose_public_key:pem_entry_encode('EdDSA448PrivateKey', EdDSA448PrivateKey), jose_public_key:pem_encode([PEMEntry]); to_pem(PK = << _:?publickeybytes/binary >>) -> EdDSA448PublicKey = to_key(PK), PEMEntry = jose_public_key:pem_entry_encode('EdDSA448PublicKey', EdDSA448PublicKey), jose_public_key:pem_encode([PEMEntry]). to_pem(Password, SK = << _:?secretkeybytes/binary >>) -> EdDSA448PrivateKey = to_key(SK), jose_jwk_pem:to_binary(Password, 'PrivateKeyInfo', EdDSA448PrivateKey); to_pem(Password, PK = << _:?publickeybytes/binary >>) -> EdDSA448PublicKey = to_key(PK), jose_jwk_pem:to_binary(Password, 'EdDSA448PublicKey', EdDSA448PublicKey). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jwk/jose_jwk_kty_ec.erl0000664000232200023220000004162415267717064024410 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 23 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwk_kty_ec). -behaviour(jose_jwk). -behaviour(jose_jwk_kty). -behaviour(jose_jwk_use_enc). -behaviour(jose_jwk_use_sig). -include_lib("public_key/include/public_key.hrl"). %% jose_jwk callbacks -export([from_map/1]). -export([to_key/1]). -export([to_map/2]). -export([to_public_map/2]). -export([to_thumbprint_map/2]). %% jose_jwk_kty callbacks -export([generate_key/1]). -export([generate_key/2]). -export([key_encryptor/3]). %% jose_jwk_use_enc callbacks -export([block_encryptor/2]). -export([derive_key/2]). %% jose_jwk_use_sig callbacks -export([sign/3]). -export([signer/2]). -export([verifier/2]). -export([verify/4]). %% API -export([from_der/1]). -export([from_der/2]). -export([from_key/1]). -export([from_pem/1]). -export([from_pem/2]). -export([to_der/1]). -export([to_der/2]). -export([to_pem/1]). -export([to_pem/2]). %% Types -type key() :: #'ECPrivateKey'{} | {#'ECPoint'{}, {namedCurve, Oid::tuple()} | #'ECParameters'{}}. -export_type([key/0]). %% Macros -ifdef(OTP_RELEASE). -if(?OTP_RELEASE >= 28). -define(EC_PRIVATE_KEY_VERSION, ecPrivkeyVer1). -define(EC_PRIVATE_KEY_VERSION_COMPAT, 1). -else. -define(EC_PRIVATE_KEY_VERSION, 1). -define(EC_PRIVATE_KEY_VERSION_COMPAT, ecPrivkeyVer1). -endif. -else. -define(EC_PRIVATE_KEY_VERSION, 1). -define(EC_PRIVATE_KEY_VERSION_COMPAT, ecPrivkeyVer1). -endif. %%==================================================================== %% jose_jwk callbacks %%==================================================================== from_map(F = #{ <<"kty">> := <<"EC">>, <<"d">> := _ }) -> from_map_ec_private_key(jose_jwa:ec_key_mode(), maps:remove(<<"kty">>, F), #'ECPrivateKey'{ version = ?EC_PRIVATE_KEY_VERSION }); from_map(F = #{ <<"kty">> := <<"EC">> }) -> from_map_ec_public_key(maps:remove(<<"kty">>, F), {#'ECPoint'{}, undefined}). to_key(ECPrivateKey=#'ECPrivateKey'{}) -> ECPrivateKey; to_key(ECPublicKey={#'ECPoint'{}, _}) -> ECPublicKey. to_map(#'ECPrivateKey'{ version = ?EC_PRIVATE_KEY_VERSION, privateKey = D, parameters = {namedCurve, Parameters}, publicKey = PublicKey}, Fields) when is_binary(D) andalso is_binary(PublicKey) -> {X, Y} = public_key_to_x_y(PublicKey), Fields#{ <<"d">> => jose_jwa_base64url:encode(D), <<"crv">> => parameters_to_crv(Parameters), <<"kty">> => <<"EC">>, <<"x">> => jose_jwa_base64url:encode(X), <<"y">> => jose_jwa_base64url:encode(Y) }; to_map({#'ECPoint'{ point = PublicKey }, {namedCurve, Parameters}}, Fields) -> {X, Y} = public_key_to_x_y(PublicKey), Fields#{ <<"crv">> => parameters_to_crv(Parameters), <<"kty">> => <<"EC">>, <<"x">> => jose_jwa_base64url:encode(X), <<"y">> => jose_jwa_base64url:encode(Y) }; to_map(ECPrivateKey0=#'ECPrivateKey'{ version = ?EC_PRIVATE_KEY_VERSION, privateKey = D, parameters = {namedCurve, _Parameters}, publicKey = {_, PublicKey}}, Fields) when is_list(D) andalso is_binary(PublicKey) -> ECPrivateKey = ECPrivateKey0#'ECPrivateKey'{ privateKey = list_to_binary(D), publicKey = PublicKey}, to_map(ECPrivateKey, Fields); to_map(ECPrivateKey0=#'ECPrivateKey'{version = ?EC_PRIVATE_KEY_VERSION_COMPAT}, Fields) -> ECPrivateKey = ECPrivateKey0#'ECPrivateKey'{version = ?EC_PRIVATE_KEY_VERSION}, to_map(ECPrivateKey, Fields). to_public_map(K=#'ECPrivateKey'{}, F) -> maps:without([<<"d">>], to_map(K, F)); to_public_map(K={#'ECPoint'{}, _}, F) -> to_map(K, F). to_thumbprint_map(K, F) -> maps:with([<<"crv">>, <<"kty">>, <<"x">>, <<"y">>], to_public_map(K, F)). %%==================================================================== %% jose_jwk_kty callbacks %%==================================================================== generate_key(P=#'ECParameters'{}) -> {public_key:generate_key(P), #{}}; generate_key({namedCurve, P}) when is_atom(P) -> generate_key({namedCurve, pubkey_cert_records:namedCurves(P)}); generate_key(P={namedCurve, _}) -> {public_key:generate_key(P), #{}}; generate_key(#'ECPrivateKey'{ parameters = P }) -> generate_key(P); 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">>) -> generate_key(secp384r1); generate_key(<<"P-521">>) -> generate_key(secp521r1); generate_key({ec, P=#'ECParameters'{}}) -> generate_key(P); generate_key({ec, P=#'ECPrivateKey'{}}) -> generate_key(P); generate_key({ec, P={#'ECPoint'{}, _}}) -> generate_key(P); generate_key({ec, P={namedCurve, _}}) -> generate_key(P); generate_key({ec, P}) when is_atom(P) -> generate_key(P); generate_key({ec, P}) when is_binary(P) -> generate_key(P). generate_key(KTY, Fields) -> {NewKTY, OtherFields} = generate_key(KTY), {NewKTY, maps:merge(maps:remove(<<"kid">>, Fields), OtherFields)}. key_encryptor(KTY, Fields, Key) -> jose_jwk_kty:key_encryptor(KTY, Fields, Key). %%==================================================================== %% jose_jwk_use_enc callbacks %%==================================================================== block_encryptor(_KTY, Fields=#{ <<"alg">> := ALG, <<"enc">> := ENC, <<"use">> := <<"enc">> }) -> Folder = fun (K, V, F) when K =:= <<"apu">> orelse K =:= <<"apv">> orelse K =:= <<"epk">> orelse K =:= <<"skid">> -> maps:put(K, V, F); (_K, _V, F) -> F end, maps:fold(Folder, #{ <<"alg">> => ALG, <<"enc">> => ENC }, Fields); block_encryptor(KTY, Fields=#{ <<"alg">> := <<"ECDH-1PU", _/binary>> }) -> block_encryptor(KTY, maps:merge(Fields, #{ <<"enc">> => case jose_jwa:is_block_cipher_supported({aes_gcm, 128}) of false -> <<"A128CBC-HS256">>; true -> <<"A128GCM">> end, <<"use">> => <<"enc">> })); block_encryptor(KTY, Fields=#{ <<"alg">> := <<"ECDH-SS", _/binary>> }) -> block_encryptor(KTY, maps:merge(Fields, #{ <<"enc">> => case jose_jwa:is_block_cipher_supported({aes_gcm, 128}) of false -> <<"A128CBC-HS256">>; true -> <<"A128GCM">> end, <<"use">> => <<"enc">> })); block_encryptor(KTY, Fields) -> block_encryptor(KTY, maps:merge(Fields, #{ <<"alg">> => <<"ECDH-ES">>, <<"enc">> => case jose_jwa:is_block_cipher_supported({aes_gcm, 128}) of false -> <<"A128CBC-HS256">>; true -> <<"A128GCM">> end, <<"use">> => <<"enc">> })). derive_key({ECPoint=#'ECPoint'{}, _}, ECPrivateKey=#'ECPrivateKey'{}) -> public_key:compute_key(ECPoint, ECPrivateKey); derive_key(#'ECPrivateKey'{parameters=ECParameters, publicKey=Octets}, ECPrivateKey=#'ECPrivateKey'{}) -> ECPoint = #'ECPoint'{point=Octets}, ECPublicKey = {ECPoint, ECParameters}, derive_key(ECPublicKey, ECPrivateKey). %%==================================================================== %% jose_jwk_use_sig callbacks %%==================================================================== sign(Message, JWSALG, ECPrivateKey=#'ECPrivateKey'{}) -> DigestType = jws_alg_to_digest_type(ECPrivateKey, JWSALG), DERSignature = public_key:sign(Message, DigestType, ECPrivateKey), #'ECDSA-Sig-Value'{ r = R, s = S } = public_key:der_decode('ECDSA-Sig-Value', DERSignature), RBin = int_to_bin(R), SBin = int_to_bin(S), Size = jws_alg_to_r_s_size(JWSALG), RPad = pad(RBin, Size), SPad = pad(SBin, Size), Signature = << RPad/binary, SPad/binary >>, Signature; sign(_Message, _JWSALG, {#'ECPoint'{}, _}) -> erlang:error(not_supported). signer(#'ECPrivateKey'{}, #{ <<"alg">> := ALG, <<"use">> := <<"sig">> }) -> #{ <<"alg">> => ALG }; signer(#'ECPrivateKey'{parameters={namedCurve, Parameters}}, _Fields) -> #{ <<"alg">> => case parameters_to_crv(Parameters) of <<"secp256k1">> -> <<"ES256K">>; <<"P-256">> -> <<"ES256">>; <<"P-384">> -> <<"ES384">>; <<"P-521">> -> <<"ES512">> end }. verifier(_KTY, #{ <<"alg">> := ALG, <<"use">> := <<"sig">> }) -> [ALG]; verifier(#'ECPrivateKey'{parameters=ECParameters, publicKey=Octets0}, Fields) -> Octets = case Octets0 of {_, Octets1} -> Octets1; _ -> Octets0 end, ECPoint = #'ECPoint'{point=Octets}, ECPublicKey = {ECPoint, ECParameters}, verifier(ECPublicKey, Fields); verifier({#'ECPoint'{}, {namedCurve, Parameters}}, _Fields) -> [ case parameters_to_crv(Parameters) of <<"secp256k1">> -> <<"ES256K">>; <<"P-256">> -> <<"ES256">>; <<"P-384">> -> <<"ES384">>; <<"P-521">> -> <<"ES512">> end ]. verify(Message, JWSALG, Signature, ECPublicKey={#'ECPoint'{}, _}) -> try jws_alg_to_digest_type(ECPublicKey, JWSALG) of DigestType -> %% Safe: jws_alg_to_digest_type/2 has already rejected every %% JWSALG that jws_alg_to_r_s_size/1 does not handle. RSSize = jws_alg_to_r_s_size(JWSALG), %% RFC 7518 3.4 fixes the JWS Signature to the octet sequence %% R || S, where each of R and S is left zero-padded to the %% byte length of the curve order. Any other length MUST be %% rejected: splitting on byte_size(Signature) div 2 and then %% relying on crypto:bytes_to_integer/1 (which ignores leading %% zero octets) would otherwise accept an unbounded family of %% distinct, re-padded encodings of the same (R, S) pair. case Signature of << RBin:RSSize/binary, SBin:RSSize/binary >> -> R = crypto:bytes_to_integer(RBin), S = crypto:bytes_to_integer(SBin), DERSignature = public_key:der_encode('ECDSA-Sig-Value', #'ECDSA-Sig-Value'{ r = R, s = S }), public_key:verify(Message, DigestType, DERSignature, ECPublicKey); _ -> false end catch error:{not_supported, _} -> false end; verify(Message, JWSALG, Signature, #'ECPrivateKey'{parameters=ECParameters, publicKey=Octets0}) -> Octets = case Octets0 of {_, Octets1} -> Octets1; _ -> Octets0 end, ECPoint = #'ECPoint'{point=Octets}, ECPublicKey = {ECPoint, ECParameters}, verify(Message, JWSALG, Signature, ECPublicKey). %%==================================================================== %% API functions %%==================================================================== from_der(DERBinary) when is_binary(DERBinary) -> case jose_jwk_der:from_binary(DERBinary) of {?MODULE, {Key, Fields}} -> {Key, Fields} end. from_der(Password, PEMBinary) when is_binary(PEMBinary) -> case jose_jwk_der:from_binary(Password, PEMBinary) of {?MODULE, {Key, Fields}} -> {Key, Fields}; PEMError -> PEMError end. from_key(ECPrivateKey=#'ECPrivateKey'{}) -> {ECPrivateKey, #{}}; from_key(ECPublicKey={#'ECPoint'{}, _}) -> {ECPublicKey, #{}}. from_pem(PEMBinary) when is_binary(PEMBinary) -> case jose_jwk_pem:from_binary(PEMBinary) of {?MODULE, {Key, Fields}} -> {Key, Fields}; PEMError -> PEMError end. from_pem(Password, PEMBinary) when is_binary(PEMBinary) -> case jose_jwk_pem:from_binary(Password, PEMBinary) of {?MODULE, {Key, Fields}} -> {Key, Fields}; PEMError -> PEMError end. to_der(ECPrivateKey=#'ECPrivateKey'{}) -> jose_public_key:der_encode('PrivateKeyInfo', ECPrivateKey); to_der(ECPublicKey={#'ECPoint'{}, _ECParameters}) -> jose_public_key:der_encode('SubjectPublicKeyInfo', ECPublicKey). to_der(Password, ECPrivateKey=#'ECPrivateKey'{}) -> jose_jwk_der:to_binary(Password, 'PrivateKeyInfo', ECPrivateKey); to_der(Password, ECPublicKey={#'ECPoint'{}, _ECParameters}) -> erlang:error({badarg, [Password, ECPublicKey]}). to_pem(ECPrivateKey=#'ECPrivateKey'{}) -> PEMEntry = jose_public_key:pem_entry_encode('PrivateKeyInfo', ECPrivateKey), jose_public_key:pem_encode([PEMEntry]); to_pem(ECPublicKey={#'ECPoint'{}, _ECParameters}) -> PEMEntry = jose_public_key:pem_entry_encode('SubjectPublicKeyInfo', ECPublicKey), jose_public_key:pem_encode([PEMEntry]). to_pem(Password, ECPrivateKey=#'ECPrivateKey'{}) -> jose_jwk_pem:to_binary(Password, 'PrivateKeyInfo', ECPrivateKey); to_pem(Password, ECPublicKey={#'ECPoint'{}, _ECParameters}) -> erlang:error({badarg, [Password, ECPublicKey]}). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private 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) -> from_map_ec_private_key(ECMode, maps:remove(<<"crv">>, F), Key#'ECPrivateKey'{ parameters = {namedCurve, pubkey_cert_records:namedCurves(secp384r1)} }); from_map_ec_private_key(ECMode, F = #{ <<"crv">> := <<"P-521">> }, Key) -> from_map_ec_private_key(ECMode, maps:remove(<<"crv">>, F), Key#'ECPrivateKey'{ parameters = {namedCurve, pubkey_cert_records:namedCurves(secp521r1)} }); from_map_ec_private_key(binary, F = #{ <<"x">> := X, <<"y">> := Y }, Key) -> from_map_ec_private_key(binary, maps:without([<<"x">>, <<"y">>], F), Key#'ECPrivateKey'{ publicKey = << 16#04, (jose_jwa_base64url:decode(X))/binary, (jose_jwa_base64url:decode(Y))/binary >> }); from_map_ec_private_key(list, F = #{ <<"x">> := X, <<"y">> := Y }, Key) -> from_map_ec_private_key(list, maps:without([<<"x">>, <<"y">>], F), Key#'ECPrivateKey'{ publicKey = {0, << 16#04, (jose_jwa_base64url:decode(X))/binary, (jose_jwa_base64url:decode(Y))/binary >>} }); 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}) -> from_map_ec_public_key(maps:remove(<<"crv">>, F), {Point, {namedCurve, pubkey_cert_records:namedCurves(secp384r1)}}); from_map_ec_public_key(F = #{ <<"crv">> := <<"P-521">> }, {Point, _Params}) -> from_map_ec_public_key(maps:remove(<<"crv">>, F), {Point, {namedCurve, pubkey_cert_records:namedCurves(secp521r1)}}); from_map_ec_public_key(F = #{ <<"x">> := X, <<"y">> := Y }, {Point, Params}) -> from_map_ec_public_key(maps:without([<<"x">>, <<"y">>], F), {Point#'ECPoint'{ point = << 16#04, (jose_jwa_base64url:decode(X))/binary, (jose_jwa_base64url:decode(Y))/binary >> }, Params}); from_map_ec_public_key(F, Key) -> {Key, F}. %% @private int_to_bin(X) when X < 0 -> int_to_bin_neg(X, []); int_to_bin(X) -> int_to_bin_pos(X, []). %% @private int_to_bin_pos(0,Ds=[_|_]) -> list_to_binary(Ds); int_to_bin_pos(X,Ds) -> int_to_bin_pos(X bsr 8, [(X band 255)|Ds]). %% @private int_to_bin_neg(-1, Ds=[MSB|_]) when MSB >= 16#80 -> list_to_binary(Ds); 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') -> sha384; jws_alg_to_digest_type(<<"P-521">>, 'ES512') -> sha512; jws_alg_to_digest_type(#'ECPrivateKey'{parameters={namedCurve, Parameters}}, ALG) -> jws_alg_to_digest_type(parameters_to_crv(Parameters), ALG); jws_alg_to_digest_type({#'ECPoint'{}, {namedCurve, Parameters}}, ALG) -> jws_alg_to_digest_type(parameters_to_crv(Parameters), ALG); 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') -> 48; jws_alg_to_r_s_size('ES512') -> 66. %% @private pad(Bin, Size) when byte_size(Bin) =:= Size -> Bin; pad(Bin, Size) -> pad(<< 0, Bin/binary >>, Size). %% @private parameters_to_crv(secp256k1) -> <<"secp256k1">>; parameters_to_crv(secp256r1) -> <<"P-256">>; parameters_to_crv(secp384r1) -> <<"P-384">>; parameters_to_crv(secp521r1) -> <<"P-521">>; parameters_to_crv(Parameters) when is_tuple(Parameters) -> parameters_to_crv(pubkey_cert_records:namedCurves(Parameters)). %% @private public_key_to_x_y(<< 16#04, X:32/binary, Y:32/binary >>) -> {X, Y}; public_key_to_x_y(<< 16#04, X:48/binary, Y:48/binary >>) -> {X, Y}; public_key_to_x_y(<< 16#04, X:66/binary, Y:66/binary >>) -> {X, Y}. potatosalad-erlang-jose-30ac019/src/jwk/jose_jwk_set.erl0000664000232200023220000000161115267717064023715 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 21 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwk_set). -include("jose_jwk.hrl"). %% API -export([from_map/1]). -export([to_map/2]). %%==================================================================== %% API functions %%==================================================================== from_map(F=#{ <<"keys">> := Keys }) -> {[jose_jwk:from_map(Key) || Key <- Keys], maps:remove(<<"keys">>, F)}. to_map(Keys, F) -> F#{ <<"keys">> => [element(2, jose_jwk:to_map(Key)) || Key <- Keys] }. potatosalad-erlang-jose-30ac019/src/jwk/jose_jwk_pem.erl0000664000232200023220000000761515267717064023715 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 24 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwk_pem). -include("jose_public_key.hrl"). %% API -export([from_binary/1]). -export([from_binary/2]). -export([from_certificate/1]). -export([from_public_key_info/1]). -export([to_binary/3]). %%==================================================================== %% API functions %%==================================================================== from_binary(PEMBinary) when is_binary(PEMBinary) -> case jose_public_key:pem_decode(PEMBinary) of [CertificatePEMEntry={'Certificate', _, not_encrypted}] -> from_certificate(CertificatePEMEntry); [PEMEntry] -> jose_jwk_kty:from_key(jose_public_key:pem_entry_decode(PEMEntry)); PEMDecodeError -> PEMDecodeError end. from_binary(Password, EncryptedPEMBinary) when is_binary(EncryptedPEMBinary) -> case jose_public_key:pem_decode(EncryptedPEMBinary) of [EncryptedPEMEntry] -> PasswordString = unicode:characters_to_list(Password), jose_jwk_kty:from_key(jose_public_key:pem_entry_decode(EncryptedPEMEntry, PasswordString)); PEMDecodeError -> PEMDecodeError end. from_certificate(CertificateBinary) when is_binary(CertificateBinary) -> case jose_public_key:pem_decode(CertificateBinary) of [CertificatePEMEntry={'Certificate', _, not_encrypted}] -> from_certificate(CertificatePEMEntry); PEMDecodeError -> {error, {pem_decode, PEMDecodeError}} end; from_certificate(CertificatePEMEntry={'Certificate', _, not_encrypted}) -> case jose_public_key:pem_entry_decode(CertificatePEMEntry) of Certificate=#'Certificate'{} -> from_certificate(Certificate); PEMEntryDecodeError -> {error, {pem_entry_decode, PEMEntryDecodeError}} end; from_certificate(#'Certificate'{tbsCertificate=#'TBSCertificate'{subjectPublicKeyInfo=#'SubjectPublicKeyInfo'{}=SubjectPublicKeyInfo}}) -> from_public_key_info(SubjectPublicKeyInfo). from_public_key_info(#'SubjectPublicKeyInfo'{algorithm=#'AlgorithmIdentifier'{}}=SubjectPublicKeyInfo) -> from_public_key_info(jose_public_key:pem_entry_encode('SubjectPublicKeyInfo', SubjectPublicKeyInfo)); from_public_key_info(PEMEntry={'SubjectPublicKeyInfo', DER, not_encrypted}) when is_binary(DER) -> jose_jwk_kty:from_key(jose_public_key:pem_entry_decode(PEMEntry)). to_binary(Password, 'PrivateKeyInfo', Key) -> CipherInfo = {"AES-256-CBC", #'PBES2-params'{ keyDerivationFunc = #'PBES2-params_keyDerivationFunc'{ algorithm = ?'id-PBKDF2', parameters = #'PBKDF2-params'{ salt = {specified, crypto:strong_rand_bytes(?JOSE_PBES2_SALT_BYTES)}, iterationCount = ?JOSE_PBES2_ITERATION_COUNT, keyLength = asn1_NOVALUE, prf = #'PBKDF2-params_prf'{ algorithm = ?'id-hmacWithSHA256', parameters = {asn1_OPENTYPE, <<5, 0>>} } } }, encryptionScheme = #'PBES2-params_encryptionScheme'{ algorithm = ?'id-aes256-CBC', parameters = {asn1_OPENTYPE, <<4, 16, (crypto:strong_rand_bytes(16))/binary>>} } }}, PasswordString = binary_to_list(iolist_to_binary(Password)), PEMEntry = jose_public_key:pem_entry_encode('PrivateKeyInfo', Key, {CipherInfo, PasswordString}), jose_public_key:pem_encode([PEMEntry]); to_binary(Password, KeyType, Key) -> CipherInfo = {"AES-256-CBC", crypto:strong_rand_bytes(16)}, PasswordString = binary_to_list(iolist_to_binary(Password)), PEMEntry = jose_public_key:pem_entry_encode(KeyType, Key, {CipherInfo, PasswordString}), jose_public_key:pem_encode([PEMEntry]). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jwk/jose_jwk_kty_rsa.erl0000664000232200023220000005276315267717064024614 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 23 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwk_kty_rsa). -behaviour(jose_jwk). -behaviour(jose_jwk_kty). -behaviour(jose_jwk_use_enc). -behaviour(jose_jwk_use_sig). -include_lib("public_key/include/public_key.hrl"). %% jose_jwk callbacks -export([from_map/1]). -export([to_key/1]). -export([to_map/2]). -export([to_public_map/2]). -export([to_thumbprint_map/2]). %% jose_jwk_kty callbacks -export([generate_key/1]). -export([generate_key/2]). -export([key_encryptor/3]). %% jose_jwk_use_enc callbacks -export([block_encryptor/2]). -export([decrypt_private/3]). -export([encrypt_public/3]). %% jose_jwk_use_sig callbacks -export([sign/3]). -export([signer/2]). -export([verifier/2]). -export([verify/4]). %% API -export([from_der/1]). -export([from_der/2]). -export([from_key/1]). -export([from_pem/1]). -export([from_pem/2]). -export([to_der/1]). -export([to_der/2]). -export([to_pem/1]). -export([to_pem/2]). %% Types -type key() :: #'RSAPrivateKey'{} | #'RSAPublicKey'{}. -export_type([key/0]). %% Recovery accepts at most 8192-bit moduli and n-sized exponents before %% calling the uninterruptible crypto:mod_pow/3 NIF. CRT imports do not %% perform this search and are not subject to these recovery limits. -define(SFM_MAX_MODULUS_BITS, 8192). %% Random bases avoid rejecting valid keys whose factors have matching %% quadratic characters for every small deterministic base. Each trial %% performs one exponentiation, then reuses successive modular squares. -define(SFM_MAX_TRIALS, 32). -define(SFM_MAX_SQUARES, 256). %% Check before every expensive operation; a NIF already in progress may %% finish after this deadline. The outer 1000 ms watchdog is a last resort, %% not the recovery work budget. Supply CRT parameters on budget exhaustion. -define(SFM_BUDGET_MS, 200). %% Large legitimate imports need several more costly trials. The separate %% deadline still leaves room for one bounded NIF before the outer watchdog. -define(SFM_LARGE_BUDGET_MS, 500). %%==================================================================== %% jose_jwk callbacks %%==================================================================== from_map(F = #{ <<"kty">> := <<"RSA">>, <<"d">> := _ }) -> from_map_rsa_private_key(maps:remove(<<"kty">>, F), #'RSAPrivateKey'{ version = 'two-prime', otherPrimeInfos = 'asn1_NOVALUE' }); from_map(F = #{ <<"kty">> := <<"RSA">> }) -> from_map_rsa_public_key(maps:remove(<<"kty">>, F), #'RSAPublicKey'{}). to_key(RSAPrivateKey=#'RSAPrivateKey'{}) -> RSAPrivateKey; to_key(RSAPublicKey=#'RSAPublicKey'{}) -> RSAPublicKey. to_map(RSAPrivateKey=#'RSAPrivateKey'{version = 0}, F) -> to_map(RSAPrivateKey#'RSAPrivateKey'{version = 'two-prime'}, F); to_map(RSAPrivateKey=#'RSAPrivateKey'{version = 1}, F) -> to_map(RSAPrivateKey#'RSAPrivateKey'{version = 'multi'}, F); to_map(#'RSAPrivateKey'{ version = 'two-prime', otherPrimeInfos = 'asn1_NOVALUE', privateExponent = D, exponent1 = DP, exponent2 = DQ, publicExponent = E, modulus = N, prime1 = P, prime2 = Q, coefficient = QI}, F) -> F#{ <<"d">> => jose_jwa_base64url:encode(int_to_bin(D)), <<"dp">> => jose_jwa_base64url:encode(int_to_bin(DP)), <<"dq">> => jose_jwa_base64url:encode(int_to_bin(DQ)), <<"e">> => jose_jwa_base64url:encode(int_to_bin(E)), <<"kty">> => <<"RSA">>, <<"n">> => jose_jwa_base64url:encode(int_to_bin(N)), <<"p">> => jose_jwa_base64url:encode(int_to_bin(P)), <<"q">> => jose_jwa_base64url:encode(int_to_bin(Q)), <<"qi">> => jose_jwa_base64url:encode(int_to_bin(QI)) }; to_map(#'RSAPrivateKey'{ version = 'multi', otherPrimeInfos = OTH, privateExponent = D, exponent1 = DP, exponent2 = DQ, publicExponent = E, modulus = N, prime1 = P, prime2 = Q, coefficient = QI}, F) -> F#{ <<"d">> => jose_jwa_base64url:encode(int_to_bin(D)), <<"dp">> => jose_jwa_base64url:encode(int_to_bin(DP)), <<"dq">> => jose_jwa_base64url:encode(int_to_bin(DQ)), <<"e">> => jose_jwa_base64url:encode(int_to_bin(E)), <<"kty">> => <<"RSA">>, <<"n">> => jose_jwa_base64url:encode(int_to_bin(N)), <<"oth">> => [begin #{ <<"d">> => jose_jwa_base64url:encode(int_to_bin(OD)), <<"r">> => jose_jwa_base64url:encode(int_to_bin(OR)), <<"t">> => jose_jwa_base64url:encode(int_to_bin(OT)) } end || #'OtherPrimeInfo'{ prime = OR, exponent = OD, coefficient = OT} <- OTH], <<"p">> => jose_jwa_base64url:encode(int_to_bin(P)), <<"q">> => jose_jwa_base64url:encode(int_to_bin(Q)), <<"qi">> => jose_jwa_base64url:encode(int_to_bin(QI)) }; to_map(#'RSAPublicKey'{ publicExponent = E, modulus = N}, F) -> F#{ <<"e">> => jose_jwa_base64url:encode(int_to_bin(E)), <<"kty">> => <<"RSA">>, <<"n">> => jose_jwa_base64url:encode(int_to_bin(N)) }. to_public_map(K=#'RSAPrivateKey'{}, F) -> maps:without([<<"d">>, <<"dp">>, <<"dq">>, <<"p">>, <<"q">>, <<"qi">>, <<"oth">>], to_map(K, F)); to_public_map(K=#'RSAPublicKey'{}, F) -> to_map(K, F). to_thumbprint_map(K, F) -> maps:with([<<"e">>, <<"kty">>, <<"n">>], to_public_map(K, F)). %%==================================================================== %% jose_jwk_kty callbacks %%==================================================================== generate_key(#'RSAPrivateKey'{ modulus = N, publicExponent = E }) -> generate_key({rsa, int_to_bit_size(N), E}); generate_key(#'RSAPublicKey'{ modulus = N, publicExponent = E }) -> generate_key({rsa, int_to_bit_size(N), E}); generate_key({rsa, ModulusSize}) when is_integer(ModulusSize) -> generate_key({rsa, ModulusSize, 65537}); generate_key({rsa, ModulusSize, ExponentSize}) when is_integer(ModulusSize) andalso is_integer(ExponentSize) -> try_generate_key([ public_key, cutkey ], ModulusSize, ExponentSize). generate_key(KTY, Fields) -> {NewKTY, OtherFields} = generate_key(KTY), {NewKTY, maps:merge(maps:remove(<<"kid">>, Fields), OtherFields)}. key_encryptor(KTY, Fields, Key) -> jose_jwk_kty:key_encryptor(KTY, Fields, Key). %%==================================================================== %% jose_jwk_use_enc callbacks %%==================================================================== block_encryptor(_KTY, #{ <<"alg">> := ALG, <<"enc">> := ENC, <<"use">> := <<"enc">> }) -> #{ <<"alg">> => ALG, <<"enc">> => ENC }; block_encryptor(_KTY, _Fields) -> #{ <<"alg">> => case jose_jwa:is_rsa_crypt_supported(rsa_oaep) of false -> <<"RSA1_5">>; true -> <<"RSA-OAEP">> end, <<"enc">> => case jose_jwa:is_block_cipher_supported({aes_gcm, 128}) of false -> <<"A128CBC-HS256">>; true -> <<"A128GCM">> end }. decrypt_private(CipherText, Options, RSAPrivateKey=#'RSAPrivateKey'{}) -> jose_jwa:decrypt_private(CipherText, RSAPrivateKey, Options). encrypt_public(PlainText, Options, RSAPublicKey=#'RSAPublicKey'{}) -> jose_jwa:encrypt_public(PlainText, RSAPublicKey, Options); encrypt_public(PlainText, Options, #'RSAPrivateKey'{modulus=Modulus, publicExponent=PublicExponent}) -> RSAPublicKey = #'RSAPublicKey'{modulus=Modulus, publicExponent=PublicExponent}, encrypt_public(PlainText, Options, RSAPublicKey). %%==================================================================== %% jose_jwk_use_sig callbacks %%==================================================================== sign(Message, JWSALG, RSAPrivateKey=#'RSAPrivateKey'{}) -> {Padding, DigestType} = jws_alg_to_digest_type(JWSALG), jose_jwa:sign(Message, DigestType, RSAPrivateKey, Padding). signer(#'RSAPrivateKey'{}, #{ <<"alg">> := ALG, <<"use">> := <<"sig">> }) -> #{ <<"alg">> => ALG }; signer(#'RSAPrivateKey'{}, _Fields) -> #{ <<"alg">> => case jose_jwa:is_rsa_sign_supported(rsa_pkcs1_pss_padding) of false -> <<"RS256">>; true -> <<"PS256">> end }. verifier(_KTY, #{ <<"alg">> := ALG, <<"use">> := <<"sig">> }) -> [ALG]; verifier(#'RSAPrivateKey'{modulus=Modulus, publicExponent=PublicExponent}, Fields) -> RSAPublicKey = #'RSAPublicKey'{modulus=Modulus, publicExponent=PublicExponent}, verifier(RSAPublicKey, Fields); verifier(#'RSAPublicKey'{}, _Fields) -> case jose_jwa:is_rsa_sign_supported(rsa_pkcs1_pss_padding) of false -> [<<"RS256">>, <<"RS384">>, <<"RS512">>]; true -> [<<"PS256">>, <<"PS384">>, <<"PS512">>, <<"RS256">>, <<"RS384">>, <<"RS512">>] end. verify(Message, JWSALG, Signature, RSAPublicKey=#'RSAPublicKey'{}) -> try jws_alg_to_digest_type(JWSALG) of {Padding, DigestType} -> jose_jwa:verify(Message, DigestType, Signature, RSAPublicKey, Padding) catch error:{not_supported, _} -> false end; verify(Message, JWSALG, Signature, #'RSAPrivateKey'{modulus=Modulus, publicExponent=PublicExponent}) -> RSAPublicKey = #'RSAPublicKey'{modulus=Modulus, publicExponent=PublicExponent}, verify(Message, JWSALG, Signature, RSAPublicKey). %%==================================================================== %% API functions %%==================================================================== from_der(DERBinary) when is_binary(DERBinary) -> case jose_jwk_der:from_binary(DERBinary) of {?MODULE, {Key, Fields}} -> {Key, Fields} end. from_der(Password, PEMBinary) when is_binary(PEMBinary) -> case jose_jwk_der:from_binary(Password, PEMBinary) of {?MODULE, {Key, Fields}} -> {Key, Fields}; PEMError -> PEMError end. from_key(RSAPrivateKey=#'RSAPrivateKey'{}) -> {RSAPrivateKey, #{}}; from_key(RSAPublicKey=#'RSAPublicKey'{}) -> {RSAPublicKey, #{}}. from_pem(PEMBinary) when is_binary(PEMBinary) -> case jose_jwk_pem:from_binary(PEMBinary) of {?MODULE, {Key, Fields}} -> {Key, Fields}; PEMError -> PEMError end. from_pem(Password, PEMBinary) when is_binary(PEMBinary) -> case jose_jwk_pem:from_binary(Password, PEMBinary) of {?MODULE, {Key, Fields}} -> {Key, Fields}; PEMError -> PEMError end. to_der(RSAPrivateKey=#'RSAPrivateKey'{}) -> jose_public_key:der_encode('PrivateKeyInfo', RSAPrivateKey); to_der(RSAPublicKey=#'RSAPublicKey'{}) -> jose_public_key:der_encode('RSAPublicKey', RSAPublicKey). to_der(Password, RSAPrivateKey=#'RSAPrivateKey'{}) -> jose_jwk_der:to_binary(Password, 'PrivateKeyInfo', RSAPrivateKey); to_der(Password, RSAPublicKey=#'RSAPublicKey'{}) -> jose_jwk_der:to_binary(Password, 'RSAPublicKey', RSAPublicKey). to_pem(RSAPrivateKey=#'RSAPrivateKey'{}) -> PEMEntry = jose_public_key:pem_entry_encode('PrivateKeyInfo', RSAPrivateKey), jose_public_key:pem_encode([PEMEntry]); to_pem(RSAPublicKey=#'RSAPublicKey'{}) -> PEMEntry = jose_public_key:pem_entry_encode('RSAPublicKey', RSAPublicKey), jose_public_key:pem_encode([PEMEntry]). to_pem(Password, RSAPrivateKey=#'RSAPrivateKey'{}) -> jose_jwk_pem:to_binary(Password, 'PrivateKeyInfo', RSAPrivateKey); to_pem(Password, RSAPublicKey=#'RSAPublicKey'{}) -> jose_jwk_pem:to_binary(Password, 'RSAPublicKey', RSAPublicKey). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private convert_sfm_to_crt(Key=#'RSAPrivateKey'{ version = 'two-prime', otherPrimeInfos = 'asn1_NOVALUE', privateExponent = D, exponent1 = undefined, exponent2 = undefined, publicExponent = E, modulus = N, prime1 = undefined, prime2 = undefined, coefficient = undefined}) when is_integer(D) andalso is_integer(E) andalso is_integer(N) -> KTOT = convert_sfm_to_crt_ktot(D, E, N), T = convert_sfm_to_crt_find_t(KTOT), Budget = case N < (1 bsl 6144) of true -> ?SFM_BUDGET_MS; false -> ?SFM_LARGE_BUDGET_MS end, Deadline = erlang:monotonic_time(millisecond) + Budget, P0 = convert_sfm_to_crt_find_p(T, KTOT div T, N, ?SFM_MAX_TRIALS, ?SFM_MAX_SQUARES, Deadline), Q0 = N div P0, %% A recovered factor alone does not validate the supplied exponents. true = (P0 > 1 andalso Q0 > 1 andalso P0 * Q0 =:= N andalso KTOT rem (P0 - 1) =:= 0 andalso KTOT rem (Q0 - 1) =:= 0), {P, Q} = case Q0 > P0 of true -> {Q0, P0}; false -> {P0, Q0} end, DP = jose_jwa_math:mod(D, P - 1), DQ = jose_jwa_math:mod(D, Q - 1), QI = convert_sfm_to_crt_mod_inverse(Q, P), true = is_integer(QI), Key#'RSAPrivateKey'{ exponent1 = DP, exponent2 = DQ, prime1 = P, prime2 = Q, coefficient = QI }. %% @private %% Reject structurally invalid triples before attacker-sized arithmetic. %% ed - 1 must be a positive even multiple of lambda(n) for a valid key. %% The recovery trials below also check this consistency for each base. convert_sfm_to_crt_ktot(D, E, N) when N > 3 andalso (N band 1) =:= 1 andalso N < (1 bsl ?SFM_MAX_MODULUS_BITS) andalso E > 2 andalso E < N andalso (E band 1) =:= 1 andalso D > 0 andalso D < N andalso (D band 1) =:= 1 -> D * E - 1; convert_sfm_to_crt_ktot(_D, _E, _N) -> erlang:error(badarg). %% @private convert_sfm_to_crt_egcd(0, B) -> {B, 0, 1}; convert_sfm_to_crt_egcd(A, B) -> {G, Y, X} = convert_sfm_to_crt_egcd(jose_jwa_math:mod(B, A), A), {G, X - (B div A) * Y, Y}. %% @private convert_sfm_to_crt_find_p(_T, _S, _N, 0, _Squares, _Deadline) -> erlang:error(badarg); convert_sfm_to_crt_find_p(T, S, N, Trials, Squares, Deadline) -> ok = convert_sfm_to_crt_check_budget(Squares, Deadline), %% The extra 128 random bits make reduction bias negligible while %% avoiding an unbounded rejection-sampling loop. Bytes = byte_size(binary:encode_unsigned(N)) + 16, A = 2 + (crypto:bytes_to_integer(crypto:strong_rand_bytes(Bytes)) rem (N - 3)), case convert_sfm_to_crt_gcd(A, N) of 1 -> C = jose_jwa_math:expmod(A, T, N), case convert_sfm_to_crt_square(C, S, N, Squares, Deadline) of {factor, P} -> P; {next, Remaining} -> convert_sfm_to_crt_find_p(T, S, N, Trials - 1, Remaining, Deadline) end; _ -> %% A shared factor skips the A^(ed-1) = 1 (mod n) consistency %% check, so it must not be returned as a prime. Valid two-prime %% moduli practically never reach this; moduli with small factors %% (e.g. 2^k - 1) would otherwise import inconsistent keys. convert_sfm_to_crt_find_p(T, S, N, Trials - 1, Squares, Deadline) end. %% @private convert_sfm_to_crt_square(C, _S, N, Squares, _Deadline) when C =:= 1; C =:= N - 1 -> {next, Squares}; convert_sfm_to_crt_square(_C, 1, _N, _Squares, _Deadline) -> %% A valid key must reach 1 (mod n) after all the squarings. erlang:error(badarg); convert_sfm_to_crt_square(C, S, N, Squares, Deadline) -> ok = convert_sfm_to_crt_check_budget(Squares, Deadline), case (C * C) rem N of 1 -> {factor, convert_sfm_to_crt_gcd(C + 1, N)}; Next -> convert_sfm_to_crt_square(Next, S div 2, N, Squares - 1, Deadline) end. %% @private convert_sfm_to_crt_check_budget(Squares, Deadline) -> case Squares > 0 andalso erlang:monotonic_time(millisecond) < Deadline of true -> ok; false -> erlang:error(badarg) end. %% @private convert_sfm_to_crt_find_t(T) when T > 0 andalso T rem 2 =:= 0 -> convert_sfm_to_crt_find_t(T div 2); convert_sfm_to_crt_find_t(T) -> T. %% @private convert_sfm_to_crt_gcd(A, 0) -> A; convert_sfm_to_crt_gcd(A, B) -> convert_sfm_to_crt_gcd(B, jose_jwa_math:mod(A, B)). %% @private convert_sfm_to_crt_mod_inverse(A, M) -> case convert_sfm_to_crt_egcd(A, M) of {1, X, _Y} -> jose_jwa_math:mod(X, M); _ -> no_inverse end. %% @private from_map_rsa_private_key(F = #{ <<"d">> := D }, Key) -> from_map_rsa_private_key(maps:remove(<<"d">>, F), Key#'RSAPrivateKey'{ privateExponent = crypto:bytes_to_integer(jose_jwa_base64url:decode(D)) }); from_map_rsa_private_key(F = #{ <<"dp">> := DP }, Key) -> from_map_rsa_private_key(maps:remove(<<"dp">>, F), Key#'RSAPrivateKey'{ exponent1 = crypto:bytes_to_integer(jose_jwa_base64url:decode(DP)) }); from_map_rsa_private_key(F = #{ <<"dq">> := DQ }, Key) -> from_map_rsa_private_key(maps:remove(<<"dq">>, F), Key#'RSAPrivateKey'{ exponent2 = crypto:bytes_to_integer(jose_jwa_base64url:decode(DQ)) }); from_map_rsa_private_key(F = #{ <<"e">> := E }, Key) -> from_map_rsa_private_key(maps:remove(<<"e">>, F), Key#'RSAPrivateKey'{ publicExponent = crypto:bytes_to_integer(jose_jwa_base64url:decode(E)) }); from_map_rsa_private_key(F = #{ <<"n">> := N }, Key) -> from_map_rsa_private_key(maps:remove(<<"n">>, F), Key#'RSAPrivateKey'{ modulus = crypto:bytes_to_integer(jose_jwa_base64url:decode(N)) }); from_map_rsa_private_key(F = #{ <<"p">> := P }, Key) -> from_map_rsa_private_key(maps:remove(<<"p">>, F), Key#'RSAPrivateKey'{ prime1 = crypto:bytes_to_integer(jose_jwa_base64url:decode(P)) }); from_map_rsa_private_key(F = #{ <<"q">> := Q }, Key) -> from_map_rsa_private_key(maps:remove(<<"q">>, F), Key#'RSAPrivateKey'{ prime2 = crypto:bytes_to_integer(jose_jwa_base64url:decode(Q)) }); from_map_rsa_private_key(F = #{ <<"qi">> := QI }, Key) -> from_map_rsa_private_key(maps:remove(<<"qi">>, F), Key#'RSAPrivateKey'{ coefficient = crypto:bytes_to_integer(jose_jwa_base64url:decode(QI)) }); from_map_rsa_private_key(F = #{ <<"oth">> := OTH }, Key) -> OtherPrimeInfos = [begin #'OtherPrimeInfo'{ prime = crypto:bytes_to_integer(jose_jwa_base64url:decode(OR)), exponent = crypto:bytes_to_integer(jose_jwa_base64url:decode(OD)), coefficient = crypto:bytes_to_integer(jose_jwa_base64url:decode(OT))} end || #{ <<"d">> := OD, <<"r">> := OR, <<"t">> := OT } <- OTH], from_map_rsa_private_key(maps:remove(<<"oth">>, F), Key#'RSAPrivateKey'{ version = 'multi', otherPrimeInfos = OtherPrimeInfos }); from_map_rsa_private_key(F, Key0=#'RSAPrivateKey'{ version = 'two-prime', otherPrimeInfos = 'asn1_NOVALUE', privateExponent = D, exponent1 = undefined, exponent2 = undefined, publicExponent = E, modulus = N, prime1 = undefined, prime2 = undefined, coefficient = undefined}) when is_integer(D) andalso is_integer(E) andalso is_integer(N) -> Ref = erlang:make_ref(), Parent = self(), {Child, Monitor} = spawn_monitor(fun() -> %% A rejected key exits quietly: the DOWN clauses below already %% turn any failure into {badarg, [Key0]}, and letting the %% exception escape would log the attacker supplied integers. try convert_sfm_to_crt(Key0) of Key -> Parent ! {Ref, Key} catch _:_ -> ok end, exit(normal) end), receive {Ref, Key} -> _ = erlang:demonitor(Monitor, [flush]), from_map_rsa_private_key(F, Key); {'DOWN', Monitor, process, Child, _Reason} -> erlang:error({badarg, [Key0]}) after 1000 -> true = erlang:exit(Child, kill), receive {Ref, Key} -> _ = erlang:demonitor(Monitor, [flush]), from_map_rsa_private_key(F, Key); {'DOWN', Monitor, process, Child, _Reason} -> erlang:error({badarg, [Key0]}) end end; from_map_rsa_private_key(F, Key) -> {Key, F}. %% @private from_map_rsa_public_key(F = #{ <<"e">> := E }, Key) -> from_map_rsa_public_key(maps:remove(<<"e">>, F), Key#'RSAPublicKey'{ publicExponent = crypto:bytes_to_integer(jose_jwa_base64url:decode(E)) }); from_map_rsa_public_key(F = #{ <<"n">> := N }, Key) -> from_map_rsa_public_key(maps:remove(<<"n">>, F), Key#'RSAPublicKey'{ modulus = crypto:bytes_to_integer(jose_jwa_base64url:decode(N)) }); from_map_rsa_public_key(F, Key) -> {Key, F}. %% @private int_to_bin(X) when X < 0 -> int_to_bin_neg(X, []); int_to_bin(X) -> int_to_bin_pos(X, []). %% @private int_to_bin_pos(0,Ds=[_|_]) -> list_to_binary(Ds); int_to_bin_pos(X,Ds) -> int_to_bin_pos(X bsr 8, [(X band 255)|Ds]). %% @private int_to_bin_neg(-1, Ds=[MSB|_]) when MSB >= 16#80 -> list_to_binary(Ds); int_to_bin_neg(X,Ds) -> int_to_bin_neg(X bsr 8, [(X band 255)|Ds]). %% @private int_to_bit_size(I) -> int_to_bit_size(I, 0). %% @private int_to_bit_size(0, B) -> B; int_to_bit_size(I, B) -> int_to_bit_size(I bsr 1, B + 1). %% @private jws_alg_to_digest_type('PS256') -> {rsa_pkcs1_pss_padding, sha256}; 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') -> {rsa_pkcs1_padding, sha384}; jws_alg_to_digest_type('RS512') -> {rsa_pkcs1_padding, sha512}; jws_alg_to_digest_type(ALG) -> erlang:error({not_supported, [ALG]}). %% @private try_generate_key([public_key | Methods], ModulusSize, ExponentSize) -> case code:ensure_loaded(crypto) of {module, crypto} -> case code:ensure_loaded(public_key) of {module, public_key} -> _ = application:ensure_all_started(public_key), case erlang:function_exported(crypto, generate_key, 2) andalso erlang:function_exported(crypto, generate_key, 3) of true -> try public_key:generate_key({rsa, ModulusSize, ExponentSize}) of Key = #'RSAPrivateKey'{} -> {Key, #{}} catch Class:Reason -> erlang:error({public_key_error, {Class, Reason}}) end; false -> try_generate_key(Methods, ModulusSize, ExponentSize) end; _ -> try_generate_key(Methods, ModulusSize, ExponentSize) end; _ -> try_generate_key(Methods, ModulusSize, ExponentSize) end; try_generate_key([cutkey | Methods], ModulusSize, ExponentSize) -> case code:ensure_loaded(cutkey) of {module, cutkey} -> _ = application:ensure_all_started(cutkey), try cutkey:rsa(ModulusSize, ExponentSize, [{return, key}]) of {ok, Key=#'RSAPrivateKey'{}} -> {Key, #{}}; {error, Reason} -> erlang:error({cutkey_error, Reason}) catch Class:Reason -> erlang:error({cutkey_error, {Class, Reason}}) end; _ -> try_generate_key(Methods, ModulusSize, ExponentSize) end; try_generate_key([], ModulusSize, ExponentSize) -> erlang:error({not_supported, [{rsa, ModulusSize, ExponentSize}]}). potatosalad-erlang-jose-30ac019/src/jwk/jose_jwk_kty_okp_ed25519ph.erl0000664000232200023220000001346715267717064026224 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 15 Jan 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwk_kty_okp_ed25519ph). -behaviour(jose_jwk). -behaviour(jose_jwk_kty). -behaviour(jose_jwk_use_sig). %% jose_jwk callbacks -export([from_map/1]). -export([to_key/1]). -export([to_map/2]). -export([to_public_map/2]). -export([to_thumbprint_map/2]). %% jose_jwk_kty callbacks -export([generate_key/1]). -export([generate_key/2]). -export([key_encryptor/3]). %% jose_jwk_use_sig callbacks -export([sign/3]). -export([signer/2]). -export([verifier/2]). -export([verify/4]). %% API -export([from_okp/1]). -export([from_openssh_key/1]). -export([to_okp/1]). -export([to_openssh_key/2]). %% Macros -define(crv, <<"Ed25519ph">>). -define(secretbytes, 32). -define(publickeybytes, 32). -define(secretkeybytes, 64). %% Types -type publickey() :: << _:256 >>. -type secretkey() :: << _:512 >>. -type key() :: publickey() | secretkey(). -export_type([key/0]). %%==================================================================== %% jose_jwk callbacks %%==================================================================== from_map(F = #{ <<"kty">> := <<"OKP">>, <<"crv">> := ?crv, <<"d">> := D, <<"x">> := X }) -> << Secret:?secretbytes/binary >> = jose_jwa_base64url:decode(D), << PK:?publickeybytes/binary >> = jose_jwa_base64url:decode(X), SK = << Secret/binary, PK/binary >>, {SK, maps:without([<<"crv">>, <<"d">>, <<"kty">>, <<"x">>], F)}; from_map(F = #{ <<"kty">> := <<"OKP">>, <<"crv">> := ?crv, <<"x">> := X }) -> << PK:?publickeybytes/binary >> = jose_jwa_base64url:decode(X), {PK, maps:without([<<"crv">>, <<"kty">>, <<"x">>], F)}. to_key(PK = << _:?publickeybytes/binary >>) -> PK; to_key(SK = << _:?secretkeybytes/binary >>) -> SK. to_map(PK = << _:?publickeybytes/binary >>, F) -> F#{ <<"crv">> => ?crv, <<"kty">> => <<"OKP">>, <<"x">> => jose_jwa_base64url:encode(PK) }; to_map(<< Secret:?secretbytes/binary, PK:?publickeybytes/binary >>, F) -> F#{ <<"crv">> => ?crv, <<"d">> => jose_jwa_base64url:encode(Secret), <<"kty">> => <<"OKP">>, <<"x">> => jose_jwa_base64url:encode(PK) }. to_public_map(PK = << _:?publickeybytes/binary >>, F) -> to_map(PK, F); to_public_map(<< _:?secretbytes/binary, PK:?publickeybytes/binary >>, F) -> to_public_map(PK, F). to_thumbprint_map(K, F) -> maps:with([<<"crv">>, <<"kty">>, <<"x">>], to_public_map(K, F)). %%==================================================================== %% jose_jwk_kty callbacks %%==================================================================== generate_key(Seed = << _:?secretbytes/binary >>) -> {_PK, SK} = jose_curve25519:eddsa_keypair(Seed), {SK, #{}}; generate_key({okp, 'Ed25519ph', Seed = << _:?secretbytes/binary >>}) -> generate_key(Seed); generate_key({okp, 'Ed25519ph'}) -> {_PK, SK} = jose_curve25519:eddsa_keypair(), {SK, #{}}. generate_key(KTY, Fields) when is_binary(KTY) andalso (byte_size(KTY) =:= ?publickeybytes orelse byte_size(KTY) =:= ?secretkeybytes) -> {NewKTY, OtherFields} = generate_key({okp, 'Ed25519ph'}), {NewKTY, maps:merge(maps:remove(<<"kid">>, Fields), OtherFields)}. key_encryptor(KTY, Fields, Key) -> jose_jwk_kty:key_encryptor(KTY, Fields, Key). %%==================================================================== %% jose_jwk_use_sig callbacks %%==================================================================== sign(Message, ALG, SK = << _:?secretkeybytes/binary >>) when ALG =:= 'Ed25519ph' orelse ALG =:= 'EdDSA' -> jose_curve25519:ed25519ph_sign(Message, SK). signer(<< _:?secretkeybytes/binary >>, #{ <<"alg">> := ALG, <<"use">> := <<"sig">> }) -> #{ <<"alg">> => ALG }; signer(<< _:?secretkeybytes/binary >>, _Fields) -> #{ <<"alg">> => <<"EdDSA">> }. verifier(<< _:?publickeybytes/binary >>, #{ <<"alg">> := ALG, <<"use">> := <<"sig">> }) -> [ALG]; verifier(<< _:?secretbytes/binary, PK:?publickeybytes/binary >>, Fields) -> verifier(PK, Fields); verifier(<< _:?publickeybytes/binary >>, _Fields) -> [?crv, <<"EdDSA">>]. verify(Message, ALG, Signature, << _:?secretbytes/binary, PK:?publickeybytes/binary >>) when ALG =:= 'Ed25519ph' orelse ALG =:= 'EdDSA' -> verify(Message, ALG, Signature, PK); verify(Message, ALG, Signature, PK = << _:?publickeybytes/binary >>) when ALG =:= 'Ed25519ph' orelse ALG =:= 'EdDSA' -> jose_curve25519:ed25519ph_verify(Signature, Message, PK). %%==================================================================== %% API functions %%==================================================================== from_okp({'Ed25519ph', SK = << Secret:?secretbytes/binary, PK:?publickeybytes/binary >>}) -> case jose_curve25519:eddsa_secret_to_public(Secret) of PK -> {SK, #{}}; _ -> erlang:error(badarg) end; from_okp({'Ed25519ph', PK = << _:?publickeybytes/binary >>}) -> {PK, #{}}. from_openssh_key({<<"ssh-ed25519ph">>, _PK, SK, Comment}) -> {KTY, OtherFields} = from_okp({'Ed25519ph', SK}), case Comment of <<>> -> {KTY, OtherFields}; _ -> {KTY, maps:merge(#{ <<"kid">> => Comment }, OtherFields)} end. to_okp(SK = << _:?secretkeybytes/binary >>) -> {'Ed25519ph', SK}; to_okp(PK = << _:?publickeybytes/binary >>) -> {'Ed25519ph', PK}. to_openssh_key(SK = << _:?secretbytes/binary, PK:?publickeybytes/binary >>, F) -> Comment = maps:get(<<"kid">>, F, <<>>), jose_jwk_openssh_key:to_binary([[{{<<"ssh-ed25519ph">>, PK}, {<<"ssh-ed25519ph">>, PK, SK, Comment}}]]). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jwk/jose_jwk_der.erl0000664000232200023220000000506515267717064023703 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 02 Jan 2020 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwk_der). -include("jose_public_key.hrl"). %% API -export([from_binary/1]). -export([from_binary/2]). -export([to_binary/3]). %%==================================================================== %% API functions %%==================================================================== from_binary(DERBinary) when is_binary(DERBinary) -> case jose_public_key:der_decode(DERBinary) of Key -> jose_jwk_kty:from_key(Key) end. from_binary(Password, DERBinary) when is_binary(DERBinary) -> case jose_public_key:der_decode(DERBinary) of #'EncryptedPrivateKeyInfo'{ encryptionAlgorithm = AlgorithmInfo, encryptedData = EncryptedDER } -> CipherInfo = jose_public_key:decrypt_parameters(AlgorithmInfo), DecryptedDER = jose_public_key:decipher({'PrivateKeyInfo', EncryptedDER, CipherInfo}, Password), from_binary(DecryptedDER); Key -> jose_jwk_kty:from_key(Key) end. to_binary(Password, _KeyType, Key) -> CipherInfo = {"AES-256-CBC", #'PBES2-params'{ keyDerivationFunc = #'PBES2-params_keyDerivationFunc'{ algorithm = ?'id-PBKDF2', parameters = #'PBKDF2-params'{ salt = {specified, crypto:strong_rand_bytes(?JOSE_PBES2_SALT_BYTES)}, iterationCount = ?JOSE_PBES2_ITERATION_COUNT, keyLength = asn1_NOVALUE, prf = #'PBKDF2-params_prf'{ algorithm = ?'id-hmacWithSHA256', parameters = {asn1_OPENTYPE, <<5, 0>>} } } }, encryptionScheme = #'PBES2-params_encryptionScheme'{ algorithm = ?'id-aes256-CBC', parameters = {asn1_OPENTYPE, <<4, 16, (crypto:strong_rand_bytes(16))/binary>>} } }}, PasswordString = binary_to_list(iolist_to_binary(Password)), DecryptedDER = jose_public_key:der_encode('PrivateKeyInfo', Key), EncryptedDER = jose_public_key:cipher(DecryptedDER, CipherInfo, PasswordString), AlgorithmInfo = jose_public_key:encrypt_parameters(CipherInfo), jose_public_key:der_encode('EncryptedPrivateKeyInfo', #'EncryptedPrivateKeyInfo'{ encryptionAlgorithm = AlgorithmInfo, encryptedData = EncryptedDER }). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jwk/jose_jwk_kty_okp_ed25519.erl0000664000232200023220000002100215267717064025654 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 15 Jan 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwk_kty_okp_ed25519). -behaviour(jose_jwk). -behaviour(jose_jwk_kty). -behaviour(jose_jwk_use_sig). -include("jose_public_key.hrl"). %% jose_jwk callbacks -export([from_map/1]). -export([to_key/1]). -export([to_map/2]). -export([to_public_map/2]). -export([to_thumbprint_map/2]). %% jose_jwk_kty callbacks -export([generate_key/1]). -export([generate_key/2]). -export([key_encryptor/3]). %% jose_jwk_use_sig callbacks -export([sign/3]). -export([signer/2]). -export([verifier/2]). -export([verify/4]). %% API -export([from_der/1]). -export([from_der/2]). -export([from_key/1]). -export([from_okp/1]). -export([from_openssh_key/1]). -export([from_pem/1]). -export([from_pem/2]). -export([to_der/1]). -export([to_der/2]). -export([to_okp/1]). -export([to_openssh_key/2]). -export([to_pem/1]). -export([to_pem/2]). %% Macros -define(crv, <<"Ed25519">>). -define(secretbytes, 32). -define(publickeybytes, 32). -define(secretkeybytes, 64). %% Types -type publickey() :: << _:256 >>. -type secretkey() :: << _:512 >>. -type key() :: publickey() | secretkey(). -export_type([key/0]). %%==================================================================== %% jose_jwk callbacks %%==================================================================== from_map(F = #{ <<"kty">> := <<"OKP">>, <<"crv">> := ?crv, <<"d">> := D, <<"x">> := X }) -> << Secret:?secretbytes/binary >> = jose_jwa_base64url:decode(D), << PK:?publickeybytes/binary >> = jose_jwa_base64url:decode(X), SK = << Secret/binary, PK/binary >>, {SK, maps:without([<<"crv">>, <<"d">>, <<"kty">>, <<"x">>], F)}; from_map(F = #{ <<"kty">> := <<"OKP">>, <<"crv">> := ?crv, <<"x">> := X }) -> << PK:?publickeybytes/binary >> = jose_jwa_base64url:decode(X), {PK, maps:without([<<"crv">>, <<"kty">>, <<"x">>], F)}. to_key(<< PublicKey:?publickeybytes/binary >>) -> #'jose_EdDSA25519PublicKey'{ publicKey = PublicKey }; to_key(<< PrivateKey:?secretbytes/binary, PublicKey:?publickeybytes/binary >>) -> #'jose_EdDSA25519PrivateKey'{ publicKey = #'jose_EdDSA25519PublicKey'{ publicKey = PublicKey }, privateKey = PrivateKey }. to_map(PK = << _:?publickeybytes/binary >>, F) -> F#{ <<"crv">> => ?crv, <<"kty">> => <<"OKP">>, <<"x">> => jose_jwa_base64url:encode(PK) }; to_map(<< Secret:?secretbytes/binary, PK:?publickeybytes/binary >>, F) -> F#{ <<"crv">> => ?crv, <<"d">> => jose_jwa_base64url:encode(Secret), <<"kty">> => <<"OKP">>, <<"x">> => jose_jwa_base64url:encode(PK) }. to_public_map(PK = << _:?publickeybytes/binary >>, F) -> to_map(PK, F); to_public_map(<< _:?secretbytes/binary, PK:?publickeybytes/binary >>, F) -> to_public_map(PK, F). to_thumbprint_map(K, F) -> maps:with([<<"crv">>, <<"kty">>, <<"x">>], to_public_map(K, F)). %%==================================================================== %% jose_jwk_kty callbacks %%==================================================================== generate_key(Seed = << _:?secretbytes/binary >>) -> {_PK, SK} = jose_curve25519:eddsa_keypair(Seed), {SK, #{}}; generate_key({okp, 'Ed25519', Seed = << _:?secretbytes/binary >>}) -> generate_key(Seed); generate_key({okp, 'Ed25519'}) -> {_PK, SK} = jose_curve25519:eddsa_keypair(), {SK, #{}}. generate_key(KTY, Fields) when is_binary(KTY) andalso (byte_size(KTY) =:= ?publickeybytes orelse byte_size(KTY) =:= ?secretkeybytes) -> {NewKTY, OtherFields} = generate_key({okp, 'Ed25519'}), {NewKTY, maps:merge(maps:remove(<<"kid">>, Fields), OtherFields)}. key_encryptor(KTY, Fields, Key) -> jose_jwk_kty:key_encryptor(KTY, Fields, Key). %%==================================================================== %% jose_jwk_use_sig callbacks %%==================================================================== sign(Message, ALG, SK = << _:?secretkeybytes/binary >>) when ALG =:= 'Ed25519' orelse ALG =:= 'EdDSA' -> jose_curve25519:ed25519_sign(Message, SK). signer(<< _:?secretkeybytes/binary >>, #{ <<"alg">> := ALG, <<"use">> := <<"sig">> }) -> #{ <<"alg">> => ALG }; signer(<< _:?secretkeybytes/binary >>, _Fields) -> #{ <<"alg">> => <<"EdDSA">> }. verifier(<< _:?publickeybytes/binary >>, #{ <<"alg">> := ALG, <<"use">> := <<"sig">> }) -> [ALG]; verifier(<< _:?secretbytes/binary, PK:?publickeybytes/binary >>, Fields) -> verifier(PK, Fields); verifier(<< _:?publickeybytes/binary >>, _Fields) -> [?crv, <<"EdDSA">>]. verify(Message, ALG, Signature, << _:?secretbytes/binary, PK:?publickeybytes/binary >>) when ALG =:= 'Ed25519' orelse ALG =:= 'EdDSA' -> verify(Message, ALG, Signature, PK); verify(Message, ALG, Signature, PK = << _:?publickeybytes/binary >>) when ALG =:= 'Ed25519' orelse ALG =:= 'EdDSA' -> jose_curve25519:ed25519_verify(Signature, Message, PK). %%==================================================================== %% API functions %%==================================================================== from_der(DERBinary) when is_binary(DERBinary) -> case jose_jwk_der:from_binary(DERBinary) of {?MODULE, {Key, Fields}} -> {Key, Fields} end. from_der(Password, DERBinary) when is_binary(DERBinary) -> case jose_jwk_der:from_binary(Password, DERBinary) of {?MODULE, {Key, Fields}} -> {Key, Fields} end. from_key(#'jose_EdDSA25519PrivateKey'{publicKey=#'jose_EdDSA25519PublicKey'{publicKey=Public}, privateKey=Secret}) -> {<< Secret/binary, Public/binary >>, #{}}; from_key(#'jose_EdDSA25519PublicKey'{publicKey=Public}) -> {Public, #{}}. from_okp({'Ed25519', SK = << Secret:?secretbytes/binary, PK:?publickeybytes/binary >>}) -> case jose_curve25519:eddsa_secret_to_public(Secret) of PK -> {SK, #{}}; _ -> erlang:error(badarg) end; from_okp({'Ed25519', PK = << _:?publickeybytes/binary >>}) -> {PK, #{}}. from_openssh_key({<<"ssh-ed25519">>, _PK, SK, Comment}) -> {KTY, OtherFields} = from_okp({'Ed25519', SK}), case Comment of <<>> -> {KTY, OtherFields}; _ -> {KTY, maps:merge(#{ <<"kid">> => Comment }, OtherFields)} end. from_pem(PEMBinary) when is_binary(PEMBinary) -> case jose_jwk_pem:from_binary(PEMBinary) of {?MODULE, {Key, Fields}} -> {Key, Fields}; PEMError -> PEMError end. from_pem(Password, PEMBinary) when is_binary(PEMBinary) -> case jose_jwk_pem:from_binary(Password, PEMBinary) of {?MODULE, {Key, Fields}} -> {Key, Fields}; PEMError -> PEMError end. to_der(SK = << _:?secretkeybytes/binary >>) -> EdDSA25519PrivateKey = to_key(SK), jose_public_key:der_encode('EdDSA25519PrivateKey', EdDSA25519PrivateKey); to_der(PK = << _:?publickeybytes/binary >>) -> EdDSA25519PublicKey = to_key(PK), jose_public_key:der_encode('EdDSA25519PublicKey', EdDSA25519PublicKey). to_der(Password, SK = << _:?secretkeybytes/binary >>) -> EdDSA25519PrivateKey = to_key(SK), jose_jwk_der:to_binary(Password, 'EdDSA25519PrivateKey', EdDSA25519PrivateKey); to_der(Password, PK = << _:?publickeybytes/binary >>) -> EdDSA25519PublicKey = to_key(PK), jose_jwk_der:to_binary(Password, 'EdDSA25519PublicKey', EdDSA25519PublicKey). to_okp(SK = << _:?secretkeybytes/binary >>) -> {'Ed25519', SK}; to_okp(PK = << _:?publickeybytes/binary >>) -> {'Ed25519', PK}. to_openssh_key(SK = << _:?secretbytes/binary, PK:?publickeybytes/binary >>, F) -> Comment = maps:get(<<"kid">>, F, <<>>), jose_jwk_openssh_key:to_binary([[{{<<"ssh-ed25519">>, PK}, {<<"ssh-ed25519">>, PK, SK, Comment}}]]). to_pem(SK = << _:?secretkeybytes/binary >>) -> EdDSA25519PrivateKey = to_key(SK), PEMEntry = jose_public_key:pem_entry_encode('EdDSA25519PrivateKey', EdDSA25519PrivateKey), jose_public_key:pem_encode([PEMEntry]); to_pem(PK = << _:?publickeybytes/binary >>) -> EdDSA25519PublicKey = to_key(PK), PEMEntry = jose_public_key:pem_entry_encode('EdDSA25519PublicKey', EdDSA25519PublicKey), jose_public_key:pem_encode([PEMEntry]). to_pem(Password, SK = << _:?secretkeybytes/binary >>) -> EdDSA25519PrivateKey = to_key(SK), jose_jwk_pem:to_binary(Password, 'PrivateKeyInfo', EdDSA25519PrivateKey); to_pem(Password, PK = << _:?publickeybytes/binary >>) -> EdDSA25519PublicKey = to_key(PK), jose_jwk_pem:to_binary(Password, 'EdDSA25519PublicKey', EdDSA25519PublicKey). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jwk/jose_jwk_use_enc.erl0000664000232200023220000000312515267717064024545 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 16 Mar 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwk_use_enc). -callback block_encryptor(KTY, Fields) -> JWEMap when KTY :: any(), Fields :: map(), JWEMap :: map(). -callback decrypt_private(CipherText, Options, KTY) -> PlainText when CipherText :: iodata(), Options :: any(), KTY :: any(), PlainText :: iodata(). -callback derive_key(KTY) -> DerivedKey when KTY :: any(), DerivedKey :: iodata(). -callback derive_key(OtherKTY, KTY) -> DerivedKey when OtherKTY :: any(), KTY :: any(), DerivedKey :: iodata(). -callback encrypt_public(PlainText, Options, KTY) -> CipherText when PlainText :: iodata(), Options :: any(), KTY :: any(), CipherText :: iodata(). -optional_callbacks([decrypt_private/3]). -optional_callbacks([derive_key/1]). -optional_callbacks([derive_key/2]). -optional_callbacks([encrypt_public/3]). %%==================================================================== %% API functions %%==================================================================== %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jwk/jose_jwk_openssh_key.erl0000664000232200023220000001652115267717064025457 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc Private key format for OpenSSH %%% See https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key %%% @end %%% Created : 16 Jan 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwk_openssh_key). %% API -export([from_binary/1]). -export([to_binary/1]). %% Macros -define(AUTH_MAGIC, "openssh-key-v1"). -define(OPENSSH_HEAD, "-----BEGIN OPENSSH PRIVATE KEY-----"). -define(OPENSSH_TAIL, "-----END OPENSSH PRIVATE KEY-----"). %%==================================================================== %% API %%==================================================================== from_binary(Binary) when is_binary(Binary) -> parse_keys(Binary, []). to_binary(List) when is_list(List) -> to_binary(List, []). %%%------------------------------------------------------------------- %%% Internal encode functions %%%------------------------------------------------------------------- to_binary([KeyList | List], Acc) when is_list(KeyList) -> to_binary(List, [write_keylist(lists:unzip(KeyList)) | Acc]); to_binary([], Acc) -> iolist_to_binary([[?OPENSSH_HEAD, $\n, chunk(jose_base64:encode(Keys), 70, []), ?OPENSSH_TAIL, $\n] || Keys <- lists:reverse(Acc)]). chunk(Bin, Size, Chunks) when byte_size(Bin) > Size -> << Chunk:Size/binary, Rest/binary >> = Bin, chunk(Rest, Size, [[Chunk, $\n] | Chunks]); chunk(Bin, _Size, Chunks) -> lists:reverse([[Bin, $\n] | Chunks]). write_keylist({PKs, SKs}) when length(PKs) =:= length(SKs) -> N = length(PKs), PKBin = write_publickeys(PKs, []), SKBin = write_secretkeys(SKs, []), Check = crypto:strong_rand_bytes(4), Unpadded = << Check:4/binary, Check:4/binary, SKBin/binary >>, Padded = add_padding(Unpadded, 0), CipherName = <<"none">>, CipherNameLen = byte_size(CipherName), KDFName = <<"none">>, KDFNameLen = byte_size(KDFName), KDFOptions = <<>>, KDFOptionsLen = byte_size(KDFOptions), PaddedLen = byte_size(Padded), << ?AUTH_MAGIC, 16#00, CipherNameLen:32/unsigned-big-integer-unit:1, CipherName:CipherNameLen/binary, KDFNameLen:32/unsigned-big-integer-unit:1, KDFName:KDFNameLen/binary, KDFOptionsLen:32/unsigned-big-integer-unit:1, KDFOptions:KDFOptionsLen/binary, N:32/unsigned-big-integer-unit:1, PKBin/binary, PaddedLen:32/unsigned-big-integer-unit:1, Padded:PaddedLen/binary >>. write_publickeys([PK | PKs], Acc) when is_binary(PK) -> PKSize = byte_size(PK), write_publickeys(PKs, [<< PKSize:32/unsigned-big-integer-unit:1, PK:PKSize/binary >> | Acc]); write_publickeys([{Type, Key} | PKs], Acc) -> TypeLen = byte_size(Type), KeyLen = byte_size(Key), PK = << TypeLen:32/unsigned-big-integer-unit:1, Type:TypeLen/binary, KeyLen:32/unsigned-big-integer-unit:1, Key:KeyLen/binary >>, write_publickeys([PK | PKs], Acc); write_publickeys([], Acc) -> iolist_to_binary(lists:reverse(Acc)). write_secretkeys([{Type, PK, SK, Comment} | SKs], Acc) -> TypeLen = byte_size(Type), PKLen = byte_size(PK), SKLen = byte_size(SK), CommentLen = byte_size(Comment), SecretKey = << TypeLen:32/unsigned-big-integer-unit:1, Type:TypeLen/binary, PKLen:32/unsigned-big-integer-unit:1, PK:PKLen/binary, SKLen:32/unsigned-big-integer-unit:1, SK:SKLen/binary, CommentLen:32/unsigned-big-integer-unit:1, Comment:CommentLen/binary >>, write_secretkeys(SKs, [SecretKey | Acc]); write_secretkeys([], Acc) -> iolist_to_binary(lists:reverse(Acc)). add_padding(U, P) when (byte_size(U) + P) rem 8 =/= 0 -> add_padding(U, P + 1); add_padding(U, P) -> << U/binary, (binary:list_to_bin(lists:seq(1, P)))/binary >>. %%%------------------------------------------------------------------- %%% Internal decode functions %%%------------------------------------------------------------------- %% @private parse_keys(<< ?OPENSSH_HEAD, SoFar/binary >>, Acc) -> case parse_key(SoFar, <<>>) of {Key, Rest} -> parse_keys(Rest, [Key | Acc]); Rest -> parse_keys(Rest, Acc) end; parse_keys(<< _, Rest/binary >>, Acc) -> parse_keys(Rest, Acc); parse_keys(<<>>, Acc) -> lists:reverse(Acc). %% @private parse_key(<< W, Rest/binary >>, Body) when W =:= $\r orelse W =:= $\n orelse W =:= $\s orelse W =:= $\t -> parse_key(Rest, Body); parse_key(<< ?OPENSSH_TAIL, Rest/binary >>, Body) -> case parse_key(jose_base64:'decode!'(Body)) of {true, Key} -> {Key, Rest}; false -> Rest end; parse_key(<< C, Rest/binary >>, Body) -> parse_key(Rest, << Body/binary, C >>); parse_key(<<>>, _Body) -> <<>>. %% @private parse_key(<< ?AUTH_MAGIC, 16#00, CipherNameLen:32/unsigned-big-integer-unit:1, CipherName:CipherNameLen/binary, KDFNameLen:32/unsigned-big-integer-unit:1, KDFName:KDFNameLen/binary, KDFOptionsLen:32/unsigned-big-integer-unit:1, KDFOptions:KDFOptionsLen/binary, N:32/unsigned-big-integer-unit:1, SoFar/binary >>) -> case parse_publickeys(SoFar, N, []) of {true, PKs, << EncryptedLen:32/unsigned-big-integer-unit:1, Encrypted:EncryptedLen/binary >>} -> Header = {CipherName, KDFName, KDFOptions, N}, case maybe_parse_secretkeys(Header, PKs, Encrypted) of {true, Key} -> {true, Key}; false -> {true, {Header, PKs, Encrypted}} end; {true, _PKs, _BadEncrypted} -> false; false -> false end; parse_key(<< _, Rest/binary >>) -> parse_key(Rest); parse_key(<<>>) -> false. %% @private parse_publickeys(Rest, 0, PKs) -> {true, lists:reverse(PKs), Rest}; parse_publickeys(<< PKLen:32/unsigned-big-integer-unit:1, PK:PKLen/binary, Rest/binary >>, N, PKs) -> case parse_publickey(PK) of {true, Type, Key} -> parse_publickeys(Rest, N - 1, [{Type, Key} | PKs]); false -> parse_publickeys(Rest, N - 1, [PK | PKs]) end; parse_publickeys(_Binary, _N, _PKs) -> false. %% @private parse_publickey(<< TypeLen:32/unsigned-big-integer-unit:1, Type:TypeLen/binary, KeyLen:32/unsigned-big-integer-unit:1, Key:KeyLen/binary >>) -> {true, Type, Key}; parse_publickey(_Binary) -> false. %% @private maybe_parse_secretkeys({<<"none">>, <<"none">>, <<>>, N}, PKs, << Check:4/binary, Check:4/binary, SoFar/binary >>) -> case parse_secretkeys(del_padding(SoFar), N, []) of {true, SKs} -> {true, lists:zip(PKs, SKs)}; false -> false end; maybe_parse_secretkeys(_Header, _PKs, _Binary) -> false. %% @private parse_secretkeys(<<>>, 0, SKs) -> {true, lists:reverse(SKs)}; parse_secretkeys(<< TypeLen:32/unsigned-big-integer-unit:1, Type:TypeLen/binary, PKLen:32/unsigned-big-integer-unit:1, PK:PKLen/binary, SKLen:32/unsigned-big-integer-unit:1, SK:SKLen/binary, CommentLen:32/unsigned-big-integer-unit:1, Comment:CommentLen/binary, Rest/binary >>, N, SKs) -> parse_secretkeys(Rest, N - 1, [{Type, PK, SK, Comment} | SKs]); parse_secretkeys(_Binary, _N, _SKs) -> false. %% @private del_padding(<<>>) -> <<>>; del_padding(Padded) when is_binary(Padded) -> Padding = binary:last(Padded), case Padding > byte_size(Padded) of true -> <<>>; false -> del_padding(Padded, Padding) end. %% @private del_padding(Padded, 0) -> Padded; del_padding(Padded, Padding) -> case binary:last(Padded) of Padding -> del_padding(binary:part(Padded, 0, byte_size(Padded) - 1), Padding - 1); _ -> <<>> end. potatosalad-erlang-jose-30ac019/src/jwk/jose_jwk_kty_okp_x25519.erl0000664000232200023220000002163615267717064025550 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 15 Jan 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwk_kty_okp_x25519). -behaviour(jose_jwk). -behaviour(jose_jwk_kty). -behaviour(jose_jwk_use_enc). -include("jose_public_key.hrl"). %% jose_jwk callbacks -export([from_map/1]). -export([to_key/1]). -export([to_map/2]). -export([to_public_map/2]). -export([to_thumbprint_map/2]). %% jose_jwk_kty callbacks -export([generate_key/1]). -export([generate_key/2]). -export([key_encryptor/3]). %% jose_jwk_use_enc callbacks -export([block_encryptor/2]). -export([derive_key/2]). %% API -export([from_der/1]). -export([from_der/2]). -export([from_key/1]). -export([from_okp/1]). -export([from_openssh_key/1]). -export([from_pem/1]). -export([from_pem/2]). -export([to_der/1]). -export([to_der/2]). -export([to_okp/1]). -export([to_openssh_key/2]). -export([to_pem/1]). -export([to_pem/2]). %% Macros -define(crv, <<"X25519">>). -define(secretbytes, 32). -define(publickeybytes, 32). -define(secretkeybytes, 64). %% Types -type publickey() :: << _:256 >>. -type secretkey() :: << _:512 >>. -type key() :: publickey() | secretkey(). -export_type([key/0]). %%==================================================================== %% jose_jwk callbacks %%==================================================================== from_map(F = #{ <<"kty">> := <<"OKP">>, <<"crv">> := ?crv, <<"d">> := D, <<"x">> := X }) -> << Secret:?secretbytes/binary >> = jose_jwa_base64url:decode(D), << PK:?publickeybytes/binary >> = jose_jwa_base64url:decode(X), SK = << Secret/binary, PK/binary >>, {SK, maps:without([<<"crv">>, <<"d">>, <<"kty">>, <<"x">>], F)}; from_map(F = #{ <<"kty">> := <<"OKP">>, <<"crv">> := ?crv, <<"x">> := X }) -> << PK:?publickeybytes/binary >> = jose_jwa_base64url:decode(X), {PK, maps:without([<<"crv">>, <<"kty">>, <<"x">>], F)}. to_key(<< PublicKey:?publickeybytes/binary >>) -> #'jose_X25519PublicKey'{ publicKey = PublicKey }; to_key(<< PrivateKey:?secretbytes/binary, PublicKey:?publickeybytes/binary >>) -> #'jose_X25519PrivateKey'{ publicKey = #'jose_X25519PublicKey'{ publicKey = PublicKey }, privateKey = PrivateKey }. to_map(PK = << _:?publickeybytes/binary >>, F) -> F#{ <<"crv">> => ?crv, <<"kty">> => <<"OKP">>, <<"x">> => jose_jwa_base64url:encode(PK) }; to_map(<< Secret:?secretbytes/binary, PK:?publickeybytes/binary >>, F) -> F#{ <<"crv">> => ?crv, <<"d">> => jose_jwa_base64url:encode(Secret), <<"kty">> => <<"OKP">>, <<"x">> => jose_jwa_base64url:encode(PK) }. to_public_map(PK = << _:?publickeybytes/binary >>, F) -> to_map(PK, F); to_public_map(<< _:?secretbytes/binary, PK:?publickeybytes/binary >>, F) -> to_public_map(PK, F). to_thumbprint_map(K, F) -> maps:with([<<"crv">>, <<"kty">>, <<"x">>], to_public_map(K, F)). %%==================================================================== %% jose_jwk_kty callbacks %%==================================================================== generate_key(Seed = << _:?secretbytes/binary >>) -> {PK, SK} = jose_curve25519:x25519_keypair(Seed), {<< SK/binary, PK/binary >>, #{}}; generate_key({okp, 'X25519', Seed = << _:?secretbytes/binary >>}) -> generate_key(Seed); generate_key({okp, 'X25519'}) -> {PK, SK} = jose_curve25519:x25519_keypair(), {<< SK/binary, PK/binary >>, #{}}. generate_key(KTY, Fields) when is_binary(KTY) andalso (byte_size(KTY) =:= ?publickeybytes orelse byte_size(KTY) =:= ?secretkeybytes) -> {NewKTY, OtherFields} = generate_key({okp, 'X25519'}), {NewKTY, maps:merge(maps:remove(<<"kid">>, Fields), OtherFields)}. key_encryptor(KTY, Fields, Key) -> jose_jwk_kty:key_encryptor(KTY, Fields, Key). %%==================================================================== %% jose_jwk_use_enc callbacks %%==================================================================== block_encryptor(_KTY, Fields=#{ <<"alg">> := ALG, <<"enc">> := ENC, <<"use">> := <<"enc">> }) -> Folder = fun (K, V, F) when K =:= <<"apu">> orelse K =:= <<"apv">> orelse K =:= <<"epk">> orelse K =:= <<"skid">> -> maps:put(K, V, F); (_K, _V, F) -> F end, maps:fold(Folder, #{ <<"alg">> => ALG, <<"enc">> => ENC }, Fields); block_encryptor(KTY, Fields=#{ <<"alg">> := <<"ECDH-1PU", _/binary>> }) -> block_encryptor(KTY, maps:merge(Fields, #{ <<"enc">> => case jose_jwa:is_block_cipher_supported({aes_gcm, 128}) of false -> <<"A128CBC-HS256">>; true -> <<"A128GCM">> end, <<"use">> => <<"enc">> })); block_encryptor(KTY, Fields=#{ <<"alg">> := <<"ECDH-SS", _/binary>> }) -> block_encryptor(KTY, maps:merge(Fields, #{ <<"enc">> => case jose_jwa:is_block_cipher_supported({aes_gcm, 128}) of false -> <<"A128CBC-HS256">>; true -> <<"A128GCM">> end, <<"use">> => <<"enc">> })); block_encryptor(KTY, Fields) -> block_encryptor(KTY, maps:merge(Fields, #{ <<"alg">> => <<"ECDH-ES">>, <<"enc">> => case jose_jwa:is_block_cipher_supported({aes_gcm, 128}) of false -> <<"A128CBC-HS256">>; true -> <<"A128GCM">> end, <<"use">> => <<"enc">> })). derive_key(<< _:?secretbytes/binary, PK:?publickeybytes/binary >>, SK = << _:?secretkeybytes/binary >>) -> derive_key(PK, SK); derive_key(PK = << _:?publickeybytes/binary >>, << Secret:?secretbytes/binary, _:?publickeybytes/binary >>) -> jose_curve25519:x25519_shared_secret(Secret, PK). %%==================================================================== %% API functions %%==================================================================== from_der(DERBinary) when is_binary(DERBinary) -> case jose_jwk_der:from_binary(DERBinary) of {?MODULE, {Key, Fields}} -> {Key, Fields} end. from_der(Password, DERBinary) when is_binary(DERBinary) -> case jose_jwk_der:from_binary(Password, DERBinary) of {?MODULE, {Key, Fields}} -> {Key, Fields} end. from_key(#'jose_X25519PrivateKey'{publicKey=#'jose_X25519PublicKey'{publicKey=Public}, privateKey=Secret}) -> {<< Secret/binary, Public/binary >>, #{}}; from_key(#'jose_X25519PublicKey'{publicKey=Public}) -> {Public, #{}}. from_okp({'X25519', SK = << Secret:?secretbytes/binary, PK:?publickeybytes/binary >>}) -> case jose_curve25519:x25519_secret_to_public(Secret) of PK -> {SK, #{}}; _ -> erlang:error(badarg) end; from_okp({'X25519', PK = << _:?publickeybytes/binary >>}) -> {PK, #{}}. from_openssh_key({<<"ssh-x25519">>, _PK, SK, Comment}) -> {KTY, OtherFields} = from_okp({'X25519', SK}), case Comment of <<>> -> {KTY, OtherFields}; _ -> {KTY, maps:merge(#{ <<"kid">> => Comment }, OtherFields)} end. from_pem(PEMBinary) when is_binary(PEMBinary) -> case jose_jwk_pem:from_binary(PEMBinary) of {?MODULE, {Key, Fields}} -> {Key, Fields}; PEMError -> PEMError end. from_pem(Password, PEMBinary) when is_binary(PEMBinary) -> case jose_jwk_pem:from_binary(Password, PEMBinary) of {?MODULE, {Key, Fields}} -> {Key, Fields}; PEMError -> PEMError end. to_der(SK = << _:?secretkeybytes/binary >>) -> X25519PrivateKey = to_key(SK), jose_public_key:der_encode('X25519PrivateKey', X25519PrivateKey); to_der(PK = << _:?publickeybytes/binary >>) -> X25519PublicKey = to_key(PK), jose_public_key:der_encode('X25519PublicKey', X25519PublicKey). to_der(Password, SK = << _:?secretkeybytes/binary >>) -> X25519PrivateKey = to_key(SK), jose_jwk_der:to_binary(Password, 'X25519PrivateKey', X25519PrivateKey); to_der(Password, PK = << _:?publickeybytes/binary >>) -> X25519PublicKey = to_key(PK), jose_jwk_der:to_binary(Password, 'X25519PublicKey', X25519PublicKey). to_okp(SK = << _:?secretkeybytes/binary >>) -> {'X25519', SK}; to_okp(PK = << _:?publickeybytes/binary >>) -> {'X25519', PK}. to_openssh_key(SK = << _:?secretbytes/binary, PK:?publickeybytes/binary >>, F) -> Comment = maps:get(<<"kid">>, F, <<>>), jose_jwk_openssh_key:to_binary([[{{<<"ssh-x25519">>, PK}, {<<"ssh-x25519">>, PK, SK, Comment}}]]). to_pem(SK = << _:?secretkeybytes/binary >>) -> X25519PrivateKey = to_key(SK), PEMEntry = jose_public_key:pem_entry_encode('X25519PrivateKey', X25519PrivateKey), jose_public_key:pem_encode([PEMEntry]); to_pem(PK = << _:?publickeybytes/binary >>) -> X25519PublicKey = to_key(PK), PEMEntry = jose_public_key:pem_entry_encode('X25519PublicKey', X25519PublicKey), jose_public_key:pem_encode([PEMEntry]). to_pem(Password, SK = << _:?secretkeybytes/binary >>) -> X25519PrivateKey = to_key(SK), jose_jwk_pem:to_binary(Password, 'PrivateKeyInfo', X25519PrivateKey); to_pem(Password, PK = << _:?publickeybytes/binary >>) -> X25519PublicKey = to_key(PK), jose_jwk_pem:to_binary(Password, 'X25519PublicKey', X25519PublicKey). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jwk/jose_jwk_oct.erl0000664000232200023220000000213615267717064023712 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 18 Aug 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwk_oct). -callback from_oct(OCTBinary) -> {KTY, Fields} when OCTBinary :: binary(), KTY :: any(), Fields :: map(). -callback to_oct(KTY) -> OCTBinary when KTY :: any(), OCTBinary :: binary(). %% API -export([from_binary/1]). %%==================================================================== %% API functions %%==================================================================== from_binary(OCTBinary) when is_binary(OCTBinary) -> jose_jwk_kty:from_oct(OCTBinary). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jwk/jose_jwk_use_sig.erl0000664000232200023220000000244615267717064024567 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 16 Mar 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwk_use_sig). -callback sign(Message, Options, KTY) -> Signature when Message :: iodata(), Options :: any(), KTY :: any(), Signature :: iodata(). -callback signer(KTY, Fields) -> JWSMap when KTY :: any(), Fields :: map(), JWSMap :: map(). -callback verifier(KTY, Fields) -> [JWSALG] when KTY :: any(), Fields :: map(), JWSALG :: iodata(). -callback verify(Message, Options, Signature, KTY) -> boolean() when Message :: iodata(), Options :: any(), Signature :: iodata(), KTY :: any(). %%==================================================================== %% API functions %%==================================================================== %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jwk/jose_jwk_kty.erl0000664000232200023220000002000415267717064023726 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 24 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwk_kty). -include("jose_public_key.hrl"). -callback generate_key(Parameters) -> KTY when Parameters :: any(), KTY :: any(). -callback generate_key(KTY, Fields) -> NewKTY when KTY :: any(), Fields :: map(), NewKTY :: any(). -callback key_encryptor(KTY, Fields, Key) -> JWEMap when KTY :: any(), Fields :: map(), Key :: any(), JWEMap :: map(). %% API -export([from_key/1]). -export([from_oct/1]). -export([generate_key/1]). -export([key_encryptor/3]). -define(KTY_EC_MODULE, jose_jwk_kty_ec). -define(KTY_OCT_MODULE, jose_jwk_kty_oct). -define(KTY_RSA_MODULE, jose_jwk_kty_rsa). -define(KTY_OKP_Ed25519_MODULE, jose_jwk_kty_okp_ed25519). -define(KTY_OKP_Ed25519ph_MODULE, jose_jwk_kty_okp_ed25519ph). -define(KTY_OKP_X25519_MODULE, jose_jwk_kty_okp_x25519). -define(KTY_OKP_Ed448_MODULE, jose_jwk_kty_okp_ed448). -define(KTY_OKP_Ed448ph_MODULE, jose_jwk_kty_okp_ed448ph). -define(KTY_OKP_X448_MODULE, jose_jwk_kty_okp_x448). %%==================================================================== %% API functions %%==================================================================== from_key(ECPrivateKey=#'ECPrivateKey'{}) -> {?KTY_EC_MODULE, ?KTY_EC_MODULE:from_key(ECPrivateKey)}; from_key({#'ECPoint'{point = PublicKey}, {namedCurve, ?'jose_id-EdDSA25519'}}) -> {?KTY_OKP_Ed25519_MODULE, ?KTY_OKP_Ed25519_MODULE:from_key(#'jose_EdDSA25519PublicKey'{ publicKey = PublicKey })}; from_key({#'ECPoint'{point = PublicKey}, {namedCurve, ?'jose_id-EdDSA448'}}) -> {?KTY_OKP_Ed448_MODULE, ?KTY_OKP_Ed448_MODULE:from_key(#'jose_EdDSA448PublicKey'{ publicKey = PublicKey })}; from_key(ECPublicKey={#'ECPoint'{}, _}) -> {?KTY_EC_MODULE, ?KTY_EC_MODULE:from_key(ECPublicKey)}; from_key(EdDSA25519PrivateKey=#'jose_EdDSA25519PrivateKey'{}) -> {?KTY_OKP_Ed25519_MODULE, ?KTY_OKP_Ed25519_MODULE:from_key(EdDSA25519PrivateKey)}; from_key(EdDSA25519PublicKey=#'jose_EdDSA25519PublicKey'{}) -> {?KTY_OKP_Ed25519_MODULE, ?KTY_OKP_Ed25519_MODULE:from_key(EdDSA25519PublicKey)}; from_key(EdDSA448PrivateKey=#'jose_EdDSA448PrivateKey'{}) -> {?KTY_OKP_Ed448_MODULE, ?KTY_OKP_Ed448_MODULE:from_key(EdDSA448PrivateKey)}; from_key(EdDSA448PublicKey=#'jose_EdDSA448PublicKey'{}) -> {?KTY_OKP_Ed448_MODULE, ?KTY_OKP_Ed448_MODULE:from_key(EdDSA448PublicKey)}; from_key(X25519PrivateKey=#'jose_X25519PrivateKey'{}) -> {?KTY_OKP_X25519_MODULE, ?KTY_OKP_X25519_MODULE:from_key(X25519PrivateKey)}; from_key(X25519PublicKey=#'jose_X25519PublicKey'{}) -> {?KTY_OKP_X25519_MODULE, ?KTY_OKP_X25519_MODULE:from_key(X25519PublicKey)}; from_key(X448PrivateKey=#'jose_X448PrivateKey'{}) -> {?KTY_OKP_X448_MODULE, ?KTY_OKP_X448_MODULE:from_key(X448PrivateKey)}; from_key(X448PublicKey=#'jose_X448PublicKey'{}) -> {?KTY_OKP_X448_MODULE, ?KTY_OKP_X448_MODULE:from_key(X448PublicKey)}; from_key(RSAPrivateKey=#'RSAPrivateKey'{}) -> {?KTY_RSA_MODULE, ?KTY_RSA_MODULE:from_key(RSAPrivateKey)}; from_key(RSAPublicKey=#'RSAPublicKey'{}) -> {?KTY_RSA_MODULE, ?KTY_RSA_MODULE:from_key(RSAPublicKey)}; from_key(#'PrivateKeyInfo'{privateKeyAlgorithm=#'PrivateKeyInfo_privateKeyAlgorithm'{algorithm=?rsaEncryption}, privateKey=PrivateKey}) -> from_key(public_key:der_decode('RSAPrivateKey', PrivateKey)); from_key(#'PrivateKeyInfo'{privateKeyAlgorithm=#'PrivateKeyInfo_privateKeyAlgorithm'{algorithm=?'id-ecPublicKey'}, privateKey=PrivateKey}) -> from_key(public_key:der_decode('ECPrivateKey', PrivateKey)); %% OTP 29+ from_key(#'PrivateKeyInfo'{privateKeyAlgorithm={'PrivateKeyAlgorithmIdentifier', ?rsaEncryption, _}, privateKey=PrivateKey}) -> from_key(public_key:der_decode('RSAPrivateKey', PrivateKey)); from_key(#'PrivateKeyInfo'{privateKeyAlgorithm={'PrivateKeyAlgorithmIdentifier', ?'id-ecPublicKey', _}, privateKey=PrivateKey}) -> from_key(public_key:der_decode('ECPrivateKey', PrivateKey)); from_key(UnknownKey) -> {error, {unknown_key, UnknownKey}}. from_oct(OCTBinary) when is_binary(OCTBinary) -> {?KTY_OCT_MODULE, ?KTY_OCT_MODULE:from_oct(OCTBinary)}; from_oct(UnknownKey) -> {error, {unknown_key, UnknownKey}}. generate_key(P=#'ECParameters'{}) -> jose_jwk:generate_key({#{ kty => ?KTY_EC_MODULE }, P}); generate_key(P=#'ECPrivateKey'{}) -> jose_jwk:generate_key({#{ kty => ?KTY_EC_MODULE }, P}); generate_key(P={#'ECPoint'{}, _}) -> jose_jwk:generate_key({#{ kty => ?KTY_EC_MODULE }, P}); generate_key(P={namedCurve, _}) -> jose_jwk:generate_key({#{ kty => ?KTY_EC_MODULE }, P}); generate_key(P) when is_atom(P) -> jose_jwk:generate_key({#{ kty => ?KTY_EC_MODULE }, P}); generate_key(P=#'RSAPrivateKey'{}) -> jose_jwk:generate_key({#{ kty => ?KTY_RSA_MODULE }, P}); generate_key(P=#'RSAPublicKey'{}) -> jose_jwk:generate_key({#{ kty => ?KTY_RSA_MODULE }, P}); generate_key(P) when is_integer(P) -> jose_jwk:generate_key({#{ kty => ?KTY_OCT_MODULE }, P}); generate_key(P={ec, #'ECParameters'{}}) -> jose_jwk:generate_key({#{ kty => ?KTY_EC_MODULE }, P}); generate_key(P={ec, #'ECPrivateKey'{}}) -> jose_jwk:generate_key({#{ kty => ?KTY_EC_MODULE }, P}); generate_key(P={ec, {#'ECPoint'{}, _}}) -> jose_jwk:generate_key({#{ kty => ?KTY_EC_MODULE }, P}); generate_key(P={ec, {namedCurve, _}}) -> jose_jwk:generate_key({#{ kty => ?KTY_EC_MODULE }, P}); generate_key(P={ec, Atom}) when is_atom(Atom) -> jose_jwk:generate_key({#{ kty => ?KTY_EC_MODULE }, P}); generate_key(P={ec, Binary}) when is_binary(Binary) -> jose_jwk:generate_key({#{ kty => ?KTY_EC_MODULE }, P}); generate_key(P={oct, Size}) when is_integer(Size) -> jose_jwk:generate_key({#{ kty => ?KTY_OCT_MODULE }, P}); generate_key(P={okp, 'Ed25519'}) -> jose_jwk:generate_key({#{ kty => ?KTY_OKP_Ed25519_MODULE }, P}); generate_key(P={okp, 'Ed25519ph'}) -> jose_jwk:generate_key({#{ kty => ?KTY_OKP_Ed25519ph_MODULE }, P}); generate_key(P={okp, 'X25519'}) -> jose_jwk:generate_key({#{ kty => ?KTY_OKP_X25519_MODULE }, P}); generate_key(P={okp, 'Ed25519', Seed}) when is_binary(Seed) -> jose_jwk:generate_key({#{ kty => ?KTY_OKP_Ed25519_MODULE }, P}); generate_key(P={okp, 'Ed25519ph', Seed}) when is_binary(Seed) -> jose_jwk:generate_key({#{ kty => ?KTY_OKP_Ed25519ph_MODULE }, P}); generate_key(P={okp, 'X25519', Seed}) when is_binary(Seed) -> jose_jwk:generate_key({#{ kty => ?KTY_OKP_X25519_MODULE }, P}); generate_key(P={okp, 'Ed448'}) -> jose_jwk:generate_key({#{ kty => ?KTY_OKP_Ed448_MODULE }, P}); generate_key(P={okp, 'Ed448ph'}) -> jose_jwk:generate_key({#{ kty => ?KTY_OKP_Ed448ph_MODULE }, P}); generate_key(P={okp, 'X448'}) -> jose_jwk:generate_key({#{ kty => ?KTY_OKP_X448_MODULE }, P}); generate_key(P={okp, 'Ed448', Seed}) when is_binary(Seed) -> jose_jwk:generate_key({#{ kty => ?KTY_OKP_Ed448_MODULE }, P}); generate_key(P={okp, 'Ed448ph', Seed}) when is_binary(Seed) -> jose_jwk:generate_key({#{ kty => ?KTY_OKP_Ed448ph_MODULE }, P}); generate_key(P={okp, 'X448', Seed}) when is_binary(Seed) -> jose_jwk:generate_key({#{ kty => ?KTY_OKP_X448_MODULE }, P}); generate_key(P={rsa, ModulusSize}) when is_integer(ModulusSize) -> jose_jwk:generate_key({#{ kty => ?KTY_RSA_MODULE }, P}); generate_key(P={rsa, ModulusSize, ExponentSize}) when is_integer(ModulusSize) andalso is_integer(ExponentSize) -> jose_jwk:generate_key({#{ kty => ?KTY_RSA_MODULE }, P}). key_encryptor(_KTY, _Fields, Key) when is_binary(Key) -> #{ <<"alg">> => <<"PBES2-HS256+A128KW">>, <<"cty">> => <<"jwk+json">>, <<"enc">> => case jose_jwa:is_block_cipher_supported({aes_gcm, 128}) of false -> <<"A128CBC-HS256">>; true -> <<"A128GCM">> end, <<"p2c">> => 4096, <<"p2s">> => jose_jwa_base64url:encode(crypto:strong_rand_bytes(16)) }. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jwk/jose_jwk_kty_okp_ed448ph.erl0000664000232200023220000001341215267717064026044 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 15 Jan 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwk_kty_okp_ed448ph). -behaviour(jose_jwk). -behaviour(jose_jwk_kty). -behaviour(jose_jwk_use_sig). %% jose_jwk callbacks -export([from_map/1]). -export([to_key/1]). -export([to_map/2]). -export([to_public_map/2]). -export([to_thumbprint_map/2]). %% jose_jwk_kty callbacks -export([generate_key/1]). -export([generate_key/2]). -export([key_encryptor/3]). %% jose_jwk_use_sig callbacks -export([sign/3]). -export([signer/2]). -export([verifier/2]). -export([verify/4]). %% API -export([from_okp/1]). -export([from_openssh_key/1]). -export([to_okp/1]). -export([to_openssh_key/2]). %% Macros -define(crv, <<"Ed448ph">>). -define(secretbytes, 57). -define(publickeybytes, 57). -define(secretkeybytes, 114). %% Types -type publickey() :: << _:456 >>. -type secretkey() :: << _:912 >>. -type key() :: publickey() | secretkey(). -export_type([key/0]). %%==================================================================== %% jose_jwk callbacks %%==================================================================== from_map(F = #{ <<"kty">> := <<"OKP">>, <<"crv">> := ?crv, <<"d">> := D, <<"x">> := X }) -> << Secret:?secretbytes/binary >> = jose_jwa_base64url:decode(D), << PK:?publickeybytes/binary >> = jose_jwa_base64url:decode(X), SK = << Secret/binary, PK/binary >>, {SK, maps:without([<<"crv">>, <<"d">>, <<"kty">>, <<"x">>], F)}; from_map(F = #{ <<"kty">> := <<"OKP">>, <<"crv">> := ?crv, <<"x">> := X }) -> << PK:?publickeybytes/binary >> = jose_jwa_base64url:decode(X), {PK, maps:without([<<"crv">>, <<"kty">>, <<"x">>], F)}. to_key(PK = << _:?publickeybytes/binary >>) -> PK; to_key(SK = << _:?secretkeybytes/binary >>) -> SK. to_map(PK = << _:?publickeybytes/binary >>, F) -> F#{ <<"crv">> => ?crv, <<"kty">> => <<"OKP">>, <<"x">> => jose_jwa_base64url:encode(PK) }; to_map(<< Secret:?secretbytes/binary, PK:?publickeybytes/binary >>, F) -> F#{ <<"crv">> => ?crv, <<"d">> => jose_jwa_base64url:encode(Secret), <<"kty">> => <<"OKP">>, <<"x">> => jose_jwa_base64url:encode(PK) }. to_public_map(PK = << _:?publickeybytes/binary >>, F) -> to_map(PK, F); to_public_map(<< _:?secretbytes/binary, PK:?publickeybytes/binary >>, F) -> to_public_map(PK, F). to_thumbprint_map(K, F) -> maps:with([<<"crv">>, <<"kty">>, <<"x">>], to_public_map(K, F)). %%==================================================================== %% jose_jwk_kty callbacks %%==================================================================== generate_key(Seed = << _:?secretbytes/binary >>) -> {_PK, SK} = jose_curve448:eddsa_keypair(Seed), {SK, #{}}; generate_key({okp, 'Ed448ph', Seed = << _:?secretbytes/binary >>}) -> generate_key(Seed); generate_key({okp, 'Ed448ph'}) -> {_PK, SK} = jose_curve448:eddsa_keypair(), {SK, #{}}. generate_key(KTY, Fields) when is_binary(KTY) andalso (byte_size(KTY) =:= ?publickeybytes orelse byte_size(KTY) =:= ?secretkeybytes) -> {NewKTY, OtherFields} = generate_key({okp, 'Ed448ph'}), {NewKTY, maps:merge(maps:remove(<<"kid">>, Fields), OtherFields)}. key_encryptor(KTY, Fields, Key) -> jose_jwk_kty:key_encryptor(KTY, Fields, Key). %%==================================================================== %% jose_jwk_use_sig callbacks %%==================================================================== sign(Message, ALG, SK = << _:?secretkeybytes/binary >>) when ALG =:= 'Ed448ph' orelse ALG =:= 'EdDSA' -> jose_curve448:ed448ph_sign(Message, SK). signer(<< _:?secretkeybytes/binary >>, #{ <<"alg">> := ALG, <<"use">> := <<"sig">> }) -> #{ <<"alg">> => ALG }; signer(<< _:?secretkeybytes/binary >>, _Fields) -> #{ <<"alg">> => <<"EdDSA">> }. verifier(<< _:?publickeybytes/binary >>, #{ <<"alg">> := ALG, <<"use">> := <<"sig">> }) -> [ALG]; verifier(<< _:?secretbytes/binary, PK:?publickeybytes/binary >>, Fields) -> verifier(PK, Fields); verifier(<< _:?publickeybytes/binary >>, _Fields) -> [?crv, <<"EdDSA">>]. verify(Message, ALG, Signature, << _:?secretbytes/binary, PK:?publickeybytes/binary >>) when ALG =:= 'Ed448ph' orelse ALG =:= 'EdDSA' -> verify(Message, ALG, Signature, PK); verify(Message, ALG, Signature, PK = << _:?publickeybytes/binary >>) when ALG =:= 'Ed448ph' orelse ALG =:= 'EdDSA' -> jose_curve448:ed448ph_verify(Signature, Message, PK). %%==================================================================== %% API functions %%==================================================================== from_okp({'Ed448ph', SK = << Secret:?secretbytes/binary, PK:?publickeybytes/binary >>}) -> case jose_curve448:eddsa_secret_to_public(Secret) of PK -> {SK, #{}}; _ -> erlang:error(badarg) end; from_okp({'Ed448ph', PK = << _:?publickeybytes/binary >>}) -> {PK, #{}}. from_openssh_key({<<"ssh-ed448ph">>, _PK, SK, Comment}) -> {KTY, OtherFields} = from_okp({'Ed448ph', SK}), case Comment of <<>> -> {KTY, OtherFields}; _ -> {KTY, maps:merge(#{ <<"kid">> => Comment }, OtherFields)} end. to_okp(SK = << _:?secretkeybytes/binary >>) -> {'Ed448ph', SK}; to_okp(PK = << _:?publickeybytes/binary >>) -> {'Ed448ph', PK}. to_openssh_key(SK = << _:?secretbytes/binary, PK:?publickeybytes/binary >>, F) -> Comment = maps:get(<<"kid">>, F, <<>>), jose_jwk_openssh_key:to_binary([[{{<<"ssh-ed448ph">>, PK}, {<<"ssh-ed448ph">>, PK, SK, Comment}}]]). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/base/0000775000232200023220000000000015267717064020643 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/src/base/jose_base64url.erl0000664000232200023220000024730615267717064024212 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc RFC 4648, Section 5: https://tools.ietf.org/html/rfc4648#section-5 %%% %%% @end %%% Created : 11 May 2017 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_base64url). -include("jose_base.hrl"). %% API -export([decode/1]). -export([decode/2]). -export(['decode!'/1]). -export(['decode!'/2]). -export([encode/1]). -export([encode/2]). -export([random/1]). -export([random/2]). % Macros -define(B64URL_TO_INT(C), case C of $A -> 16#00; $B -> 16#01; $C -> 16#02; $D -> 16#03; $E -> 16#04; $F -> 16#05; $G -> 16#06; $H -> 16#07; $I -> 16#08; $J -> 16#09; $K -> 16#0A; $L -> 16#0B; $M -> 16#0C; $N -> 16#0D; $O -> 16#0E; $P -> 16#0F; $Q -> 16#10; $R -> 16#11; $S -> 16#12; $T -> 16#13; $U -> 16#14; $V -> 16#15; $W -> 16#16; $X -> 16#17; $Y -> 16#18; $Z -> 16#19; $a -> 16#1A; $b -> 16#1B; $c -> 16#1C; $d -> 16#1D; $e -> 16#1E; $f -> 16#1F; $g -> 16#20; $h -> 16#21; $i -> 16#22; $j -> 16#23; $k -> 16#24; $l -> 16#25; $m -> 16#26; $n -> 16#27; $o -> 16#28; $p -> 16#29; $q -> 16#2A; $r -> 16#2B; $s -> 16#2C; $t -> 16#2D; $u -> 16#2E; $v -> 16#2F; $w -> 16#30; $x -> 16#31; $y -> 16#32; $z -> 16#33; $0 -> 16#34; $1 -> 16#35; $2 -> 16#36; $3 -> 16#37; $4 -> 16#38; $5 -> 16#39; $6 -> 16#3A; $7 -> 16#3B; $8 -> 16#3C; $9 -> 16#3D; $- -> 16#3E; $_ -> 16#3F end). %%%=================================================================== %%% API functions %%%=================================================================== decode(Input) when ?is_iodata(Input) -> decode(Input, #{}). decode(Input, Opts) when ?is_iodata(Input) andalso is_map(Opts) -> try 'decode!'(Input, Opts) of Output when is_binary(Output) -> {ok, Output} catch _:_ -> error end; decode(Input, Opts) when ?is_iodata(Input) andalso is_list(Opts) -> decode(Input, maps:from_list(Opts)). 'decode!'(Input) when ?is_iodata(Input) -> 'decode!'(Input, #{}). 'decode!'([], #{}) -> <<>>; 'decode!'(<<>>, #{}) -> <<>>; 'decode!'(Input, Opts) when ?is_iodata(Input) andalso is_map(Opts) -> Padding = maps:get('padding', Opts, nil), Size = erlang:iolist_size(Input), Offset = case Padding of _ when (Padding == false orelse Padding == nil) andalso Size =< 4 -> 0; _ when (Padding == false orelse Padding == nil) andalso (Size rem 4) =/= 0 -> Size - (Size rem 4); _ when (Padding == false orelse Padding == nil) -> Size - 4; _ when (Padding == true orelse Padding == nil) andalso Size >= 4 -> Size - 4; _ -> erlang:error({badarg, [Input, Opts]}) end, << Head0:Offset/binary, Tail0/binary >> = ?to_binary(Input), Head = << << (?B64URL_TO_INT(V)):6 >> || << V >> <= Head0 >>, Tail = case Padding of false -> case Tail0 of << T0:8, T1:8 >> -> << (?B64URL_TO_INT(T0)):6, (?B64URL_TO_INT(T1) bsr 4):2 >>; << T0:8, T1:8, T2:8 >> -> << (?B64URL_TO_INT(T0)):6, (?B64URL_TO_INT(T1)):6, (?B64URL_TO_INT(T2) bsr 2):4 >>; << T:4/binary >> -> << << (?B64URL_TO_INT(V)):6 >> || << V >> <= T >>; <<>> -> <<>>; _ -> erlang:error({badarg, [Input, Opts]}) end; nil -> case Tail0 of << T0:8, T1:8, $=, $= >> -> << (?B64URL_TO_INT(T0)):6, (?B64URL_TO_INT(T1) bsr 4):2 >>; << T0:8, T1:8, T2:8, $= >> -> << (?B64URL_TO_INT(T0)):6, (?B64URL_TO_INT(T1)):6, (?B64URL_TO_INT(T2) bsr 2):4 >>; << T0:8, T1:8 >> -> << (?B64URL_TO_INT(T0)):6, (?B64URL_TO_INT(T1) bsr 4):2 >>; << T0:8, T1:8, T2:8 >> -> << (?B64URL_TO_INT(T0)):6, (?B64URL_TO_INT(T1)):6, (?B64URL_TO_INT(T2) bsr 2):4 >>; << T:4/binary >> -> << << (?B64URL_TO_INT(V)):6 >> || << V >> <= T >>; <<>> -> <<>> end; true -> case Tail0 of << T0:8, T1:8, $=, $= >> -> << (?B64URL_TO_INT(T0)):6, (?B64URL_TO_INT(T1) bsr 4):2 >>; << T0:8, T1:8, T2:8, $= >> -> << (?B64URL_TO_INT(T0)):6, (?B64URL_TO_INT(T1)):6, (?B64URL_TO_INT(T2) bsr 2):4 >>; << T:4/binary >> -> << << (?B64URL_TO_INT(V)):6 >> || << V >> <= T >>; <<>> -> <<>>; _ -> erlang:error({badarg, [Input, Opts]}) end end, << Head/binary, Tail/binary >>; 'decode!'(Input, Opts) when ?is_iodata(Input) andalso is_list(Opts) -> 'decode!'(Input, maps:from_list(Opts)). encode(Input) when ?is_iodata(Input) -> encode(Input, #{}). encode(Input, Opts) when ?is_iodata(Input) andalso is_map(Opts) -> Padding = maps:get('padding', Opts, true), Offset = 6 * (erlang:iolist_size(Input) div 6), << Head:Offset/binary, Tail/binary >> = ?to_binary(Input), H = << << (encode_pair(V0)):16, (encode_pair(V1)):16, (encode_pair(V2)):16, (encode_pair(V3)):16 >> || << V0:12, V1:12, V2:12, V3:12 >> <= Head >>, {T, Pad} = case Tail of << T0:12, T1:12, T2:12, T3:4 >> -> {<< (encode_pair(T0)):16, (encode_pair(T1)):16, (encode_pair(T2)):16, (encode_char(T3 bsl 2)):8 >>, << $= >>}; << T0:12, T1:12, T2:8 >> -> {<< (encode_pair(T0)):16, (encode_pair(T1)):16, (encode_pair(T2 bsl 4)):16 >>, << $=, $= >>}; << T0:12, T1:12 >> -> {<< (encode_pair(T0)):16, (encode_pair(T1)):16 >>, <<>>}; << T0:12, T1:4 >> -> {<< (encode_pair(T0)):16, (encode_char(T1 bsl 2)):8 >>, <<>>}; << T0:8 >> -> {<< (encode_pair(T0 bsl 4)):16 >>, << $=, $= >>}; <<>> -> {<<>>, <<>>} end, case Padding of true -> << H/binary, T/binary, Pad/binary >>; false -> << H/binary, T/binary >>; _ -> erlang:error({badarg, [Input, Opts]}) end; encode(Input, Opts) when ?is_iodata(Input) andalso is_list(Opts) -> encode(Input, maps:from_list(Opts)). random(Bytes) when is_integer(Bytes) andalso Bytes >= 0 -> random(Bytes, #{}). random(0, Opts) when is_map(Opts) -> <<>>; random(Bytes, Opts) when (Bytes =:= 1) andalso is_map(Opts) -> erlang:error({badarg, [Bytes, Opts]}); random(Bytes, Opts) when is_integer(Bytes) andalso Bytes > 0 andalso is_map(Opts) -> Padding = maps:get('padding', Opts, true), R = (Bytes rem 4), Size = case Padding of true when R =:= 0 -> (Bytes * 3) div 4; false when R =:= 0 orelse R =:= 2 orelse R =:= 3 -> (Bytes * 3) div 4; _ -> erlang:error({badarg, [Bytes, Opts]}) end, Binary = crypto:strong_rand_bytes(Size), encode(Binary, Opts); random(Bytes, Opts) when is_integer(Bytes) andalso Bytes >= 0 andalso is_list(Opts) -> random(Bytes, maps:from_list(Opts)). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- -compile({inline, [{encode_char, 1}]}). %% @private encode_char(V) -> element(V + 1, { $A, $B, $C, $D, $E, $F, $G, $H, $I, $J, $K, $L, $M, $N, $O, $P, $Q, $R, $S, $T, $U, $V, $W, $X, $Y, $Z, $a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m, $n, $o, $p, $q, $r, $s, $t, $u, $v, $w, $x, $y, $z, $0, $1, $2, $3, $4, $5, $6, $7, $8, $9, $-, $_ }). -compile({inline, [{encode_pair, 1}]}). %% @private encode_pair(V) -> element(V + 1, { $\x{4141}, $\x{4142}, $\x{4143}, $\x{4144}, $\x{4145}, $\x{4146}, $\x{4147}, $\x{4148}, $\x{4149}, $\x{414A}, $\x{414B}, $\x{414C}, $\x{414D}, $\x{414E}, $\x{414F}, $\x{4150}, $\x{4151}, $\x{4152}, $\x{4153}, $\x{4154}, $\x{4155}, $\x{4156}, $\x{4157}, $\x{4158}, $\x{4159}, $\x{415A}, $\x{4161}, $\x{4162}, $\x{4163}, $\x{4164}, $\x{4165}, $\x{4166}, $\x{4167}, $\x{4168}, $\x{4169}, $\x{416A}, $\x{416B}, $\x{416C}, $\x{416D}, $\x{416E}, $\x{416F}, $\x{4170}, $\x{4171}, $\x{4172}, $\x{4173}, $\x{4174}, $\x{4175}, $\x{4176}, $\x{4177}, $\x{4178}, $\x{4179}, $\x{417A}, $\x{4130}, $\x{4131}, $\x{4132}, $\x{4133}, $\x{4134}, $\x{4135}, $\x{4136}, $\x{4137}, $\x{4138}, $\x{4139}, $\x{412D}, $\x{415F}, $\x{4241}, $\x{4242}, $\x{4243}, $\x{4244}, $\x{4245}, $\x{4246}, $\x{4247}, $\x{4248}, $\x{4249}, $\x{424A}, $\x{424B}, $\x{424C}, $\x{424D}, $\x{424E}, $\x{424F}, $\x{4250}, $\x{4251}, $\x{4252}, $\x{4253}, $\x{4254}, $\x{4255}, $\x{4256}, $\x{4257}, $\x{4258}, $\x{4259}, $\x{425A}, $\x{4261}, $\x{4262}, $\x{4263}, $\x{4264}, $\x{4265}, $\x{4266}, $\x{4267}, $\x{4268}, $\x{4269}, $\x{426A}, $\x{426B}, $\x{426C}, $\x{426D}, $\x{426E}, $\x{426F}, $\x{4270}, $\x{4271}, $\x{4272}, $\x{4273}, $\x{4274}, $\x{4275}, $\x{4276}, $\x{4277}, $\x{4278}, $\x{4279}, $\x{427A}, $\x{4230}, $\x{4231}, $\x{4232}, $\x{4233}, $\x{4234}, $\x{4235}, $\x{4236}, $\x{4237}, $\x{4238}, $\x{4239}, $\x{422D}, $\x{425F}, $\x{4341}, $\x{4342}, $\x{4343}, $\x{4344}, $\x{4345}, $\x{4346}, $\x{4347}, $\x{4348}, $\x{4349}, $\x{434A}, $\x{434B}, $\x{434C}, $\x{434D}, $\x{434E}, $\x{434F}, $\x{4350}, $\x{4351}, $\x{4352}, $\x{4353}, $\x{4354}, $\x{4355}, $\x{4356}, $\x{4357}, $\x{4358}, $\x{4359}, $\x{435A}, $\x{4361}, $\x{4362}, $\x{4363}, $\x{4364}, $\x{4365}, $\x{4366}, $\x{4367}, $\x{4368}, $\x{4369}, $\x{436A}, $\x{436B}, $\x{436C}, $\x{436D}, $\x{436E}, $\x{436F}, $\x{4370}, $\x{4371}, $\x{4372}, $\x{4373}, $\x{4374}, $\x{4375}, $\x{4376}, $\x{4377}, $\x{4378}, $\x{4379}, $\x{437A}, $\x{4330}, $\x{4331}, $\x{4332}, $\x{4333}, $\x{4334}, $\x{4335}, $\x{4336}, $\x{4337}, $\x{4338}, $\x{4339}, $\x{432D}, $\x{435F}, $\x{4441}, $\x{4442}, $\x{4443}, $\x{4444}, $\x{4445}, $\x{4446}, $\x{4447}, $\x{4448}, $\x{4449}, $\x{444A}, $\x{444B}, $\x{444C}, $\x{444D}, $\x{444E}, $\x{444F}, $\x{4450}, $\x{4451}, $\x{4452}, $\x{4453}, $\x{4454}, $\x{4455}, $\x{4456}, $\x{4457}, $\x{4458}, $\x{4459}, $\x{445A}, $\x{4461}, $\x{4462}, $\x{4463}, $\x{4464}, $\x{4465}, $\x{4466}, $\x{4467}, $\x{4468}, $\x{4469}, $\x{446A}, $\x{446B}, $\x{446C}, $\x{446D}, $\x{446E}, $\x{446F}, $\x{4470}, $\x{4471}, $\x{4472}, $\x{4473}, $\x{4474}, $\x{4475}, $\x{4476}, $\x{4477}, $\x{4478}, $\x{4479}, $\x{447A}, $\x{4430}, $\x{4431}, $\x{4432}, $\x{4433}, $\x{4434}, $\x{4435}, $\x{4436}, $\x{4437}, $\x{4438}, $\x{4439}, $\x{442D}, $\x{445F}, $\x{4541}, $\x{4542}, $\x{4543}, $\x{4544}, $\x{4545}, $\x{4546}, $\x{4547}, $\x{4548}, $\x{4549}, $\x{454A}, $\x{454B}, $\x{454C}, $\x{454D}, $\x{454E}, $\x{454F}, $\x{4550}, $\x{4551}, $\x{4552}, $\x{4553}, $\x{4554}, $\x{4555}, $\x{4556}, $\x{4557}, $\x{4558}, $\x{4559}, $\x{455A}, $\x{4561}, $\x{4562}, $\x{4563}, $\x{4564}, $\x{4565}, $\x{4566}, $\x{4567}, $\x{4568}, $\x{4569}, $\x{456A}, $\x{456B}, $\x{456C}, $\x{456D}, $\x{456E}, $\x{456F}, $\x{4570}, $\x{4571}, $\x{4572}, $\x{4573}, $\x{4574}, $\x{4575}, $\x{4576}, $\x{4577}, $\x{4578}, $\x{4579}, $\x{457A}, $\x{4530}, $\x{4531}, $\x{4532}, $\x{4533}, $\x{4534}, $\x{4535}, $\x{4536}, $\x{4537}, $\x{4538}, $\x{4539}, $\x{452D}, $\x{455F}, $\x{4641}, $\x{4642}, $\x{4643}, $\x{4644}, $\x{4645}, $\x{4646}, $\x{4647}, $\x{4648}, $\x{4649}, $\x{464A}, $\x{464B}, $\x{464C}, $\x{464D}, $\x{464E}, $\x{464F}, $\x{4650}, $\x{4651}, $\x{4652}, $\x{4653}, $\x{4654}, $\x{4655}, $\x{4656}, $\x{4657}, $\x{4658}, $\x{4659}, $\x{465A}, $\x{4661}, $\x{4662}, $\x{4663}, $\x{4664}, $\x{4665}, $\x{4666}, $\x{4667}, $\x{4668}, $\x{4669}, $\x{466A}, $\x{466B}, $\x{466C}, $\x{466D}, $\x{466E}, $\x{466F}, $\x{4670}, $\x{4671}, $\x{4672}, $\x{4673}, $\x{4674}, $\x{4675}, $\x{4676}, $\x{4677}, $\x{4678}, $\x{4679}, $\x{467A}, $\x{4630}, $\x{4631}, $\x{4632}, $\x{4633}, $\x{4634}, $\x{4635}, $\x{4636}, $\x{4637}, $\x{4638}, $\x{4639}, $\x{462D}, $\x{465F}, $\x{4741}, $\x{4742}, $\x{4743}, $\x{4744}, $\x{4745}, $\x{4746}, $\x{4747}, $\x{4748}, $\x{4749}, $\x{474A}, $\x{474B}, $\x{474C}, $\x{474D}, $\x{474E}, $\x{474F}, $\x{4750}, $\x{4751}, $\x{4752}, $\x{4753}, $\x{4754}, $\x{4755}, $\x{4756}, $\x{4757}, $\x{4758}, $\x{4759}, $\x{475A}, $\x{4761}, $\x{4762}, $\x{4763}, $\x{4764}, $\x{4765}, $\x{4766}, $\x{4767}, $\x{4768}, $\x{4769}, $\x{476A}, $\x{476B}, $\x{476C}, $\x{476D}, $\x{476E}, $\x{476F}, $\x{4770}, $\x{4771}, $\x{4772}, $\x{4773}, $\x{4774}, $\x{4775}, $\x{4776}, $\x{4777}, $\x{4778}, $\x{4779}, $\x{477A}, $\x{4730}, $\x{4731}, $\x{4732}, $\x{4733}, $\x{4734}, $\x{4735}, $\x{4736}, $\x{4737}, $\x{4738}, $\x{4739}, $\x{472D}, $\x{475F}, $\x{4841}, $\x{4842}, $\x{4843}, $\x{4844}, $\x{4845}, $\x{4846}, $\x{4847}, $\x{4848}, $\x{4849}, $\x{484A}, $\x{484B}, $\x{484C}, $\x{484D}, $\x{484E}, $\x{484F}, $\x{4850}, $\x{4851}, $\x{4852}, $\x{4853}, $\x{4854}, $\x{4855}, $\x{4856}, $\x{4857}, $\x{4858}, $\x{4859}, $\x{485A}, $\x{4861}, $\x{4862}, $\x{4863}, $\x{4864}, $\x{4865}, $\x{4866}, $\x{4867}, $\x{4868}, $\x{4869}, $\x{486A}, $\x{486B}, $\x{486C}, $\x{486D}, $\x{486E}, $\x{486F}, $\x{4870}, $\x{4871}, $\x{4872}, $\x{4873}, $\x{4874}, $\x{4875}, $\x{4876}, $\x{4877}, $\x{4878}, $\x{4879}, $\x{487A}, $\x{4830}, $\x{4831}, $\x{4832}, $\x{4833}, $\x{4834}, $\x{4835}, $\x{4836}, $\x{4837}, $\x{4838}, $\x{4839}, $\x{482D}, $\x{485F}, $\x{4941}, $\x{4942}, $\x{4943}, $\x{4944}, $\x{4945}, $\x{4946}, $\x{4947}, $\x{4948}, $\x{4949}, $\x{494A}, $\x{494B}, $\x{494C}, $\x{494D}, $\x{494E}, $\x{494F}, $\x{4950}, $\x{4951}, $\x{4952}, $\x{4953}, $\x{4954}, $\x{4955}, $\x{4956}, $\x{4957}, $\x{4958}, $\x{4959}, $\x{495A}, $\x{4961}, $\x{4962}, $\x{4963}, $\x{4964}, $\x{4965}, $\x{4966}, $\x{4967}, $\x{4968}, $\x{4969}, $\x{496A}, $\x{496B}, $\x{496C}, $\x{496D}, $\x{496E}, $\x{496F}, $\x{4970}, $\x{4971}, $\x{4972}, $\x{4973}, $\x{4974}, $\x{4975}, $\x{4976}, $\x{4977}, $\x{4978}, $\x{4979}, $\x{497A}, $\x{4930}, $\x{4931}, $\x{4932}, $\x{4933}, $\x{4934}, $\x{4935}, $\x{4936}, $\x{4937}, $\x{4938}, $\x{4939}, $\x{492D}, $\x{495F}, $\x{4A41}, $\x{4A42}, $\x{4A43}, $\x{4A44}, $\x{4A45}, $\x{4A46}, $\x{4A47}, $\x{4A48}, $\x{4A49}, $\x{4A4A}, $\x{4A4B}, $\x{4A4C}, $\x{4A4D}, $\x{4A4E}, $\x{4A4F}, $\x{4A50}, $\x{4A51}, $\x{4A52}, $\x{4A53}, $\x{4A54}, $\x{4A55}, $\x{4A56}, $\x{4A57}, $\x{4A58}, $\x{4A59}, $\x{4A5A}, $\x{4A61}, $\x{4A62}, $\x{4A63}, $\x{4A64}, $\x{4A65}, $\x{4A66}, $\x{4A67}, $\x{4A68}, $\x{4A69}, $\x{4A6A}, $\x{4A6B}, $\x{4A6C}, $\x{4A6D}, $\x{4A6E}, $\x{4A6F}, $\x{4A70}, $\x{4A71}, $\x{4A72}, $\x{4A73}, $\x{4A74}, $\x{4A75}, $\x{4A76}, $\x{4A77}, $\x{4A78}, $\x{4A79}, $\x{4A7A}, $\x{4A30}, $\x{4A31}, $\x{4A32}, $\x{4A33}, $\x{4A34}, $\x{4A35}, $\x{4A36}, $\x{4A37}, $\x{4A38}, $\x{4A39}, $\x{4A2D}, $\x{4A5F}, $\x{4B41}, $\x{4B42}, $\x{4B43}, $\x{4B44}, $\x{4B45}, $\x{4B46}, $\x{4B47}, $\x{4B48}, $\x{4B49}, $\x{4B4A}, $\x{4B4B}, $\x{4B4C}, $\x{4B4D}, $\x{4B4E}, $\x{4B4F}, $\x{4B50}, $\x{4B51}, $\x{4B52}, $\x{4B53}, $\x{4B54}, $\x{4B55}, $\x{4B56}, $\x{4B57}, $\x{4B58}, $\x{4B59}, $\x{4B5A}, $\x{4B61}, $\x{4B62}, $\x{4B63}, $\x{4B64}, $\x{4B65}, $\x{4B66}, $\x{4B67}, $\x{4B68}, $\x{4B69}, $\x{4B6A}, $\x{4B6B}, $\x{4B6C}, $\x{4B6D}, $\x{4B6E}, $\x{4B6F}, $\x{4B70}, $\x{4B71}, $\x{4B72}, $\x{4B73}, $\x{4B74}, $\x{4B75}, $\x{4B76}, $\x{4B77}, $\x{4B78}, $\x{4B79}, $\x{4B7A}, $\x{4B30}, $\x{4B31}, $\x{4B32}, $\x{4B33}, $\x{4B34}, $\x{4B35}, $\x{4B36}, $\x{4B37}, $\x{4B38}, $\x{4B39}, $\x{4B2D}, $\x{4B5F}, $\x{4C41}, $\x{4C42}, $\x{4C43}, $\x{4C44}, $\x{4C45}, $\x{4C46}, $\x{4C47}, $\x{4C48}, $\x{4C49}, $\x{4C4A}, $\x{4C4B}, $\x{4C4C}, $\x{4C4D}, $\x{4C4E}, $\x{4C4F}, $\x{4C50}, $\x{4C51}, $\x{4C52}, $\x{4C53}, $\x{4C54}, $\x{4C55}, $\x{4C56}, $\x{4C57}, $\x{4C58}, $\x{4C59}, $\x{4C5A}, $\x{4C61}, $\x{4C62}, $\x{4C63}, $\x{4C64}, $\x{4C65}, $\x{4C66}, $\x{4C67}, $\x{4C68}, $\x{4C69}, $\x{4C6A}, $\x{4C6B}, $\x{4C6C}, $\x{4C6D}, $\x{4C6E}, $\x{4C6F}, $\x{4C70}, $\x{4C71}, $\x{4C72}, $\x{4C73}, $\x{4C74}, $\x{4C75}, $\x{4C76}, $\x{4C77}, $\x{4C78}, $\x{4C79}, $\x{4C7A}, $\x{4C30}, $\x{4C31}, $\x{4C32}, $\x{4C33}, $\x{4C34}, $\x{4C35}, $\x{4C36}, $\x{4C37}, $\x{4C38}, $\x{4C39}, $\x{4C2D}, $\x{4C5F}, $\x{4D41}, $\x{4D42}, $\x{4D43}, $\x{4D44}, $\x{4D45}, $\x{4D46}, $\x{4D47}, $\x{4D48}, $\x{4D49}, $\x{4D4A}, $\x{4D4B}, $\x{4D4C}, $\x{4D4D}, $\x{4D4E}, $\x{4D4F}, $\x{4D50}, $\x{4D51}, $\x{4D52}, $\x{4D53}, $\x{4D54}, $\x{4D55}, $\x{4D56}, $\x{4D57}, $\x{4D58}, $\x{4D59}, $\x{4D5A}, $\x{4D61}, $\x{4D62}, $\x{4D63}, $\x{4D64}, $\x{4D65}, $\x{4D66}, $\x{4D67}, $\x{4D68}, $\x{4D69}, $\x{4D6A}, $\x{4D6B}, $\x{4D6C}, $\x{4D6D}, $\x{4D6E}, $\x{4D6F}, $\x{4D70}, $\x{4D71}, $\x{4D72}, $\x{4D73}, $\x{4D74}, $\x{4D75}, $\x{4D76}, $\x{4D77}, $\x{4D78}, $\x{4D79}, $\x{4D7A}, $\x{4D30}, $\x{4D31}, $\x{4D32}, $\x{4D33}, $\x{4D34}, $\x{4D35}, $\x{4D36}, $\x{4D37}, $\x{4D38}, $\x{4D39}, $\x{4D2D}, $\x{4D5F}, $\x{4E41}, $\x{4E42}, $\x{4E43}, $\x{4E44}, $\x{4E45}, $\x{4E46}, $\x{4E47}, $\x{4E48}, $\x{4E49}, $\x{4E4A}, $\x{4E4B}, $\x{4E4C}, $\x{4E4D}, $\x{4E4E}, $\x{4E4F}, $\x{4E50}, $\x{4E51}, $\x{4E52}, $\x{4E53}, $\x{4E54}, $\x{4E55}, $\x{4E56}, $\x{4E57}, $\x{4E58}, $\x{4E59}, $\x{4E5A}, $\x{4E61}, $\x{4E62}, $\x{4E63}, $\x{4E64}, $\x{4E65}, $\x{4E66}, $\x{4E67}, $\x{4E68}, $\x{4E69}, $\x{4E6A}, $\x{4E6B}, $\x{4E6C}, $\x{4E6D}, $\x{4E6E}, $\x{4E6F}, $\x{4E70}, $\x{4E71}, $\x{4E72}, $\x{4E73}, $\x{4E74}, $\x{4E75}, $\x{4E76}, $\x{4E77}, $\x{4E78}, $\x{4E79}, $\x{4E7A}, $\x{4E30}, $\x{4E31}, $\x{4E32}, $\x{4E33}, $\x{4E34}, $\x{4E35}, $\x{4E36}, $\x{4E37}, $\x{4E38}, $\x{4E39}, $\x{4E2D}, $\x{4E5F}, $\x{4F41}, $\x{4F42}, $\x{4F43}, $\x{4F44}, $\x{4F45}, $\x{4F46}, $\x{4F47}, $\x{4F48}, $\x{4F49}, $\x{4F4A}, $\x{4F4B}, $\x{4F4C}, $\x{4F4D}, $\x{4F4E}, $\x{4F4F}, $\x{4F50}, $\x{4F51}, $\x{4F52}, $\x{4F53}, $\x{4F54}, $\x{4F55}, $\x{4F56}, $\x{4F57}, $\x{4F58}, $\x{4F59}, $\x{4F5A}, $\x{4F61}, $\x{4F62}, $\x{4F63}, $\x{4F64}, $\x{4F65}, $\x{4F66}, $\x{4F67}, $\x{4F68}, $\x{4F69}, $\x{4F6A}, $\x{4F6B}, $\x{4F6C}, $\x{4F6D}, $\x{4F6E}, $\x{4F6F}, $\x{4F70}, $\x{4F71}, $\x{4F72}, $\x{4F73}, $\x{4F74}, $\x{4F75}, $\x{4F76}, $\x{4F77}, $\x{4F78}, $\x{4F79}, $\x{4F7A}, $\x{4F30}, $\x{4F31}, $\x{4F32}, $\x{4F33}, $\x{4F34}, $\x{4F35}, $\x{4F36}, $\x{4F37}, $\x{4F38}, $\x{4F39}, $\x{4F2D}, $\x{4F5F}, $\x{5041}, $\x{5042}, $\x{5043}, $\x{5044}, $\x{5045}, $\x{5046}, $\x{5047}, $\x{5048}, $\x{5049}, $\x{504A}, $\x{504B}, $\x{504C}, $\x{504D}, $\x{504E}, $\x{504F}, $\x{5050}, $\x{5051}, $\x{5052}, $\x{5053}, $\x{5054}, $\x{5055}, $\x{5056}, $\x{5057}, $\x{5058}, $\x{5059}, $\x{505A}, $\x{5061}, $\x{5062}, $\x{5063}, $\x{5064}, $\x{5065}, $\x{5066}, $\x{5067}, $\x{5068}, $\x{5069}, $\x{506A}, $\x{506B}, $\x{506C}, $\x{506D}, $\x{506E}, $\x{506F}, $\x{5070}, $\x{5071}, $\x{5072}, $\x{5073}, $\x{5074}, $\x{5075}, $\x{5076}, $\x{5077}, $\x{5078}, $\x{5079}, $\x{507A}, $\x{5030}, $\x{5031}, $\x{5032}, $\x{5033}, $\x{5034}, $\x{5035}, $\x{5036}, $\x{5037}, $\x{5038}, $\x{5039}, $\x{502D}, $\x{505F}, $\x{5141}, $\x{5142}, $\x{5143}, $\x{5144}, $\x{5145}, $\x{5146}, $\x{5147}, $\x{5148}, $\x{5149}, $\x{514A}, $\x{514B}, $\x{514C}, $\x{514D}, $\x{514E}, $\x{514F}, $\x{5150}, $\x{5151}, $\x{5152}, $\x{5153}, $\x{5154}, $\x{5155}, $\x{5156}, $\x{5157}, $\x{5158}, $\x{5159}, $\x{515A}, $\x{5161}, $\x{5162}, $\x{5163}, $\x{5164}, $\x{5165}, $\x{5166}, $\x{5167}, $\x{5168}, $\x{5169}, $\x{516A}, $\x{516B}, $\x{516C}, $\x{516D}, $\x{516E}, $\x{516F}, $\x{5170}, $\x{5171}, $\x{5172}, $\x{5173}, $\x{5174}, $\x{5175}, $\x{5176}, $\x{5177}, $\x{5178}, $\x{5179}, $\x{517A}, $\x{5130}, $\x{5131}, $\x{5132}, $\x{5133}, $\x{5134}, $\x{5135}, $\x{5136}, $\x{5137}, $\x{5138}, $\x{5139}, $\x{512D}, $\x{515F}, $\x{5241}, $\x{5242}, $\x{5243}, $\x{5244}, $\x{5245}, $\x{5246}, $\x{5247}, $\x{5248}, $\x{5249}, $\x{524A}, $\x{524B}, $\x{524C}, $\x{524D}, $\x{524E}, $\x{524F}, $\x{5250}, $\x{5251}, $\x{5252}, $\x{5253}, $\x{5254}, $\x{5255}, $\x{5256}, $\x{5257}, $\x{5258}, $\x{5259}, $\x{525A}, $\x{5261}, $\x{5262}, $\x{5263}, $\x{5264}, $\x{5265}, $\x{5266}, $\x{5267}, $\x{5268}, $\x{5269}, $\x{526A}, $\x{526B}, $\x{526C}, $\x{526D}, $\x{526E}, $\x{526F}, $\x{5270}, $\x{5271}, $\x{5272}, $\x{5273}, $\x{5274}, $\x{5275}, $\x{5276}, $\x{5277}, $\x{5278}, $\x{5279}, $\x{527A}, $\x{5230}, $\x{5231}, $\x{5232}, $\x{5233}, $\x{5234}, $\x{5235}, $\x{5236}, $\x{5237}, $\x{5238}, $\x{5239}, $\x{522D}, $\x{525F}, $\x{5341}, $\x{5342}, $\x{5343}, $\x{5344}, $\x{5345}, $\x{5346}, $\x{5347}, $\x{5348}, $\x{5349}, $\x{534A}, $\x{534B}, $\x{534C}, $\x{534D}, $\x{534E}, $\x{534F}, $\x{5350}, $\x{5351}, $\x{5352}, $\x{5353}, $\x{5354}, $\x{5355}, $\x{5356}, $\x{5357}, $\x{5358}, $\x{5359}, $\x{535A}, $\x{5361}, $\x{5362}, $\x{5363}, $\x{5364}, $\x{5365}, $\x{5366}, $\x{5367}, $\x{5368}, $\x{5369}, $\x{536A}, $\x{536B}, $\x{536C}, $\x{536D}, $\x{536E}, $\x{536F}, $\x{5370}, $\x{5371}, $\x{5372}, $\x{5373}, $\x{5374}, $\x{5375}, $\x{5376}, $\x{5377}, $\x{5378}, $\x{5379}, $\x{537A}, $\x{5330}, $\x{5331}, $\x{5332}, $\x{5333}, $\x{5334}, $\x{5335}, $\x{5336}, $\x{5337}, $\x{5338}, $\x{5339}, $\x{532D}, $\x{535F}, $\x{5441}, $\x{5442}, $\x{5443}, $\x{5444}, $\x{5445}, $\x{5446}, $\x{5447}, $\x{5448}, $\x{5449}, $\x{544A}, $\x{544B}, $\x{544C}, $\x{544D}, $\x{544E}, $\x{544F}, $\x{5450}, $\x{5451}, $\x{5452}, $\x{5453}, $\x{5454}, $\x{5455}, $\x{5456}, $\x{5457}, $\x{5458}, $\x{5459}, $\x{545A}, $\x{5461}, $\x{5462}, $\x{5463}, $\x{5464}, $\x{5465}, $\x{5466}, $\x{5467}, $\x{5468}, $\x{5469}, $\x{546A}, $\x{546B}, $\x{546C}, $\x{546D}, $\x{546E}, $\x{546F}, $\x{5470}, $\x{5471}, $\x{5472}, $\x{5473}, $\x{5474}, $\x{5475}, $\x{5476}, $\x{5477}, $\x{5478}, $\x{5479}, $\x{547A}, $\x{5430}, $\x{5431}, $\x{5432}, $\x{5433}, $\x{5434}, $\x{5435}, $\x{5436}, $\x{5437}, $\x{5438}, $\x{5439}, $\x{542D}, $\x{545F}, $\x{5541}, $\x{5542}, $\x{5543}, $\x{5544}, $\x{5545}, $\x{5546}, $\x{5547}, $\x{5548}, $\x{5549}, $\x{554A}, $\x{554B}, $\x{554C}, $\x{554D}, $\x{554E}, $\x{554F}, $\x{5550}, $\x{5551}, $\x{5552}, $\x{5553}, $\x{5554}, $\x{5555}, $\x{5556}, $\x{5557}, $\x{5558}, $\x{5559}, $\x{555A}, $\x{5561}, $\x{5562}, $\x{5563}, $\x{5564}, $\x{5565}, $\x{5566}, $\x{5567}, $\x{5568}, $\x{5569}, $\x{556A}, $\x{556B}, $\x{556C}, $\x{556D}, $\x{556E}, $\x{556F}, $\x{5570}, $\x{5571}, $\x{5572}, $\x{5573}, $\x{5574}, $\x{5575}, $\x{5576}, $\x{5577}, $\x{5578}, $\x{5579}, $\x{557A}, $\x{5530}, $\x{5531}, $\x{5532}, $\x{5533}, $\x{5534}, $\x{5535}, $\x{5536}, $\x{5537}, $\x{5538}, $\x{5539}, $\x{552D}, $\x{555F}, $\x{5641}, $\x{5642}, $\x{5643}, $\x{5644}, $\x{5645}, $\x{5646}, $\x{5647}, $\x{5648}, $\x{5649}, $\x{564A}, $\x{564B}, $\x{564C}, $\x{564D}, $\x{564E}, $\x{564F}, $\x{5650}, $\x{5651}, $\x{5652}, $\x{5653}, $\x{5654}, $\x{5655}, $\x{5656}, $\x{5657}, $\x{5658}, $\x{5659}, $\x{565A}, $\x{5661}, $\x{5662}, $\x{5663}, $\x{5664}, $\x{5665}, $\x{5666}, $\x{5667}, $\x{5668}, $\x{5669}, $\x{566A}, $\x{566B}, $\x{566C}, $\x{566D}, $\x{566E}, $\x{566F}, $\x{5670}, $\x{5671}, $\x{5672}, $\x{5673}, $\x{5674}, $\x{5675}, $\x{5676}, $\x{5677}, $\x{5678}, $\x{5679}, $\x{567A}, $\x{5630}, $\x{5631}, $\x{5632}, $\x{5633}, $\x{5634}, $\x{5635}, $\x{5636}, $\x{5637}, $\x{5638}, $\x{5639}, $\x{562D}, $\x{565F}, $\x{5741}, $\x{5742}, $\x{5743}, $\x{5744}, $\x{5745}, $\x{5746}, $\x{5747}, $\x{5748}, $\x{5749}, $\x{574A}, $\x{574B}, $\x{574C}, $\x{574D}, $\x{574E}, $\x{574F}, $\x{5750}, $\x{5751}, $\x{5752}, $\x{5753}, $\x{5754}, $\x{5755}, $\x{5756}, $\x{5757}, $\x{5758}, $\x{5759}, $\x{575A}, $\x{5761}, $\x{5762}, $\x{5763}, $\x{5764}, $\x{5765}, $\x{5766}, $\x{5767}, $\x{5768}, $\x{5769}, $\x{576A}, $\x{576B}, $\x{576C}, $\x{576D}, $\x{576E}, $\x{576F}, $\x{5770}, $\x{5771}, $\x{5772}, $\x{5773}, $\x{5774}, $\x{5775}, $\x{5776}, $\x{5777}, $\x{5778}, $\x{5779}, $\x{577A}, $\x{5730}, $\x{5731}, $\x{5732}, $\x{5733}, $\x{5734}, $\x{5735}, $\x{5736}, $\x{5737}, $\x{5738}, $\x{5739}, $\x{572D}, $\x{575F}, $\x{5841}, $\x{5842}, $\x{5843}, $\x{5844}, $\x{5845}, $\x{5846}, $\x{5847}, $\x{5848}, $\x{5849}, $\x{584A}, $\x{584B}, $\x{584C}, $\x{584D}, $\x{584E}, $\x{584F}, $\x{5850}, $\x{5851}, $\x{5852}, $\x{5853}, $\x{5854}, $\x{5855}, $\x{5856}, $\x{5857}, $\x{5858}, $\x{5859}, $\x{585A}, $\x{5861}, $\x{5862}, $\x{5863}, $\x{5864}, $\x{5865}, $\x{5866}, $\x{5867}, $\x{5868}, $\x{5869}, $\x{586A}, $\x{586B}, $\x{586C}, $\x{586D}, $\x{586E}, $\x{586F}, $\x{5870}, $\x{5871}, $\x{5872}, $\x{5873}, $\x{5874}, $\x{5875}, $\x{5876}, $\x{5877}, $\x{5878}, $\x{5879}, $\x{587A}, $\x{5830}, $\x{5831}, $\x{5832}, $\x{5833}, $\x{5834}, $\x{5835}, $\x{5836}, $\x{5837}, $\x{5838}, $\x{5839}, $\x{582D}, $\x{585F}, $\x{5941}, $\x{5942}, $\x{5943}, $\x{5944}, $\x{5945}, $\x{5946}, $\x{5947}, $\x{5948}, $\x{5949}, $\x{594A}, $\x{594B}, $\x{594C}, $\x{594D}, $\x{594E}, $\x{594F}, $\x{5950}, $\x{5951}, $\x{5952}, $\x{5953}, $\x{5954}, $\x{5955}, $\x{5956}, $\x{5957}, $\x{5958}, $\x{5959}, $\x{595A}, $\x{5961}, $\x{5962}, $\x{5963}, $\x{5964}, $\x{5965}, $\x{5966}, $\x{5967}, $\x{5968}, $\x{5969}, $\x{596A}, $\x{596B}, $\x{596C}, $\x{596D}, $\x{596E}, $\x{596F}, $\x{5970}, $\x{5971}, $\x{5972}, $\x{5973}, $\x{5974}, $\x{5975}, $\x{5976}, $\x{5977}, $\x{5978}, $\x{5979}, $\x{597A}, $\x{5930}, $\x{5931}, $\x{5932}, $\x{5933}, $\x{5934}, $\x{5935}, $\x{5936}, $\x{5937}, $\x{5938}, $\x{5939}, $\x{592D}, $\x{595F}, $\x{5A41}, $\x{5A42}, $\x{5A43}, $\x{5A44}, $\x{5A45}, $\x{5A46}, $\x{5A47}, $\x{5A48}, $\x{5A49}, $\x{5A4A}, $\x{5A4B}, $\x{5A4C}, $\x{5A4D}, $\x{5A4E}, $\x{5A4F}, $\x{5A50}, $\x{5A51}, $\x{5A52}, $\x{5A53}, $\x{5A54}, $\x{5A55}, $\x{5A56}, $\x{5A57}, $\x{5A58}, $\x{5A59}, $\x{5A5A}, $\x{5A61}, $\x{5A62}, $\x{5A63}, $\x{5A64}, $\x{5A65}, $\x{5A66}, $\x{5A67}, $\x{5A68}, $\x{5A69}, $\x{5A6A}, $\x{5A6B}, $\x{5A6C}, $\x{5A6D}, $\x{5A6E}, $\x{5A6F}, $\x{5A70}, $\x{5A71}, $\x{5A72}, $\x{5A73}, $\x{5A74}, $\x{5A75}, $\x{5A76}, $\x{5A77}, $\x{5A78}, $\x{5A79}, $\x{5A7A}, $\x{5A30}, $\x{5A31}, $\x{5A32}, $\x{5A33}, $\x{5A34}, $\x{5A35}, $\x{5A36}, $\x{5A37}, $\x{5A38}, $\x{5A39}, $\x{5A2D}, $\x{5A5F}, $\x{6141}, $\x{6142}, $\x{6143}, $\x{6144}, $\x{6145}, $\x{6146}, $\x{6147}, $\x{6148}, $\x{6149}, $\x{614A}, $\x{614B}, $\x{614C}, $\x{614D}, $\x{614E}, $\x{614F}, $\x{6150}, $\x{6151}, $\x{6152}, $\x{6153}, $\x{6154}, $\x{6155}, $\x{6156}, $\x{6157}, $\x{6158}, $\x{6159}, $\x{615A}, $\x{6161}, $\x{6162}, $\x{6163}, $\x{6164}, $\x{6165}, $\x{6166}, $\x{6167}, $\x{6168}, $\x{6169}, $\x{616A}, $\x{616B}, $\x{616C}, $\x{616D}, $\x{616E}, $\x{616F}, $\x{6170}, $\x{6171}, $\x{6172}, $\x{6173}, $\x{6174}, $\x{6175}, $\x{6176}, $\x{6177}, $\x{6178}, $\x{6179}, $\x{617A}, $\x{6130}, $\x{6131}, $\x{6132}, $\x{6133}, $\x{6134}, $\x{6135}, $\x{6136}, $\x{6137}, $\x{6138}, $\x{6139}, $\x{612D}, $\x{615F}, $\x{6241}, $\x{6242}, $\x{6243}, $\x{6244}, $\x{6245}, $\x{6246}, $\x{6247}, $\x{6248}, $\x{6249}, $\x{624A}, $\x{624B}, $\x{624C}, $\x{624D}, $\x{624E}, $\x{624F}, $\x{6250}, $\x{6251}, $\x{6252}, $\x{6253}, $\x{6254}, $\x{6255}, $\x{6256}, $\x{6257}, $\x{6258}, $\x{6259}, $\x{625A}, $\x{6261}, $\x{6262}, $\x{6263}, $\x{6264}, $\x{6265}, $\x{6266}, $\x{6267}, $\x{6268}, $\x{6269}, $\x{626A}, $\x{626B}, $\x{626C}, $\x{626D}, $\x{626E}, $\x{626F}, $\x{6270}, $\x{6271}, $\x{6272}, $\x{6273}, $\x{6274}, $\x{6275}, $\x{6276}, $\x{6277}, $\x{6278}, $\x{6279}, $\x{627A}, $\x{6230}, $\x{6231}, $\x{6232}, $\x{6233}, $\x{6234}, $\x{6235}, $\x{6236}, $\x{6237}, $\x{6238}, $\x{6239}, $\x{622D}, $\x{625F}, $\x{6341}, $\x{6342}, $\x{6343}, $\x{6344}, $\x{6345}, $\x{6346}, $\x{6347}, $\x{6348}, $\x{6349}, $\x{634A}, $\x{634B}, $\x{634C}, $\x{634D}, $\x{634E}, $\x{634F}, $\x{6350}, $\x{6351}, $\x{6352}, $\x{6353}, $\x{6354}, $\x{6355}, $\x{6356}, $\x{6357}, $\x{6358}, $\x{6359}, $\x{635A}, $\x{6361}, $\x{6362}, $\x{6363}, $\x{6364}, $\x{6365}, $\x{6366}, $\x{6367}, $\x{6368}, $\x{6369}, $\x{636A}, $\x{636B}, $\x{636C}, $\x{636D}, $\x{636E}, $\x{636F}, $\x{6370}, $\x{6371}, $\x{6372}, $\x{6373}, $\x{6374}, $\x{6375}, $\x{6376}, $\x{6377}, $\x{6378}, $\x{6379}, $\x{637A}, $\x{6330}, $\x{6331}, $\x{6332}, $\x{6333}, $\x{6334}, $\x{6335}, $\x{6336}, $\x{6337}, $\x{6338}, $\x{6339}, $\x{632D}, $\x{635F}, $\x{6441}, $\x{6442}, $\x{6443}, $\x{6444}, $\x{6445}, $\x{6446}, $\x{6447}, $\x{6448}, $\x{6449}, $\x{644A}, $\x{644B}, $\x{644C}, $\x{644D}, $\x{644E}, $\x{644F}, $\x{6450}, $\x{6451}, $\x{6452}, $\x{6453}, $\x{6454}, $\x{6455}, $\x{6456}, $\x{6457}, $\x{6458}, $\x{6459}, $\x{645A}, $\x{6461}, $\x{6462}, $\x{6463}, $\x{6464}, $\x{6465}, $\x{6466}, $\x{6467}, $\x{6468}, $\x{6469}, $\x{646A}, $\x{646B}, $\x{646C}, $\x{646D}, $\x{646E}, $\x{646F}, $\x{6470}, $\x{6471}, $\x{6472}, $\x{6473}, $\x{6474}, $\x{6475}, $\x{6476}, $\x{6477}, $\x{6478}, $\x{6479}, $\x{647A}, $\x{6430}, $\x{6431}, $\x{6432}, $\x{6433}, $\x{6434}, $\x{6435}, $\x{6436}, $\x{6437}, $\x{6438}, $\x{6439}, $\x{642D}, $\x{645F}, $\x{6541}, $\x{6542}, $\x{6543}, $\x{6544}, $\x{6545}, $\x{6546}, $\x{6547}, $\x{6548}, $\x{6549}, $\x{654A}, $\x{654B}, $\x{654C}, $\x{654D}, $\x{654E}, $\x{654F}, $\x{6550}, $\x{6551}, $\x{6552}, $\x{6553}, $\x{6554}, $\x{6555}, $\x{6556}, $\x{6557}, $\x{6558}, $\x{6559}, $\x{655A}, $\x{6561}, $\x{6562}, $\x{6563}, $\x{6564}, $\x{6565}, $\x{6566}, $\x{6567}, $\x{6568}, $\x{6569}, $\x{656A}, $\x{656B}, $\x{656C}, $\x{656D}, $\x{656E}, $\x{656F}, $\x{6570}, $\x{6571}, $\x{6572}, $\x{6573}, $\x{6574}, $\x{6575}, $\x{6576}, $\x{6577}, $\x{6578}, $\x{6579}, $\x{657A}, $\x{6530}, $\x{6531}, $\x{6532}, $\x{6533}, $\x{6534}, $\x{6535}, $\x{6536}, $\x{6537}, $\x{6538}, $\x{6539}, $\x{652D}, $\x{655F}, $\x{6641}, $\x{6642}, $\x{6643}, $\x{6644}, $\x{6645}, $\x{6646}, $\x{6647}, $\x{6648}, $\x{6649}, $\x{664A}, $\x{664B}, $\x{664C}, $\x{664D}, $\x{664E}, $\x{664F}, $\x{6650}, $\x{6651}, $\x{6652}, $\x{6653}, $\x{6654}, $\x{6655}, $\x{6656}, $\x{6657}, $\x{6658}, $\x{6659}, $\x{665A}, $\x{6661}, $\x{6662}, $\x{6663}, $\x{6664}, $\x{6665}, $\x{6666}, $\x{6667}, $\x{6668}, $\x{6669}, $\x{666A}, $\x{666B}, $\x{666C}, $\x{666D}, $\x{666E}, $\x{666F}, $\x{6670}, $\x{6671}, $\x{6672}, $\x{6673}, $\x{6674}, $\x{6675}, $\x{6676}, $\x{6677}, $\x{6678}, $\x{6679}, $\x{667A}, $\x{6630}, $\x{6631}, $\x{6632}, $\x{6633}, $\x{6634}, $\x{6635}, $\x{6636}, $\x{6637}, $\x{6638}, $\x{6639}, $\x{662D}, $\x{665F}, $\x{6741}, $\x{6742}, $\x{6743}, $\x{6744}, $\x{6745}, $\x{6746}, $\x{6747}, $\x{6748}, $\x{6749}, $\x{674A}, $\x{674B}, $\x{674C}, $\x{674D}, $\x{674E}, $\x{674F}, $\x{6750}, $\x{6751}, $\x{6752}, $\x{6753}, $\x{6754}, $\x{6755}, $\x{6756}, $\x{6757}, $\x{6758}, $\x{6759}, $\x{675A}, $\x{6761}, $\x{6762}, $\x{6763}, $\x{6764}, $\x{6765}, $\x{6766}, $\x{6767}, $\x{6768}, $\x{6769}, $\x{676A}, $\x{676B}, $\x{676C}, $\x{676D}, $\x{676E}, $\x{676F}, $\x{6770}, $\x{6771}, $\x{6772}, $\x{6773}, $\x{6774}, $\x{6775}, $\x{6776}, $\x{6777}, $\x{6778}, $\x{6779}, $\x{677A}, $\x{6730}, $\x{6731}, $\x{6732}, $\x{6733}, $\x{6734}, $\x{6735}, $\x{6736}, $\x{6737}, $\x{6738}, $\x{6739}, $\x{672D}, $\x{675F}, $\x{6841}, $\x{6842}, $\x{6843}, $\x{6844}, $\x{6845}, $\x{6846}, $\x{6847}, $\x{6848}, $\x{6849}, $\x{684A}, $\x{684B}, $\x{684C}, $\x{684D}, $\x{684E}, $\x{684F}, $\x{6850}, $\x{6851}, $\x{6852}, $\x{6853}, $\x{6854}, $\x{6855}, $\x{6856}, $\x{6857}, $\x{6858}, $\x{6859}, $\x{685A}, $\x{6861}, $\x{6862}, $\x{6863}, $\x{6864}, $\x{6865}, $\x{6866}, $\x{6867}, $\x{6868}, $\x{6869}, $\x{686A}, $\x{686B}, $\x{686C}, $\x{686D}, $\x{686E}, $\x{686F}, $\x{6870}, $\x{6871}, $\x{6872}, $\x{6873}, $\x{6874}, $\x{6875}, $\x{6876}, $\x{6877}, $\x{6878}, $\x{6879}, $\x{687A}, $\x{6830}, $\x{6831}, $\x{6832}, $\x{6833}, $\x{6834}, $\x{6835}, $\x{6836}, $\x{6837}, $\x{6838}, $\x{6839}, $\x{682D}, $\x{685F}, $\x{6941}, $\x{6942}, $\x{6943}, $\x{6944}, $\x{6945}, $\x{6946}, $\x{6947}, $\x{6948}, $\x{6949}, $\x{694A}, $\x{694B}, $\x{694C}, $\x{694D}, $\x{694E}, $\x{694F}, $\x{6950}, $\x{6951}, $\x{6952}, $\x{6953}, $\x{6954}, $\x{6955}, $\x{6956}, $\x{6957}, $\x{6958}, $\x{6959}, $\x{695A}, $\x{6961}, $\x{6962}, $\x{6963}, $\x{6964}, $\x{6965}, $\x{6966}, $\x{6967}, $\x{6968}, $\x{6969}, $\x{696A}, $\x{696B}, $\x{696C}, $\x{696D}, $\x{696E}, $\x{696F}, $\x{6970}, $\x{6971}, $\x{6972}, $\x{6973}, $\x{6974}, $\x{6975}, $\x{6976}, $\x{6977}, $\x{6978}, $\x{6979}, $\x{697A}, $\x{6930}, $\x{6931}, $\x{6932}, $\x{6933}, $\x{6934}, $\x{6935}, $\x{6936}, $\x{6937}, $\x{6938}, $\x{6939}, $\x{692D}, $\x{695F}, $\x{6A41}, $\x{6A42}, $\x{6A43}, $\x{6A44}, $\x{6A45}, $\x{6A46}, $\x{6A47}, $\x{6A48}, $\x{6A49}, $\x{6A4A}, $\x{6A4B}, $\x{6A4C}, $\x{6A4D}, $\x{6A4E}, $\x{6A4F}, $\x{6A50}, $\x{6A51}, $\x{6A52}, $\x{6A53}, $\x{6A54}, $\x{6A55}, $\x{6A56}, $\x{6A57}, $\x{6A58}, $\x{6A59}, $\x{6A5A}, $\x{6A61}, $\x{6A62}, $\x{6A63}, $\x{6A64}, $\x{6A65}, $\x{6A66}, $\x{6A67}, $\x{6A68}, $\x{6A69}, $\x{6A6A}, $\x{6A6B}, $\x{6A6C}, $\x{6A6D}, $\x{6A6E}, $\x{6A6F}, $\x{6A70}, $\x{6A71}, $\x{6A72}, $\x{6A73}, $\x{6A74}, $\x{6A75}, $\x{6A76}, $\x{6A77}, $\x{6A78}, $\x{6A79}, $\x{6A7A}, $\x{6A30}, $\x{6A31}, $\x{6A32}, $\x{6A33}, $\x{6A34}, $\x{6A35}, $\x{6A36}, $\x{6A37}, $\x{6A38}, $\x{6A39}, $\x{6A2D}, $\x{6A5F}, $\x{6B41}, $\x{6B42}, $\x{6B43}, $\x{6B44}, $\x{6B45}, $\x{6B46}, $\x{6B47}, $\x{6B48}, $\x{6B49}, $\x{6B4A}, $\x{6B4B}, $\x{6B4C}, $\x{6B4D}, $\x{6B4E}, $\x{6B4F}, $\x{6B50}, $\x{6B51}, $\x{6B52}, $\x{6B53}, $\x{6B54}, $\x{6B55}, $\x{6B56}, $\x{6B57}, $\x{6B58}, $\x{6B59}, $\x{6B5A}, $\x{6B61}, $\x{6B62}, $\x{6B63}, $\x{6B64}, $\x{6B65}, $\x{6B66}, $\x{6B67}, $\x{6B68}, $\x{6B69}, $\x{6B6A}, $\x{6B6B}, $\x{6B6C}, $\x{6B6D}, $\x{6B6E}, $\x{6B6F}, $\x{6B70}, $\x{6B71}, $\x{6B72}, $\x{6B73}, $\x{6B74}, $\x{6B75}, $\x{6B76}, $\x{6B77}, $\x{6B78}, $\x{6B79}, $\x{6B7A}, $\x{6B30}, $\x{6B31}, $\x{6B32}, $\x{6B33}, $\x{6B34}, $\x{6B35}, $\x{6B36}, $\x{6B37}, $\x{6B38}, $\x{6B39}, $\x{6B2D}, $\x{6B5F}, $\x{6C41}, $\x{6C42}, $\x{6C43}, $\x{6C44}, $\x{6C45}, $\x{6C46}, $\x{6C47}, $\x{6C48}, $\x{6C49}, $\x{6C4A}, $\x{6C4B}, $\x{6C4C}, $\x{6C4D}, $\x{6C4E}, $\x{6C4F}, $\x{6C50}, $\x{6C51}, $\x{6C52}, $\x{6C53}, $\x{6C54}, $\x{6C55}, $\x{6C56}, $\x{6C57}, $\x{6C58}, $\x{6C59}, $\x{6C5A}, $\x{6C61}, $\x{6C62}, $\x{6C63}, $\x{6C64}, $\x{6C65}, $\x{6C66}, $\x{6C67}, $\x{6C68}, $\x{6C69}, $\x{6C6A}, $\x{6C6B}, $\x{6C6C}, $\x{6C6D}, $\x{6C6E}, $\x{6C6F}, $\x{6C70}, $\x{6C71}, $\x{6C72}, $\x{6C73}, $\x{6C74}, $\x{6C75}, $\x{6C76}, $\x{6C77}, $\x{6C78}, $\x{6C79}, $\x{6C7A}, $\x{6C30}, $\x{6C31}, $\x{6C32}, $\x{6C33}, $\x{6C34}, $\x{6C35}, $\x{6C36}, $\x{6C37}, $\x{6C38}, $\x{6C39}, $\x{6C2D}, $\x{6C5F}, $\x{6D41}, $\x{6D42}, $\x{6D43}, $\x{6D44}, $\x{6D45}, $\x{6D46}, $\x{6D47}, $\x{6D48}, $\x{6D49}, $\x{6D4A}, $\x{6D4B}, $\x{6D4C}, $\x{6D4D}, $\x{6D4E}, $\x{6D4F}, $\x{6D50}, $\x{6D51}, $\x{6D52}, $\x{6D53}, $\x{6D54}, $\x{6D55}, $\x{6D56}, $\x{6D57}, $\x{6D58}, $\x{6D59}, $\x{6D5A}, $\x{6D61}, $\x{6D62}, $\x{6D63}, $\x{6D64}, $\x{6D65}, $\x{6D66}, $\x{6D67}, $\x{6D68}, $\x{6D69}, $\x{6D6A}, $\x{6D6B}, $\x{6D6C}, $\x{6D6D}, $\x{6D6E}, $\x{6D6F}, $\x{6D70}, $\x{6D71}, $\x{6D72}, $\x{6D73}, $\x{6D74}, $\x{6D75}, $\x{6D76}, $\x{6D77}, $\x{6D78}, $\x{6D79}, $\x{6D7A}, $\x{6D30}, $\x{6D31}, $\x{6D32}, $\x{6D33}, $\x{6D34}, $\x{6D35}, $\x{6D36}, $\x{6D37}, $\x{6D38}, $\x{6D39}, $\x{6D2D}, $\x{6D5F}, $\x{6E41}, $\x{6E42}, $\x{6E43}, $\x{6E44}, $\x{6E45}, $\x{6E46}, $\x{6E47}, $\x{6E48}, $\x{6E49}, $\x{6E4A}, $\x{6E4B}, $\x{6E4C}, $\x{6E4D}, $\x{6E4E}, $\x{6E4F}, $\x{6E50}, $\x{6E51}, $\x{6E52}, $\x{6E53}, $\x{6E54}, $\x{6E55}, $\x{6E56}, $\x{6E57}, $\x{6E58}, $\x{6E59}, $\x{6E5A}, $\x{6E61}, $\x{6E62}, $\x{6E63}, $\x{6E64}, $\x{6E65}, $\x{6E66}, $\x{6E67}, $\x{6E68}, $\x{6E69}, $\x{6E6A}, $\x{6E6B}, $\x{6E6C}, $\x{6E6D}, $\x{6E6E}, $\x{6E6F}, $\x{6E70}, $\x{6E71}, $\x{6E72}, $\x{6E73}, $\x{6E74}, $\x{6E75}, $\x{6E76}, $\x{6E77}, $\x{6E78}, $\x{6E79}, $\x{6E7A}, $\x{6E30}, $\x{6E31}, $\x{6E32}, $\x{6E33}, $\x{6E34}, $\x{6E35}, $\x{6E36}, $\x{6E37}, $\x{6E38}, $\x{6E39}, $\x{6E2D}, $\x{6E5F}, $\x{6F41}, $\x{6F42}, $\x{6F43}, $\x{6F44}, $\x{6F45}, $\x{6F46}, $\x{6F47}, $\x{6F48}, $\x{6F49}, $\x{6F4A}, $\x{6F4B}, $\x{6F4C}, $\x{6F4D}, $\x{6F4E}, $\x{6F4F}, $\x{6F50}, $\x{6F51}, $\x{6F52}, $\x{6F53}, $\x{6F54}, $\x{6F55}, $\x{6F56}, $\x{6F57}, $\x{6F58}, $\x{6F59}, $\x{6F5A}, $\x{6F61}, $\x{6F62}, $\x{6F63}, $\x{6F64}, $\x{6F65}, $\x{6F66}, $\x{6F67}, $\x{6F68}, $\x{6F69}, $\x{6F6A}, $\x{6F6B}, $\x{6F6C}, $\x{6F6D}, $\x{6F6E}, $\x{6F6F}, $\x{6F70}, $\x{6F71}, $\x{6F72}, $\x{6F73}, $\x{6F74}, $\x{6F75}, $\x{6F76}, $\x{6F77}, $\x{6F78}, $\x{6F79}, $\x{6F7A}, $\x{6F30}, $\x{6F31}, $\x{6F32}, $\x{6F33}, $\x{6F34}, $\x{6F35}, $\x{6F36}, $\x{6F37}, $\x{6F38}, $\x{6F39}, $\x{6F2D}, $\x{6F5F}, $\x{7041}, $\x{7042}, $\x{7043}, $\x{7044}, $\x{7045}, $\x{7046}, $\x{7047}, $\x{7048}, $\x{7049}, $\x{704A}, $\x{704B}, $\x{704C}, $\x{704D}, $\x{704E}, $\x{704F}, $\x{7050}, $\x{7051}, $\x{7052}, $\x{7053}, $\x{7054}, $\x{7055}, $\x{7056}, $\x{7057}, $\x{7058}, $\x{7059}, $\x{705A}, $\x{7061}, $\x{7062}, $\x{7063}, $\x{7064}, $\x{7065}, $\x{7066}, $\x{7067}, $\x{7068}, $\x{7069}, $\x{706A}, $\x{706B}, $\x{706C}, $\x{706D}, $\x{706E}, $\x{706F}, $\x{7070}, $\x{7071}, $\x{7072}, $\x{7073}, $\x{7074}, $\x{7075}, $\x{7076}, $\x{7077}, $\x{7078}, $\x{7079}, $\x{707A}, $\x{7030}, $\x{7031}, $\x{7032}, $\x{7033}, $\x{7034}, $\x{7035}, $\x{7036}, $\x{7037}, $\x{7038}, $\x{7039}, $\x{702D}, $\x{705F}, $\x{7141}, $\x{7142}, $\x{7143}, $\x{7144}, $\x{7145}, $\x{7146}, $\x{7147}, $\x{7148}, $\x{7149}, $\x{714A}, $\x{714B}, $\x{714C}, $\x{714D}, $\x{714E}, $\x{714F}, $\x{7150}, $\x{7151}, $\x{7152}, $\x{7153}, $\x{7154}, $\x{7155}, $\x{7156}, $\x{7157}, $\x{7158}, $\x{7159}, $\x{715A}, $\x{7161}, $\x{7162}, $\x{7163}, $\x{7164}, $\x{7165}, $\x{7166}, $\x{7167}, $\x{7168}, $\x{7169}, $\x{716A}, $\x{716B}, $\x{716C}, $\x{716D}, $\x{716E}, $\x{716F}, $\x{7170}, $\x{7171}, $\x{7172}, $\x{7173}, $\x{7174}, $\x{7175}, $\x{7176}, $\x{7177}, $\x{7178}, $\x{7179}, $\x{717A}, $\x{7130}, $\x{7131}, $\x{7132}, $\x{7133}, $\x{7134}, $\x{7135}, $\x{7136}, $\x{7137}, $\x{7138}, $\x{7139}, $\x{712D}, $\x{715F}, $\x{7241}, $\x{7242}, $\x{7243}, $\x{7244}, $\x{7245}, $\x{7246}, $\x{7247}, $\x{7248}, $\x{7249}, $\x{724A}, $\x{724B}, $\x{724C}, $\x{724D}, $\x{724E}, $\x{724F}, $\x{7250}, $\x{7251}, $\x{7252}, $\x{7253}, $\x{7254}, $\x{7255}, $\x{7256}, $\x{7257}, $\x{7258}, $\x{7259}, $\x{725A}, $\x{7261}, $\x{7262}, $\x{7263}, $\x{7264}, $\x{7265}, $\x{7266}, $\x{7267}, $\x{7268}, $\x{7269}, $\x{726A}, $\x{726B}, $\x{726C}, $\x{726D}, $\x{726E}, $\x{726F}, $\x{7270}, $\x{7271}, $\x{7272}, $\x{7273}, $\x{7274}, $\x{7275}, $\x{7276}, $\x{7277}, $\x{7278}, $\x{7279}, $\x{727A}, $\x{7230}, $\x{7231}, $\x{7232}, $\x{7233}, $\x{7234}, $\x{7235}, $\x{7236}, $\x{7237}, $\x{7238}, $\x{7239}, $\x{722D}, $\x{725F}, $\x{7341}, $\x{7342}, $\x{7343}, $\x{7344}, $\x{7345}, $\x{7346}, $\x{7347}, $\x{7348}, $\x{7349}, $\x{734A}, $\x{734B}, $\x{734C}, $\x{734D}, $\x{734E}, $\x{734F}, $\x{7350}, $\x{7351}, $\x{7352}, $\x{7353}, $\x{7354}, $\x{7355}, $\x{7356}, $\x{7357}, $\x{7358}, $\x{7359}, $\x{735A}, $\x{7361}, $\x{7362}, $\x{7363}, $\x{7364}, $\x{7365}, $\x{7366}, $\x{7367}, $\x{7368}, $\x{7369}, $\x{736A}, $\x{736B}, $\x{736C}, $\x{736D}, $\x{736E}, $\x{736F}, $\x{7370}, $\x{7371}, $\x{7372}, $\x{7373}, $\x{7374}, $\x{7375}, $\x{7376}, $\x{7377}, $\x{7378}, $\x{7379}, $\x{737A}, $\x{7330}, $\x{7331}, $\x{7332}, $\x{7333}, $\x{7334}, $\x{7335}, $\x{7336}, $\x{7337}, $\x{7338}, $\x{7339}, $\x{732D}, $\x{735F}, $\x{7441}, $\x{7442}, $\x{7443}, $\x{7444}, $\x{7445}, $\x{7446}, $\x{7447}, $\x{7448}, $\x{7449}, $\x{744A}, $\x{744B}, $\x{744C}, $\x{744D}, $\x{744E}, $\x{744F}, $\x{7450}, $\x{7451}, $\x{7452}, $\x{7453}, $\x{7454}, $\x{7455}, $\x{7456}, $\x{7457}, $\x{7458}, $\x{7459}, $\x{745A}, $\x{7461}, $\x{7462}, $\x{7463}, $\x{7464}, $\x{7465}, $\x{7466}, $\x{7467}, $\x{7468}, $\x{7469}, $\x{746A}, $\x{746B}, $\x{746C}, $\x{746D}, $\x{746E}, $\x{746F}, $\x{7470}, $\x{7471}, $\x{7472}, $\x{7473}, $\x{7474}, $\x{7475}, $\x{7476}, $\x{7477}, $\x{7478}, $\x{7479}, $\x{747A}, $\x{7430}, $\x{7431}, $\x{7432}, $\x{7433}, $\x{7434}, $\x{7435}, $\x{7436}, $\x{7437}, $\x{7438}, $\x{7439}, $\x{742D}, $\x{745F}, $\x{7541}, $\x{7542}, $\x{7543}, $\x{7544}, $\x{7545}, $\x{7546}, $\x{7547}, $\x{7548}, $\x{7549}, $\x{754A}, $\x{754B}, $\x{754C}, $\x{754D}, $\x{754E}, $\x{754F}, $\x{7550}, $\x{7551}, $\x{7552}, $\x{7553}, $\x{7554}, $\x{7555}, $\x{7556}, $\x{7557}, $\x{7558}, $\x{7559}, $\x{755A}, $\x{7561}, $\x{7562}, $\x{7563}, $\x{7564}, $\x{7565}, $\x{7566}, $\x{7567}, $\x{7568}, $\x{7569}, $\x{756A}, $\x{756B}, $\x{756C}, $\x{756D}, $\x{756E}, $\x{756F}, $\x{7570}, $\x{7571}, $\x{7572}, $\x{7573}, $\x{7574}, $\x{7575}, $\x{7576}, $\x{7577}, $\x{7578}, $\x{7579}, $\x{757A}, $\x{7530}, $\x{7531}, $\x{7532}, $\x{7533}, $\x{7534}, $\x{7535}, $\x{7536}, $\x{7537}, $\x{7538}, $\x{7539}, $\x{752D}, $\x{755F}, $\x{7641}, $\x{7642}, $\x{7643}, $\x{7644}, $\x{7645}, $\x{7646}, $\x{7647}, $\x{7648}, $\x{7649}, $\x{764A}, $\x{764B}, $\x{764C}, $\x{764D}, $\x{764E}, $\x{764F}, $\x{7650}, $\x{7651}, $\x{7652}, $\x{7653}, $\x{7654}, $\x{7655}, $\x{7656}, $\x{7657}, $\x{7658}, $\x{7659}, $\x{765A}, $\x{7661}, $\x{7662}, $\x{7663}, $\x{7664}, $\x{7665}, $\x{7666}, $\x{7667}, $\x{7668}, $\x{7669}, $\x{766A}, $\x{766B}, $\x{766C}, $\x{766D}, $\x{766E}, $\x{766F}, $\x{7670}, $\x{7671}, $\x{7672}, $\x{7673}, $\x{7674}, $\x{7675}, $\x{7676}, $\x{7677}, $\x{7678}, $\x{7679}, $\x{767A}, $\x{7630}, $\x{7631}, $\x{7632}, $\x{7633}, $\x{7634}, $\x{7635}, $\x{7636}, $\x{7637}, $\x{7638}, $\x{7639}, $\x{762D}, $\x{765F}, $\x{7741}, $\x{7742}, $\x{7743}, $\x{7744}, $\x{7745}, $\x{7746}, $\x{7747}, $\x{7748}, $\x{7749}, $\x{774A}, $\x{774B}, $\x{774C}, $\x{774D}, $\x{774E}, $\x{774F}, $\x{7750}, $\x{7751}, $\x{7752}, $\x{7753}, $\x{7754}, $\x{7755}, $\x{7756}, $\x{7757}, $\x{7758}, $\x{7759}, $\x{775A}, $\x{7761}, $\x{7762}, $\x{7763}, $\x{7764}, $\x{7765}, $\x{7766}, $\x{7767}, $\x{7768}, $\x{7769}, $\x{776A}, $\x{776B}, $\x{776C}, $\x{776D}, $\x{776E}, $\x{776F}, $\x{7770}, $\x{7771}, $\x{7772}, $\x{7773}, $\x{7774}, $\x{7775}, $\x{7776}, $\x{7777}, $\x{7778}, $\x{7779}, $\x{777A}, $\x{7730}, $\x{7731}, $\x{7732}, $\x{7733}, $\x{7734}, $\x{7735}, $\x{7736}, $\x{7737}, $\x{7738}, $\x{7739}, $\x{772D}, $\x{775F}, $\x{7841}, $\x{7842}, $\x{7843}, $\x{7844}, $\x{7845}, $\x{7846}, $\x{7847}, $\x{7848}, $\x{7849}, $\x{784A}, $\x{784B}, $\x{784C}, $\x{784D}, $\x{784E}, $\x{784F}, $\x{7850}, $\x{7851}, $\x{7852}, $\x{7853}, $\x{7854}, $\x{7855}, $\x{7856}, $\x{7857}, $\x{7858}, $\x{7859}, $\x{785A}, $\x{7861}, $\x{7862}, $\x{7863}, $\x{7864}, $\x{7865}, $\x{7866}, $\x{7867}, $\x{7868}, $\x{7869}, $\x{786A}, $\x{786B}, $\x{786C}, $\x{786D}, $\x{786E}, $\x{786F}, $\x{7870}, $\x{7871}, $\x{7872}, $\x{7873}, $\x{7874}, $\x{7875}, $\x{7876}, $\x{7877}, $\x{7878}, $\x{7879}, $\x{787A}, $\x{7830}, $\x{7831}, $\x{7832}, $\x{7833}, $\x{7834}, $\x{7835}, $\x{7836}, $\x{7837}, $\x{7838}, $\x{7839}, $\x{782D}, $\x{785F}, $\x{7941}, $\x{7942}, $\x{7943}, $\x{7944}, $\x{7945}, $\x{7946}, $\x{7947}, $\x{7948}, $\x{7949}, $\x{794A}, $\x{794B}, $\x{794C}, $\x{794D}, $\x{794E}, $\x{794F}, $\x{7950}, $\x{7951}, $\x{7952}, $\x{7953}, $\x{7954}, $\x{7955}, $\x{7956}, $\x{7957}, $\x{7958}, $\x{7959}, $\x{795A}, $\x{7961}, $\x{7962}, $\x{7963}, $\x{7964}, $\x{7965}, $\x{7966}, $\x{7967}, $\x{7968}, $\x{7969}, $\x{796A}, $\x{796B}, $\x{796C}, $\x{796D}, $\x{796E}, $\x{796F}, $\x{7970}, $\x{7971}, $\x{7972}, $\x{7973}, $\x{7974}, $\x{7975}, $\x{7976}, $\x{7977}, $\x{7978}, $\x{7979}, $\x{797A}, $\x{7930}, $\x{7931}, $\x{7932}, $\x{7933}, $\x{7934}, $\x{7935}, $\x{7936}, $\x{7937}, $\x{7938}, $\x{7939}, $\x{792D}, $\x{795F}, $\x{7A41}, $\x{7A42}, $\x{7A43}, $\x{7A44}, $\x{7A45}, $\x{7A46}, $\x{7A47}, $\x{7A48}, $\x{7A49}, $\x{7A4A}, $\x{7A4B}, $\x{7A4C}, $\x{7A4D}, $\x{7A4E}, $\x{7A4F}, $\x{7A50}, $\x{7A51}, $\x{7A52}, $\x{7A53}, $\x{7A54}, $\x{7A55}, $\x{7A56}, $\x{7A57}, $\x{7A58}, $\x{7A59}, $\x{7A5A}, $\x{7A61}, $\x{7A62}, $\x{7A63}, $\x{7A64}, $\x{7A65}, $\x{7A66}, $\x{7A67}, $\x{7A68}, $\x{7A69}, $\x{7A6A}, $\x{7A6B}, $\x{7A6C}, $\x{7A6D}, $\x{7A6E}, $\x{7A6F}, $\x{7A70}, $\x{7A71}, $\x{7A72}, $\x{7A73}, $\x{7A74}, $\x{7A75}, $\x{7A76}, $\x{7A77}, $\x{7A78}, $\x{7A79}, $\x{7A7A}, $\x{7A30}, $\x{7A31}, $\x{7A32}, $\x{7A33}, $\x{7A34}, $\x{7A35}, $\x{7A36}, $\x{7A37}, $\x{7A38}, $\x{7A39}, $\x{7A2D}, $\x{7A5F}, $\x{3041}, $\x{3042}, $\x{3043}, $\x{3044}, $\x{3045}, $\x{3046}, $\x{3047}, $\x{3048}, $\x{3049}, $\x{304A}, $\x{304B}, $\x{304C}, $\x{304D}, $\x{304E}, $\x{304F}, $\x{3050}, $\x{3051}, $\x{3052}, $\x{3053}, $\x{3054}, $\x{3055}, $\x{3056}, $\x{3057}, $\x{3058}, $\x{3059}, $\x{305A}, $\x{3061}, $\x{3062}, $\x{3063}, $\x{3064}, $\x{3065}, $\x{3066}, $\x{3067}, $\x{3068}, $\x{3069}, $\x{306A}, $\x{306B}, $\x{306C}, $\x{306D}, $\x{306E}, $\x{306F}, $\x{3070}, $\x{3071}, $\x{3072}, $\x{3073}, $\x{3074}, $\x{3075}, $\x{3076}, $\x{3077}, $\x{3078}, $\x{3079}, $\x{307A}, $\x{3030}, $\x{3031}, $\x{3032}, $\x{3033}, $\x{3034}, $\x{3035}, $\x{3036}, $\x{3037}, $\x{3038}, $\x{3039}, $\x{302D}, $\x{305F}, $\x{3141}, $\x{3142}, $\x{3143}, $\x{3144}, $\x{3145}, $\x{3146}, $\x{3147}, $\x{3148}, $\x{3149}, $\x{314A}, $\x{314B}, $\x{314C}, $\x{314D}, $\x{314E}, $\x{314F}, $\x{3150}, $\x{3151}, $\x{3152}, $\x{3153}, $\x{3154}, $\x{3155}, $\x{3156}, $\x{3157}, $\x{3158}, $\x{3159}, $\x{315A}, $\x{3161}, $\x{3162}, $\x{3163}, $\x{3164}, $\x{3165}, $\x{3166}, $\x{3167}, $\x{3168}, $\x{3169}, $\x{316A}, $\x{316B}, $\x{316C}, $\x{316D}, $\x{316E}, $\x{316F}, $\x{3170}, $\x{3171}, $\x{3172}, $\x{3173}, $\x{3174}, $\x{3175}, $\x{3176}, $\x{3177}, $\x{3178}, $\x{3179}, $\x{317A}, $\x{3130}, $\x{3131}, $\x{3132}, $\x{3133}, $\x{3134}, $\x{3135}, $\x{3136}, $\x{3137}, $\x{3138}, $\x{3139}, $\x{312D}, $\x{315F}, $\x{3241}, $\x{3242}, $\x{3243}, $\x{3244}, $\x{3245}, $\x{3246}, $\x{3247}, $\x{3248}, $\x{3249}, $\x{324A}, $\x{324B}, $\x{324C}, $\x{324D}, $\x{324E}, $\x{324F}, $\x{3250}, $\x{3251}, $\x{3252}, $\x{3253}, $\x{3254}, $\x{3255}, $\x{3256}, $\x{3257}, $\x{3258}, $\x{3259}, $\x{325A}, $\x{3261}, $\x{3262}, $\x{3263}, $\x{3264}, $\x{3265}, $\x{3266}, $\x{3267}, $\x{3268}, $\x{3269}, $\x{326A}, $\x{326B}, $\x{326C}, $\x{326D}, $\x{326E}, $\x{326F}, $\x{3270}, $\x{3271}, $\x{3272}, $\x{3273}, $\x{3274}, $\x{3275}, $\x{3276}, $\x{3277}, $\x{3278}, $\x{3279}, $\x{327A}, $\x{3230}, $\x{3231}, $\x{3232}, $\x{3233}, $\x{3234}, $\x{3235}, $\x{3236}, $\x{3237}, $\x{3238}, $\x{3239}, $\x{322D}, $\x{325F}, $\x{3341}, $\x{3342}, $\x{3343}, $\x{3344}, $\x{3345}, $\x{3346}, $\x{3347}, $\x{3348}, $\x{3349}, $\x{334A}, $\x{334B}, $\x{334C}, $\x{334D}, $\x{334E}, $\x{334F}, $\x{3350}, $\x{3351}, $\x{3352}, $\x{3353}, $\x{3354}, $\x{3355}, $\x{3356}, $\x{3357}, $\x{3358}, $\x{3359}, $\x{335A}, $\x{3361}, $\x{3362}, $\x{3363}, $\x{3364}, $\x{3365}, $\x{3366}, $\x{3367}, $\x{3368}, $\x{3369}, $\x{336A}, $\x{336B}, $\x{336C}, $\x{336D}, $\x{336E}, $\x{336F}, $\x{3370}, $\x{3371}, $\x{3372}, $\x{3373}, $\x{3374}, $\x{3375}, $\x{3376}, $\x{3377}, $\x{3378}, $\x{3379}, $\x{337A}, $\x{3330}, $\x{3331}, $\x{3332}, $\x{3333}, $\x{3334}, $\x{3335}, $\x{3336}, $\x{3337}, $\x{3338}, $\x{3339}, $\x{332D}, $\x{335F}, $\x{3441}, $\x{3442}, $\x{3443}, $\x{3444}, $\x{3445}, $\x{3446}, $\x{3447}, $\x{3448}, $\x{3449}, $\x{344A}, $\x{344B}, $\x{344C}, $\x{344D}, $\x{344E}, $\x{344F}, $\x{3450}, $\x{3451}, $\x{3452}, $\x{3453}, $\x{3454}, $\x{3455}, $\x{3456}, $\x{3457}, $\x{3458}, $\x{3459}, $\x{345A}, $\x{3461}, $\x{3462}, $\x{3463}, $\x{3464}, $\x{3465}, $\x{3466}, $\x{3467}, $\x{3468}, $\x{3469}, $\x{346A}, $\x{346B}, $\x{346C}, $\x{346D}, $\x{346E}, $\x{346F}, $\x{3470}, $\x{3471}, $\x{3472}, $\x{3473}, $\x{3474}, $\x{3475}, $\x{3476}, $\x{3477}, $\x{3478}, $\x{3479}, $\x{347A}, $\x{3430}, $\x{3431}, $\x{3432}, $\x{3433}, $\x{3434}, $\x{3435}, $\x{3436}, $\x{3437}, $\x{3438}, $\x{3439}, $\x{342D}, $\x{345F}, $\x{3541}, $\x{3542}, $\x{3543}, $\x{3544}, $\x{3545}, $\x{3546}, $\x{3547}, $\x{3548}, $\x{3549}, $\x{354A}, $\x{354B}, $\x{354C}, $\x{354D}, $\x{354E}, $\x{354F}, $\x{3550}, $\x{3551}, $\x{3552}, $\x{3553}, $\x{3554}, $\x{3555}, $\x{3556}, $\x{3557}, $\x{3558}, $\x{3559}, $\x{355A}, $\x{3561}, $\x{3562}, $\x{3563}, $\x{3564}, $\x{3565}, $\x{3566}, $\x{3567}, $\x{3568}, $\x{3569}, $\x{356A}, $\x{356B}, $\x{356C}, $\x{356D}, $\x{356E}, $\x{356F}, $\x{3570}, $\x{3571}, $\x{3572}, $\x{3573}, $\x{3574}, $\x{3575}, $\x{3576}, $\x{3577}, $\x{3578}, $\x{3579}, $\x{357A}, $\x{3530}, $\x{3531}, $\x{3532}, $\x{3533}, $\x{3534}, $\x{3535}, $\x{3536}, $\x{3537}, $\x{3538}, $\x{3539}, $\x{352D}, $\x{355F}, $\x{3641}, $\x{3642}, $\x{3643}, $\x{3644}, $\x{3645}, $\x{3646}, $\x{3647}, $\x{3648}, $\x{3649}, $\x{364A}, $\x{364B}, $\x{364C}, $\x{364D}, $\x{364E}, $\x{364F}, $\x{3650}, $\x{3651}, $\x{3652}, $\x{3653}, $\x{3654}, $\x{3655}, $\x{3656}, $\x{3657}, $\x{3658}, $\x{3659}, $\x{365A}, $\x{3661}, $\x{3662}, $\x{3663}, $\x{3664}, $\x{3665}, $\x{3666}, $\x{3667}, $\x{3668}, $\x{3669}, $\x{366A}, $\x{366B}, $\x{366C}, $\x{366D}, $\x{366E}, $\x{366F}, $\x{3670}, $\x{3671}, $\x{3672}, $\x{3673}, $\x{3674}, $\x{3675}, $\x{3676}, $\x{3677}, $\x{3678}, $\x{3679}, $\x{367A}, $\x{3630}, $\x{3631}, $\x{3632}, $\x{3633}, $\x{3634}, $\x{3635}, $\x{3636}, $\x{3637}, $\x{3638}, $\x{3639}, $\x{362D}, $\x{365F}, $\x{3741}, $\x{3742}, $\x{3743}, $\x{3744}, $\x{3745}, $\x{3746}, $\x{3747}, $\x{3748}, $\x{3749}, $\x{374A}, $\x{374B}, $\x{374C}, $\x{374D}, $\x{374E}, $\x{374F}, $\x{3750}, $\x{3751}, $\x{3752}, $\x{3753}, $\x{3754}, $\x{3755}, $\x{3756}, $\x{3757}, $\x{3758}, $\x{3759}, $\x{375A}, $\x{3761}, $\x{3762}, $\x{3763}, $\x{3764}, $\x{3765}, $\x{3766}, $\x{3767}, $\x{3768}, $\x{3769}, $\x{376A}, $\x{376B}, $\x{376C}, $\x{376D}, $\x{376E}, $\x{376F}, $\x{3770}, $\x{3771}, $\x{3772}, $\x{3773}, $\x{3774}, $\x{3775}, $\x{3776}, $\x{3777}, $\x{3778}, $\x{3779}, $\x{377A}, $\x{3730}, $\x{3731}, $\x{3732}, $\x{3733}, $\x{3734}, $\x{3735}, $\x{3736}, $\x{3737}, $\x{3738}, $\x{3739}, $\x{372D}, $\x{375F}, $\x{3841}, $\x{3842}, $\x{3843}, $\x{3844}, $\x{3845}, $\x{3846}, $\x{3847}, $\x{3848}, $\x{3849}, $\x{384A}, $\x{384B}, $\x{384C}, $\x{384D}, $\x{384E}, $\x{384F}, $\x{3850}, $\x{3851}, $\x{3852}, $\x{3853}, $\x{3854}, $\x{3855}, $\x{3856}, $\x{3857}, $\x{3858}, $\x{3859}, $\x{385A}, $\x{3861}, $\x{3862}, $\x{3863}, $\x{3864}, $\x{3865}, $\x{3866}, $\x{3867}, $\x{3868}, $\x{3869}, $\x{386A}, $\x{386B}, $\x{386C}, $\x{386D}, $\x{386E}, $\x{386F}, $\x{3870}, $\x{3871}, $\x{3872}, $\x{3873}, $\x{3874}, $\x{3875}, $\x{3876}, $\x{3877}, $\x{3878}, $\x{3879}, $\x{387A}, $\x{3830}, $\x{3831}, $\x{3832}, $\x{3833}, $\x{3834}, $\x{3835}, $\x{3836}, $\x{3837}, $\x{3838}, $\x{3839}, $\x{382D}, $\x{385F}, $\x{3941}, $\x{3942}, $\x{3943}, $\x{3944}, $\x{3945}, $\x{3946}, $\x{3947}, $\x{3948}, $\x{3949}, $\x{394A}, $\x{394B}, $\x{394C}, $\x{394D}, $\x{394E}, $\x{394F}, $\x{3950}, $\x{3951}, $\x{3952}, $\x{3953}, $\x{3954}, $\x{3955}, $\x{3956}, $\x{3957}, $\x{3958}, $\x{3959}, $\x{395A}, $\x{3961}, $\x{3962}, $\x{3963}, $\x{3964}, $\x{3965}, $\x{3966}, $\x{3967}, $\x{3968}, $\x{3969}, $\x{396A}, $\x{396B}, $\x{396C}, $\x{396D}, $\x{396E}, $\x{396F}, $\x{3970}, $\x{3971}, $\x{3972}, $\x{3973}, $\x{3974}, $\x{3975}, $\x{3976}, $\x{3977}, $\x{3978}, $\x{3979}, $\x{397A}, $\x{3930}, $\x{3931}, $\x{3932}, $\x{3933}, $\x{3934}, $\x{3935}, $\x{3936}, $\x{3937}, $\x{3938}, $\x{3939}, $\x{392D}, $\x{395F}, $\x{2D41}, $\x{2D42}, $\x{2D43}, $\x{2D44}, $\x{2D45}, $\x{2D46}, $\x{2D47}, $\x{2D48}, $\x{2D49}, $\x{2D4A}, $\x{2D4B}, $\x{2D4C}, $\x{2D4D}, $\x{2D4E}, $\x{2D4F}, $\x{2D50}, $\x{2D51}, $\x{2D52}, $\x{2D53}, $\x{2D54}, $\x{2D55}, $\x{2D56}, $\x{2D57}, $\x{2D58}, $\x{2D59}, $\x{2D5A}, $\x{2D61}, $\x{2D62}, $\x{2D63}, $\x{2D64}, $\x{2D65}, $\x{2D66}, $\x{2D67}, $\x{2D68}, $\x{2D69}, $\x{2D6A}, $\x{2D6B}, $\x{2D6C}, $\x{2D6D}, $\x{2D6E}, $\x{2D6F}, $\x{2D70}, $\x{2D71}, $\x{2D72}, $\x{2D73}, $\x{2D74}, $\x{2D75}, $\x{2D76}, $\x{2D77}, $\x{2D78}, $\x{2D79}, $\x{2D7A}, $\x{2D30}, $\x{2D31}, $\x{2D32}, $\x{2D33}, $\x{2D34}, $\x{2D35}, $\x{2D36}, $\x{2D37}, $\x{2D38}, $\x{2D39}, $\x{2D2D}, $\x{2D5F}, $\x{5F41}, $\x{5F42}, $\x{5F43}, $\x{5F44}, $\x{5F45}, $\x{5F46}, $\x{5F47}, $\x{5F48}, $\x{5F49}, $\x{5F4A}, $\x{5F4B}, $\x{5F4C}, $\x{5F4D}, $\x{5F4E}, $\x{5F4F}, $\x{5F50}, $\x{5F51}, $\x{5F52}, $\x{5F53}, $\x{5F54}, $\x{5F55}, $\x{5F56}, $\x{5F57}, $\x{5F58}, $\x{5F59}, $\x{5F5A}, $\x{5F61}, $\x{5F62}, $\x{5F63}, $\x{5F64}, $\x{5F65}, $\x{5F66}, $\x{5F67}, $\x{5F68}, $\x{5F69}, $\x{5F6A}, $\x{5F6B}, $\x{5F6C}, $\x{5F6D}, $\x{5F6E}, $\x{5F6F}, $\x{5F70}, $\x{5F71}, $\x{5F72}, $\x{5F73}, $\x{5F74}, $\x{5F75}, $\x{5F76}, $\x{5F77}, $\x{5F78}, $\x{5F79}, $\x{5F7A}, $\x{5F30}, $\x{5F31}, $\x{5F32}, $\x{5F33}, $\x{5F34}, $\x{5F35}, $\x{5F36}, $\x{5F37}, $\x{5F38}, $\x{5F39}, $\x{5F2D}, $\x{5F5F} }). potatosalad-erlang-jose-30ac019/src/base/jose_base64.erl0000664000232200023220000024717015267717064023466 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc RFC 4648, Section 4: https://tools.ietf.org/html/rfc4648#section-4 %%% %%% @end %%% Created : 11 May 2017 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_base64). -include("jose_base.hrl"). %% API -export([decode/1]). -export([decode/2]). -export(['decode!'/1]). -export(['decode!'/2]). -export([encode/1]). -export([encode/2]). -export([random/1]). -export([random/2]). % Macros -define(B64_TO_INT(C), case C of $A -> 16#00; $B -> 16#01; $C -> 16#02; $D -> 16#03; $E -> 16#04; $F -> 16#05; $G -> 16#06; $H -> 16#07; $I -> 16#08; $J -> 16#09; $K -> 16#0A; $L -> 16#0B; $M -> 16#0C; $N -> 16#0D; $O -> 16#0E; $P -> 16#0F; $Q -> 16#10; $R -> 16#11; $S -> 16#12; $T -> 16#13; $U -> 16#14; $V -> 16#15; $W -> 16#16; $X -> 16#17; $Y -> 16#18; $Z -> 16#19; $a -> 16#1A; $b -> 16#1B; $c -> 16#1C; $d -> 16#1D; $e -> 16#1E; $f -> 16#1F; $g -> 16#20; $h -> 16#21; $i -> 16#22; $j -> 16#23; $k -> 16#24; $l -> 16#25; $m -> 16#26; $n -> 16#27; $o -> 16#28; $p -> 16#29; $q -> 16#2A; $r -> 16#2B; $s -> 16#2C; $t -> 16#2D; $u -> 16#2E; $v -> 16#2F; $w -> 16#30; $x -> 16#31; $y -> 16#32; $z -> 16#33; $0 -> 16#34; $1 -> 16#35; $2 -> 16#36; $3 -> 16#37; $4 -> 16#38; $5 -> 16#39; $6 -> 16#3A; $7 -> 16#3B; $8 -> 16#3C; $9 -> 16#3D; $+ -> 16#3E; $/ -> 16#3F end). %%%=================================================================== %%% API functions %%%=================================================================== decode(Input) when ?is_iodata(Input) -> decode(Input, #{}). decode(Input, Opts) when ?is_iodata(Input) andalso is_map(Opts) -> try 'decode!'(Input, Opts) of Output when is_binary(Output) -> {ok, Output} catch _:_ -> error end; decode(Input, Opts) when ?is_iodata(Input) andalso is_list(Opts) -> decode(Input, maps:from_list(Opts)). 'decode!'(Input) when ?is_iodata(Input) -> 'decode!'(Input, #{}). 'decode!'([], #{}) -> <<>>; 'decode!'(<<>>, #{}) -> <<>>; 'decode!'(Input, Opts) when ?is_iodata(Input) andalso is_map(Opts) -> Padding = maps:get('padding', Opts, nil), Size = erlang:iolist_size(Input), Offset = case Padding of _ when (Padding == false orelse Padding == nil) andalso Size =< 4 -> 0; _ when (Padding == false orelse Padding == nil) andalso (Size rem 4) =/= 0 -> Size - (Size rem 4); _ when (Padding == false orelse Padding == nil) -> Size - 4; _ when (Padding == true orelse Padding == nil) andalso Size >= 4 -> Size - 4; _ -> erlang:error({badarg, [Input, Opts]}) end, << Head0:Offset/binary, Tail0/binary >> = ?to_binary(Input), Head = << << (?B64_TO_INT(V)):6 >> || << V >> <= Head0 >>, Tail = case Padding of false -> case Tail0 of << T0:8, T1:8 >> -> << (?B64_TO_INT(T0)):6, (?B64_TO_INT(T1) bsr 4):2 >>; << T0:8, T1:8, T2:8 >> -> << (?B64_TO_INT(T0)):6, (?B64_TO_INT(T1)):6, (?B64_TO_INT(T2) bsr 2):4 >>; << T:4/binary >> -> << << (?B64_TO_INT(V)):6 >> || << V >> <= T >>; <<>> -> <<>>; _ -> erlang:error({badarg, [Input, Opts]}) end; nil -> case Tail0 of << T0:8, T1:8, $=, $= >> -> << (?B64_TO_INT(T0)):6, (?B64_TO_INT(T1) bsr 4):2 >>; << T0:8, T1:8, T2:8, $= >> -> << (?B64_TO_INT(T0)):6, (?B64_TO_INT(T1)):6, (?B64_TO_INT(T2) bsr 2):4 >>; << T0:8, T1:8 >> -> << (?B64_TO_INT(T0)):6, (?B64_TO_INT(T1) bsr 4):2 >>; << T0:8, T1:8, T2:8 >> -> << (?B64_TO_INT(T0)):6, (?B64_TO_INT(T1)):6, (?B64_TO_INT(T2) bsr 2):4 >>; << T:4/binary >> -> << << (?B64_TO_INT(V)):6 >> || << V >> <= T >>; <<>> -> <<>> end; true -> case Tail0 of << T0:8, T1:8, $=, $= >> -> << (?B64_TO_INT(T0)):6, (?B64_TO_INT(T1) bsr 4):2 >>; << T0:8, T1:8, T2:8, $= >> -> << (?B64_TO_INT(T0)):6, (?B64_TO_INT(T1)):6, (?B64_TO_INT(T2) bsr 2):4 >>; << T:4/binary >> -> << << (?B64_TO_INT(V)):6 >> || << V >> <= T >>; <<>> -> <<>>; _ -> erlang:error({badarg, [Input, Opts]}) end end, << Head/binary, Tail/binary >>; 'decode!'(Input, Opts) when ?is_iodata(Input) andalso is_list(Opts) -> 'decode!'(Input, maps:from_list(Opts)). encode(Input) when ?is_iodata(Input) -> encode(Input, #{}). encode(Input, Opts) when ?is_iodata(Input) andalso is_map(Opts) -> Padding = maps:get('padding', Opts, true), Offset = 6 * (erlang:iolist_size(Input) div 6), << Head:Offset/binary, Tail/binary >> = ?to_binary(Input), H = << << (encode_pair(V0)):16, (encode_pair(V1)):16, (encode_pair(V2)):16, (encode_pair(V3)):16 >> || << V0:12, V1:12, V2:12, V3:12 >> <= Head >>, {T, Pad} = case Tail of << T0:12, T1:12, T2:12, T3:4 >> -> {<< (encode_pair(T0)):16, (encode_pair(T1)):16, (encode_pair(T2)):16, (encode_char(T3 bsl 2)):8 >>, << $= >>}; << T0:12, T1:12, T2:8 >> -> {<< (encode_pair(T0)):16, (encode_pair(T1)):16, (encode_pair(T2 bsl 4)):16 >>, << $=, $= >>}; << T0:12, T1:12 >> -> {<< (encode_pair(T0)):16, (encode_pair(T1)):16 >>, <<>>}; << T0:12, T1:4 >> -> {<< (encode_pair(T0)):16, (encode_char(T1 bsl 2)):8 >>, <<>>}; << T0:8 >> -> {<< (encode_pair(T0 bsl 4)):16 >>, << $=, $= >>}; <<>> -> {<<>>, <<>>} end, case Padding of true -> << H/binary, T/binary, Pad/binary >>; false -> << H/binary, T/binary >>; _ -> erlang:error({badarg, [Input, Opts]}) end; encode(Input, Opts) when ?is_iodata(Input) andalso is_list(Opts) -> encode(Input, maps:from_list(Opts)). random(Bytes) when is_integer(Bytes) andalso Bytes >= 0 -> random(Bytes, #{}). random(0, Opts) when is_map(Opts) -> <<>>; random(Bytes, Opts) when (Bytes =:= 1) andalso is_map(Opts) -> erlang:error({badarg, [Bytes, Opts]}); random(Bytes, Opts) when is_integer(Bytes) andalso Bytes > 0 andalso is_map(Opts) -> Padding = maps:get('padding', Opts, true), R = (Bytes rem 4), Size = case Padding of true when R =:= 0 -> (Bytes * 3) div 4; false when R =:= 0 orelse R =:= 2 orelse R =:= 3 -> (Bytes * 3) div 4; _ -> erlang:error({badarg, [Bytes, Opts]}) end, Binary = crypto:strong_rand_bytes(Size), encode(Binary, Opts); random(Bytes, Opts) when is_integer(Bytes) andalso Bytes >= 0 andalso is_list(Opts) -> random(Bytes, maps:from_list(Opts)). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- -compile({inline, [{encode_char, 1}]}). %% @private encode_char(V) -> element(V + 1, { $A, $B, $C, $D, $E, $F, $G, $H, $I, $J, $K, $L, $M, $N, $O, $P, $Q, $R, $S, $T, $U, $V, $W, $X, $Y, $Z, $a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m, $n, $o, $p, $q, $r, $s, $t, $u, $v, $w, $x, $y, $z, $0, $1, $2, $3, $4, $5, $6, $7, $8, $9, $+, $/ }). -compile({inline, [{encode_pair, 1}]}). %% @private encode_pair(V) -> element(V + 1, { $\x{4141}, $\x{4142}, $\x{4143}, $\x{4144}, $\x{4145}, $\x{4146}, $\x{4147}, $\x{4148}, $\x{4149}, $\x{414A}, $\x{414B}, $\x{414C}, $\x{414D}, $\x{414E}, $\x{414F}, $\x{4150}, $\x{4151}, $\x{4152}, $\x{4153}, $\x{4154}, $\x{4155}, $\x{4156}, $\x{4157}, $\x{4158}, $\x{4159}, $\x{415A}, $\x{4161}, $\x{4162}, $\x{4163}, $\x{4164}, $\x{4165}, $\x{4166}, $\x{4167}, $\x{4168}, $\x{4169}, $\x{416A}, $\x{416B}, $\x{416C}, $\x{416D}, $\x{416E}, $\x{416F}, $\x{4170}, $\x{4171}, $\x{4172}, $\x{4173}, $\x{4174}, $\x{4175}, $\x{4176}, $\x{4177}, $\x{4178}, $\x{4179}, $\x{417A}, $\x{4130}, $\x{4131}, $\x{4132}, $\x{4133}, $\x{4134}, $\x{4135}, $\x{4136}, $\x{4137}, $\x{4138}, $\x{4139}, $\x{412B}, $\x{412F}, $\x{4241}, $\x{4242}, $\x{4243}, $\x{4244}, $\x{4245}, $\x{4246}, $\x{4247}, $\x{4248}, $\x{4249}, $\x{424A}, $\x{424B}, $\x{424C}, $\x{424D}, $\x{424E}, $\x{424F}, $\x{4250}, $\x{4251}, $\x{4252}, $\x{4253}, $\x{4254}, $\x{4255}, $\x{4256}, $\x{4257}, $\x{4258}, $\x{4259}, $\x{425A}, $\x{4261}, $\x{4262}, $\x{4263}, $\x{4264}, $\x{4265}, $\x{4266}, $\x{4267}, $\x{4268}, $\x{4269}, $\x{426A}, $\x{426B}, $\x{426C}, $\x{426D}, $\x{426E}, $\x{426F}, $\x{4270}, $\x{4271}, $\x{4272}, $\x{4273}, $\x{4274}, $\x{4275}, $\x{4276}, $\x{4277}, $\x{4278}, $\x{4279}, $\x{427A}, $\x{4230}, $\x{4231}, $\x{4232}, $\x{4233}, $\x{4234}, $\x{4235}, $\x{4236}, $\x{4237}, $\x{4238}, $\x{4239}, $\x{422B}, $\x{422F}, $\x{4341}, $\x{4342}, $\x{4343}, $\x{4344}, $\x{4345}, $\x{4346}, $\x{4347}, $\x{4348}, $\x{4349}, $\x{434A}, $\x{434B}, $\x{434C}, $\x{434D}, $\x{434E}, $\x{434F}, $\x{4350}, $\x{4351}, $\x{4352}, $\x{4353}, $\x{4354}, $\x{4355}, $\x{4356}, $\x{4357}, $\x{4358}, $\x{4359}, $\x{435A}, $\x{4361}, $\x{4362}, $\x{4363}, $\x{4364}, $\x{4365}, $\x{4366}, $\x{4367}, $\x{4368}, $\x{4369}, $\x{436A}, $\x{436B}, $\x{436C}, $\x{436D}, $\x{436E}, $\x{436F}, $\x{4370}, $\x{4371}, $\x{4372}, $\x{4373}, $\x{4374}, $\x{4375}, $\x{4376}, $\x{4377}, $\x{4378}, $\x{4379}, $\x{437A}, $\x{4330}, $\x{4331}, $\x{4332}, $\x{4333}, $\x{4334}, $\x{4335}, $\x{4336}, $\x{4337}, $\x{4338}, $\x{4339}, $\x{432B}, $\x{432F}, $\x{4441}, $\x{4442}, $\x{4443}, $\x{4444}, $\x{4445}, $\x{4446}, $\x{4447}, $\x{4448}, $\x{4449}, $\x{444A}, $\x{444B}, $\x{444C}, $\x{444D}, $\x{444E}, $\x{444F}, $\x{4450}, $\x{4451}, $\x{4452}, $\x{4453}, $\x{4454}, $\x{4455}, $\x{4456}, $\x{4457}, $\x{4458}, $\x{4459}, $\x{445A}, $\x{4461}, $\x{4462}, $\x{4463}, $\x{4464}, $\x{4465}, $\x{4466}, $\x{4467}, $\x{4468}, $\x{4469}, $\x{446A}, $\x{446B}, $\x{446C}, $\x{446D}, $\x{446E}, $\x{446F}, $\x{4470}, $\x{4471}, $\x{4472}, $\x{4473}, $\x{4474}, $\x{4475}, $\x{4476}, $\x{4477}, $\x{4478}, $\x{4479}, $\x{447A}, $\x{4430}, $\x{4431}, $\x{4432}, $\x{4433}, $\x{4434}, $\x{4435}, $\x{4436}, $\x{4437}, $\x{4438}, $\x{4439}, $\x{442B}, $\x{442F}, $\x{4541}, $\x{4542}, $\x{4543}, $\x{4544}, $\x{4545}, $\x{4546}, $\x{4547}, $\x{4548}, $\x{4549}, $\x{454A}, $\x{454B}, $\x{454C}, $\x{454D}, $\x{454E}, $\x{454F}, $\x{4550}, $\x{4551}, $\x{4552}, $\x{4553}, $\x{4554}, $\x{4555}, $\x{4556}, $\x{4557}, $\x{4558}, $\x{4559}, $\x{455A}, $\x{4561}, $\x{4562}, $\x{4563}, $\x{4564}, $\x{4565}, $\x{4566}, $\x{4567}, $\x{4568}, $\x{4569}, $\x{456A}, $\x{456B}, $\x{456C}, $\x{456D}, $\x{456E}, $\x{456F}, $\x{4570}, $\x{4571}, $\x{4572}, $\x{4573}, $\x{4574}, $\x{4575}, $\x{4576}, $\x{4577}, $\x{4578}, $\x{4579}, $\x{457A}, $\x{4530}, $\x{4531}, $\x{4532}, $\x{4533}, $\x{4534}, $\x{4535}, $\x{4536}, $\x{4537}, $\x{4538}, $\x{4539}, $\x{452B}, $\x{452F}, $\x{4641}, $\x{4642}, $\x{4643}, $\x{4644}, $\x{4645}, $\x{4646}, $\x{4647}, $\x{4648}, $\x{4649}, $\x{464A}, $\x{464B}, $\x{464C}, $\x{464D}, $\x{464E}, $\x{464F}, $\x{4650}, $\x{4651}, $\x{4652}, $\x{4653}, $\x{4654}, $\x{4655}, $\x{4656}, $\x{4657}, $\x{4658}, $\x{4659}, $\x{465A}, $\x{4661}, $\x{4662}, $\x{4663}, $\x{4664}, $\x{4665}, $\x{4666}, $\x{4667}, $\x{4668}, $\x{4669}, $\x{466A}, $\x{466B}, $\x{466C}, $\x{466D}, $\x{466E}, $\x{466F}, $\x{4670}, $\x{4671}, $\x{4672}, $\x{4673}, $\x{4674}, $\x{4675}, $\x{4676}, $\x{4677}, $\x{4678}, $\x{4679}, $\x{467A}, $\x{4630}, $\x{4631}, $\x{4632}, $\x{4633}, $\x{4634}, $\x{4635}, $\x{4636}, $\x{4637}, $\x{4638}, $\x{4639}, $\x{462B}, $\x{462F}, $\x{4741}, $\x{4742}, $\x{4743}, $\x{4744}, $\x{4745}, $\x{4746}, $\x{4747}, $\x{4748}, $\x{4749}, $\x{474A}, $\x{474B}, $\x{474C}, $\x{474D}, $\x{474E}, $\x{474F}, $\x{4750}, $\x{4751}, $\x{4752}, $\x{4753}, $\x{4754}, $\x{4755}, $\x{4756}, $\x{4757}, $\x{4758}, $\x{4759}, $\x{475A}, $\x{4761}, $\x{4762}, $\x{4763}, $\x{4764}, $\x{4765}, $\x{4766}, $\x{4767}, $\x{4768}, $\x{4769}, $\x{476A}, $\x{476B}, $\x{476C}, $\x{476D}, $\x{476E}, $\x{476F}, $\x{4770}, $\x{4771}, $\x{4772}, $\x{4773}, $\x{4774}, $\x{4775}, $\x{4776}, $\x{4777}, $\x{4778}, $\x{4779}, $\x{477A}, $\x{4730}, $\x{4731}, $\x{4732}, $\x{4733}, $\x{4734}, $\x{4735}, $\x{4736}, $\x{4737}, $\x{4738}, $\x{4739}, $\x{472B}, $\x{472F}, $\x{4841}, $\x{4842}, $\x{4843}, $\x{4844}, $\x{4845}, $\x{4846}, $\x{4847}, $\x{4848}, $\x{4849}, $\x{484A}, $\x{484B}, $\x{484C}, $\x{484D}, $\x{484E}, $\x{484F}, $\x{4850}, $\x{4851}, $\x{4852}, $\x{4853}, $\x{4854}, $\x{4855}, $\x{4856}, $\x{4857}, $\x{4858}, $\x{4859}, $\x{485A}, $\x{4861}, $\x{4862}, $\x{4863}, $\x{4864}, $\x{4865}, $\x{4866}, $\x{4867}, $\x{4868}, $\x{4869}, $\x{486A}, $\x{486B}, $\x{486C}, $\x{486D}, $\x{486E}, $\x{486F}, $\x{4870}, $\x{4871}, $\x{4872}, $\x{4873}, $\x{4874}, $\x{4875}, $\x{4876}, $\x{4877}, $\x{4878}, $\x{4879}, $\x{487A}, $\x{4830}, $\x{4831}, $\x{4832}, $\x{4833}, $\x{4834}, $\x{4835}, $\x{4836}, $\x{4837}, $\x{4838}, $\x{4839}, $\x{482B}, $\x{482F}, $\x{4941}, $\x{4942}, $\x{4943}, $\x{4944}, $\x{4945}, $\x{4946}, $\x{4947}, $\x{4948}, $\x{4949}, $\x{494A}, $\x{494B}, $\x{494C}, $\x{494D}, $\x{494E}, $\x{494F}, $\x{4950}, $\x{4951}, $\x{4952}, $\x{4953}, $\x{4954}, $\x{4955}, $\x{4956}, $\x{4957}, $\x{4958}, $\x{4959}, $\x{495A}, $\x{4961}, $\x{4962}, $\x{4963}, $\x{4964}, $\x{4965}, $\x{4966}, $\x{4967}, $\x{4968}, $\x{4969}, $\x{496A}, $\x{496B}, $\x{496C}, $\x{496D}, $\x{496E}, $\x{496F}, $\x{4970}, $\x{4971}, $\x{4972}, $\x{4973}, $\x{4974}, $\x{4975}, $\x{4976}, $\x{4977}, $\x{4978}, $\x{4979}, $\x{497A}, $\x{4930}, $\x{4931}, $\x{4932}, $\x{4933}, $\x{4934}, $\x{4935}, $\x{4936}, $\x{4937}, $\x{4938}, $\x{4939}, $\x{492B}, $\x{492F}, $\x{4A41}, $\x{4A42}, $\x{4A43}, $\x{4A44}, $\x{4A45}, $\x{4A46}, $\x{4A47}, $\x{4A48}, $\x{4A49}, $\x{4A4A}, $\x{4A4B}, $\x{4A4C}, $\x{4A4D}, $\x{4A4E}, $\x{4A4F}, $\x{4A50}, $\x{4A51}, $\x{4A52}, $\x{4A53}, $\x{4A54}, $\x{4A55}, $\x{4A56}, $\x{4A57}, $\x{4A58}, $\x{4A59}, $\x{4A5A}, $\x{4A61}, $\x{4A62}, $\x{4A63}, $\x{4A64}, $\x{4A65}, $\x{4A66}, $\x{4A67}, $\x{4A68}, $\x{4A69}, $\x{4A6A}, $\x{4A6B}, $\x{4A6C}, $\x{4A6D}, $\x{4A6E}, $\x{4A6F}, $\x{4A70}, $\x{4A71}, $\x{4A72}, $\x{4A73}, $\x{4A74}, $\x{4A75}, $\x{4A76}, $\x{4A77}, $\x{4A78}, $\x{4A79}, $\x{4A7A}, $\x{4A30}, $\x{4A31}, $\x{4A32}, $\x{4A33}, $\x{4A34}, $\x{4A35}, $\x{4A36}, $\x{4A37}, $\x{4A38}, $\x{4A39}, $\x{4A2B}, $\x{4A2F}, $\x{4B41}, $\x{4B42}, $\x{4B43}, $\x{4B44}, $\x{4B45}, $\x{4B46}, $\x{4B47}, $\x{4B48}, $\x{4B49}, $\x{4B4A}, $\x{4B4B}, $\x{4B4C}, $\x{4B4D}, $\x{4B4E}, $\x{4B4F}, $\x{4B50}, $\x{4B51}, $\x{4B52}, $\x{4B53}, $\x{4B54}, $\x{4B55}, $\x{4B56}, $\x{4B57}, $\x{4B58}, $\x{4B59}, $\x{4B5A}, $\x{4B61}, $\x{4B62}, $\x{4B63}, $\x{4B64}, $\x{4B65}, $\x{4B66}, $\x{4B67}, $\x{4B68}, $\x{4B69}, $\x{4B6A}, $\x{4B6B}, $\x{4B6C}, $\x{4B6D}, $\x{4B6E}, $\x{4B6F}, $\x{4B70}, $\x{4B71}, $\x{4B72}, $\x{4B73}, $\x{4B74}, $\x{4B75}, $\x{4B76}, $\x{4B77}, $\x{4B78}, $\x{4B79}, $\x{4B7A}, $\x{4B30}, $\x{4B31}, $\x{4B32}, $\x{4B33}, $\x{4B34}, $\x{4B35}, $\x{4B36}, $\x{4B37}, $\x{4B38}, $\x{4B39}, $\x{4B2B}, $\x{4B2F}, $\x{4C41}, $\x{4C42}, $\x{4C43}, $\x{4C44}, $\x{4C45}, $\x{4C46}, $\x{4C47}, $\x{4C48}, $\x{4C49}, $\x{4C4A}, $\x{4C4B}, $\x{4C4C}, $\x{4C4D}, $\x{4C4E}, $\x{4C4F}, $\x{4C50}, $\x{4C51}, $\x{4C52}, $\x{4C53}, $\x{4C54}, $\x{4C55}, $\x{4C56}, $\x{4C57}, $\x{4C58}, $\x{4C59}, $\x{4C5A}, $\x{4C61}, $\x{4C62}, $\x{4C63}, $\x{4C64}, $\x{4C65}, $\x{4C66}, $\x{4C67}, $\x{4C68}, $\x{4C69}, $\x{4C6A}, $\x{4C6B}, $\x{4C6C}, $\x{4C6D}, $\x{4C6E}, $\x{4C6F}, $\x{4C70}, $\x{4C71}, $\x{4C72}, $\x{4C73}, $\x{4C74}, $\x{4C75}, $\x{4C76}, $\x{4C77}, $\x{4C78}, $\x{4C79}, $\x{4C7A}, $\x{4C30}, $\x{4C31}, $\x{4C32}, $\x{4C33}, $\x{4C34}, $\x{4C35}, $\x{4C36}, $\x{4C37}, $\x{4C38}, $\x{4C39}, $\x{4C2B}, $\x{4C2F}, $\x{4D41}, $\x{4D42}, $\x{4D43}, $\x{4D44}, $\x{4D45}, $\x{4D46}, $\x{4D47}, $\x{4D48}, $\x{4D49}, $\x{4D4A}, $\x{4D4B}, $\x{4D4C}, $\x{4D4D}, $\x{4D4E}, $\x{4D4F}, $\x{4D50}, $\x{4D51}, $\x{4D52}, $\x{4D53}, $\x{4D54}, $\x{4D55}, $\x{4D56}, $\x{4D57}, $\x{4D58}, $\x{4D59}, $\x{4D5A}, $\x{4D61}, $\x{4D62}, $\x{4D63}, $\x{4D64}, $\x{4D65}, $\x{4D66}, $\x{4D67}, $\x{4D68}, $\x{4D69}, $\x{4D6A}, $\x{4D6B}, $\x{4D6C}, $\x{4D6D}, $\x{4D6E}, $\x{4D6F}, $\x{4D70}, $\x{4D71}, $\x{4D72}, $\x{4D73}, $\x{4D74}, $\x{4D75}, $\x{4D76}, $\x{4D77}, $\x{4D78}, $\x{4D79}, $\x{4D7A}, $\x{4D30}, $\x{4D31}, $\x{4D32}, $\x{4D33}, $\x{4D34}, $\x{4D35}, $\x{4D36}, $\x{4D37}, $\x{4D38}, $\x{4D39}, $\x{4D2B}, $\x{4D2F}, $\x{4E41}, $\x{4E42}, $\x{4E43}, $\x{4E44}, $\x{4E45}, $\x{4E46}, $\x{4E47}, $\x{4E48}, $\x{4E49}, $\x{4E4A}, $\x{4E4B}, $\x{4E4C}, $\x{4E4D}, $\x{4E4E}, $\x{4E4F}, $\x{4E50}, $\x{4E51}, $\x{4E52}, $\x{4E53}, $\x{4E54}, $\x{4E55}, $\x{4E56}, $\x{4E57}, $\x{4E58}, $\x{4E59}, $\x{4E5A}, $\x{4E61}, $\x{4E62}, $\x{4E63}, $\x{4E64}, $\x{4E65}, $\x{4E66}, $\x{4E67}, $\x{4E68}, $\x{4E69}, $\x{4E6A}, $\x{4E6B}, $\x{4E6C}, $\x{4E6D}, $\x{4E6E}, $\x{4E6F}, $\x{4E70}, $\x{4E71}, $\x{4E72}, $\x{4E73}, $\x{4E74}, $\x{4E75}, $\x{4E76}, $\x{4E77}, $\x{4E78}, $\x{4E79}, $\x{4E7A}, $\x{4E30}, $\x{4E31}, $\x{4E32}, $\x{4E33}, $\x{4E34}, $\x{4E35}, $\x{4E36}, $\x{4E37}, $\x{4E38}, $\x{4E39}, $\x{4E2B}, $\x{4E2F}, $\x{4F41}, $\x{4F42}, $\x{4F43}, $\x{4F44}, $\x{4F45}, $\x{4F46}, $\x{4F47}, $\x{4F48}, $\x{4F49}, $\x{4F4A}, $\x{4F4B}, $\x{4F4C}, $\x{4F4D}, $\x{4F4E}, $\x{4F4F}, $\x{4F50}, $\x{4F51}, $\x{4F52}, $\x{4F53}, $\x{4F54}, $\x{4F55}, $\x{4F56}, $\x{4F57}, $\x{4F58}, $\x{4F59}, $\x{4F5A}, $\x{4F61}, $\x{4F62}, $\x{4F63}, $\x{4F64}, $\x{4F65}, $\x{4F66}, $\x{4F67}, $\x{4F68}, $\x{4F69}, $\x{4F6A}, $\x{4F6B}, $\x{4F6C}, $\x{4F6D}, $\x{4F6E}, $\x{4F6F}, $\x{4F70}, $\x{4F71}, $\x{4F72}, $\x{4F73}, $\x{4F74}, $\x{4F75}, $\x{4F76}, $\x{4F77}, $\x{4F78}, $\x{4F79}, $\x{4F7A}, $\x{4F30}, $\x{4F31}, $\x{4F32}, $\x{4F33}, $\x{4F34}, $\x{4F35}, $\x{4F36}, $\x{4F37}, $\x{4F38}, $\x{4F39}, $\x{4F2B}, $\x{4F2F}, $\x{5041}, $\x{5042}, $\x{5043}, $\x{5044}, $\x{5045}, $\x{5046}, $\x{5047}, $\x{5048}, $\x{5049}, $\x{504A}, $\x{504B}, $\x{504C}, $\x{504D}, $\x{504E}, $\x{504F}, $\x{5050}, $\x{5051}, $\x{5052}, $\x{5053}, $\x{5054}, $\x{5055}, $\x{5056}, $\x{5057}, $\x{5058}, $\x{5059}, $\x{505A}, $\x{5061}, $\x{5062}, $\x{5063}, $\x{5064}, $\x{5065}, $\x{5066}, $\x{5067}, $\x{5068}, $\x{5069}, $\x{506A}, $\x{506B}, $\x{506C}, $\x{506D}, $\x{506E}, $\x{506F}, $\x{5070}, $\x{5071}, $\x{5072}, $\x{5073}, $\x{5074}, $\x{5075}, $\x{5076}, $\x{5077}, $\x{5078}, $\x{5079}, $\x{507A}, $\x{5030}, $\x{5031}, $\x{5032}, $\x{5033}, $\x{5034}, $\x{5035}, $\x{5036}, $\x{5037}, $\x{5038}, $\x{5039}, $\x{502B}, $\x{502F}, $\x{5141}, $\x{5142}, $\x{5143}, $\x{5144}, $\x{5145}, $\x{5146}, $\x{5147}, $\x{5148}, $\x{5149}, $\x{514A}, $\x{514B}, $\x{514C}, $\x{514D}, $\x{514E}, $\x{514F}, $\x{5150}, $\x{5151}, $\x{5152}, $\x{5153}, $\x{5154}, $\x{5155}, $\x{5156}, $\x{5157}, $\x{5158}, $\x{5159}, $\x{515A}, $\x{5161}, $\x{5162}, $\x{5163}, $\x{5164}, $\x{5165}, $\x{5166}, $\x{5167}, $\x{5168}, $\x{5169}, $\x{516A}, $\x{516B}, $\x{516C}, $\x{516D}, $\x{516E}, $\x{516F}, $\x{5170}, $\x{5171}, $\x{5172}, $\x{5173}, $\x{5174}, $\x{5175}, $\x{5176}, $\x{5177}, $\x{5178}, $\x{5179}, $\x{517A}, $\x{5130}, $\x{5131}, $\x{5132}, $\x{5133}, $\x{5134}, $\x{5135}, $\x{5136}, $\x{5137}, $\x{5138}, $\x{5139}, $\x{512B}, $\x{512F}, $\x{5241}, $\x{5242}, $\x{5243}, $\x{5244}, $\x{5245}, $\x{5246}, $\x{5247}, $\x{5248}, $\x{5249}, $\x{524A}, $\x{524B}, $\x{524C}, $\x{524D}, $\x{524E}, $\x{524F}, $\x{5250}, $\x{5251}, $\x{5252}, $\x{5253}, $\x{5254}, $\x{5255}, $\x{5256}, $\x{5257}, $\x{5258}, $\x{5259}, $\x{525A}, $\x{5261}, $\x{5262}, $\x{5263}, $\x{5264}, $\x{5265}, $\x{5266}, $\x{5267}, $\x{5268}, $\x{5269}, $\x{526A}, $\x{526B}, $\x{526C}, $\x{526D}, $\x{526E}, $\x{526F}, $\x{5270}, $\x{5271}, $\x{5272}, $\x{5273}, $\x{5274}, $\x{5275}, $\x{5276}, $\x{5277}, $\x{5278}, $\x{5279}, $\x{527A}, $\x{5230}, $\x{5231}, $\x{5232}, $\x{5233}, $\x{5234}, $\x{5235}, $\x{5236}, $\x{5237}, $\x{5238}, $\x{5239}, $\x{522B}, $\x{522F}, $\x{5341}, $\x{5342}, $\x{5343}, $\x{5344}, $\x{5345}, $\x{5346}, $\x{5347}, $\x{5348}, $\x{5349}, $\x{534A}, $\x{534B}, $\x{534C}, $\x{534D}, $\x{534E}, $\x{534F}, $\x{5350}, $\x{5351}, $\x{5352}, $\x{5353}, $\x{5354}, $\x{5355}, $\x{5356}, $\x{5357}, $\x{5358}, $\x{5359}, $\x{535A}, $\x{5361}, $\x{5362}, $\x{5363}, $\x{5364}, $\x{5365}, $\x{5366}, $\x{5367}, $\x{5368}, $\x{5369}, $\x{536A}, $\x{536B}, $\x{536C}, $\x{536D}, $\x{536E}, $\x{536F}, $\x{5370}, $\x{5371}, $\x{5372}, $\x{5373}, $\x{5374}, $\x{5375}, $\x{5376}, $\x{5377}, $\x{5378}, $\x{5379}, $\x{537A}, $\x{5330}, $\x{5331}, $\x{5332}, $\x{5333}, $\x{5334}, $\x{5335}, $\x{5336}, $\x{5337}, $\x{5338}, $\x{5339}, $\x{532B}, $\x{532F}, $\x{5441}, $\x{5442}, $\x{5443}, $\x{5444}, $\x{5445}, $\x{5446}, $\x{5447}, $\x{5448}, $\x{5449}, $\x{544A}, $\x{544B}, $\x{544C}, $\x{544D}, $\x{544E}, $\x{544F}, $\x{5450}, $\x{5451}, $\x{5452}, $\x{5453}, $\x{5454}, $\x{5455}, $\x{5456}, $\x{5457}, $\x{5458}, $\x{5459}, $\x{545A}, $\x{5461}, $\x{5462}, $\x{5463}, $\x{5464}, $\x{5465}, $\x{5466}, $\x{5467}, $\x{5468}, $\x{5469}, $\x{546A}, $\x{546B}, $\x{546C}, $\x{546D}, $\x{546E}, $\x{546F}, $\x{5470}, $\x{5471}, $\x{5472}, $\x{5473}, $\x{5474}, $\x{5475}, $\x{5476}, $\x{5477}, $\x{5478}, $\x{5479}, $\x{547A}, $\x{5430}, $\x{5431}, $\x{5432}, $\x{5433}, $\x{5434}, $\x{5435}, $\x{5436}, $\x{5437}, $\x{5438}, $\x{5439}, $\x{542B}, $\x{542F}, $\x{5541}, $\x{5542}, $\x{5543}, $\x{5544}, $\x{5545}, $\x{5546}, $\x{5547}, $\x{5548}, $\x{5549}, $\x{554A}, $\x{554B}, $\x{554C}, $\x{554D}, $\x{554E}, $\x{554F}, $\x{5550}, $\x{5551}, $\x{5552}, $\x{5553}, $\x{5554}, $\x{5555}, $\x{5556}, $\x{5557}, $\x{5558}, $\x{5559}, $\x{555A}, $\x{5561}, $\x{5562}, $\x{5563}, $\x{5564}, $\x{5565}, $\x{5566}, $\x{5567}, $\x{5568}, $\x{5569}, $\x{556A}, $\x{556B}, $\x{556C}, $\x{556D}, $\x{556E}, $\x{556F}, $\x{5570}, $\x{5571}, $\x{5572}, $\x{5573}, $\x{5574}, $\x{5575}, $\x{5576}, $\x{5577}, $\x{5578}, $\x{5579}, $\x{557A}, $\x{5530}, $\x{5531}, $\x{5532}, $\x{5533}, $\x{5534}, $\x{5535}, $\x{5536}, $\x{5537}, $\x{5538}, $\x{5539}, $\x{552B}, $\x{552F}, $\x{5641}, $\x{5642}, $\x{5643}, $\x{5644}, $\x{5645}, $\x{5646}, $\x{5647}, $\x{5648}, $\x{5649}, $\x{564A}, $\x{564B}, $\x{564C}, $\x{564D}, $\x{564E}, $\x{564F}, $\x{5650}, $\x{5651}, $\x{5652}, $\x{5653}, $\x{5654}, $\x{5655}, $\x{5656}, $\x{5657}, $\x{5658}, $\x{5659}, $\x{565A}, $\x{5661}, $\x{5662}, $\x{5663}, $\x{5664}, $\x{5665}, $\x{5666}, $\x{5667}, $\x{5668}, $\x{5669}, $\x{566A}, $\x{566B}, $\x{566C}, $\x{566D}, $\x{566E}, $\x{566F}, $\x{5670}, $\x{5671}, $\x{5672}, $\x{5673}, $\x{5674}, $\x{5675}, $\x{5676}, $\x{5677}, $\x{5678}, $\x{5679}, $\x{567A}, $\x{5630}, $\x{5631}, $\x{5632}, $\x{5633}, $\x{5634}, $\x{5635}, $\x{5636}, $\x{5637}, $\x{5638}, $\x{5639}, $\x{562B}, $\x{562F}, $\x{5741}, $\x{5742}, $\x{5743}, $\x{5744}, $\x{5745}, $\x{5746}, $\x{5747}, $\x{5748}, $\x{5749}, $\x{574A}, $\x{574B}, $\x{574C}, $\x{574D}, $\x{574E}, $\x{574F}, $\x{5750}, $\x{5751}, $\x{5752}, $\x{5753}, $\x{5754}, $\x{5755}, $\x{5756}, $\x{5757}, $\x{5758}, $\x{5759}, $\x{575A}, $\x{5761}, $\x{5762}, $\x{5763}, $\x{5764}, $\x{5765}, $\x{5766}, $\x{5767}, $\x{5768}, $\x{5769}, $\x{576A}, $\x{576B}, $\x{576C}, $\x{576D}, $\x{576E}, $\x{576F}, $\x{5770}, $\x{5771}, $\x{5772}, $\x{5773}, $\x{5774}, $\x{5775}, $\x{5776}, $\x{5777}, $\x{5778}, $\x{5779}, $\x{577A}, $\x{5730}, $\x{5731}, $\x{5732}, $\x{5733}, $\x{5734}, $\x{5735}, $\x{5736}, $\x{5737}, $\x{5738}, $\x{5739}, $\x{572B}, $\x{572F}, $\x{5841}, $\x{5842}, $\x{5843}, $\x{5844}, $\x{5845}, $\x{5846}, $\x{5847}, $\x{5848}, $\x{5849}, $\x{584A}, $\x{584B}, $\x{584C}, $\x{584D}, $\x{584E}, $\x{584F}, $\x{5850}, $\x{5851}, $\x{5852}, $\x{5853}, $\x{5854}, $\x{5855}, $\x{5856}, $\x{5857}, $\x{5858}, $\x{5859}, $\x{585A}, $\x{5861}, $\x{5862}, $\x{5863}, $\x{5864}, $\x{5865}, $\x{5866}, $\x{5867}, $\x{5868}, $\x{5869}, $\x{586A}, $\x{586B}, $\x{586C}, $\x{586D}, $\x{586E}, $\x{586F}, $\x{5870}, $\x{5871}, $\x{5872}, $\x{5873}, $\x{5874}, $\x{5875}, $\x{5876}, $\x{5877}, $\x{5878}, $\x{5879}, $\x{587A}, $\x{5830}, $\x{5831}, $\x{5832}, $\x{5833}, $\x{5834}, $\x{5835}, $\x{5836}, $\x{5837}, $\x{5838}, $\x{5839}, $\x{582B}, $\x{582F}, $\x{5941}, $\x{5942}, $\x{5943}, $\x{5944}, $\x{5945}, $\x{5946}, $\x{5947}, $\x{5948}, $\x{5949}, $\x{594A}, $\x{594B}, $\x{594C}, $\x{594D}, $\x{594E}, $\x{594F}, $\x{5950}, $\x{5951}, $\x{5952}, $\x{5953}, $\x{5954}, $\x{5955}, $\x{5956}, $\x{5957}, $\x{5958}, $\x{5959}, $\x{595A}, $\x{5961}, $\x{5962}, $\x{5963}, $\x{5964}, $\x{5965}, $\x{5966}, $\x{5967}, $\x{5968}, $\x{5969}, $\x{596A}, $\x{596B}, $\x{596C}, $\x{596D}, $\x{596E}, $\x{596F}, $\x{5970}, $\x{5971}, $\x{5972}, $\x{5973}, $\x{5974}, $\x{5975}, $\x{5976}, $\x{5977}, $\x{5978}, $\x{5979}, $\x{597A}, $\x{5930}, $\x{5931}, $\x{5932}, $\x{5933}, $\x{5934}, $\x{5935}, $\x{5936}, $\x{5937}, $\x{5938}, $\x{5939}, $\x{592B}, $\x{592F}, $\x{5A41}, $\x{5A42}, $\x{5A43}, $\x{5A44}, $\x{5A45}, $\x{5A46}, $\x{5A47}, $\x{5A48}, $\x{5A49}, $\x{5A4A}, $\x{5A4B}, $\x{5A4C}, $\x{5A4D}, $\x{5A4E}, $\x{5A4F}, $\x{5A50}, $\x{5A51}, $\x{5A52}, $\x{5A53}, $\x{5A54}, $\x{5A55}, $\x{5A56}, $\x{5A57}, $\x{5A58}, $\x{5A59}, $\x{5A5A}, $\x{5A61}, $\x{5A62}, $\x{5A63}, $\x{5A64}, $\x{5A65}, $\x{5A66}, $\x{5A67}, $\x{5A68}, $\x{5A69}, $\x{5A6A}, $\x{5A6B}, $\x{5A6C}, $\x{5A6D}, $\x{5A6E}, $\x{5A6F}, $\x{5A70}, $\x{5A71}, $\x{5A72}, $\x{5A73}, $\x{5A74}, $\x{5A75}, $\x{5A76}, $\x{5A77}, $\x{5A78}, $\x{5A79}, $\x{5A7A}, $\x{5A30}, $\x{5A31}, $\x{5A32}, $\x{5A33}, $\x{5A34}, $\x{5A35}, $\x{5A36}, $\x{5A37}, $\x{5A38}, $\x{5A39}, $\x{5A2B}, $\x{5A2F}, $\x{6141}, $\x{6142}, $\x{6143}, $\x{6144}, $\x{6145}, $\x{6146}, $\x{6147}, $\x{6148}, $\x{6149}, $\x{614A}, $\x{614B}, $\x{614C}, $\x{614D}, $\x{614E}, $\x{614F}, $\x{6150}, $\x{6151}, $\x{6152}, $\x{6153}, $\x{6154}, $\x{6155}, $\x{6156}, $\x{6157}, $\x{6158}, $\x{6159}, $\x{615A}, $\x{6161}, $\x{6162}, $\x{6163}, $\x{6164}, $\x{6165}, $\x{6166}, $\x{6167}, $\x{6168}, $\x{6169}, $\x{616A}, $\x{616B}, $\x{616C}, $\x{616D}, $\x{616E}, $\x{616F}, $\x{6170}, $\x{6171}, $\x{6172}, $\x{6173}, $\x{6174}, $\x{6175}, $\x{6176}, $\x{6177}, $\x{6178}, $\x{6179}, $\x{617A}, $\x{6130}, $\x{6131}, $\x{6132}, $\x{6133}, $\x{6134}, $\x{6135}, $\x{6136}, $\x{6137}, $\x{6138}, $\x{6139}, $\x{612B}, $\x{612F}, $\x{6241}, $\x{6242}, $\x{6243}, $\x{6244}, $\x{6245}, $\x{6246}, $\x{6247}, $\x{6248}, $\x{6249}, $\x{624A}, $\x{624B}, $\x{624C}, $\x{624D}, $\x{624E}, $\x{624F}, $\x{6250}, $\x{6251}, $\x{6252}, $\x{6253}, $\x{6254}, $\x{6255}, $\x{6256}, $\x{6257}, $\x{6258}, $\x{6259}, $\x{625A}, $\x{6261}, $\x{6262}, $\x{6263}, $\x{6264}, $\x{6265}, $\x{6266}, $\x{6267}, $\x{6268}, $\x{6269}, $\x{626A}, $\x{626B}, $\x{626C}, $\x{626D}, $\x{626E}, $\x{626F}, $\x{6270}, $\x{6271}, $\x{6272}, $\x{6273}, $\x{6274}, $\x{6275}, $\x{6276}, $\x{6277}, $\x{6278}, $\x{6279}, $\x{627A}, $\x{6230}, $\x{6231}, $\x{6232}, $\x{6233}, $\x{6234}, $\x{6235}, $\x{6236}, $\x{6237}, $\x{6238}, $\x{6239}, $\x{622B}, $\x{622F}, $\x{6341}, $\x{6342}, $\x{6343}, $\x{6344}, $\x{6345}, $\x{6346}, $\x{6347}, $\x{6348}, $\x{6349}, $\x{634A}, $\x{634B}, $\x{634C}, $\x{634D}, $\x{634E}, $\x{634F}, $\x{6350}, $\x{6351}, $\x{6352}, $\x{6353}, $\x{6354}, $\x{6355}, $\x{6356}, $\x{6357}, $\x{6358}, $\x{6359}, $\x{635A}, $\x{6361}, $\x{6362}, $\x{6363}, $\x{6364}, $\x{6365}, $\x{6366}, $\x{6367}, $\x{6368}, $\x{6369}, $\x{636A}, $\x{636B}, $\x{636C}, $\x{636D}, $\x{636E}, $\x{636F}, $\x{6370}, $\x{6371}, $\x{6372}, $\x{6373}, $\x{6374}, $\x{6375}, $\x{6376}, $\x{6377}, $\x{6378}, $\x{6379}, $\x{637A}, $\x{6330}, $\x{6331}, $\x{6332}, $\x{6333}, $\x{6334}, $\x{6335}, $\x{6336}, $\x{6337}, $\x{6338}, $\x{6339}, $\x{632B}, $\x{632F}, $\x{6441}, $\x{6442}, $\x{6443}, $\x{6444}, $\x{6445}, $\x{6446}, $\x{6447}, $\x{6448}, $\x{6449}, $\x{644A}, $\x{644B}, $\x{644C}, $\x{644D}, $\x{644E}, $\x{644F}, $\x{6450}, $\x{6451}, $\x{6452}, $\x{6453}, $\x{6454}, $\x{6455}, $\x{6456}, $\x{6457}, $\x{6458}, $\x{6459}, $\x{645A}, $\x{6461}, $\x{6462}, $\x{6463}, $\x{6464}, $\x{6465}, $\x{6466}, $\x{6467}, $\x{6468}, $\x{6469}, $\x{646A}, $\x{646B}, $\x{646C}, $\x{646D}, $\x{646E}, $\x{646F}, $\x{6470}, $\x{6471}, $\x{6472}, $\x{6473}, $\x{6474}, $\x{6475}, $\x{6476}, $\x{6477}, $\x{6478}, $\x{6479}, $\x{647A}, $\x{6430}, $\x{6431}, $\x{6432}, $\x{6433}, $\x{6434}, $\x{6435}, $\x{6436}, $\x{6437}, $\x{6438}, $\x{6439}, $\x{642B}, $\x{642F}, $\x{6541}, $\x{6542}, $\x{6543}, $\x{6544}, $\x{6545}, $\x{6546}, $\x{6547}, $\x{6548}, $\x{6549}, $\x{654A}, $\x{654B}, $\x{654C}, $\x{654D}, $\x{654E}, $\x{654F}, $\x{6550}, $\x{6551}, $\x{6552}, $\x{6553}, $\x{6554}, $\x{6555}, $\x{6556}, $\x{6557}, $\x{6558}, $\x{6559}, $\x{655A}, $\x{6561}, $\x{6562}, $\x{6563}, $\x{6564}, $\x{6565}, $\x{6566}, $\x{6567}, $\x{6568}, $\x{6569}, $\x{656A}, $\x{656B}, $\x{656C}, $\x{656D}, $\x{656E}, $\x{656F}, $\x{6570}, $\x{6571}, $\x{6572}, $\x{6573}, $\x{6574}, $\x{6575}, $\x{6576}, $\x{6577}, $\x{6578}, $\x{6579}, $\x{657A}, $\x{6530}, $\x{6531}, $\x{6532}, $\x{6533}, $\x{6534}, $\x{6535}, $\x{6536}, $\x{6537}, $\x{6538}, $\x{6539}, $\x{652B}, $\x{652F}, $\x{6641}, $\x{6642}, $\x{6643}, $\x{6644}, $\x{6645}, $\x{6646}, $\x{6647}, $\x{6648}, $\x{6649}, $\x{664A}, $\x{664B}, $\x{664C}, $\x{664D}, $\x{664E}, $\x{664F}, $\x{6650}, $\x{6651}, $\x{6652}, $\x{6653}, $\x{6654}, $\x{6655}, $\x{6656}, $\x{6657}, $\x{6658}, $\x{6659}, $\x{665A}, $\x{6661}, $\x{6662}, $\x{6663}, $\x{6664}, $\x{6665}, $\x{6666}, $\x{6667}, $\x{6668}, $\x{6669}, $\x{666A}, $\x{666B}, $\x{666C}, $\x{666D}, $\x{666E}, $\x{666F}, $\x{6670}, $\x{6671}, $\x{6672}, $\x{6673}, $\x{6674}, $\x{6675}, $\x{6676}, $\x{6677}, $\x{6678}, $\x{6679}, $\x{667A}, $\x{6630}, $\x{6631}, $\x{6632}, $\x{6633}, $\x{6634}, $\x{6635}, $\x{6636}, $\x{6637}, $\x{6638}, $\x{6639}, $\x{662B}, $\x{662F}, $\x{6741}, $\x{6742}, $\x{6743}, $\x{6744}, $\x{6745}, $\x{6746}, $\x{6747}, $\x{6748}, $\x{6749}, $\x{674A}, $\x{674B}, $\x{674C}, $\x{674D}, $\x{674E}, $\x{674F}, $\x{6750}, $\x{6751}, $\x{6752}, $\x{6753}, $\x{6754}, $\x{6755}, $\x{6756}, $\x{6757}, $\x{6758}, $\x{6759}, $\x{675A}, $\x{6761}, $\x{6762}, $\x{6763}, $\x{6764}, $\x{6765}, $\x{6766}, $\x{6767}, $\x{6768}, $\x{6769}, $\x{676A}, $\x{676B}, $\x{676C}, $\x{676D}, $\x{676E}, $\x{676F}, $\x{6770}, $\x{6771}, $\x{6772}, $\x{6773}, $\x{6774}, $\x{6775}, $\x{6776}, $\x{6777}, $\x{6778}, $\x{6779}, $\x{677A}, $\x{6730}, $\x{6731}, $\x{6732}, $\x{6733}, $\x{6734}, $\x{6735}, $\x{6736}, $\x{6737}, $\x{6738}, $\x{6739}, $\x{672B}, $\x{672F}, $\x{6841}, $\x{6842}, $\x{6843}, $\x{6844}, $\x{6845}, $\x{6846}, $\x{6847}, $\x{6848}, $\x{6849}, $\x{684A}, $\x{684B}, $\x{684C}, $\x{684D}, $\x{684E}, $\x{684F}, $\x{6850}, $\x{6851}, $\x{6852}, $\x{6853}, $\x{6854}, $\x{6855}, $\x{6856}, $\x{6857}, $\x{6858}, $\x{6859}, $\x{685A}, $\x{6861}, $\x{6862}, $\x{6863}, $\x{6864}, $\x{6865}, $\x{6866}, $\x{6867}, $\x{6868}, $\x{6869}, $\x{686A}, $\x{686B}, $\x{686C}, $\x{686D}, $\x{686E}, $\x{686F}, $\x{6870}, $\x{6871}, $\x{6872}, $\x{6873}, $\x{6874}, $\x{6875}, $\x{6876}, $\x{6877}, $\x{6878}, $\x{6879}, $\x{687A}, $\x{6830}, $\x{6831}, $\x{6832}, $\x{6833}, $\x{6834}, $\x{6835}, $\x{6836}, $\x{6837}, $\x{6838}, $\x{6839}, $\x{682B}, $\x{682F}, $\x{6941}, $\x{6942}, $\x{6943}, $\x{6944}, $\x{6945}, $\x{6946}, $\x{6947}, $\x{6948}, $\x{6949}, $\x{694A}, $\x{694B}, $\x{694C}, $\x{694D}, $\x{694E}, $\x{694F}, $\x{6950}, $\x{6951}, $\x{6952}, $\x{6953}, $\x{6954}, $\x{6955}, $\x{6956}, $\x{6957}, $\x{6958}, $\x{6959}, $\x{695A}, $\x{6961}, $\x{6962}, $\x{6963}, $\x{6964}, $\x{6965}, $\x{6966}, $\x{6967}, $\x{6968}, $\x{6969}, $\x{696A}, $\x{696B}, $\x{696C}, $\x{696D}, $\x{696E}, $\x{696F}, $\x{6970}, $\x{6971}, $\x{6972}, $\x{6973}, $\x{6974}, $\x{6975}, $\x{6976}, $\x{6977}, $\x{6978}, $\x{6979}, $\x{697A}, $\x{6930}, $\x{6931}, $\x{6932}, $\x{6933}, $\x{6934}, $\x{6935}, $\x{6936}, $\x{6937}, $\x{6938}, $\x{6939}, $\x{692B}, $\x{692F}, $\x{6A41}, $\x{6A42}, $\x{6A43}, $\x{6A44}, $\x{6A45}, $\x{6A46}, $\x{6A47}, $\x{6A48}, $\x{6A49}, $\x{6A4A}, $\x{6A4B}, $\x{6A4C}, $\x{6A4D}, $\x{6A4E}, $\x{6A4F}, $\x{6A50}, $\x{6A51}, $\x{6A52}, $\x{6A53}, $\x{6A54}, $\x{6A55}, $\x{6A56}, $\x{6A57}, $\x{6A58}, $\x{6A59}, $\x{6A5A}, $\x{6A61}, $\x{6A62}, $\x{6A63}, $\x{6A64}, $\x{6A65}, $\x{6A66}, $\x{6A67}, $\x{6A68}, $\x{6A69}, $\x{6A6A}, $\x{6A6B}, $\x{6A6C}, $\x{6A6D}, $\x{6A6E}, $\x{6A6F}, $\x{6A70}, $\x{6A71}, $\x{6A72}, $\x{6A73}, $\x{6A74}, $\x{6A75}, $\x{6A76}, $\x{6A77}, $\x{6A78}, $\x{6A79}, $\x{6A7A}, $\x{6A30}, $\x{6A31}, $\x{6A32}, $\x{6A33}, $\x{6A34}, $\x{6A35}, $\x{6A36}, $\x{6A37}, $\x{6A38}, $\x{6A39}, $\x{6A2B}, $\x{6A2F}, $\x{6B41}, $\x{6B42}, $\x{6B43}, $\x{6B44}, $\x{6B45}, $\x{6B46}, $\x{6B47}, $\x{6B48}, $\x{6B49}, $\x{6B4A}, $\x{6B4B}, $\x{6B4C}, $\x{6B4D}, $\x{6B4E}, $\x{6B4F}, $\x{6B50}, $\x{6B51}, $\x{6B52}, $\x{6B53}, $\x{6B54}, $\x{6B55}, $\x{6B56}, $\x{6B57}, $\x{6B58}, $\x{6B59}, $\x{6B5A}, $\x{6B61}, $\x{6B62}, $\x{6B63}, $\x{6B64}, $\x{6B65}, $\x{6B66}, $\x{6B67}, $\x{6B68}, $\x{6B69}, $\x{6B6A}, $\x{6B6B}, $\x{6B6C}, $\x{6B6D}, $\x{6B6E}, $\x{6B6F}, $\x{6B70}, $\x{6B71}, $\x{6B72}, $\x{6B73}, $\x{6B74}, $\x{6B75}, $\x{6B76}, $\x{6B77}, $\x{6B78}, $\x{6B79}, $\x{6B7A}, $\x{6B30}, $\x{6B31}, $\x{6B32}, $\x{6B33}, $\x{6B34}, $\x{6B35}, $\x{6B36}, $\x{6B37}, $\x{6B38}, $\x{6B39}, $\x{6B2B}, $\x{6B2F}, $\x{6C41}, $\x{6C42}, $\x{6C43}, $\x{6C44}, $\x{6C45}, $\x{6C46}, $\x{6C47}, $\x{6C48}, $\x{6C49}, $\x{6C4A}, $\x{6C4B}, $\x{6C4C}, $\x{6C4D}, $\x{6C4E}, $\x{6C4F}, $\x{6C50}, $\x{6C51}, $\x{6C52}, $\x{6C53}, $\x{6C54}, $\x{6C55}, $\x{6C56}, $\x{6C57}, $\x{6C58}, $\x{6C59}, $\x{6C5A}, $\x{6C61}, $\x{6C62}, $\x{6C63}, $\x{6C64}, $\x{6C65}, $\x{6C66}, $\x{6C67}, $\x{6C68}, $\x{6C69}, $\x{6C6A}, $\x{6C6B}, $\x{6C6C}, $\x{6C6D}, $\x{6C6E}, $\x{6C6F}, $\x{6C70}, $\x{6C71}, $\x{6C72}, $\x{6C73}, $\x{6C74}, $\x{6C75}, $\x{6C76}, $\x{6C77}, $\x{6C78}, $\x{6C79}, $\x{6C7A}, $\x{6C30}, $\x{6C31}, $\x{6C32}, $\x{6C33}, $\x{6C34}, $\x{6C35}, $\x{6C36}, $\x{6C37}, $\x{6C38}, $\x{6C39}, $\x{6C2B}, $\x{6C2F}, $\x{6D41}, $\x{6D42}, $\x{6D43}, $\x{6D44}, $\x{6D45}, $\x{6D46}, $\x{6D47}, $\x{6D48}, $\x{6D49}, $\x{6D4A}, $\x{6D4B}, $\x{6D4C}, $\x{6D4D}, $\x{6D4E}, $\x{6D4F}, $\x{6D50}, $\x{6D51}, $\x{6D52}, $\x{6D53}, $\x{6D54}, $\x{6D55}, $\x{6D56}, $\x{6D57}, $\x{6D58}, $\x{6D59}, $\x{6D5A}, $\x{6D61}, $\x{6D62}, $\x{6D63}, $\x{6D64}, $\x{6D65}, $\x{6D66}, $\x{6D67}, $\x{6D68}, $\x{6D69}, $\x{6D6A}, $\x{6D6B}, $\x{6D6C}, $\x{6D6D}, $\x{6D6E}, $\x{6D6F}, $\x{6D70}, $\x{6D71}, $\x{6D72}, $\x{6D73}, $\x{6D74}, $\x{6D75}, $\x{6D76}, $\x{6D77}, $\x{6D78}, $\x{6D79}, $\x{6D7A}, $\x{6D30}, $\x{6D31}, $\x{6D32}, $\x{6D33}, $\x{6D34}, $\x{6D35}, $\x{6D36}, $\x{6D37}, $\x{6D38}, $\x{6D39}, $\x{6D2B}, $\x{6D2F}, $\x{6E41}, $\x{6E42}, $\x{6E43}, $\x{6E44}, $\x{6E45}, $\x{6E46}, $\x{6E47}, $\x{6E48}, $\x{6E49}, $\x{6E4A}, $\x{6E4B}, $\x{6E4C}, $\x{6E4D}, $\x{6E4E}, $\x{6E4F}, $\x{6E50}, $\x{6E51}, $\x{6E52}, $\x{6E53}, $\x{6E54}, $\x{6E55}, $\x{6E56}, $\x{6E57}, $\x{6E58}, $\x{6E59}, $\x{6E5A}, $\x{6E61}, $\x{6E62}, $\x{6E63}, $\x{6E64}, $\x{6E65}, $\x{6E66}, $\x{6E67}, $\x{6E68}, $\x{6E69}, $\x{6E6A}, $\x{6E6B}, $\x{6E6C}, $\x{6E6D}, $\x{6E6E}, $\x{6E6F}, $\x{6E70}, $\x{6E71}, $\x{6E72}, $\x{6E73}, $\x{6E74}, $\x{6E75}, $\x{6E76}, $\x{6E77}, $\x{6E78}, $\x{6E79}, $\x{6E7A}, $\x{6E30}, $\x{6E31}, $\x{6E32}, $\x{6E33}, $\x{6E34}, $\x{6E35}, $\x{6E36}, $\x{6E37}, $\x{6E38}, $\x{6E39}, $\x{6E2B}, $\x{6E2F}, $\x{6F41}, $\x{6F42}, $\x{6F43}, $\x{6F44}, $\x{6F45}, $\x{6F46}, $\x{6F47}, $\x{6F48}, $\x{6F49}, $\x{6F4A}, $\x{6F4B}, $\x{6F4C}, $\x{6F4D}, $\x{6F4E}, $\x{6F4F}, $\x{6F50}, $\x{6F51}, $\x{6F52}, $\x{6F53}, $\x{6F54}, $\x{6F55}, $\x{6F56}, $\x{6F57}, $\x{6F58}, $\x{6F59}, $\x{6F5A}, $\x{6F61}, $\x{6F62}, $\x{6F63}, $\x{6F64}, $\x{6F65}, $\x{6F66}, $\x{6F67}, $\x{6F68}, $\x{6F69}, $\x{6F6A}, $\x{6F6B}, $\x{6F6C}, $\x{6F6D}, $\x{6F6E}, $\x{6F6F}, $\x{6F70}, $\x{6F71}, $\x{6F72}, $\x{6F73}, $\x{6F74}, $\x{6F75}, $\x{6F76}, $\x{6F77}, $\x{6F78}, $\x{6F79}, $\x{6F7A}, $\x{6F30}, $\x{6F31}, $\x{6F32}, $\x{6F33}, $\x{6F34}, $\x{6F35}, $\x{6F36}, $\x{6F37}, $\x{6F38}, $\x{6F39}, $\x{6F2B}, $\x{6F2F}, $\x{7041}, $\x{7042}, $\x{7043}, $\x{7044}, $\x{7045}, $\x{7046}, $\x{7047}, $\x{7048}, $\x{7049}, $\x{704A}, $\x{704B}, $\x{704C}, $\x{704D}, $\x{704E}, $\x{704F}, $\x{7050}, $\x{7051}, $\x{7052}, $\x{7053}, $\x{7054}, $\x{7055}, $\x{7056}, $\x{7057}, $\x{7058}, $\x{7059}, $\x{705A}, $\x{7061}, $\x{7062}, $\x{7063}, $\x{7064}, $\x{7065}, $\x{7066}, $\x{7067}, $\x{7068}, $\x{7069}, $\x{706A}, $\x{706B}, $\x{706C}, $\x{706D}, $\x{706E}, $\x{706F}, $\x{7070}, $\x{7071}, $\x{7072}, $\x{7073}, $\x{7074}, $\x{7075}, $\x{7076}, $\x{7077}, $\x{7078}, $\x{7079}, $\x{707A}, $\x{7030}, $\x{7031}, $\x{7032}, $\x{7033}, $\x{7034}, $\x{7035}, $\x{7036}, $\x{7037}, $\x{7038}, $\x{7039}, $\x{702B}, $\x{702F}, $\x{7141}, $\x{7142}, $\x{7143}, $\x{7144}, $\x{7145}, $\x{7146}, $\x{7147}, $\x{7148}, $\x{7149}, $\x{714A}, $\x{714B}, $\x{714C}, $\x{714D}, $\x{714E}, $\x{714F}, $\x{7150}, $\x{7151}, $\x{7152}, $\x{7153}, $\x{7154}, $\x{7155}, $\x{7156}, $\x{7157}, $\x{7158}, $\x{7159}, $\x{715A}, $\x{7161}, $\x{7162}, $\x{7163}, $\x{7164}, $\x{7165}, $\x{7166}, $\x{7167}, $\x{7168}, $\x{7169}, $\x{716A}, $\x{716B}, $\x{716C}, $\x{716D}, $\x{716E}, $\x{716F}, $\x{7170}, $\x{7171}, $\x{7172}, $\x{7173}, $\x{7174}, $\x{7175}, $\x{7176}, $\x{7177}, $\x{7178}, $\x{7179}, $\x{717A}, $\x{7130}, $\x{7131}, $\x{7132}, $\x{7133}, $\x{7134}, $\x{7135}, $\x{7136}, $\x{7137}, $\x{7138}, $\x{7139}, $\x{712B}, $\x{712F}, $\x{7241}, $\x{7242}, $\x{7243}, $\x{7244}, $\x{7245}, $\x{7246}, $\x{7247}, $\x{7248}, $\x{7249}, $\x{724A}, $\x{724B}, $\x{724C}, $\x{724D}, $\x{724E}, $\x{724F}, $\x{7250}, $\x{7251}, $\x{7252}, $\x{7253}, $\x{7254}, $\x{7255}, $\x{7256}, $\x{7257}, $\x{7258}, $\x{7259}, $\x{725A}, $\x{7261}, $\x{7262}, $\x{7263}, $\x{7264}, $\x{7265}, $\x{7266}, $\x{7267}, $\x{7268}, $\x{7269}, $\x{726A}, $\x{726B}, $\x{726C}, $\x{726D}, $\x{726E}, $\x{726F}, $\x{7270}, $\x{7271}, $\x{7272}, $\x{7273}, $\x{7274}, $\x{7275}, $\x{7276}, $\x{7277}, $\x{7278}, $\x{7279}, $\x{727A}, $\x{7230}, $\x{7231}, $\x{7232}, $\x{7233}, $\x{7234}, $\x{7235}, $\x{7236}, $\x{7237}, $\x{7238}, $\x{7239}, $\x{722B}, $\x{722F}, $\x{7341}, $\x{7342}, $\x{7343}, $\x{7344}, $\x{7345}, $\x{7346}, $\x{7347}, $\x{7348}, $\x{7349}, $\x{734A}, $\x{734B}, $\x{734C}, $\x{734D}, $\x{734E}, $\x{734F}, $\x{7350}, $\x{7351}, $\x{7352}, $\x{7353}, $\x{7354}, $\x{7355}, $\x{7356}, $\x{7357}, $\x{7358}, $\x{7359}, $\x{735A}, $\x{7361}, $\x{7362}, $\x{7363}, $\x{7364}, $\x{7365}, $\x{7366}, $\x{7367}, $\x{7368}, $\x{7369}, $\x{736A}, $\x{736B}, $\x{736C}, $\x{736D}, $\x{736E}, $\x{736F}, $\x{7370}, $\x{7371}, $\x{7372}, $\x{7373}, $\x{7374}, $\x{7375}, $\x{7376}, $\x{7377}, $\x{7378}, $\x{7379}, $\x{737A}, $\x{7330}, $\x{7331}, $\x{7332}, $\x{7333}, $\x{7334}, $\x{7335}, $\x{7336}, $\x{7337}, $\x{7338}, $\x{7339}, $\x{732B}, $\x{732F}, $\x{7441}, $\x{7442}, $\x{7443}, $\x{7444}, $\x{7445}, $\x{7446}, $\x{7447}, $\x{7448}, $\x{7449}, $\x{744A}, $\x{744B}, $\x{744C}, $\x{744D}, $\x{744E}, $\x{744F}, $\x{7450}, $\x{7451}, $\x{7452}, $\x{7453}, $\x{7454}, $\x{7455}, $\x{7456}, $\x{7457}, $\x{7458}, $\x{7459}, $\x{745A}, $\x{7461}, $\x{7462}, $\x{7463}, $\x{7464}, $\x{7465}, $\x{7466}, $\x{7467}, $\x{7468}, $\x{7469}, $\x{746A}, $\x{746B}, $\x{746C}, $\x{746D}, $\x{746E}, $\x{746F}, $\x{7470}, $\x{7471}, $\x{7472}, $\x{7473}, $\x{7474}, $\x{7475}, $\x{7476}, $\x{7477}, $\x{7478}, $\x{7479}, $\x{747A}, $\x{7430}, $\x{7431}, $\x{7432}, $\x{7433}, $\x{7434}, $\x{7435}, $\x{7436}, $\x{7437}, $\x{7438}, $\x{7439}, $\x{742B}, $\x{742F}, $\x{7541}, $\x{7542}, $\x{7543}, $\x{7544}, $\x{7545}, $\x{7546}, $\x{7547}, $\x{7548}, $\x{7549}, $\x{754A}, $\x{754B}, $\x{754C}, $\x{754D}, $\x{754E}, $\x{754F}, $\x{7550}, $\x{7551}, $\x{7552}, $\x{7553}, $\x{7554}, $\x{7555}, $\x{7556}, $\x{7557}, $\x{7558}, $\x{7559}, $\x{755A}, $\x{7561}, $\x{7562}, $\x{7563}, $\x{7564}, $\x{7565}, $\x{7566}, $\x{7567}, $\x{7568}, $\x{7569}, $\x{756A}, $\x{756B}, $\x{756C}, $\x{756D}, $\x{756E}, $\x{756F}, $\x{7570}, $\x{7571}, $\x{7572}, $\x{7573}, $\x{7574}, $\x{7575}, $\x{7576}, $\x{7577}, $\x{7578}, $\x{7579}, $\x{757A}, $\x{7530}, $\x{7531}, $\x{7532}, $\x{7533}, $\x{7534}, $\x{7535}, $\x{7536}, $\x{7537}, $\x{7538}, $\x{7539}, $\x{752B}, $\x{752F}, $\x{7641}, $\x{7642}, $\x{7643}, $\x{7644}, $\x{7645}, $\x{7646}, $\x{7647}, $\x{7648}, $\x{7649}, $\x{764A}, $\x{764B}, $\x{764C}, $\x{764D}, $\x{764E}, $\x{764F}, $\x{7650}, $\x{7651}, $\x{7652}, $\x{7653}, $\x{7654}, $\x{7655}, $\x{7656}, $\x{7657}, $\x{7658}, $\x{7659}, $\x{765A}, $\x{7661}, $\x{7662}, $\x{7663}, $\x{7664}, $\x{7665}, $\x{7666}, $\x{7667}, $\x{7668}, $\x{7669}, $\x{766A}, $\x{766B}, $\x{766C}, $\x{766D}, $\x{766E}, $\x{766F}, $\x{7670}, $\x{7671}, $\x{7672}, $\x{7673}, $\x{7674}, $\x{7675}, $\x{7676}, $\x{7677}, $\x{7678}, $\x{7679}, $\x{767A}, $\x{7630}, $\x{7631}, $\x{7632}, $\x{7633}, $\x{7634}, $\x{7635}, $\x{7636}, $\x{7637}, $\x{7638}, $\x{7639}, $\x{762B}, $\x{762F}, $\x{7741}, $\x{7742}, $\x{7743}, $\x{7744}, $\x{7745}, $\x{7746}, $\x{7747}, $\x{7748}, $\x{7749}, $\x{774A}, $\x{774B}, $\x{774C}, $\x{774D}, $\x{774E}, $\x{774F}, $\x{7750}, $\x{7751}, $\x{7752}, $\x{7753}, $\x{7754}, $\x{7755}, $\x{7756}, $\x{7757}, $\x{7758}, $\x{7759}, $\x{775A}, $\x{7761}, $\x{7762}, $\x{7763}, $\x{7764}, $\x{7765}, $\x{7766}, $\x{7767}, $\x{7768}, $\x{7769}, $\x{776A}, $\x{776B}, $\x{776C}, $\x{776D}, $\x{776E}, $\x{776F}, $\x{7770}, $\x{7771}, $\x{7772}, $\x{7773}, $\x{7774}, $\x{7775}, $\x{7776}, $\x{7777}, $\x{7778}, $\x{7779}, $\x{777A}, $\x{7730}, $\x{7731}, $\x{7732}, $\x{7733}, $\x{7734}, $\x{7735}, $\x{7736}, $\x{7737}, $\x{7738}, $\x{7739}, $\x{772B}, $\x{772F}, $\x{7841}, $\x{7842}, $\x{7843}, $\x{7844}, $\x{7845}, $\x{7846}, $\x{7847}, $\x{7848}, $\x{7849}, $\x{784A}, $\x{784B}, $\x{784C}, $\x{784D}, $\x{784E}, $\x{784F}, $\x{7850}, $\x{7851}, $\x{7852}, $\x{7853}, $\x{7854}, $\x{7855}, $\x{7856}, $\x{7857}, $\x{7858}, $\x{7859}, $\x{785A}, $\x{7861}, $\x{7862}, $\x{7863}, $\x{7864}, $\x{7865}, $\x{7866}, $\x{7867}, $\x{7868}, $\x{7869}, $\x{786A}, $\x{786B}, $\x{786C}, $\x{786D}, $\x{786E}, $\x{786F}, $\x{7870}, $\x{7871}, $\x{7872}, $\x{7873}, $\x{7874}, $\x{7875}, $\x{7876}, $\x{7877}, $\x{7878}, $\x{7879}, $\x{787A}, $\x{7830}, $\x{7831}, $\x{7832}, $\x{7833}, $\x{7834}, $\x{7835}, $\x{7836}, $\x{7837}, $\x{7838}, $\x{7839}, $\x{782B}, $\x{782F}, $\x{7941}, $\x{7942}, $\x{7943}, $\x{7944}, $\x{7945}, $\x{7946}, $\x{7947}, $\x{7948}, $\x{7949}, $\x{794A}, $\x{794B}, $\x{794C}, $\x{794D}, $\x{794E}, $\x{794F}, $\x{7950}, $\x{7951}, $\x{7952}, $\x{7953}, $\x{7954}, $\x{7955}, $\x{7956}, $\x{7957}, $\x{7958}, $\x{7959}, $\x{795A}, $\x{7961}, $\x{7962}, $\x{7963}, $\x{7964}, $\x{7965}, $\x{7966}, $\x{7967}, $\x{7968}, $\x{7969}, $\x{796A}, $\x{796B}, $\x{796C}, $\x{796D}, $\x{796E}, $\x{796F}, $\x{7970}, $\x{7971}, $\x{7972}, $\x{7973}, $\x{7974}, $\x{7975}, $\x{7976}, $\x{7977}, $\x{7978}, $\x{7979}, $\x{797A}, $\x{7930}, $\x{7931}, $\x{7932}, $\x{7933}, $\x{7934}, $\x{7935}, $\x{7936}, $\x{7937}, $\x{7938}, $\x{7939}, $\x{792B}, $\x{792F}, $\x{7A41}, $\x{7A42}, $\x{7A43}, $\x{7A44}, $\x{7A45}, $\x{7A46}, $\x{7A47}, $\x{7A48}, $\x{7A49}, $\x{7A4A}, $\x{7A4B}, $\x{7A4C}, $\x{7A4D}, $\x{7A4E}, $\x{7A4F}, $\x{7A50}, $\x{7A51}, $\x{7A52}, $\x{7A53}, $\x{7A54}, $\x{7A55}, $\x{7A56}, $\x{7A57}, $\x{7A58}, $\x{7A59}, $\x{7A5A}, $\x{7A61}, $\x{7A62}, $\x{7A63}, $\x{7A64}, $\x{7A65}, $\x{7A66}, $\x{7A67}, $\x{7A68}, $\x{7A69}, $\x{7A6A}, $\x{7A6B}, $\x{7A6C}, $\x{7A6D}, $\x{7A6E}, $\x{7A6F}, $\x{7A70}, $\x{7A71}, $\x{7A72}, $\x{7A73}, $\x{7A74}, $\x{7A75}, $\x{7A76}, $\x{7A77}, $\x{7A78}, $\x{7A79}, $\x{7A7A}, $\x{7A30}, $\x{7A31}, $\x{7A32}, $\x{7A33}, $\x{7A34}, $\x{7A35}, $\x{7A36}, $\x{7A37}, $\x{7A38}, $\x{7A39}, $\x{7A2B}, $\x{7A2F}, $\x{3041}, $\x{3042}, $\x{3043}, $\x{3044}, $\x{3045}, $\x{3046}, $\x{3047}, $\x{3048}, $\x{3049}, $\x{304A}, $\x{304B}, $\x{304C}, $\x{304D}, $\x{304E}, $\x{304F}, $\x{3050}, $\x{3051}, $\x{3052}, $\x{3053}, $\x{3054}, $\x{3055}, $\x{3056}, $\x{3057}, $\x{3058}, $\x{3059}, $\x{305A}, $\x{3061}, $\x{3062}, $\x{3063}, $\x{3064}, $\x{3065}, $\x{3066}, $\x{3067}, $\x{3068}, $\x{3069}, $\x{306A}, $\x{306B}, $\x{306C}, $\x{306D}, $\x{306E}, $\x{306F}, $\x{3070}, $\x{3071}, $\x{3072}, $\x{3073}, $\x{3074}, $\x{3075}, $\x{3076}, $\x{3077}, $\x{3078}, $\x{3079}, $\x{307A}, $\x{3030}, $\x{3031}, $\x{3032}, $\x{3033}, $\x{3034}, $\x{3035}, $\x{3036}, $\x{3037}, $\x{3038}, $\x{3039}, $\x{302B}, $\x{302F}, $\x{3141}, $\x{3142}, $\x{3143}, $\x{3144}, $\x{3145}, $\x{3146}, $\x{3147}, $\x{3148}, $\x{3149}, $\x{314A}, $\x{314B}, $\x{314C}, $\x{314D}, $\x{314E}, $\x{314F}, $\x{3150}, $\x{3151}, $\x{3152}, $\x{3153}, $\x{3154}, $\x{3155}, $\x{3156}, $\x{3157}, $\x{3158}, $\x{3159}, $\x{315A}, $\x{3161}, $\x{3162}, $\x{3163}, $\x{3164}, $\x{3165}, $\x{3166}, $\x{3167}, $\x{3168}, $\x{3169}, $\x{316A}, $\x{316B}, $\x{316C}, $\x{316D}, $\x{316E}, $\x{316F}, $\x{3170}, $\x{3171}, $\x{3172}, $\x{3173}, $\x{3174}, $\x{3175}, $\x{3176}, $\x{3177}, $\x{3178}, $\x{3179}, $\x{317A}, $\x{3130}, $\x{3131}, $\x{3132}, $\x{3133}, $\x{3134}, $\x{3135}, $\x{3136}, $\x{3137}, $\x{3138}, $\x{3139}, $\x{312B}, $\x{312F}, $\x{3241}, $\x{3242}, $\x{3243}, $\x{3244}, $\x{3245}, $\x{3246}, $\x{3247}, $\x{3248}, $\x{3249}, $\x{324A}, $\x{324B}, $\x{324C}, $\x{324D}, $\x{324E}, $\x{324F}, $\x{3250}, $\x{3251}, $\x{3252}, $\x{3253}, $\x{3254}, $\x{3255}, $\x{3256}, $\x{3257}, $\x{3258}, $\x{3259}, $\x{325A}, $\x{3261}, $\x{3262}, $\x{3263}, $\x{3264}, $\x{3265}, $\x{3266}, $\x{3267}, $\x{3268}, $\x{3269}, $\x{326A}, $\x{326B}, $\x{326C}, $\x{326D}, $\x{326E}, $\x{326F}, $\x{3270}, $\x{3271}, $\x{3272}, $\x{3273}, $\x{3274}, $\x{3275}, $\x{3276}, $\x{3277}, $\x{3278}, $\x{3279}, $\x{327A}, $\x{3230}, $\x{3231}, $\x{3232}, $\x{3233}, $\x{3234}, $\x{3235}, $\x{3236}, $\x{3237}, $\x{3238}, $\x{3239}, $\x{322B}, $\x{322F}, $\x{3341}, $\x{3342}, $\x{3343}, $\x{3344}, $\x{3345}, $\x{3346}, $\x{3347}, $\x{3348}, $\x{3349}, $\x{334A}, $\x{334B}, $\x{334C}, $\x{334D}, $\x{334E}, $\x{334F}, $\x{3350}, $\x{3351}, $\x{3352}, $\x{3353}, $\x{3354}, $\x{3355}, $\x{3356}, $\x{3357}, $\x{3358}, $\x{3359}, $\x{335A}, $\x{3361}, $\x{3362}, $\x{3363}, $\x{3364}, $\x{3365}, $\x{3366}, $\x{3367}, $\x{3368}, $\x{3369}, $\x{336A}, $\x{336B}, $\x{336C}, $\x{336D}, $\x{336E}, $\x{336F}, $\x{3370}, $\x{3371}, $\x{3372}, $\x{3373}, $\x{3374}, $\x{3375}, $\x{3376}, $\x{3377}, $\x{3378}, $\x{3379}, $\x{337A}, $\x{3330}, $\x{3331}, $\x{3332}, $\x{3333}, $\x{3334}, $\x{3335}, $\x{3336}, $\x{3337}, $\x{3338}, $\x{3339}, $\x{332B}, $\x{332F}, $\x{3441}, $\x{3442}, $\x{3443}, $\x{3444}, $\x{3445}, $\x{3446}, $\x{3447}, $\x{3448}, $\x{3449}, $\x{344A}, $\x{344B}, $\x{344C}, $\x{344D}, $\x{344E}, $\x{344F}, $\x{3450}, $\x{3451}, $\x{3452}, $\x{3453}, $\x{3454}, $\x{3455}, $\x{3456}, $\x{3457}, $\x{3458}, $\x{3459}, $\x{345A}, $\x{3461}, $\x{3462}, $\x{3463}, $\x{3464}, $\x{3465}, $\x{3466}, $\x{3467}, $\x{3468}, $\x{3469}, $\x{346A}, $\x{346B}, $\x{346C}, $\x{346D}, $\x{346E}, $\x{346F}, $\x{3470}, $\x{3471}, $\x{3472}, $\x{3473}, $\x{3474}, $\x{3475}, $\x{3476}, $\x{3477}, $\x{3478}, $\x{3479}, $\x{347A}, $\x{3430}, $\x{3431}, $\x{3432}, $\x{3433}, $\x{3434}, $\x{3435}, $\x{3436}, $\x{3437}, $\x{3438}, $\x{3439}, $\x{342B}, $\x{342F}, $\x{3541}, $\x{3542}, $\x{3543}, $\x{3544}, $\x{3545}, $\x{3546}, $\x{3547}, $\x{3548}, $\x{3549}, $\x{354A}, $\x{354B}, $\x{354C}, $\x{354D}, $\x{354E}, $\x{354F}, $\x{3550}, $\x{3551}, $\x{3552}, $\x{3553}, $\x{3554}, $\x{3555}, $\x{3556}, $\x{3557}, $\x{3558}, $\x{3559}, $\x{355A}, $\x{3561}, $\x{3562}, $\x{3563}, $\x{3564}, $\x{3565}, $\x{3566}, $\x{3567}, $\x{3568}, $\x{3569}, $\x{356A}, $\x{356B}, $\x{356C}, $\x{356D}, $\x{356E}, $\x{356F}, $\x{3570}, $\x{3571}, $\x{3572}, $\x{3573}, $\x{3574}, $\x{3575}, $\x{3576}, $\x{3577}, $\x{3578}, $\x{3579}, $\x{357A}, $\x{3530}, $\x{3531}, $\x{3532}, $\x{3533}, $\x{3534}, $\x{3535}, $\x{3536}, $\x{3537}, $\x{3538}, $\x{3539}, $\x{352B}, $\x{352F}, $\x{3641}, $\x{3642}, $\x{3643}, $\x{3644}, $\x{3645}, $\x{3646}, $\x{3647}, $\x{3648}, $\x{3649}, $\x{364A}, $\x{364B}, $\x{364C}, $\x{364D}, $\x{364E}, $\x{364F}, $\x{3650}, $\x{3651}, $\x{3652}, $\x{3653}, $\x{3654}, $\x{3655}, $\x{3656}, $\x{3657}, $\x{3658}, $\x{3659}, $\x{365A}, $\x{3661}, $\x{3662}, $\x{3663}, $\x{3664}, $\x{3665}, $\x{3666}, $\x{3667}, $\x{3668}, $\x{3669}, $\x{366A}, $\x{366B}, $\x{366C}, $\x{366D}, $\x{366E}, $\x{366F}, $\x{3670}, $\x{3671}, $\x{3672}, $\x{3673}, $\x{3674}, $\x{3675}, $\x{3676}, $\x{3677}, $\x{3678}, $\x{3679}, $\x{367A}, $\x{3630}, $\x{3631}, $\x{3632}, $\x{3633}, $\x{3634}, $\x{3635}, $\x{3636}, $\x{3637}, $\x{3638}, $\x{3639}, $\x{362B}, $\x{362F}, $\x{3741}, $\x{3742}, $\x{3743}, $\x{3744}, $\x{3745}, $\x{3746}, $\x{3747}, $\x{3748}, $\x{3749}, $\x{374A}, $\x{374B}, $\x{374C}, $\x{374D}, $\x{374E}, $\x{374F}, $\x{3750}, $\x{3751}, $\x{3752}, $\x{3753}, $\x{3754}, $\x{3755}, $\x{3756}, $\x{3757}, $\x{3758}, $\x{3759}, $\x{375A}, $\x{3761}, $\x{3762}, $\x{3763}, $\x{3764}, $\x{3765}, $\x{3766}, $\x{3767}, $\x{3768}, $\x{3769}, $\x{376A}, $\x{376B}, $\x{376C}, $\x{376D}, $\x{376E}, $\x{376F}, $\x{3770}, $\x{3771}, $\x{3772}, $\x{3773}, $\x{3774}, $\x{3775}, $\x{3776}, $\x{3777}, $\x{3778}, $\x{3779}, $\x{377A}, $\x{3730}, $\x{3731}, $\x{3732}, $\x{3733}, $\x{3734}, $\x{3735}, $\x{3736}, $\x{3737}, $\x{3738}, $\x{3739}, $\x{372B}, $\x{372F}, $\x{3841}, $\x{3842}, $\x{3843}, $\x{3844}, $\x{3845}, $\x{3846}, $\x{3847}, $\x{3848}, $\x{3849}, $\x{384A}, $\x{384B}, $\x{384C}, $\x{384D}, $\x{384E}, $\x{384F}, $\x{3850}, $\x{3851}, $\x{3852}, $\x{3853}, $\x{3854}, $\x{3855}, $\x{3856}, $\x{3857}, $\x{3858}, $\x{3859}, $\x{385A}, $\x{3861}, $\x{3862}, $\x{3863}, $\x{3864}, $\x{3865}, $\x{3866}, $\x{3867}, $\x{3868}, $\x{3869}, $\x{386A}, $\x{386B}, $\x{386C}, $\x{386D}, $\x{386E}, $\x{386F}, $\x{3870}, $\x{3871}, $\x{3872}, $\x{3873}, $\x{3874}, $\x{3875}, $\x{3876}, $\x{3877}, $\x{3878}, $\x{3879}, $\x{387A}, $\x{3830}, $\x{3831}, $\x{3832}, $\x{3833}, $\x{3834}, $\x{3835}, $\x{3836}, $\x{3837}, $\x{3838}, $\x{3839}, $\x{382B}, $\x{382F}, $\x{3941}, $\x{3942}, $\x{3943}, $\x{3944}, $\x{3945}, $\x{3946}, $\x{3947}, $\x{3948}, $\x{3949}, $\x{394A}, $\x{394B}, $\x{394C}, $\x{394D}, $\x{394E}, $\x{394F}, $\x{3950}, $\x{3951}, $\x{3952}, $\x{3953}, $\x{3954}, $\x{3955}, $\x{3956}, $\x{3957}, $\x{3958}, $\x{3959}, $\x{395A}, $\x{3961}, $\x{3962}, $\x{3963}, $\x{3964}, $\x{3965}, $\x{3966}, $\x{3967}, $\x{3968}, $\x{3969}, $\x{396A}, $\x{396B}, $\x{396C}, $\x{396D}, $\x{396E}, $\x{396F}, $\x{3970}, $\x{3971}, $\x{3972}, $\x{3973}, $\x{3974}, $\x{3975}, $\x{3976}, $\x{3977}, $\x{3978}, $\x{3979}, $\x{397A}, $\x{3930}, $\x{3931}, $\x{3932}, $\x{3933}, $\x{3934}, $\x{3935}, $\x{3936}, $\x{3937}, $\x{3938}, $\x{3939}, $\x{392B}, $\x{392F}, $\x{2B41}, $\x{2B42}, $\x{2B43}, $\x{2B44}, $\x{2B45}, $\x{2B46}, $\x{2B47}, $\x{2B48}, $\x{2B49}, $\x{2B4A}, $\x{2B4B}, $\x{2B4C}, $\x{2B4D}, $\x{2B4E}, $\x{2B4F}, $\x{2B50}, $\x{2B51}, $\x{2B52}, $\x{2B53}, $\x{2B54}, $\x{2B55}, $\x{2B56}, $\x{2B57}, $\x{2B58}, $\x{2B59}, $\x{2B5A}, $\x{2B61}, $\x{2B62}, $\x{2B63}, $\x{2B64}, $\x{2B65}, $\x{2B66}, $\x{2B67}, $\x{2B68}, $\x{2B69}, $\x{2B6A}, $\x{2B6B}, $\x{2B6C}, $\x{2B6D}, $\x{2B6E}, $\x{2B6F}, $\x{2B70}, $\x{2B71}, $\x{2B72}, $\x{2B73}, $\x{2B74}, $\x{2B75}, $\x{2B76}, $\x{2B77}, $\x{2B78}, $\x{2B79}, $\x{2B7A}, $\x{2B30}, $\x{2B31}, $\x{2B32}, $\x{2B33}, $\x{2B34}, $\x{2B35}, $\x{2B36}, $\x{2B37}, $\x{2B38}, $\x{2B39}, $\x{2B2B}, $\x{2B2F}, $\x{2F41}, $\x{2F42}, $\x{2F43}, $\x{2F44}, $\x{2F45}, $\x{2F46}, $\x{2F47}, $\x{2F48}, $\x{2F49}, $\x{2F4A}, $\x{2F4B}, $\x{2F4C}, $\x{2F4D}, $\x{2F4E}, $\x{2F4F}, $\x{2F50}, $\x{2F51}, $\x{2F52}, $\x{2F53}, $\x{2F54}, $\x{2F55}, $\x{2F56}, $\x{2F57}, $\x{2F58}, $\x{2F59}, $\x{2F5A}, $\x{2F61}, $\x{2F62}, $\x{2F63}, $\x{2F64}, $\x{2F65}, $\x{2F66}, $\x{2F67}, $\x{2F68}, $\x{2F69}, $\x{2F6A}, $\x{2F6B}, $\x{2F6C}, $\x{2F6D}, $\x{2F6E}, $\x{2F6F}, $\x{2F70}, $\x{2F71}, $\x{2F72}, $\x{2F73}, $\x{2F74}, $\x{2F75}, $\x{2F76}, $\x{2F77}, $\x{2F78}, $\x{2F79}, $\x{2F7A}, $\x{2F30}, $\x{2F31}, $\x{2F32}, $\x{2F33}, $\x{2F34}, $\x{2F35}, $\x{2F36}, $\x{2F37}, $\x{2F38}, $\x{2F39}, $\x{2F2B}, $\x{2F2F} }). potatosalad-erlang-jose-30ac019/src/jose.app.src0000664000232200023220000000115315267717064022161 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet {application, jose, [ {description, "JSON Object Signing and Encryption (JOSE) for Erlang and Elixir."}, {vsn, "1.11.13"}, {id, "git"}, {mod, {'jose_app', []}}, {registered, []}, {applications, [ kernel, stdlib, crypto, asn1, public_key ]}, {modules, []}, {maintainers, ["Andrew Bennett"]}, {licenses, ["MIT"]}, {links, [{"Github", "https://github.com/potatosalad/erlang-jose"}]}, {env, [ {crypto_fallback, false}, {pbes2_count_maximum, 10000}, {zip_size_maximum, 1048576} ]} ]}. potatosalad-erlang-jose-30ac019/src/jose_public_key.erl0000664000232200023220000010754415267717064023616 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 18 May 2017 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_public_key). -include("jose_compat.hrl"). -include("jose_public_key.hrl"). %% API -export([cipher/3]). -export([decipher/2]). -export([encrypt_parameters/1]). -export([decrypt_parameters/1]). -export([der_decode/1]). -export([der_decode/2]). -export([der_encode/2]). -export([pem_decode/1]). -export([pem_encode/1]). -export([pem_entry_decode/1]). -export([pem_entry_decode/2]). -export([pem_entry_encode/2]). -export([pem_entry_encode/3]). %%==================================================================== %% API functions %%==================================================================== cipher(DecryptedDER, CipherInfo={Cipher, #'PBES2-params'{}}, Password) when Cipher == "AES-128-CBC" orelse Cipher == "AES-192-CBC" orelse Cipher == "AES-256-CBC" -> {true, EncryptedDER} = pem_cipher(DecryptedDER, CipherInfo, Password), EncryptedDER; cipher(DecryptedDER, CipherInfo, Password) -> try pubkey_pem:cipher(DecryptedDER, CipherInfo, Password) catch ?COMPAT_CATCH(Class, Reason, ST) -> case pem_cipher(DecryptedDER, CipherInfo, Password) of {true, EncryptedDER} -> EncryptedDER; false -> erlang:raise(Class, Reason, ?COMPAT_GET_STACKTRACE(ST)) end end. decipher({_, EncryptedDER, CipherInfo={Cipher, #'PBES2-params'{}}}, Password) when Cipher == "AES-128-CBC" orelse Cipher == "AES-192-CBC" orelse Cipher == "AES-256-CBC" -> {true, DecryptedDER} = pem_decipher(EncryptedDER, CipherInfo, Password), DecryptedDER; decipher(Encrypted = {_, EncryptedDER, CipherInfo}, Password) -> try pubkey_pem:decipher(Encrypted, Password) catch ?COMPAT_CATCH(Class, Reason, ST) -> case pem_decipher(EncryptedDER, CipherInfo, Password) of {true, DecryptedDER} -> DecryptedDER; false -> erlang:raise(Class, Reason, ?COMPAT_GET_STACKTRACE(ST)) end end. encrypt_parameters(Arg = {Cipher, Params}) -> try pubkey_pbe:encrypt_parameters(Arg) catch ?COMPAT_CATCH(Class, Reason, ST) -> case encrypt_parameters(Cipher, Params) of {true, Result} -> Result; false -> erlang:raise(Class, Reason, ?COMPAT_GET_STACKTRACE(ST)) end end. %% @private encrypt_parameters(_Cipher, #'PBES2-params'{} = Params) -> {ok, Der} ='PKCS-FRAME':encode('PBES2-params', Params), {true, #'EncryptedPrivateKeyInfo_encryptionAlgorithm'{algorithm = ?'id-PBES2', parameters = encode_handle_open_type_wrapper(Der)}}; encrypt_parameters(_, _) -> false. decrypt_parameters(Arg = #'EncryptedPrivateKeyInfo_encryptionAlgorithm'{algorithm = Oid, parameters = Param}) -> try pubkey_pbe:decrypt_parameters(Arg) catch ?COMPAT_CATCH(Class, Reason, ST) -> case decrypt_parameters(Oid, decode_handle_open_type_wrapper(Param)) of {true, Result} -> Result; false -> erlang:raise(Class, Reason, ?COMPAT_GET_STACKTRACE(ST)) end end. %% @private decrypt_parameters(?'id-PBES2', DekParams) -> {ok, Params} = 'PKCS-FRAME':decode('PBES2-params', DekParams), case cipher(Params#'PBES2-params'.encryptionScheme) of {true, Cipher} -> {true, {Cipher, Params}}; false -> false end; decrypt_parameters(_, _) -> false. %% @private cipher(#'PBES2-params_encryptionScheme'{algorithm = ?'id-aes128-CBC'}) -> {true, "AES-128-CBC"}; cipher(#'PBES2-params_encryptionScheme'{algorithm = ?'id-aes192-CBC'}) -> {true, "AES-192-CBC"}; cipher(#'PBES2-params_encryptionScheme'{algorithm = ?'id-aes256-CBC'}) -> {true, "AES-256-CBC"}; cipher(_) -> false. der_decode(DER) when is_binary(DER) -> Result = try_der_decode([ 'Certificate', 'RSAPrivateKey', 'RSAPublicKey', 'SubjectPublicKeyInfo', 'DSAPrivateKey', 'DHParameter', 'PrivateKeyInfo', 'EncryptedPrivateKeyInfo', 'CertificationRequest', 'ContentInfo', 'CertificateList', 'EcpkParameters', 'ECPrivateKey', {no_asn1, new_openssh} %% Temporarily in the prototype of this format ], DER), case Result of PrivateKeyInfo=#'PrivateKeyInfo'{} -> i2k(PrivateKeyInfo); ECPrivateKey={'ECPrivateKey', _, _, _, _, _} -> %% OTP 24 i2k(ECPrivateKey); ECPrivateKey={'ECPrivateKey', _, _, _, _} -> %% OTP 23 i2k(ECPrivateKey); SubjectPublicKeyInfo=#'SubjectPublicKeyInfo'{} -> i2k(SubjectPublicKeyInfo); Other -> Other end. der_decode('EcpkParameters', EcpkParameters = {namedCurve, _}) -> EcpkParameters; der_decode(ASN1Type, DER) when is_atom(ASN1Type) andalso is_binary(DER) -> public_key:der_decode(ASN1Type, DER). der_encode(ASN1Type, Entity) when is_atom(ASN1Type) -> try public_key:der_encode(ASN1Type, Entity) catch ?COMPAT_CATCH(Class, Reason, ST) -> case der_enc(ASN1Type, Entity) of {true, DERBinary} -> DERBinary; false -> erlang:raise(Class, Reason, ?COMPAT_GET_STACKTRACE(ST)) end end. pem_decode(PEMBinary) when is_binary(PEMBinary) -> try public_key:pem_decode(PEMBinary) catch ?COMPAT_CATCH(Class, Reason, ST) -> case pem_dec(PEMBinary) of {true, PEMEntries} -> PEMEntries; false -> erlang:raise(Class, Reason, ?COMPAT_GET_STACKTRACE(ST)) end end. pem_encode(PEMEntries) when is_list(PEMEntries) -> try public_key:pem_encode(PEMEntries) catch ?COMPAT_CATCH(Class, Reason, ST) -> case pem_enc(PEMEntries) of {true, PEMBinary} -> PEMBinary; false -> erlang:raise(Class, Reason, ?COMPAT_GET_STACKTRACE(ST)) end end. pem_entry_decode(PEMEntry) -> Result = try public_key:pem_entry_decode(PEMEntry) catch ?COMPAT_CATCH(Class, Reason, ST) -> case pem_entry_dec(PEMEntry) of {true, DecodedPEMEntry} -> DecodedPEMEntry; false -> erlang:raise(Class, Reason, ?COMPAT_GET_STACKTRACE(ST)) end end, case Result of PrivateKeyInfo=#'PrivateKeyInfo'{} -> i2k(PrivateKeyInfo); ECPrivateKey={'ECPrivateKey', _, _, _, _, _} -> %% OTP 24 i2k(ECPrivateKey); ECPrivateKey={'ECPrivateKey', _, _, _, _} -> %% OTP 23 i2k(ECPrivateKey); SubjectPublicKeyInfo=#'SubjectPublicKeyInfo'{} -> i2k(SubjectPublicKeyInfo); Other -> Other end. pem_entry_decode(PEMEntry, Password) -> Result = try case PEMEntry of {Type, _, {Cipher, #'PBES2-params'{}}} when Cipher == "AES-128-CBC" orelse Cipher == "AES-192-CBC" orelse Cipher == "AES-256-CBC" -> public_key:pem_entry_decode({Type, decipher(PEMEntry, Password), not_encrypted}); _ -> public_key:pem_entry_decode(PEMEntry, Password) end catch ?COMPAT_CATCH(Class, Reason, ST) -> case pem_entry_dec(PEMEntry, Password) of {true, DecodedPEMEntry} -> DecodedPEMEntry; false -> erlang:raise(Class, Reason, ?COMPAT_GET_STACKTRACE(ST)) end end, case Result of PrivateKeyInfo=#'PrivateKeyInfo'{} -> i2k(PrivateKeyInfo); ECPrivateKey={'ECPrivateKey', _, _, _, _, _} -> %% OTP 24 i2k(ECPrivateKey); ECPrivateKey={'ECPrivateKey', _, _, _, _} -> %% OTP 23 i2k(ECPrivateKey); SubjectPublicKeyInfo=#'SubjectPublicKeyInfo'{} -> i2k(SubjectPublicKeyInfo); Other -> Other end. pem_entry_encode(ASN1Type, Entity) -> try public_key:pem_entry_encode(ASN1Type, Entity) catch ?COMPAT_CATCH(Class, Reason, ST) -> case pem_entry_enc(ASN1Type, Entity) of {true, PEMEntry} -> PEMEntry; false -> erlang:raise(Class, Reason, ?COMPAT_GET_STACKTRACE(ST)) end end. pem_entry_encode('PrivateKeyInfo', Entity, Password={{Cipher, #'PBES2-params'{}}, _}) when Cipher == "AES-128-CBC" orelse Cipher == "AES-192-CBC" orelse Cipher == "AES-256-CBC" -> {true, Encoded} = pem_entry_enc1('PrivateKeyInfo', Entity, Password), Encoded; pem_entry_encode(ASN1Type, Entity, Password) -> try public_key:pem_entry_encode(ASN1Type, Entity, Password) catch ?COMPAT_CATCH(Class, Reason, ST) -> case pem_entry_enc(ASN1Type, Entity, Password) of {true, PEMEntry} -> PEMEntry; false -> erlang:raise(Class, Reason, ?COMPAT_GET_STACKTRACE(ST)) end end. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private try_der_decode([ASN1Type | Rest], DER) -> try der_decode(ASN1Type, DER) of Result -> Result catch ?COMPAT_CATCH(error, Reason={badmatch, _}, ST) -> try_der_decode(Rest, DER, {error, Reason, ?COMPAT_GET_STACKTRACE(ST)}) end. %% @private try_der_decode([], _DER, {Class, Reason, Stacktrace}) -> erlang:raise(Class, Reason, Stacktrace); try_der_decode([ASN1Type | Rest], DER, _) -> try der_decode(ASN1Type, DER) of Result -> Result catch ?COMPAT_CATCH(error, Reason={badmatch, _}, ST) -> try_der_decode(Rest, DER, {error, Reason, ?COMPAT_GET_STACKTRACE(ST)}) end. %% @private der_enc('EdDSA25519PrivateKey', K=#'jose_EdDSA25519PrivateKey'{}) -> EncodedDER = public_key:der_encode('PrivateKeyInfo', k2i(K)), {true, EncodedDER}; der_enc('EdDSA25519PublicKey', K=#'jose_EdDSA25519PublicKey'{}) -> EncodedDER = public_key:der_encode('SubjectPublicKeyInfo', k2i(K)), {true, EncodedDER}; der_enc('EdDSA448PrivateKey', K=#'jose_EdDSA448PrivateKey'{}) -> EncodedDER = public_key:der_encode('PrivateKeyInfo', k2i(K)), {true, EncodedDER}; der_enc('EdDSA448PublicKey', K=#'jose_EdDSA448PublicKey'{}) -> EncodedDER = public_key:der_encode('SubjectPublicKeyInfo', k2i(K)), {true, EncodedDER}; %% OTP 29+ expects the ECParameters as a term instead of a DER binary, so %% let public_key build the AlgorithmIdentifier for the running version. der_enc('SubjectPublicKeyInfo', K={#'ECPoint'{}, _ECParameters}) -> {'SubjectPublicKeyInfo', EncodedDER, not_encrypted} = public_key:pem_entry_encode('SubjectPublicKeyInfo', K), {true, EncodedDER}; der_enc('SubjectPublicKeyInfo', K) -> EncodedDER = public_key:der_encode('SubjectPublicKeyInfo', k2i(K)), {true, EncodedDER}; der_enc('X25519PrivateKey', K=#'jose_X25519PrivateKey'{}) -> EncodedDER = public_key:der_encode('PrivateKeyInfo', k2i(K)), {true, EncodedDER}; der_enc('X25519PublicKey', K=#'jose_X25519PublicKey'{}) -> EncodedDER = public_key:der_encode('SubjectPublicKeyInfo', k2i(K)), {true, EncodedDER}; der_enc('X448PrivateKey', K=#'jose_X448PrivateKey'{}) -> EncodedDER = public_key:der_encode('PrivateKeyInfo', k2i(K)), {true, EncodedDER}; der_enc('X448PublicKey', K=#'jose_X448PublicKey'{}) -> EncodedDER = public_key:der_encode('SubjectPublicKeyInfo', k2i(K)), {true, EncodedDER}; der_enc('PrivateKeyInfo', K) -> case K of #'jose_EdDSA25519PrivateKey'{} -> der_enc('EdDSA25519PrivateKey', K); #'jose_EdDSA448PrivateKey'{} -> der_enc('EdDSA448PrivateKey', K); #'jose_X25519PrivateKey'{} -> der_enc('X25519PrivateKey', K); #'jose_X448PrivateKey'{} -> der_enc('X448PrivateKey', K); #'ECPrivateKey'{} -> der_enc('ECPrivateKey', K); #'RSAPrivateKey'{} -> der_enc('RSAPrivateKey', K); _ -> false end; %% Compatibility between PKCS1 and PKCS8 versions of public_key der_enc('ECPrivateKey', K=#'ECPrivateKey'{}) -> EncodedDER = public_key:der_encode('PrivateKeyInfo', k2i(K)), {true, EncodedDER}; der_enc('RSAPrivateKey', K=#'RSAPrivateKey'{}) -> EncodedDER = public_key:der_encode('PrivateKeyInfo', k2i(K)), {true, EncodedDER}; der_enc(_, _) -> false. %% @private pem_dec(PEMBinary) -> pem_dec(pem_dec_split_bin(PEMBinary), []). %% @private pem_dec([], Entries) -> {true, lists:reverse(Entries)}; pem_dec([<<>>], Entries) -> {true, lists:reverse(Entries)}; pem_dec([<<>> | Lines], Entries) -> pem_dec(Lines, Entries); pem_dec([Start| Lines], Entries) -> case pem_end(Start) of undefined -> pem_dec(Lines, Entries); _End -> {Entry, RestLines} = pem_dec_join_entry(Lines, []), case pem_dec_entry(Start, Entry) of {true, Head} -> pem_dec(RestLines, [Head | Entries]); false -> false end end. %% @private pem_dec_entry(Start, Lines) -> Type = asn1_type(Start), Cs = erlang:iolist_to_binary(Lines), Decoded = base64:mime_decode(Cs), case Type of 'EncryptedPrivateKeyInfo'-> decode_encrypted_private_keyinfo(Decoded); _ -> {true, {Type, Decoded, not_encrypted}} end. %% @private decode_encrypted_private_keyinfo(Der) -> #'EncryptedPrivateKeyInfo'{encryptionAlgorithm = AlgorithmInfo, encryptedData = Data} = der_decode('EncryptedPrivateKeyInfo', Der), DecryptParams = decrypt_parameters(AlgorithmInfo), {true, {'PrivateKeyInfo', Data, DecryptParams}}. %% @private %% Ignore white space at end of line pem_dec_join_entry([<<"-----END ", _/binary>>| Lines], Entry) -> {lists:reverse(Entry), Lines}; pem_dec_join_entry([<<"-----END X509 CRL-----", _/binary>>| Lines], Entry) -> {lists:reverse(Entry), Lines}; pem_dec_join_entry([Line | Lines], Entry) -> pem_dec_join_entry(Lines, [Line | Entry]). %% @private pem_dec_split_bin(Bin) -> pem_dec_split_bin(0, Bin). %% @private pem_dec_split_bin(N, Bin) -> case Bin of <> -> [Line | pem_dec_split_bin(0, Rest)]; <> -> [Line | pem_dec_split_bin(0, Rest)]; <> -> [Line]; _ -> pem_dec_split_bin(N+1, Bin) end. %% @private asn1_type(<<"-----BEGIN CERTIFICATE-----">>) -> 'Certificate'; asn1_type(<<"-----BEGIN RSA PRIVATE KEY-----">>) -> 'RSAPrivateKey'; asn1_type(<<"-----BEGIN RSA PUBLIC KEY-----">>) -> 'RSAPublicKey'; asn1_type(<<"-----BEGIN PUBLIC KEY-----">>) -> 'SubjectPublicKeyInfo'; asn1_type(<<"-----BEGIN DSA PRIVATE KEY-----">>) -> 'DSAPrivateKey'; asn1_type(<<"-----BEGIN DH PARAMETERS-----">>) -> 'DHParameter'; asn1_type(<<"-----BEGIN PRIVATE KEY-----">>) -> 'PrivateKeyInfo'; asn1_type(<<"-----BEGIN ENCRYPTED PRIVATE KEY-----">>) -> 'EncryptedPrivateKeyInfo'; asn1_type(<<"-----BEGIN CERTIFICATE REQUEST-----">>) -> 'CertificationRequest'; asn1_type(<<"-----BEGIN PKCS7-----">>) -> 'ContentInfo'; asn1_type(<<"-----BEGIN X509 CRL-----">>) -> 'CertificateList'; asn1_type(<<"-----BEGIN EC PARAMETERS-----">>) -> 'EcpkParameters'; asn1_type(<<"-----BEGIN EC PRIVATE KEY-----">>) -> 'ECPrivateKey'; asn1_type(<<"-----BEGIN OPENSSH PRIVATE KEY-----">>) -> {no_asn1, new_openssh}. %% Temporarily in the prototype of this format %% @private pem_end(<<"-----BEGIN CERTIFICATE-----">>) -> <<"-----END CERTIFICATE-----">>; pem_end(<<"-----BEGIN RSA PRIVATE KEY-----">>) -> <<"-----END RSA PRIVATE KEY-----">>; pem_end(<<"-----BEGIN RSA PUBLIC KEY-----">>) -> <<"-----END RSA PUBLIC KEY-----">>; pem_end(<<"-----BEGIN PUBLIC KEY-----">>) -> <<"-----END PUBLIC KEY-----">>; pem_end(<<"-----BEGIN DSA PRIVATE KEY-----">>) -> <<"-----END DSA PRIVATE KEY-----">>; pem_end(<<"-----BEGIN DH PARAMETERS-----">>) -> <<"-----END DH PARAMETERS-----">>; pem_end(<<"-----BEGIN PRIVATE KEY-----">>) -> <<"-----END PRIVATE KEY-----">>; pem_end(<<"-----BEGIN ENCRYPTED PRIVATE KEY-----">>) -> <<"-----END ENCRYPTED PRIVATE KEY-----">>; pem_end(<<"-----BEGIN CERTIFICATE REQUEST-----">>) -> <<"-----END CERTIFICATE REQUEST-----">>; pem_end(<<"-----BEGIN PKCS7-----">>) -> <<"-----END PKCS7-----">>; pem_end(<<"-----BEGIN X509 CRL-----">>) -> <<"-----END X509 CRL-----">>; pem_end(<<"-----BEGIN EC PARAMETERS-----">>) -> <<"-----END EC PARAMETERS-----">>; pem_end(<<"-----BEGIN EC PRIVATE KEY-----">>) -> <<"-----END EC PRIVATE KEY-----">>; pem_end(<<"-----BEGIN OPENSSH PRIVATE KEY-----">>) -> <<"-----END OPENSSH PRIVATE KEY-----">>; pem_end(_) -> undefined. %% @private pem_enc(Entries) -> pem_enc(Entries, []). %% @private pem_enc([Entry={'PrivateKeyInfo', _, _} | Entries], Acc) -> Encoded = try public_key:pem_encode([Entry]) catch _:_ -> pem_entry_enc(Entry) end, pem_enc(Entries, [Encoded | Acc]); pem_enc([Entry | Entries], Acc) -> Encoded = public_key:pem_encode([Entry]), pem_enc(Entries, [Encoded | Acc]); pem_enc([], Acc) -> {true, erlang:iolist_to_binary(lists:reverse(Acc))}. %% @private pem_entry_dec({ASN1Type='PrivateKeyInfo', Der, not_encrypted}) -> Entity = der_decode(ASN1Type, Der), {true, i2k(Entity)}; pem_entry_dec({ASN1Type='SubjectPublicKeyInfo', Der, not_encrypted}) -> Entity = der_decode(ASN1Type, Der), {true, i2k(Entity)}; pem_entry_dec(_) -> false. %% @private pem_entry_dec({Asn1Type, EncryptedDer, CipherInfo = {_, #'PBES2-params'{}}}, Password) when is_atom(Asn1Type) -> case decipher({Asn1Type, EncryptedDer, CipherInfo}, Password) of DecryptedDer when is_binary(DecryptedDer) -> {true, der_decode(Asn1Type, DecryptedDer)}; _ -> false end; pem_entry_dec(PEMEntry, _Password) -> pem_entry_dec(PEMEntry). %% @private pem_entry_enc({'PrivateKeyInfo', Der, EncParams}) -> EncodedPEM = public_key:pem_encode([{'ECPrivateKey', Der, EncParams}]), erlang:iolist_to_binary(binary:split(EncodedPEM, <<" EC">>, [global, trim_all])); pem_entry_enc(Entry) -> Entry. %% @private pem_entry_enc('EdDSA25519PrivateKey', K=#'jose_EdDSA25519PrivateKey'{}) -> EncodedPEMEntry = public_key:pem_entry_encode('PrivateKeyInfo', k2i(K)), {true, EncodedPEMEntry}; pem_entry_enc('EdDSA25519PublicKey', K=#'jose_EdDSA25519PublicKey'{}) -> EncodedPEMEntry = public_key:pem_entry_encode('SubjectPublicKeyInfo', k2i(K)), {true, EncodedPEMEntry}; pem_entry_enc('EdDSA448PrivateKey', K=#'jose_EdDSA448PrivateKey'{}) -> EncodedPEMEntry = public_key:pem_entry_encode('PrivateKeyInfo', k2i(K)), {true, EncodedPEMEntry}; pem_entry_enc('EdDSA448PublicKey', K=#'jose_EdDSA448PublicKey'{}) -> EncodedPEMEntry = public_key:pem_entry_encode('SubjectPublicKeyInfo', k2i(K)), {true, EncodedPEMEntry}; pem_entry_enc('X25519PrivateKey', K=#'jose_X25519PrivateKey'{}) -> EncodedPEMEntry = public_key:pem_entry_encode('PrivateKeyInfo', k2i(K)), {true, EncodedPEMEntry}; pem_entry_enc('X25519PublicKey', K=#'jose_X25519PublicKey'{}) -> EncodedPEMEntry = public_key:pem_entry_encode('SubjectPublicKeyInfo', k2i(K)), {true, EncodedPEMEntry}; pem_entry_enc('X448PrivateKey', K=#'jose_X448PrivateKey'{}) -> EncodedPEMEntry = public_key:pem_entry_encode('PrivateKeyInfo', k2i(K)), {true, EncodedPEMEntry}; pem_entry_enc('X448PublicKey', K=#'jose_X448PublicKey'{}) -> EncodedPEMEntry = public_key:pem_entry_encode('SubjectPublicKeyInfo', k2i(K)), {true, EncodedPEMEntry}; pem_entry_enc('PrivateKeyInfo', K) -> case K of #'jose_EdDSA25519PrivateKey'{} -> pem_entry_enc('EdDSA25519PrivateKey', K); #'jose_EdDSA448PrivateKey'{} -> pem_entry_enc('EdDSA448PrivateKey', K); #'jose_X25519PrivateKey'{} -> pem_entry_enc('X25519PrivateKey', K); #'jose_X448PrivateKey'{} -> pem_entry_enc('X448PrivateKey', K); #'ECPrivateKey'{} -> pem_entry_enc('ECPrivateKey', K); #'RSAPrivateKey'{} -> pem_entry_enc('RSAPrivateKey', K); _ -> false end; %% Compatibility between PKCS1 and PKCS8 versions of public_key pem_entry_enc('ECPrivateKey', K=#'ECPrivateKey'{}) -> EncodedPEMEntry = public_key:pem_entry_encode('PrivateKeyInfo', k2i(K)), {true, EncodedPEMEntry}; pem_entry_enc('RSAPrivateKey', K=#'RSAPrivateKey'{}) -> EncodedPEMEntry = public_key:pem_entry_encode('PrivateKeyInfo', k2i(K)), {true, EncodedPEMEntry}; pem_entry_enc(_, _) -> false. %% @private pem_entry_enc('EdDSA25519PrivateKey', K=#'jose_EdDSA25519PrivateKey'{}, Password) -> EncodedPEMEntry = pem_entry_enc0('PrivateKeyInfo', k2i(K), Password), {true, EncodedPEMEntry}; pem_entry_enc('EdDSA25519PublicKey', K=#'jose_EdDSA25519PublicKey'{}, Password) -> EncodedPEMEntry = pem_entry_enc0('SubjectPublicKeyInfo', k2i(K), Password), {true, EncodedPEMEntry}; pem_entry_enc('EdDSA448PrivateKey', K=#'jose_EdDSA448PrivateKey'{}, Password) -> EncodedPEMEntry = pem_entry_enc0('PrivateKeyInfo', k2i(K), Password), {true, EncodedPEMEntry}; pem_entry_enc('EdDSA448PublicKey', K=#'jose_EdDSA448PublicKey'{}, Password) -> EncodedPEMEntry = pem_entry_enc0('SubjectPublicKeyInfo', k2i(K), Password), {true, EncodedPEMEntry}; pem_entry_enc('X25519PrivateKey', K=#'jose_X25519PrivateKey'{}, Password) -> EncodedPEMEntry = pem_entry_enc0('PrivateKeyInfo', k2i(K), Password), {true, EncodedPEMEntry}; pem_entry_enc('X25519PublicKey', K=#'jose_X25519PublicKey'{}, Password) -> EncodedPEMEntry = pem_entry_enc0('SubjectPublicKeyInfo', k2i(K), Password), {true, EncodedPEMEntry}; pem_entry_enc('X448PrivateKey', K=#'jose_X448PrivateKey'{}, Password) -> EncodedPEMEntry = pem_entry_enc0('PrivateKeyInfo', k2i(K), Password), {true, EncodedPEMEntry}; pem_entry_enc('X448PublicKey', K=#'jose_X448PublicKey'{}, Password) -> EncodedPEMEntry = pem_entry_enc0('SubjectPublicKeyInfo', k2i(K), Password), {true, EncodedPEMEntry}; pem_entry_enc('PrivateKeyInfo', K, Password) -> case K of #'jose_EdDSA25519PrivateKey'{} -> pem_entry_enc('EdDSA25519PrivateKey', K, Password); #'jose_EdDSA448PrivateKey'{} -> pem_entry_enc('EdDSA448PrivateKey', K, Password); #'jose_X25519PrivateKey'{} -> pem_entry_enc('X25519PrivateKey', K, Password); #'jose_X448PrivateKey'{} -> pem_entry_enc('X448PrivateKey', K, Password); #'ECPrivateKey'{} -> pem_entry_enc('ECPrivateKey', K, Password); #'RSAPrivateKey'{} -> pem_entry_enc('RSAPrivateKey', K, Password); _ -> false end; %% Compatibility between PKCS1 and PKCS8 versions of public_key pem_entry_enc('ECPrivateKey', K=#'ECPrivateKey'{}, Password) -> EncodedPEMEntry = pem_entry_enc0('PrivateKeyInfo', k2i(K), Password), {true, EncodedPEMEntry}; pem_entry_enc('RSAPrivateKey', K=#'RSAPrivateKey'{}, Password) -> EncodedPEMEntry = pem_entry_enc0('PrivateKeyInfo', k2i(K), Password), {true, EncodedPEMEntry}; pem_entry_enc(_, _, _) -> false. %% @private pem_entry_enc0(ASN1Type, Entry, Cipher) -> try public_key:pem_entry_encode(ASN1Type, Entry, Cipher) catch ?COMPAT_CATCH(Class, Reason, ST) -> case pem_entry_enc1(ASN1Type, Entry, Cipher) of {true, Encoded} -> Encoded; false -> erlang:raise(Class, Reason, ?COMPAT_GET_STACKTRACE(ST)) end end. %% @private pem_entry_enc1(ASN1Type, Entry, {CipherInfo={C, _}, Password}) when C == "AES-128-CBC" orelse C == "AES-192-CBC" orelse C == "AES-256-CBC" -> DecryptedDer = der_encode(ASN1Type, Entry), case cipher(DecryptedDer, CipherInfo, Password) of EncryptedDer when is_binary(EncryptedDer) -> {true, {ASN1Type, EncryptedDer, CipherInfo}}; _ -> false end; pem_entry_enc1(_, _, _) -> false. %% @private pem_cipher(Data, {Cipher = "AES-128-CBC", KeyDevParams}, Password) -> {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), {true, jose_crypto_compat:crypto_one_time(aes_128_cbc, Key, IV, jose_jwa_pkcs7:pad(Data), true)}; pem_cipher(Data, {Cipher = "AES-192-CBC", KeyDevParams}, Password) -> {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), {true, jose_crypto_compat:crypto_one_time(aes_192_cbc, Key, IV, jose_jwa_pkcs7:pad(Data), true)}; pem_cipher(Data, {Cipher = "AES-256-CBC", KeyDevParams}, Password) -> {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), {true, jose_crypto_compat:crypto_one_time(aes_256_cbc, Key, IV, jose_jwa_pkcs7:pad(Data), true)}; pem_cipher(_, _, _) -> false. %% @private pem_decipher(Data, {Cipher = "AES-128-CBC", KeyDevParams}, Password) -> {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), {true, jose_crypto_compat:crypto_one_time(aes_128_cbc, Key, IV, Data, false)}; pem_decipher(Data, {Cipher = "AES-192-CBC", KeyDevParams}, Password) -> {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), {true, jose_crypto_compat:crypto_one_time(aes_192_cbc, Key, IV, Data, false)}; pem_decipher(Data, {Cipher = "AES-256-CBC", KeyDevParams}, Password) -> {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), {true, jose_crypto_compat:crypto_one_time(aes_256_cbc, Key, IV, Data, false)}; pem_decipher(_, _, _) -> false. %% @private ceiling(Float) -> erlang:round(Float + 0.5). %% @private derived_key_length(_, Len) when is_integer(Len) -> Len; derived_key_length(Cipher, _) when (Cipher == "AES-128-CBC"); (Cipher == ?'id-aes128-CBC') -> 16; derived_key_length(Cipher,_) when (Cipher == "AES-192-CBC"); (Cipher == ?'id-aes192-CBC') -> 24; derived_key_length(Cipher,_) when (Cipher == "AES-256-CBC"); (Cipher == ?'id-aes256-CBC') -> 32. %% @private password_to_key_and_iv(Password, _Cipher, Params = #'PBES2-params'{}) -> {Salt, ItrCount, KeyLen, PseudoRandomFunction, PseudoHash, PseudoOtputLen, IV} = key_derivation_params(Params), %% Use the native implementation for the strengthened export defaults when %% available (OTP 24+). Older runtimes keep the existing derivation path. KeyMaterial = case erlang:function_exported(crypto, pbkdf2_hmac, 5) of true -> crypto:pbkdf2_hmac(PseudoHash, iolist_to_binary(Password), iolist_to_binary(Salt), ItrCount, KeyLen); false -> pubkey_pbe:pbdkdf2(Password, Salt, ItrCount, KeyLen, PseudoRandomFunction, PseudoHash, PseudoOtputLen) end, <> = KeyMaterial, {Key, IV}; password_to_key_and_iv(Password, _Cipher, {#'PBEParameter'{salt = Salt, iterationCount = Count}, Hash}) -> <> = pubkey_pbe:pbdkdf1(Password, Salt, Count, Hash), {Key, IV}; password_to_key_and_iv(Password, Cipher, KeyDevParams) -> %% PKCS5_SALT_LEN is 8 bytes <> = KeyDevParams, KeyLen = derived_key_length(Cipher, undefined), <> = pem_encrypt(<<>>, Password, Salt, ceiling(KeyLen div 16), <<>>, md5), %% Old PEM encryption does not use standard encryption method %% pbdkdf1 and uses then salt as IV {Key, KeyDevParams}. %% @private pem_encrypt(_, _, _, 0, Acc, _) -> Acc; pem_encrypt(Prev, Password, Salt, Count, Acc, Hash) -> Result = crypto:hash(Hash, [Prev, Password, Salt]), pem_encrypt(Result, Password, Salt, Count-1 , <>, Hash). %% @private key_derivation_params(#'PBES2-params'{keyDerivationFunc = KeyDerivationFunc, encryptionScheme = EncScheme}) -> #'PBES2-params_keyDerivationFunc'{ algorithm = ?'id-PBKDF2', parameters = #'PBKDF2-params'{ salt = {specified, OctetSalt}, iterationCount = Count, keyLength = Length, prf = Prf } } = KeyDerivationFunc, #'PBES2-params_encryptionScheme'{algorithm = Algo} = EncScheme, {PseudoRandomFunction, PseudoHash, PseudoOtputLen} = pseudo_random_function(Prf), KeyLen = derived_key_length(Algo, Length), {OctetSalt, Count, KeyLen, PseudoRandomFunction, PseudoHash, PseudoOtputLen, iv(EncScheme)}. %% @private %% This function currently matches a tuple that ougth to be the value %% ?'id-hmacWithSHA1, but we need some kind of ASN1-fix for this. pseudo_random_function(#'PBKDF2-params_prf'{algorithm = {_,_, _,'id-hmacWithSHA1'}}) -> {fun hmac/4, sha, pseudo_output_length(?'id-hmacWithSHA1')}; pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA1' = Algo}) -> {fun hmac/4, sha, pseudo_output_length(Algo)}; pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA224'= Algo}) -> {fun hmac/4, sha224, pseudo_output_length(Algo)}; pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA256' = Algo}) -> {fun hmac/4, sha256, pseudo_output_length(Algo)}; pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA384' = Algo}) -> {fun hmac/4, sha384, pseudo_output_length(Algo)}; pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA512' = Algo}) -> {fun hmac/4, sha512, pseudo_output_length(Algo)}. %% @private hmac(SubType, Key, Data, MacLength) -> jose_crypto_compat:mac(hmac, SubType, Key, Data, MacLength). %% @private pseudo_output_length(?'id-hmacWithSHA1') -> 20; %%160/8 pseudo_output_length(?'id-hmacWithSHA224') -> 28; %%%224/8 pseudo_output_length(?'id-hmacWithSHA256') -> 32; %%256/8 pseudo_output_length(?'id-hmacWithSHA384') -> 48; %%384/8 pseudo_output_length(?'id-hmacWithSHA512') -> 64. %%512/8 %% @private iv(#'PBES2-params_encryptionScheme'{algorithm = ?'rc2CBC', parameters = ASN1IV}) -> {ok, #'RC2-CBC-Parameter'{iv = IV}} = 'PKCS-FRAME':decode('RC2-CBC-Parameter', decode_handle_open_type_wrapper(ASN1IV)), erlang:iolist_to_binary(IV); iv(#'PBES2-params_encryptionScheme'{algorithm = _Algo, parameters = ASN1IV}) -> <<4, Len:8/unsigned-big-integer, IV:Len/binary>> = decode_handle_open_type_wrapper(ASN1IV), IV. %% @private decode_handle_open_type_wrapper({asn1_OPENTYPE, Type}) -> Type. %% @private encode_handle_open_type_wrapper(Type) -> {asn1_OPENTYPE, Type}. %% @private %% OTP 29+ decodes the algorithm as 'PrivateKeyAlgorithmIdentifier'. i2k(PrivateKeyInfo=#'PrivateKeyInfo'{ privateKeyAlgorithm = {'PrivateKeyAlgorithmIdentifier', Algorithm, Parameters} }) -> i2k(PrivateKeyInfo#'PrivateKeyInfo'{ privateKeyAlgorithm = #'PrivateKeyInfo_privateKeyAlgorithm'{ algorithm = Algorithm, parameters = Parameters } }); i2k(#'PrivateKeyInfo'{ privateKeyAlgorithm = #'PrivateKeyInfo_privateKeyAlgorithm'{ algorithm = ?'jose_id-EdDSA25519' }, privateKey = << 4, 32:8/integer, PrivateKey:32/binary >> }) -> PublicKey = jose_curve25519:eddsa_secret_to_public(PrivateKey), #'jose_EdDSA25519PrivateKey'{ publicKey = #'jose_EdDSA25519PublicKey'{ publicKey = PublicKey }, privateKey = PrivateKey }; i2k(ECPrivateKey = {'ECPrivateKey', _, PrivateKey, {namedCurve, ?'jose_id-EdDSA25519'}, _, _}) -> i2k_eddsa25519(ECPrivateKey, PrivateKey); i2k(ECPrivateKey = {'ECPrivateKey', _, PrivateKey, {namedCurve, ?'jose_id-EdDSA25519'}, _, _, _}) -> i2k_eddsa25519(ECPrivateKey, PrivateKey); i2k(#'SubjectPublicKeyInfo'{ algorithm = #'AlgorithmIdentifier'{ algorithm = ?'jose_id-EdDSA25519' }, subjectPublicKey = << PublicKey:32/binary >> }) -> #'jose_EdDSA25519PublicKey'{ publicKey = PublicKey }; i2k(#'PrivateKeyInfo'{ privateKeyAlgorithm = #'PrivateKeyInfo_privateKeyAlgorithm'{ algorithm = ?'jose_id-EdDSA448' }, privateKey = << 4, 57:8/integer, PrivateKey:57/binary >> }) -> PublicKey = jose_curve448:eddsa_secret_to_public(PrivateKey), #'jose_EdDSA448PrivateKey'{ publicKey = #'jose_EdDSA448PublicKey'{ publicKey = PublicKey }, privateKey = PrivateKey }; i2k(ECPrivateKey = {'ECPrivateKey', _, PrivateKey, {namedCurve, ?'jose_id-EdDSA448'}, _, _}) -> i2k_eddsa448(ECPrivateKey, PrivateKey); i2k(ECPrivateKey = {'ECPrivateKey', _, PrivateKey, {namedCurve, ?'jose_id-EdDSA448'}, _, _, _}) -> i2k_eddsa448(ECPrivateKey, PrivateKey); i2k(#'SubjectPublicKeyInfo'{ algorithm = #'AlgorithmIdentifier'{ algorithm = ?'jose_id-EdDSA448' }, subjectPublicKey = << PublicKey:57/binary >> }) -> #'jose_EdDSA448PublicKey'{ publicKey = PublicKey }; i2k(#'PrivateKeyInfo'{ privateKeyAlgorithm = #'PrivateKeyInfo_privateKeyAlgorithm'{ algorithm = ?'jose_id-X25519' }, privateKey = << 4, 32:8/integer, PrivateKey:32/binary >> }) -> PublicKey = jose_curve25519:x25519_secret_to_public(PrivateKey), #'jose_X25519PrivateKey'{ publicKey = #'jose_X25519PublicKey'{ publicKey = PublicKey }, privateKey = PrivateKey }; i2k(#'SubjectPublicKeyInfo'{ algorithm = #'AlgorithmIdentifier'{ algorithm = ?'jose_id-X25519' }, subjectPublicKey = << PublicKey:32/binary >> }) -> #'jose_X25519PublicKey'{ publicKey = PublicKey }; i2k(#'PrivateKeyInfo'{ privateKeyAlgorithm = #'PrivateKeyInfo_privateKeyAlgorithm'{ algorithm = ?'jose_id-X448' }, privateKey = << 4, 56:8/integer, PrivateKey:56/binary >> }) -> PublicKey = jose_curve448:x448_secret_to_public(PrivateKey), #'jose_X448PrivateKey'{ publicKey = #'jose_X448PublicKey'{ publicKey = PublicKey }, privateKey = PrivateKey }; i2k(#'SubjectPublicKeyInfo'{ algorithm = #'AlgorithmIdentifier'{ algorithm = ?'jose_id-X448' }, subjectPublicKey = << PublicKey:56/binary >> }) -> #'jose_X448PublicKey'{ publicKey = PublicKey }; % public_key compat i2k(#'SubjectPublicKeyInfo'{ algorithm = #'AlgorithmIdentifier'{ algorithm = ?'id-ecPublicKey', parameters = ECParameters }, subjectPublicKey = ECPublicKey }) -> {#'ECPoint'{point = ECPublicKey}, der_decode('EcpkParameters', ECParameters)}; i2k(PrivateKeyInfo=#'PrivateKeyInfo'{ privateKeyAlgorithm = #'PrivateKeyInfo_privateKeyAlgorithm'{ algorithm = ?'id-ecPublicKey', parameters = {asn1_OPENTYPE, EcpkParameters} }, privateKey = PrivateKey }) -> case der_decode('ECPrivateKey', PrivateKey) of ECPrivateKey = #'ECPrivateKey'{parameters = asn1_NOVALUE} -> ECPrivateKey#'ECPrivateKey'{parameters = der_decode('EcpkParameters', EcpkParameters)}; _ -> PrivateKeyInfo end; i2k(Info) -> Info. %% @private i2k_eddsa25519(_ECPrivateKey, << 4, 32:8/integer, PrivateKey:32/binary >>) -> PublicKey = jose_curve25519:eddsa_secret_to_public(PrivateKey), #'jose_EdDSA25519PrivateKey'{ publicKey = #'jose_EdDSA25519PublicKey'{ publicKey = PublicKey }, privateKey = PrivateKey }; i2k_eddsa25519(_ECPrivateKey, << PrivateKey:32/binary >>) -> PublicKey = jose_curve25519:eddsa_secret_to_public(PrivateKey), #'jose_EdDSA25519PrivateKey'{ publicKey = #'jose_EdDSA25519PublicKey'{ publicKey = PublicKey }, privateKey = PrivateKey }. %% @private i2k_eddsa448(_ECPrivateKey, << 4, 57:8/integer, PrivateKey:57/binary >>) -> PublicKey = jose_curve448:eddsa_secret_to_public(PrivateKey), #'jose_EdDSA448PrivateKey'{ publicKey = #'jose_EdDSA448PublicKey'{ publicKey = PublicKey }, privateKey = PrivateKey }; i2k_eddsa448(_ECPrivateKey, << PrivateKey:57/binary >>) -> PublicKey = jose_curve448:eddsa_secret_to_public(PrivateKey), #'jose_EdDSA448PrivateKey'{ publicKey = #'jose_EdDSA448PublicKey'{ publicKey = PublicKey }, privateKey = PrivateKey }. %% @private k2i(#'jose_EdDSA25519PrivateKey'{privateKey=PrivateKey}) -> #'PrivateKeyInfo'{ version = v1, privateKeyAlgorithm = #'PrivateKeyInfo_privateKeyAlgorithm'{ algorithm = ?'jose_id-EdDSA25519', parameters = asn1_NOVALUE }, privateKey = << 4, 32:8/integer, PrivateKey:32/binary >>, attributes = asn1_NOVALUE }; k2i(#'jose_EdDSA25519PublicKey'{publicKey=PublicKey}) -> #'SubjectPublicKeyInfo'{ algorithm = #'AlgorithmIdentifier'{ algorithm = ?'jose_id-EdDSA25519', parameters = asn1_NOVALUE }, subjectPublicKey = << PublicKey:32/binary >> }; k2i(#'jose_EdDSA448PrivateKey'{privateKey=PrivateKey}) -> #'PrivateKeyInfo'{ version = v1, privateKeyAlgorithm = #'PrivateKeyInfo_privateKeyAlgorithm'{ algorithm = ?'jose_id-EdDSA448', parameters = asn1_NOVALUE }, privateKey = << 4, 57:8/integer, PrivateKey:57/binary >>, attributes = asn1_NOVALUE }; k2i(#'jose_EdDSA448PublicKey'{publicKey=PublicKey}) -> #'SubjectPublicKeyInfo'{ algorithm = #'AlgorithmIdentifier'{ algorithm = ?'jose_id-EdDSA448', parameters = asn1_NOVALUE }, subjectPublicKey = << PublicKey:57/binary >> }; k2i(#'jose_X25519PrivateKey'{privateKey=PrivateKey}) -> #'PrivateKeyInfo'{ version = v1, privateKeyAlgorithm = #'PrivateKeyInfo_privateKeyAlgorithm'{ algorithm = ?'jose_id-X25519', parameters = asn1_NOVALUE }, privateKey = << 4, 32:8/integer, PrivateKey:32/binary >>, attributes = asn1_NOVALUE }; k2i(#'jose_X25519PublicKey'{publicKey=PublicKey}) -> #'SubjectPublicKeyInfo'{ algorithm = #'AlgorithmIdentifier'{ algorithm = ?'jose_id-X25519', parameters = asn1_NOVALUE }, subjectPublicKey = << PublicKey:32/binary >> }; k2i(#'jose_X448PrivateKey'{privateKey=PrivateKey}) -> #'PrivateKeyInfo'{ version = v1, privateKeyAlgorithm = #'PrivateKeyInfo_privateKeyAlgorithm'{ algorithm = ?'jose_id-X448', parameters = asn1_NOVALUE }, privateKey = << 4, 56:8/integer, PrivateKey:56/binary >>, attributes = asn1_NOVALUE }; k2i(#'jose_X448PublicKey'{publicKey=PublicKey}) -> #'SubjectPublicKeyInfo'{ algorithm = #'AlgorithmIdentifier'{ algorithm = ?'jose_id-X448', parameters = asn1_NOVALUE }, subjectPublicKey = << PublicKey:56/binary >> }; % public_key compat k2i(PrivateKey=#'ECPrivateKey'{parameters=ECParameters}) -> #'PrivateKeyInfo'{ version = v1, privateKeyAlgorithm = #'PrivateKeyInfo_privateKeyAlgorithm'{ algorithm = ?'id-ecPublicKey', parameters = encode_handle_open_type_wrapper(der_encode('EcpkParameters', ECParameters)) }, privateKey = der_encode('ECPrivateKey', PrivateKey#'ECPrivateKey'{parameters=asn1_NOVALUE}), attributes = asn1_NOVALUE }; k2i(PrivateKey=#'RSAPrivateKey'{}) -> #'PrivateKeyInfo'{ version = v1, privateKeyAlgorithm = #'PrivateKeyInfo_privateKeyAlgorithm'{ algorithm = ?'rsaEncryption', parameters = asn1_NOVALUE }, privateKey = der_encode('RSAPrivateKey', PrivateKey), attributes = asn1_NOVALUE }. potatosalad-erlang-jose-30ac019/src/jose_sup.erl0000664000232200023220000000261615267717064022271 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 06 Aug 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_sup). -behaviour(supervisor). -define(SERVER, ?MODULE). %% API -export([start_link/0]). %% Supervisor callbacks -export([init/1]). %% Helper macro for declaring children of supervisor -define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). %%%=================================================================== %%% API functions %%%=================================================================== start_link() -> supervisor:start_link({local, ?SERVER}, ?MODULE, []). %%%=================================================================== %%% Supervisor callbacks %%%=================================================================== %% @private init([]) -> ChildSpecs = [ ?CHILD(jose_server, worker) ], Restart = {one_for_one, 10, 10}, {ok, {Restart, ChildSpecs}}. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/json/0000775000232200023220000000000015267717064020702 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/src/json/jose_json_ojson.erl0000664000232200023220000000171115267717064024607 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 18 May 2017 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_json_ojson). -behaviour(jose_json). %% jose_json callbacks -export([decode/1]). -export([encode/1]). %%==================================================================== %% jose_json callbacks %%==================================================================== decode(Binary) -> ojson:'decode!'(Binary). encode(Term) -> ojson:'encode!'(Term). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/json/jose_json_jsone.erl0000664000232200023220000000172515267717064024602 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 20 Aug 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_json_jsone). -behaviour(jose_json). %% jose_json callbacks -export([decode/1]). -export([encode/1]). %%==================================================================== %% jose_json callbacks %%==================================================================== decode(Binary) -> jsone:decode(Binary). encode(Term) -> jsone:encode(Term, [canonical_form]). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/json/jose_json.erl0000664000232200023220000000103315267717064023374 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 14 Aug 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_json). -callback decode(binary()) -> dynamic(). -callback encode(dynamic()) -> binary(). potatosalad-erlang-jose-30ac019/src/json/jose_json_poison_lexical_encoder.erl0000664000232200023220000000203115267717064030162 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 14 Aug 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_json_poison_lexical_encoder). -behaviour(jose_json). -compile(inline_list_funcs). %% jose_json callbacks -export([decode/1]). -export([encode/1]). %%==================================================================== %% jose_json callbacks %%==================================================================== decode(Binary) -> 'Elixir.Poison':'decode!'(Binary). encode(Term) -> 'Elixir.JOSE.Poison':'lexical_encode!'(Term). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/json/jose_json_jiffy.erl0000664000232200023220000000300515267717064024564 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 20 Aug 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_json_jiffy). -behaviour(jose_json). %% jose_json callbacks -export([decode/1]). -export([encode/1]). %%==================================================================== %% jose_json callbacks %%==================================================================== decode(Binary) -> jiffy:decode(Binary, [return_maps]). encode(Map) when is_map(Map) -> ensure_binary(jiffy:encode(sort(Map))); encode(List) when is_list(List) -> ensure_binary(jiffy:encode(sort(List))); encode(Term) -> ensure_binary(jiffy:encode(Term)). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private sort(Map) when is_map(Map) -> {[{sort(Key), sort(Val)} || {Key, Val} <- maps:to_list(Map)]}; sort(List) when is_list(List) -> [sort(Term) || Term <- List]; sort(Term) -> Term. %% @private %% NOTE: jiffy may return an iolist instead of a binary when encoding %% big objects. ensure_binary(List) when is_list(List) -> iolist_to_binary(List); ensure_binary(Binary) -> Binary. potatosalad-erlang-jose-30ac019/src/json/jose_json_poison_compat_encoder.erl0000664000232200023220000001065415267717064030036 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 14 Aug 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_json_poison_compat_encoder). -behaviour(jose_json). %% jose_json callbacks -export([decode/1]). -export([encode/1]). %%==================================================================== %% jose_json callbacks %%==================================================================== decode(Binary) -> 'Elixir.Poison':'decode!'(Binary). encode(Term) -> 'Elixir.IO':'iodata_to_binary'(lexical_encode(Term)). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private lexical_encode(Atom) when is_atom(Atom) -> apply('Elixir.Poison.Encoder.Atom', 'encode', [Atom, #{}]); lexical_encode(BitString) when is_bitstring(BitString) -> apply('Elixir.Poison.Encoder.BitString', 'encode', [BitString, #{}]); lexical_encode(Integer) when is_integer(Integer) -> apply('Elixir.Poison.Encoder.Integer', 'encode', [Integer, #{}]); lexical_encode(Float) when is_float(Float) -> apply('Elixir.Poison.Encoder.Float', 'encode', [Float, #{}]); lexical_encode(Struct = #{ '__struct__' := Type }) -> lexical_encode_struct(Type, Struct); lexical_encode(Map) when is_map(Map) -> lexical_encode_map(Map); lexical_encode(List) when is_list(List) -> lexical_encode_list(List); lexical_encode(Any) -> erlang:error('Elixir.Poison.EncodeError':'exception'([{value, Any}])). %% @private lexical_encode_name(Binary) when is_binary(Binary) -> Binary; lexical_encode_name(Atom) when is_atom(Atom) -> 'Elixir.Atom':'to_string'(Atom); lexical_encode_name(Any) -> erlang:error('Elixir.Poison.EncodeError':'exception'([ {value, Any}, {message, << "expected string or atom key, got: ", ('Elixir.Kernel':'inspect'(Any))/binary >>} ])). %% @private lexical_encode_map(Map) when map_size(Map) < 1 -> <<"{}">>; lexical_encode_map(Map) when is_map(Map) -> Folder = fun (Key, Acc) -> [ $,, apply('Elixir.Poison.Encoder.BitString', 'encode', [lexical_encode_name(Key), #{}]), $:, lexical_encode(maps:get(Key, Map)) | Acc ] end, [ ${, tl(lists:foldr(Folder, [], lists:sort(maps:keys(Map)))), $} ]. %% @private lexical_encode_list([]) -> <<"[]">>; lexical_encode_list(List) when is_list(List) -> Folder = fun (Element, Acc) -> [ $,, lexical_encode(Element) | Acc ] end, [ $[, tl(lists:foldr(Folder, [], List)), $] ]. %% @private lexical_encode_struct(Type, Struct) when Type == 'Elixir.Range' orelse Type == 'Elixir.Stream' orelse Type == 'Elixir.MapSet' orelse Type == 'Elixir.HashSet' -> FlatMapper = fun (Element) -> [ $,, lexical_encode(Element) ] end, case 'Elixir.Enum':'flat_map'(Struct, FlatMapper) of [] -> <<"[]">>; [_ | Tail] -> [ $[, Tail, $] ] end; lexical_encode_struct('Elixir.HashDict', HashDict) -> case 'Elixir.HashDict':'size'(HashDict) < 1 of true -> <<"{}">>; false -> FlatMapper = fun ({Key, Value}) -> [ $,, apply('Elixir.Poison.Encoder.BitString', 'encode', [lexical_encode_name(Key), #{}]), $:, lexical_encode(Value) ] end, [ ${, tl('Elixir.Enum':'flat_map'(HashDict, FlatMapper)), $} ] end; lexical_encode_struct(Type, Struct) when Type == 'Elixir.Date' orelse Type == 'Elixir.Time' orelse Type == 'Elixir.NaiveDateTime' orelse Type == 'Elixir.DateTime' -> apply('Elixir.Poison.Encoder.BitString', 'encode', [Type:'to_iso8601'(Struct), #{}]); lexical_encode_struct(Type, Struct) -> case find_encoder(Type) of {true, Encoder} -> apply(Encoder, 'encode', [Struct, #{}]); false -> lexical_encode_map('Elixir.Map':'from_struct'(Struct)) end. %% @private find_encoder(ElixirType) -> case atom_to_binary(ElixirType, unicode) of << "Elixir.", Type/binary >> -> try binary_to_existing_atom(<< "Elixir.Poison.Encoder.", Type/binary >>, unicode) of EncoderType -> case code:ensure_loaded(EncoderType) of {module, EncoderType} -> {true, EncoderType}; _ -> false end catch _:_ -> false end; _ -> false end. potatosalad-erlang-jose-30ac019/src/json/jose_json_otp.erl0000664000232200023220000000242715267717064024266 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_json_otp). -behaviour(jose_json). %% jose_json callbacks -export([decode/1]). -export([encode/1]). %%==================================================================== %% jose_json callbacks %%==================================================================== -spec decode(binary()) -> dynamic(). decode(Binary) -> json:decode(Binary). -spec encode(dynamic()) -> binary(). encode(Term) -> iolist_to_binary(json:encode(Term, fun encoder/2)). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private -compile({inline, [dynamic_cast/1]}). -spec dynamic_cast(term()) -> dynamic(). dynamic_cast(X) -> X. %% @private -spec encoder(dynamic(), json:encoder()) -> iodata(). encoder(List, Encoder) when is_list(List) -> json:encode_list(List, Encoder); encoder(Map, Encoder) when is_map(Map) -> KeyValueList = maps:to_list(dynamic_cast(maps:iterator(Map, ordered))), json:encode_key_value_list(KeyValueList, Encoder); encoder('nil', Encoder) -> json:encode_atom(null, Encoder); encoder(Value, Encoder) -> json:encode_value(Value, Encoder). potatosalad-erlang-jose-30ac019/src/json/jose_json_poison.erl0000664000232200023220000000173615267717064024775 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 14 Aug 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_json_poison). -behaviour(jose_json). %% jose_json callbacks -export([decode/1]). -export([encode/1]). %%==================================================================== %% jose_json callbacks %%==================================================================== decode(Binary) -> 'Elixir.Poison':'decode!'(Binary). encode(Term) -> 'Elixir.Poison':'encode!'(Term). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/json/jose_json_thoas.erl0000664000232200023220000000155415267717064024602 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Michael Klishin %%% @copyright 2022, Michael Klishin %%% @doc %%% %%% @end %%% Created : 30 Jul 2022 by Michael Klishin %%%------------------------------------------------------------------- -module(jose_json_thoas). -behaviour(jose_json). %% jose_json callbacks -export([decode/1]). -export([encode/1]). %%==================================================================== %% jose_json callbacks %%==================================================================== decode(Binary) -> case thoas:decode(Binary) of {ok, Value} -> Value; {error, _} = Error -> error(Error) end. encode(Term) -> thoas:encode(Term). potatosalad-erlang-jose-30ac019/src/json/jose_json_jason.erl0000664000232200023220000000122515267717064024571 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet -module(jose_json_jason). -behaviour(jose_json). %% jose_json callbacks -export([decode/1]). -export([encode/1]). %%==================================================================== %% jose_json callbacks %%==================================================================== decode(Binary) -> 'Elixir.Jason':'decode!'(Binary). encode(Term) -> 'Elixir.Jason':'encode!'(Term). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/json/jose_json_unsupported.erl0000664000232200023220000000175715267717064026061 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 14 Aug 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_json_unsupported). -behaviour(jose_json). %% jose_json callbacks -export([decode/1]). -export([encode/1]). %%==================================================================== %% jose_json callbacks %%==================================================================== decode(_Binary) -> erlang:error(unsupported_json_module). encode(_Term) -> erlang:error(unsupported_json_module). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/json/jose_json_jsx.erl0000664000232200023220000000171415267717064024266 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 14 Aug 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_json_jsx). -behaviour(jose_json). %% jose_json callbacks -export([decode/1]). -export([encode/1]). %%==================================================================== %% jose_json callbacks %%==================================================================== decode(Binary) -> jsx:decode(Binary, [return_maps]). encode(Term) -> jsx:encode(Term). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jose_app.erl0000664000232200023220000000156615267717064022245 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 06 Aug 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_app). -behaviour(application). %% Application callbacks -export([start/2]). -export([stop/1]). -export([config_change/3]). %%%=================================================================== %%% Application callbacks %%%=================================================================== start(_Type, _Args) -> jose_sup:start_link(). stop(_State) -> ok. config_change(_Changed, _New, _Removed) -> jose_server:config_change(). potatosalad-erlang-jose-30ac019/src/jwa/0000775000232200023220000000000015267717064020512 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/src/jwa/jose_jwa.erl0000664000232200023220000003532715267717064023031 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 21 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwa). -include_lib("public_key/include/public_key.hrl"). %% Crypto API -export([block_decrypt/3]). -export([block_encrypt/3]). -export([block_decrypt/4]). -export([block_encrypt/4]). %% Public Key API -export([decrypt_private/3]). -export([encrypt_public/3]). -export([sign/4]). -export([verify/5]). %% API -export([block_cipher/1]). -export([crypto_ciphers/0]). -export([crypto_fallback/0]). -export([crypto_fallback/1]). -export([crypto_supports/0]). -export([constant_time_compare/2]). -export([ec_key_mode/0]). -export([is_block_cipher_supported/1]). -export([is_chacha20_poly1305_supported/0]). -export([is_rsa_crypt_supported/1]). -export([is_rsa_sign_supported/1]). -export([is_xchacha20_poly1305_supported/0]). -export([supports/0]). -export([unsecured_signing/0]). -export([unsecured_signing/1]). -define(TAB, ?MODULE). -define(MAYBE_START_JOSE(F), try F catch _:_ -> _ = jose:start(), F end). %%==================================================================== %% Crypto API functions %%==================================================================== block_decrypt(Cipher, Key, CipherText) when is_binary(CipherText) -> case block_cipher(Cipher) of {crypto, BlockCipher} -> jose_crypto_compat:crypto_one_time(BlockCipher, Key, CipherText, false); {Module, BlockCipher} -> Module:block_decrypt(BlockCipher, Key, CipherText) end. block_encrypt(Cipher, Key, PlainText) when is_binary(PlainText) -> case block_cipher(Cipher) of {crypto, BlockCipher} -> jose_crypto_compat:crypto_one_time(BlockCipher, Key, PlainText, true); {Module, BlockCipher} -> Module:block_encrypt(BlockCipher, Key, PlainText) end. block_decrypt(Cipher, Key, IV, CipherText) when is_binary(CipherText) -> case block_cipher(Cipher) of {crypto, BlockCipher} -> jose_crypto_compat:crypto_one_time(BlockCipher, Key, IV, CipherText, false); {Module, BlockCipher} -> Module:block_decrypt(BlockCipher, Key, IV, CipherText) end; %% Every AEAD cipher reachable through this dispatcher (AES-GCM, %% ChaCha20-Poly1305 and XChaCha20-Poly1305) authenticates with a %% 128-bit tag. The tag arrives straight from the attacker-supplied %% JWE token, so a truncated tag must never be verified: doing so %% would only compare bit_size(CipherTag) bits of the real tag and %% drop forgery resistance to 2^-bit_size(CipherTag). Reject any tag %% that is not exactly 128 bits, using the same `error' return that a %% failed authentication uses. block_decrypt(_Cipher, _Key, _IV, {AAD, CipherText, CipherTag}) when is_binary(AAD) andalso is_binary(CipherText) andalso is_binary(CipherTag) andalso bit_size(CipherTag) =/= 128 -> error; block_decrypt(Cipher, Key, IV, {AAD, CipherText, CipherTag}) when is_binary(AAD) andalso is_binary(CipherText) andalso is_binary(CipherTag) -> case block_cipher(Cipher) of {crypto, BlockCipher} -> jose_crypto_compat:crypto_one_time(BlockCipher, Key, IV, {AAD, CipherText, CipherTag}, false); {Module, BlockCipher} -> Module:block_decrypt(BlockCipher, Key, IV, {AAD, CipherText, CipherTag}) end. block_encrypt(Cipher, Key, IV, PlainText) when is_binary(PlainText) -> case block_cipher(Cipher) of {crypto, BlockCipher} -> jose_crypto_compat:crypto_one_time(BlockCipher, Key, IV, PlainText, true); {Module, BlockCipher} -> Module:block_encrypt(BlockCipher, Key, IV, PlainText) end; block_encrypt(Cipher, Key, IV, {AAD, PlainText}) when is_binary(AAD) andalso is_binary(PlainText) -> case block_cipher(Cipher) of {crypto, BlockCipher} -> jose_crypto_compat:crypto_one_time(BlockCipher, Key, IV, {AAD, PlainText}, true); {Module, BlockCipher} -> Module:block_encrypt(BlockCipher, Key, IV, {AAD, PlainText}) end. %%==================================================================== %% Public Key API functions %%==================================================================== decrypt_private(CipherText, RSAPrivateKey=#'RSAPrivateKey'{}, Algorithm) when is_atom(Algorithm) -> {Module, Options} = rsa_crypt(Algorithm), Module:decrypt_private(CipherText, RSAPrivateKey, Options); decrypt_private(CipherText, PrivateKey, Options) -> public_key:decrypt_private(CipherText, PrivateKey, Options). encrypt_public(PlainText, RSAPublicKey=#'RSAPublicKey'{}, Algorithm) when is_atom(Algorithm) -> {Module, Options} = rsa_crypt(Algorithm), Module:encrypt_public(PlainText, RSAPublicKey, Options); encrypt_public(PlainText, PublicKey, Options) -> public_key:encrypt_public(PlainText, PublicKey, Options). sign(Message, DigestType, RSAPrivateKey=#'RSAPrivateKey'{}, Padding) when is_atom(Padding) -> case rsa_sign(Padding) of {Module, undefined} -> Module:sign(Message, DigestType, RSAPrivateKey); {Module, Options} -> Module:sign(Message, DigestType, RSAPrivateKey, Options) end; sign(Message, DigestType, PrivateKey, _Options) -> public_key:sign(Message, DigestType, PrivateKey). verify(Message, DigestType, Signature, RSAPublicKey=#'RSAPublicKey'{}, Padding) when is_atom(Padding) -> case rsa_sign(Padding) of {Module, undefined} -> Module:verify(Message, DigestType, Signature, RSAPublicKey); {Module, Options} -> Module:verify(Message, DigestType, Signature, RSAPublicKey, Options) end; verify(Message, DigestType, Signature, PublicKey, _Options) -> public_key:verify(Message, DigestType, Signature, PublicKey). %%==================================================================== %% API functions %%==================================================================== block_cipher(Cipher) -> ?MAYBE_START_JOSE(ets:lookup_element(?TAB, {cipher, Cipher}, 2)). crypto_ciphers() -> ?MAYBE_START_JOSE(ets:select(?TAB, [{ {{cipher, '$1'}, {'$2', '_'}}, [{'=/=', '$2', 'jose_jwa_unsupported'}], [{{'$1', '$2'}}] }])). crypto_fallback() -> application:get_env(jose, crypto_fallback, false). crypto_fallback(Boolean) when is_boolean(Boolean) -> application:set_env(jose, crypto_fallback, Boolean), ?MAYBE_START_JOSE(jose_server:config_change()). crypto_supports() -> Ciphers = ?MAYBE_START_JOSE(ets:select(?TAB, [{ {{cipher, '$1'}, {'$2', '_'}}, [{'=/=', '$2', 'jose_jwa_unsupported'}], ['$1'] }])), RSACrypt = ?MAYBE_START_JOSE(ets:select(?TAB, [{ {{rsa_crypt, '$1'}, {'$2', '_'}}, [{'=/=', '$2', 'jose_jwa_unsupported'}], ['$1'] }])), RSASign = ?MAYBE_START_JOSE(ets:select(?TAB, [{ {{rsa_sign, '$1'}, {'$2', '_'}}, [{'=/=', '$2', 'jose_jwa_unsupported'}], ['$1'] }])), ExternalHashs = external_checks([ {poly1305, fun() -> jose_chacha20_poly1305:authenticate(<<>>, <<0:256>>, <<0:96>>) end}, {shake256, fun() -> jose_sha3:shake256(<<>>, 0) end} ]), ExternalPublicKeys = external_checks([ {ed25519, fun jose_curve25519:eddsa_keypair/0}, {ed25519ph, fun jose_curve25519:eddsa_keypair/0}, {ed448, fun jose_curve448:eddsa_keypair/0}, {ed448ph, fun jose_curve448:eddsa_keypair/0}, {x25519, fun jose_curve25519:x25519_keypair/0}, {x448, fun jose_curve448:x448_keypair/0} ]), Supports = crypto:supports(), RecommendedHashs = [md5, poly1305, sha, sha256, sha384, sha512, shake256], Hashs = RecommendedHashs -- ((RecommendedHashs -- proplists:get_value(hashs, Supports)) -- ExternalHashs), RecommendedPublicKeys = [ec_gf2m, ecdh, ecdsa, ed25519, ed25519ph, ed448, ed448ph, rsa, x25519, x448], PublicKeys = RecommendedPublicKeys -- ((RecommendedPublicKeys -- proplists:get_value(public_keys, Supports)) -- ExternalPublicKeys), [ {ciphers, Ciphers}, {hashs, Hashs}, {public_keys, PublicKeys}, {rsa_crypt, RSACrypt}, {rsa_sign, RSASign} ]. constant_time_compare(<<>>, _) -> false; constant_time_compare(_, <<>>) -> false; constant_time_compare(A, B) when is_binary(A) andalso is_binary(B) andalso (byte_size(A) =/= byte_size(B)) -> false; constant_time_compare(A, B) when is_binary(A) andalso is_binary(B) andalso (byte_size(A) =:= byte_size(B)) -> constant_time_compare(A, B, 0). ec_key_mode() -> ?MAYBE_START_JOSE(ets:lookup_element(?TAB, ec_key_mode, 2)). is_block_cipher_supported(Cipher) -> try block_cipher(Cipher) of {crypto, _} -> true; _ -> false catch _:_ -> false end. is_chacha20_poly1305_supported() -> try ?MAYBE_START_JOSE(ets:lookup_element(?TAB, chacha20_poly1305_module, 2)) of jose_chacha20_poly1305_unsupported -> false; _ -> true catch _:_ -> true end. is_rsa_crypt_supported(Padding) -> try rsa_crypt(Padding) of {public_key, _} -> true; _ -> false catch _:_ -> false end. is_rsa_sign_supported(Padding) -> try rsa_sign(Padding) of {public_key, _} -> true; _ -> false catch _:_ -> false end. is_xchacha20_poly1305_supported() -> try ?MAYBE_START_JOSE(ets:lookup_element(?TAB, xchacha20_poly1305_module, 2)) of jose_xchacha20_poly1305_unsupported -> false; _ -> true catch _:_ -> true end. supports() -> Supports = crypto_supports(), JWEALG = support_check([ {<<"A128GCMKW">>, ciphers, {aes_gcm, 128}}, {<<"A192GCMKW">>, ciphers, {aes_gcm, 192}}, {<<"A256GCMKW">>, ciphers, {aes_gcm, 256}}, {<<"A128KW">>, ciphers, {aes_ecb, 128}}, {<<"A192KW">>, ciphers, {aes_ecb, 192}}, {<<"A256KW">>, ciphers, {aes_ecb, 256}}, {<<"C20PKW">>, ciphers, {chacha20_poly1305, 256}}, <<"ECDH-1PU">>, {<<"ECDH-1PU+A128GCMKW">>, ciphers, {aes_gcm, 128}}, {<<"ECDH-1PU+A192GCMKW">>, ciphers, {aes_gcm, 192}}, {<<"ECDH-1PU+A256GCMKW">>, ciphers, {aes_gcm, 256}}, <<"ECDH-1PU+A128KW">>, <<"ECDH-1PU+A192KW">>, <<"ECDH-1PU+A256KW">>, {<<"ECDH-1PU+C20PKW">>, ciphers, {chacha20_poly1305, 256}}, {<<"ECDH-1PU+XC20PKW">>, ciphers, {xchacha20_poly1305, 256}}, <<"ECDH-ES">>, {<<"ECDH-ES+A128GCMKW">>, ciphers, {aes_gcm, 128}}, {<<"ECDH-ES+A192GCMKW">>, ciphers, {aes_gcm, 192}}, {<<"ECDH-ES+A256GCMKW">>, ciphers, {aes_gcm, 256}}, <<"ECDH-ES+A128KW">>, <<"ECDH-ES+A192KW">>, <<"ECDH-ES+A256KW">>, {<<"ECDH-ES+C20PKW">>, ciphers, {chacha20_poly1305, 256}}, {<<"ECDH-ES+XC20PKW">>, ciphers, {xchacha20_poly1305, 256}}, <<"ECDH-SS">>, {<<"ECDH-SS+A128GCMKW">>, ciphers, {aes_gcm, 128}}, {<<"ECDH-SS+A192GCMKW">>, ciphers, {aes_gcm, 192}}, {<<"ECDH-SS+A256GCMKW">>, ciphers, {aes_gcm, 256}}, <<"ECDH-SS+A128KW">>, <<"ECDH-SS+A192KW">>, <<"ECDH-SS+A256KW">>, {<<"ECDH-SS+C20PKW">>, ciphers, {chacha20_poly1305, 256}}, {<<"ECDH-SS+XC20PKW">>, ciphers, {xchacha20_poly1305, 256}}, {<<"PBES2-HS256+A128GCMKW">>, ciphers, {aes_gcm, 128}}, {<<"PBES2-HS384+A192GCMKW">>, ciphers, {aes_gcm, 192}}, {<<"PBES2-HS512+A256GCMKW">>, ciphers, {aes_gcm, 256}}, {<<"PBES2-HS256+A128KW">>, ciphers, {aes_ecb, 128}}, {<<"PBES2-HS384+A192KW">>, ciphers, {aes_ecb, 192}}, {<<"PBES2-HS512+A256KW">>, ciphers, {aes_ecb, 256}}, {<<"PBES2-HS512+C20PKW">>, ciphers, {chacha20_poly1305, 256}}, {<<"PBES2-HS512+XC20PKW">>, ciphers, {xchacha20_poly1305, 256}}, {<<"RSA1_5">>, rsa_crypt, rsa1_5}, {<<"RSA-OAEP">>, rsa_crypt, rsa_oaep}, {<<"RSA-OAEP-256">>, rsa_crypt, rsa_oaep_256}, {<<"XC20PKW">>, ciphers, {xchacha20_poly1305, 256}}, <<"dir">> ], Supports, []), JWEENC = support_check([ {<<"A128CBC-HS256">>, ciphers, {aes_cbc, 128}}, {<<"A192CBC-HS384">>, ciphers, {aes_cbc, 192}}, {<<"A256CBC-HS512">>, ciphers, {aes_cbc, 256}}, {<<"A128GCM">>, ciphers, {aes_gcm, 128}}, {<<"A192GCM">>, ciphers, {aes_gcm, 192}}, {<<"A256GCM">>, ciphers, {aes_gcm, 256}}, {<<"C20P">>, ciphers, {chacha20_poly1305, 256}}, {<<"XC20P">>, ciphers, {xchacha20_poly1305, 256}} ], Supports, []), JWEZIP = support_check([ <<"DEF">> ], Supports, []), JWKKTY = support_check([ <<"EC">>, <<"oct">>, <<"OKP">>, <<"RSA">> ], Supports, []), JWKKTYOKPcrv = support_check([ {<<"Ed25519">>, public_keys, ed25519}, {<<"Ed25519ph">>, public_keys, ed25519ph}, {<<"Ed448">>, public_keys, ed448}, {<<"Ed448ph">>, public_keys, ed448ph}, {<<"X25519">>, public_keys, x25519}, {<<"X448">>, public_keys, x448} ], Supports, []), JWSALG = support_check([ {<<"Ed25519">>, public_keys, ed25519}, {<<"Ed25519ph">>, public_keys, ed25519ph}, {<<"Ed448">>, public_keys, ed448}, {<<"Ed448ph">>, public_keys, ed448ph}, {<<"EdDSA">>, public_keys, ed25519}, {<<"ES256">>, public_keys, ecdsa}, {<<"ES256K">>, public_keys, ecdsa}, {<<"ES384">>, public_keys, ecdsa}, {<<"ES512">>, public_keys, ecdsa}, <<"HS256">>, <<"HS384">>, <<"HS512">>, {<<"PS256">>, rsa_sign, rsa_pkcs1_pss_padding}, {<<"PS384">>, rsa_sign, rsa_pkcs1_pss_padding}, {<<"PS512">>, rsa_sign, rsa_pkcs1_pss_padding}, {<<"Poly1305">>, hashs, poly1305}, {<<"RS1">>, rsa_sign, rsa_pkcs1_padding}, {<<"RS256">>, rsa_sign, rsa_pkcs1_padding}, {<<"RS384">>, rsa_sign, rsa_pkcs1_padding}, {<<"RS512">>, rsa_sign, rsa_pkcs1_padding}, {<<"none">>, fun unsecured_signing/0} ], Supports, []), [ {jwe, {alg, JWEALG}, {enc, JWEENC}, {zip, JWEZIP}}, {jwk, {kty, JWKKTY}, {kty_OKP_crv, JWKKTYOKPcrv}}, {jws, {alg, JWSALG}} ]. unsecured_signing() -> jose:unsecured_signing(). unsecured_signing(Boolean) when is_boolean(Boolean) -> application:set_env(jose, unsecured_signing, Boolean), ?MAYBE_START_JOSE(jose_server:config_change()). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private constant_time_compare(<< AH, AT/binary >>, << BH, BT/binary >>, R) -> constant_time_compare(AT, BT, R bor (BH bxor AH)); constant_time_compare(<<>>, <<>>, R) -> R =:= 0. %% @private external_checks(Checks) -> external_checks(Checks, []). %% @private external_checks([{Key, Check} | Checks], Acc) -> try Check(), external_checks(Checks, [Key | Acc]) catch _:_ -> external_checks(Checks, Acc) end; external_checks([], Acc) -> lists:reverse(Acc). %% @private rsa_crypt(Algorithm) -> ?MAYBE_START_JOSE(ets:lookup_element(?TAB, {rsa_crypt, Algorithm}, 2)). %% @private rsa_sign(Padding) -> ?MAYBE_START_JOSE(ets:lookup_element(?TAB, {rsa_sign, Padding}, 2)). %% @private support_check([], _Supports, Acc) -> lists:usort(Acc); support_check([{ALG, Key, Val} | Rest], Supports, Acc) -> case lists:member(Val, proplists:get_value(Key, Supports)) of false -> support_check(Rest, Supports, Acc); true -> support_check(Rest, Supports, [ALG | Acc]) end; support_check([{ALG, Check} | Rest], Supports, Acc) when is_function(Check, 0) -> case Check() of false -> support_check(Rest, Supports, Acc); true -> support_check(Rest, Supports, [ALG | Acc]) end; support_check([ALG | Rest], Supports, Acc) when is_binary(ALG) -> support_check(Rest, Supports, [ALG | Acc]). potatosalad-erlang-jose-30ac019/src/jwa/xchacha20_poly1305/0000775000232200023220000000000015267717064023627 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/src/jwa/xchacha20_poly1305/jose_xchacha20_poly1305.erl0000664000232200023220000000612215267717064030471 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 14 Sep 2019 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_xchacha20_poly1305). -behaviour(jose_block_encryptor). %% Types -type additional_authenticated_data() :: binary(). -type cipher_text() :: binary(). -type message() :: binary(). -type plain_text() :: binary(). -type poly1305_mac() :: <<_:128>>. -type xchacha20_key() :: <<_:256>>. -type xchacha20_nonce() :: <<_:192>>. -export_type([ additional_authenticated_data/0, cipher_text/0, message/0, plain_text/0, poly1305_mac/0, xchacha20_key/0, xchacha20_nonce/0 ]). -callback decrypt(CipherText, CipherTag, AAD, IV, Key) -> PlainText | error when CipherText :: cipher_text(), CipherTag :: poly1305_mac(), AAD :: additional_authenticated_data(), IV :: xchacha20_nonce(), Key :: xchacha20_key(), PlainText :: binary(). -callback encrypt(PlainText, AAD, IV, Key) -> {CipherText, CipherTag} when PlainText :: plain_text(), AAD :: additional_authenticated_data(), IV :: xchacha20_nonce(), Key :: xchacha20_key(), CipherText :: cipher_text(), CipherTag :: poly1305_mac(). -callback authenticate(Message, Key, Nonce) -> MAC when Message :: message(), Key :: xchacha20_key(), Nonce :: xchacha20_nonce(), MAC :: poly1305_mac(). -callback verify(MAC, Message, Key, Nonce) -> boolean() when MAC :: poly1305_mac(), Message :: message(), Key :: xchacha20_key(), Nonce :: xchacha20_nonce(). %% jose_block_encryptor callbacks -export([block_decrypt/4]). -export([block_encrypt/4]). %% jose_xchacha20_poly1305 callbacks -export([decrypt/5]). -export([encrypt/4]). -export([authenticate/3]). -export([verify/4]). %% Macros -define(JOSE_XCHACHA20_POLY1305, (jose:xchacha20_poly1305_module())). %%==================================================================== %% jose_block_encryptor callbacks %%==================================================================== block_decrypt({xchacha20_poly1305, 256}, Key, IV, {AAD, CipherText, CipherTag}) -> decrypt(CipherText, CipherTag, AAD, IV, Key). block_encrypt({xchacha20_poly1305, 256}, Key, IV, {AAD, PlainText}) -> encrypt(PlainText, AAD, IV, Key). %%==================================================================== %% jose_xchacha20_poly1305 callbacks %%==================================================================== decrypt(CipherText, CipherTag, AAD, IV, CEK) -> ?JOSE_XCHACHA20_POLY1305:decrypt(CipherText, CipherTag, AAD, IV, CEK). encrypt(PlainText, AAD, IV, CEK) -> ?JOSE_XCHACHA20_POLY1305:encrypt(PlainText, AAD, IV, CEK). authenticate(Message, Key, Nonce) -> ?JOSE_XCHACHA20_POLY1305:authenticate(Message, Key, Nonce). verify(MAC, Message, Key, Nonce) -> ?JOSE_XCHACHA20_POLY1305:verify(MAC, Message, Key, Nonce). potatosalad-erlang-jose-30ac019/src/jwa/xchacha20_poly1305/jose_xchacha20_poly1305_crypto.erl0000664000232200023220000001443415267717064032076 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 14 Sep 2019 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_xchacha20_poly1305_crypto). -behaviour(jose_xchacha20_poly1305). %% jose_xchacha20_poly1305 callbacks -export([decrypt/5]). -export([encrypt/4]). -export([authenticate/3]). -export([verify/4]). %% Internal API -export([hchacha20/2]). -export([poly1305_key_gen/2]). -export([xchacha20_subkey_and_nonce/2]). %% Types -type chacha20_key() :: <<_:256>>. -type chacha20_nonce() :: <<_:96>>. -type hchacha20_nonce() :: <<_:128>>. -type hchacha20_block() :: <<_:256>>. -type poly1305_otk() :: <<_:256>>. -type xchacha20_nonce() :: <<_:192>>. % -type poly1305_mac() :: <<_:128>>. %%==================================================================== %% jose_xchacha20_poly1305 callbacks %%==================================================================== decrypt(CipherText, CipherTag, AAD, IV, CEK) -> {Subkey, Nonce} = xchacha20_subkey_and_nonce(CEK, IV), crypto:crypto_one_time_aead(chacha20_poly1305, Subkey, Nonce, CipherText, AAD, CipherTag, false). encrypt(PlainText, AAD, IV, CEK) -> {Subkey, Nonce} = xchacha20_subkey_and_nonce(CEK, IV), crypto:crypto_one_time_aead(chacha20_poly1305, Subkey, Nonce, PlainText, AAD, true). authenticate(Message, Key, Nonce0) -> {Subkey, Nonce} = xchacha20_subkey_and_nonce(Key, Nonce0), OTK = poly1305_key_gen(Subkey, Nonce), crypto:mac(poly1305, OTK, Message). verify(MAC, Message, Key, Nonce) -> Challenge = authenticate(Message, Key, Nonce), jose_jwa:constant_time_compare(MAC, Challenge). %%==================================================================== %% Internal API Functions %%==================================================================== %% @doc Short example of why this works: `HChaCha20 = ChaCha20 - State0' %% %% Longer example of why this works: %% %% ``` %% K = 256-bit key %% C = 32-bit counter %% N = 96-bit nonce %% X = 128-bit nonce %% || = concatenation %% ++ = 32-bit word little endian addition %% -- = 32-bit word little endian subtraction %% %% ChaCha20(K, C, N) = %% State0 = "expand 32-byte k" || K || C || N %% State1 = Rounds(State0, 10) %% State2 = State1 ++ State2 %% return State2 %% %% HChaCha20(K, X) = %% State0 = "expand 32-byte k" || K || X %% State1 = Rounds(State0, 10) %% return FirstBits(State1, 128) || LastBits(State1, 128) %% %% # Let's rewrite HChaCha20 to use ChaCha20 with State0 subtraction: %% %% HChaCha20(K, X) = %% C = FirstBits(X, 32) %% N = LastBits(X, 96) %% State0 = "expand 32-byte k" || K || X %% State2 = ChaCha20(K, C, N) %% State1 = State2 -- State0 %% return FirstBits(State1, 128) || LastBits(State1, 128) %% %% # Let's further reduce to not use K and use a Mask for blinding: %% %% HChaCha20(K, X) = %% Mask = CSPRNG(512) %% C = FirstBits(X, 32) %% N = LastBits(X, 96) %% KeyStream = ChaCha20(K, C, N) ^ Mask %% State2 = (FirstBits(KeyStream, 128) || LastBits(KeyStream, 128)) ^ %% (FirstBits(Mask, 128) || LastBits(Mask, 128)) %% State0 = "expand 32-byte k" || X %% State1 = State2 -- State0 %% return State1 %% ''' %% %% See: [https://tools.ietf.org/html/rfc7539#section-2.3] %% See: [https://tools.ietf.org/html/draft-irtf-cfrg-xchacha-03#section-2.2] -spec hchacha20( Key :: chacha20_key(), Nonce :: hchacha20_nonce() ) -> hchacha20_block(). hchacha20( <>, <> ) -> %% 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-unit:1, X01:32/unsigned-little-integer-unit:1, X02:32/unsigned-little-integer-unit:1, X03:32/unsigned-little-integer-unit:1, X12:32/unsigned-little-integer-unit:1, X13:32/unsigned-little-integer-unit:1, X14:32/unsigned-little-integer-unit:1, X15:32/unsigned-little-integer-unit:1 >> = 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-unit:1, Y01:32/unsigned-little-integer-unit:1, Y02:32/unsigned-little-integer-unit:1, Y03:32/unsigned-little-integer-unit:1, Y12:32/unsigned-little-integer-unit:1, Y13:32/unsigned-little-integer-unit:1, Y14:32/unsigned-little-integer-unit:1, Y15:32/unsigned-little-integer-unit:1 >> = <<"expand 32-byte k", Nonce:128/bits>>, << (X00 - Y00):32/unsigned-little-integer-unit:1, (X01 - Y01):32/unsigned-little-integer-unit:1, (X02 - Y02):32/unsigned-little-integer-unit:1, (X03 - Y03):32/unsigned-little-integer-unit:1, (X12 - Y12):32/unsigned-little-integer-unit:1, (X13 - Y13):32/unsigned-little-integer-unit:1, (X14 - Y14):32/unsigned-little-integer-unit:1, (X15 - Y15):32/unsigned-little-integer-unit:1 >>. -spec poly1305_key_gen( Key :: chacha20_key(), Nonce :: chacha20_nonce() ) -> poly1305_otk(). poly1305_key_gen( <>, <> ) -> crypto:crypto_one_time(chacha20, Key, <<0:32, Nonce:96/bits>>, <<0:256>>, true). -spec xchacha20_subkey_and_nonce( Key :: chacha20_key(), Nonce :: xchacha20_nonce() ) -> {chacha20_key(), chacha20_nonce()}. xchacha20_subkey_and_nonce( <>, <> ) -> Subkey = hchacha20(Key, Nonce0), Nonce = <<0:32, Nonce1:64/bitstring>>, {Subkey, Nonce}. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jwa/xchacha20_poly1305/jose_xchacha20_poly1305_libsodium.erl0000664000232200023220000000341115267717064032536 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 31 Aug 2022 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_xchacha20_poly1305_libsodium). -behaviour(jose_xchacha20_poly1305). %% jose_xchacha20_poly1305 callbacks -export([decrypt/5]). -export([encrypt/4]). -export([authenticate/3]). -export([verify/4]). %%==================================================================== %% jose_xchacha20_poly1305 callbacks %%==================================================================== decrypt(CipherText, CipherTag, AAD, IV, CEK) -> case libsodium_crypto_aead_xchacha20poly1305:ietf_decrypt_detached(CipherText, CipherTag, AAD, IV, CEK) of -1 -> error; PlainText when is_binary(PlainText) -> PlainText end. encrypt(PlainText, AAD, IV, CEK) -> libsodium_crypto_aead_xchacha20poly1305:ietf_encrypt_detached(PlainText, AAD, IV, CEK). authenticate(Message, Key, Nonce) -> OTK = one_time_key(Key, Nonce), libsodium_crypto_onetimeauth_poly1305:crypto_onetimeauth_poly1305(Message, OTK). verify(MAC, Message, Key, Nonce) -> OTK = one_time_key(Key, Nonce), case libsodium_crypto_onetimeauth_poly1305:verify(MAC, Message, OTK) of 0 -> true; _ -> false end. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private one_time_key(Key, Nonce) -> libsodium_crypto_stream_xchacha20:xor_ic(<< 0:256 >>, Nonce, 0, Key). potatosalad-erlang-jose-30ac019/src/jwa/xchacha20_poly1305/jose_xchacha20_poly1305_unsupported.erl0000664000232200023220000000216315267717064033142 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 14 Sep 2019 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_xchacha20_poly1305_unsupported). -behaviour(jose_xchacha20_poly1305). %% jose_xchacha20_poly1305 callbacks -export([decrypt/5]). -export([encrypt/4]). -export([authenticate/3]). -export([verify/4]). %% Macros -define(unsupported, erlang:error(operation_not_supported)). %%==================================================================== %% jose_xchacha20_poly1305 callbacks %%==================================================================== decrypt(_CipherText, _CipherTag, _AAD, _IV, _Key) -> ?unsupported. encrypt(_PlainText, _AAD, _IV, _Key) -> ?unsupported. authenticate(_Message, _Key, _Nonce) -> ?unsupported. verify(_MAC, _Message, _Key, _Nonce) -> ?unsupported. potatosalad-erlang-jose-30ac019/src/jwa/sha3/0000775000232200023220000000000015267717064021350 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/src/jwa/sha3/jose_sha3_keccakf1600_driver.erl0000664000232200023220000000241715267717064027267 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 20 Jan 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_sha3_keccakf1600_driver). -behaviour(jose_sha3). %% jose_sha3 callbacks -export([sha3_224/1]). -export([sha3_256/1]). -export([sha3_384/1]). -export([sha3_512/1]). -export([shake128/2]). -export([shake256/2]). %%==================================================================== %% jose_sha3 callbacks %%==================================================================== sha3_224(InputBytes) -> keccakf1600_fips202:sha3_224(InputBytes). sha3_256(InputBytes) -> keccakf1600_fips202:sha3_256(InputBytes). sha3_384(InputBytes) -> keccakf1600_fips202:sha3_384(InputBytes). sha3_512(InputBytes) -> keccakf1600_fips202:sha3_512(InputBytes). shake128(InputBytes, OutputByteLen) -> keccakf1600_fips202:shake128(InputBytes, OutputByteLen). shake256(InputBytes, OutputByteLen) -> keccakf1600_fips202:shake256(InputBytes, OutputByteLen). potatosalad-erlang-jose-30ac019/src/jwa/sha3/jose_sha3_libdecaf.erl0000664000232200023220000000240515267717064025544 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 01 Mar 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_sha3_libdecaf). -behaviour(jose_sha3). %% jose_sha3 callbacks -export([sha3_224/1]). -export([sha3_256/1]). -export([sha3_384/1]). -export([sha3_512/1]). -export([shake128/2]). -export([shake256/2]). %%==================================================================== %% jose_sha3 callbacks %%==================================================================== sha3_224(InputBytes) -> libdecaf_sha3:hash(sha3_224, InputBytes). sha3_256(InputBytes) -> libdecaf_sha3:hash(sha3_256, InputBytes). sha3_384(InputBytes) -> libdecaf_sha3:hash(sha3_384, InputBytes). sha3_512(InputBytes) -> libdecaf_sha3:hash(sha3_512, InputBytes). shake128(InputBytes, OutputByteLen) -> libdecaf_sha3:hash(shake128, InputBytes, OutputByteLen). shake256(InputBytes, OutputByteLen) -> libdecaf_sha3:hash(shake256, InputBytes, OutputByteLen). potatosalad-erlang-jose-30ac019/src/jwa/sha3/jose_sha3_unsupported.erl0000664000232200023220000000222215267717064026400 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 11 Jan 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_sha3_unsupported). -behaviour(jose_sha3). %% jose_sha3 callbacks -export([sha3_224/1]). -export([sha3_256/1]). -export([sha3_384/1]). -export([sha3_512/1]). -export([shake128/2]). -export([shake256/2]). %% Macros -define(unsupported, erlang:error(operation_not_supported)). %%==================================================================== %% jose_sha3 callbacks %%==================================================================== sha3_224(_InputBytes) -> ?unsupported. sha3_256(_InputBytes) -> ?unsupported. sha3_384(_InputBytes) -> ?unsupported. sha3_512(_InputBytes) -> ?unsupported. shake128(_InputBytes, _OutputByteLen) -> ?unsupported. shake256(_InputBytes, _OutputByteLen) -> ?unsupported. potatosalad-erlang-jose-30ac019/src/jwa/sha3/jose_sha3.erl0000664000232200023220000000325215267717064023734 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 11 Jan 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_sha3). -callback sha3_224(InputBytes::binary()) -> OutputBytes::binary(). -callback sha3_256(InputBytes::binary()) -> OutputBytes::binary(). -callback sha3_384(InputBytes::binary()) -> OutputBytes::binary(). -callback sha3_512(InputBytes::binary()) -> OutputBytes::binary(). -callback shake128(InputBytes::binary(), OutputByteLen::integer()) -> OutputBytes::binary(). -callback shake256(InputBytes::binary(), OutputByteLen::integer()) -> OutputBytes::binary(). %% jose_sha3 callbacks -export([sha3_224/1]). -export([sha3_256/1]). -export([sha3_384/1]). -export([sha3_512/1]). -export([shake128/2]). -export([shake256/2]). %% Macros -define(JOSE_SHA3, (jose:sha3_module())). %%==================================================================== %% jose_sha3 callbacks %%==================================================================== sha3_224(InputBytes) -> ?JOSE_SHA3:sha3_224(InputBytes). sha3_256(InputBytes) -> ?JOSE_SHA3:sha3_256(InputBytes). sha3_384(InputBytes) -> ?JOSE_SHA3:sha3_384(InputBytes). sha3_512(InputBytes) -> ?JOSE_SHA3:sha3_512(InputBytes). shake128(InputBytes, OutputByteLen) -> ?JOSE_SHA3:shake128(InputBytes, OutputByteLen). shake256(InputBytes, OutputByteLen) -> ?JOSE_SHA3:shake256(InputBytes, OutputByteLen). potatosalad-erlang-jose-30ac019/src/jwa/sha3/jose_sha3_keccakf1600_nif.erl0000664000232200023220000000240015267717064026540 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 05 Feb 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_sha3_keccakf1600_nif). -behaviour(jose_sha3). %% jose_sha3 callbacks -export([sha3_224/1]). -export([sha3_256/1]). -export([sha3_384/1]). -export([sha3_512/1]). -export([shake128/2]). -export([shake256/2]). %%==================================================================== %% jose_sha3 callbacks %%==================================================================== sha3_224(InputBytes) -> keccakf1600:hash(sha3_224, InputBytes). sha3_256(InputBytes) -> keccakf1600:hash(sha3_256, InputBytes). sha3_384(InputBytes) -> keccakf1600:hash(sha3_384, InputBytes). sha3_512(InputBytes) -> keccakf1600:hash(sha3_512, InputBytes). shake128(InputBytes, OutputByteLen) -> keccakf1600:hash(shake128, InputBytes, OutputByteLen). shake256(InputBytes, OutputByteLen) -> keccakf1600:hash(shake256, InputBytes, OutputByteLen). potatosalad-erlang-jose-30ac019/src/jwa/jose_jwa_ed25519.erl0000664000232200023220000002737115267717064024107 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc Edwards-Curve Digital Signature Algorithm (EdDSA) - Ed25519, Ed25519ctx, Ed25519ph %%% See https://datatracker.ietf.org/doc/html/rfc8032 %%% @end %%% Created : 06 Jan 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwa_ed25519). %% API -export([xrecover/1]). -export([encode_point/1]). -export([decode_point/1]). -export([edwards_add/2]). -export([edwards_double/1]). -export([edwards_equal/2]). -export([scalarmult/2]). -export([scalarmult_base/1]). -export([normalize_point/1]). -export([secret/0]). -export([secret_to_curve25519/1]). -export([secret_to_pk/1]). -export([keypair/0]). -export([keypair/1]). -export([sk_to_secret/1]). -export([sk_to_pk/1]). -export([sk_to_curve25519/1]). -export([pk_to_curve25519/1]). -export([dom2/2]). -export([sign_internal/4]). -export([ed25519_sign/2]). -export([ed25519ctx_sign/3]). -export([ed25519ph_sign/2]). -export([ed25519ph_sign/3]). -export([verify_internal/5]). -export([ed25519_verify/3]). -export([ed25519ctx_verify/4]). -export([ed25519ph_verify/3]). -export([ed25519ph_verify/4]). %% Macros -define(math, jose_jwa_math). -define(inv(Z), ?math:expmod(Z, ?p - 2, ?p)). % $= z^{-1} \mod p$, for z != 0 % 3. EdDSA Algorithm - https://datatracker.ietf.org/doc/html/rfc8032#section-3 % 5.1. Ed25519ph and Ed25519 - https://datatracker.ietf.org/doc/html/rfc8032-01#section-5.1 -define(d, -4513249062541557337682894930092624173785641285191125241628941591882900924598840740). % (-121665) * ?inv(121666) -define(I, 19681161376707505956807079304988542015446066515923890162744021073123829784752). % ?math:expmod(2, (?p - 1) div 4, ?p) %% 1. An odd prime power p. EdDSA uses an elliptic curve over the %% finite field GF(p). -define(p, 57896044618658097711785492504343953926634992332820282019728792003956564819949). % ?math:intpow(2, 255) - 19 %% 2. An integer b with 2^(b-1) > p. EdDSA public keys have exactly b %% bits, and EdDSA signatures have exactly 2*b bits. b is %% recommended to be multiple of 8, so public key and signature %% lengths are integral number of octets. -define(b, 256). % ?math:intpow(2, ?b - 1) > ?p %% 3. A (b-1)-bit encoding of elements of the finite field GF(p). -define(GFp, << 16#ED,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF, 16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF, 16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF, 16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,16#FE >>). % << ?p:(?b - 1)/unsigned-little-integer-unit:1, 0:1 >> %% 4. A cryptographic hash function H producing 2*b-bit output. %% Conservative hash functions are recommended and do not have much %% impact on the total cost of EdDSA. -define(H(M), crypto:hash(sha512, M)). -define(HBits, 512). % ?b * 2 %% 5. An integer c that is 2 or 3. Secret EdDSA scalars are multiples %% of 2^c. The integer c is the base-2 logarithm of the so called %% cofactor. -define(c, 3). %% 6. An integer n with c <= n < b. Secret EdDSA scalars have exactly %% n + 1 bits, with the top bit (the 2^n position) always set and %% the bottom c bits always cleared. -define(n, 254). % ?c =< ?n andalso ?n < ?b %% 7. A nonzero square element a of GF(p). The usual recommendation %% for best performance is a = -1 if p mod 4 = 1, and a = 1 if p %% mod 4 = 3. -define(a, -1). %% 8. An element B != (0,1) of the set E = { (x,y) is a member of %% GF(p) x GF(p) such that a * x^2 + y^2 = 1 + d * x^2 * y^2 }. -define(By, 46316835694926478169428394003475163141307993866256225615783033603165251855960). % 4 * ?inv(5) -define(Bx, 15112221349535400772501151409588531511454012693041857206046113283949847762202). % xrecover(?By) -define(B, {?Bx, ?By, 1, 46827403850823179245072216630277197565144205554125654976674165829533817101731}). % {?Bx, ?By, 1, (?Bx * ?Bx) rem ?p} % (?a * ?math:intpow(?Bx, 2) + ?math:intpow(?By, 2)) rem ?p == (1 + ?d * ?math:intpow(?Bx, 2) * ?math:intpow(?By, 2)) rem ?p %% 9. An odd prime l such that [l]B = 0 and 2^c * l = #E. The number %% #E (the number of points on the curve) is part of the standard %% data provided for an elliptic curve E. -define(l, 7237005577332262213973186563042994240857116359379907606001950938285454250989). % ?math:intpow(2, 252) + 27742317777372353535851937790883648493 -define(E, ?math:intpow(2, ?c) * ?l). %% 10. A "prehash" function PH. PureEdDSA means EdDSA where PH is the %% identity function, i.e., PH(M) = M. HashEdDSA means EdDSA where %% PH generates a short output, no matter how long the message is; %% for example, PH(M) = SHA-512(M). -define(PH(M), crypto:hash(sha512, M)). -define(secretbytes, 32). % (?b + 7) div 8 -define(publickeybytes, 32). % (?b + 7) div 8 -define(secretkeybytes, 64). % ?secretbytes + ?publickeybytes %%==================================================================== %% API %%==================================================================== % 5.1.1. Modular arithmetic - https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.1 xrecover(Y) -> YY = Y * Y, A = (YY - 1) * ?inv((?d * YY) + 1), X = ?math:expmod(A, (?p + 3) div 8, ?p), case ((X * X) - A) rem ?p =:= 0 of true -> % x^2 = a (mod p). Then x is a square root. case X rem 2 of 0 -> X; _ -> ?p - X end; false -> case ((X * X) + A) rem ?p =:= 0 of true -> % x^2 = -a (mod p). Then 2^((p-1)/4) x is a square root. Xi = (X * ?I) rem ?p, case Xi rem 2 of 0 -> Xi; _ -> ?p - Xi end; false -> % a is not a square modulo p. erlang:error(badarg) end end. % 5.1.2. Encoding - https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.2 encode_point({X, Y, Z, _T}) -> Zi = ?inv(Z), Xp = ?math:mod((X * Zi), ?p), Yp = ?math:mod((Y * Zi), ?p), << YpHead:(?b - 8)/bitstring, YpTail:8/integer >> = << Yp:?b/unsigned-little-integer-unit:1 >>, << YpHead/bitstring, (YpTail bxor (16#80 * (Xp band 1))):8/integer >>. % 5.1.3. Decoding - https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.3 decode_point(<< YpHead:(?b - 8)/bitstring, Xb:1, YpTail:7/bitstring >>) -> << Yp:?b/unsigned-little-integer-unit:1 >> = << YpHead/bitstring, 0:1, YpTail/bitstring >>, case Yp >= ?p of true -> erlang:error(badarg); false -> Xp = xrecover(Yp), X = case Xp band 1 of Xb -> Xp; _ -> ?p - Xp end, {X, Yp, 1, (X * Yp) rem ?p} end. % 5.1.4. Point addition - https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.4 edwards_add({X1, Y1, Z1, T1}, {X2, Y2, Z2, T2}) -> A = ((Y1 - X1) * (Y2 - X2)) rem ?p, B = ((Y1 + X1) * (Y2 + X2)) rem ?p, C = (T1 * 2 * ?d * T2) rem ?p, D = (Z1 * 2 * Z2) rem ?p, E = B - A, F = D - C, G = D + C, H = B + A, X3 = E * F, Y3 = G * H, T3 = E * H, Z3 = F * G, {X3 rem ?p, Y3 rem ?p, Z3 rem ?p, T3 rem ?p}. edwards_double(P) -> edwards_add(P, P). edwards_equal({X1, Y1, Z1, _T1}, {X2, Y2, Z2, _T2}) -> Z1i = ?inv(Z1), X1p = ?math:mod((X1 * Z1i), ?p), Y1p = ?math:mod((Y1 * Z1i), ?p), Z2i = ?inv(Z2), X2p = ?math:mod((X2 * Z2i), ?p), Y2p = ?math:mod((Y2 * Z2i), ?p), {X1p, Y1p} =:= {X2p, Y2p}. scalarmult(_P, 0) -> {0, 1, 1, 0}; scalarmult(P, E) -> Q = scalarmult(P, E div 2), QQ = edwards_double(Q), case E band 1 of 0 -> QQ; 1 -> edwards_add(QQ, P) end. scalarmult_base(E) -> scalarmult(?B, E). normalize_point({X, Y, Z, _T}) -> Zi = ?inv(Z), Xp = ?math:mod((X * Zi), ?p), Yp = ?math:mod((Y * Zi), ?p), Zp = ?math:mod((Z * Zi), ?p), {Xp, Yp, Zp, ?math:mod((Xp * Yp), ?p)}. % 5.1.5. Key Generation - https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.5 secret() -> crypto:strong_rand_bytes(?secretbytes). secret_to_curve25519(Secret = << _:?secretbytes/binary >>) -> << HHead0:8/integer, HBody:30/binary, HFoot0:8/integer, _/binary >> = ?H(Secret), HHead = HHead0 band 248, HFoot = (HFoot0 band 63) bor 64, << HHead:8/integer, HBody/binary, HFoot:8/integer >>. secret_to_pk(Secret = << _:?secretbytes/binary >>) -> << As:?b/unsigned-little-integer-unit:1 >> = secret_to_curve25519(Secret), A = scalarmult(?B, As), encode_point(A). keypair() -> Secret = secret(), keypair(Secret). keypair(Secret = << _:?secretbytes/binary >>) -> PK = secret_to_pk(Secret), SK = << Secret/binary, PK/binary >>, {PK, SK}. sk_to_secret(<< Secret:?secretbytes/binary, _:?publickeybytes/binary >>) -> Secret. sk_to_pk(<< _:?secretbytes/binary, PK:?publickeybytes/binary >>) -> PK. sk_to_curve25519(<< Secret:?secretbytes/binary, _:?publickeybytes/binary >>) -> secret_to_curve25519(Secret). pk_to_curve25519(<< PK:?publickeybytes/binary >>) -> _A = {_X, Y, _Z, _T} = decode_point(PK), U = ?math:mod((1 + Y) * ?inv(1 - Y), ?p), << U:?b/unsigned-little-integer-unit:1 >>. % 5.1.6. Sign - https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.6 dom2(0, <<>>) -> <<>>; dom2(PHFlag, C) when (PHFlag =:= 0 orelse PHFlag =:= 1) andalso is_binary(C) -> <<"SigEd25519 no Ed25519 collisions", PHFlag:8, (byte_size(C)):8/integer, C/binary>>. sign_internal(M, << Secret:?secretbytes/binary, PK:?publickeybytes/binary >>, PHFlag, C) when is_binary(M) andalso (PHFlag =:= 0 orelse PHFlag =:= 1) andalso is_binary(C) -> % Recalculate PK to prevent misuse as described in https://github.com/MystenLabs/ed25519-unsafe-libs PK = secret_to_pk(Secret), Dom2 = dom2(PHFlag, C), << HHead0:8/integer, HBody:30/binary, HFoot0:8/integer, HTail:32/binary >> = ?H(Secret), HHead = HHead0 band 248, HFoot = (HFoot0 band 63) bor 64, << As:?b/unsigned-little-integer-unit:1 >> = << HHead:8/integer, HBody/binary, HFoot:8/integer >>, << Rs:?HBits/unsigned-little-integer-unit:1 >> = ?H(<< Dom2/binary, HTail/binary, M/binary >>), R = encode_point(scalarmult(?B, Rs)), << K:?HBits/unsigned-little-integer-unit:1 >> = ?H(<< Dom2/binary, R/binary, PK/binary, M/binary >>), S = ?math:mod(Rs + (K * As), ?l), << R/binary, S:?b/unsigned-little-integer-unit:1 >>. ed25519_sign(M, SK = << _:?secretkeybytes/binary >>) when is_binary(M) -> sign_internal(M, SK, 0, <<>>). ed25519ctx_sign(M, SK = << _:?secretkeybytes/binary >>, C) when is_binary(M) andalso is_binary(C) -> sign_internal(M, SK, 0, C). ed25519ph_sign(M, SK = << _:?secretkeybytes/binary >>) when is_binary(M) -> sign_internal(?PH(M), SK, 1, <<>>). ed25519ph_sign(M, SK = << _:?secretkeybytes/binary >>, C) when is_binary(M) andalso is_binary(C) -> sign_internal(?PH(M), SK, 1, C). % 5.1.7. Verify - https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.7 verify_internal(<< R:?b/bitstring, S:?b/unsigned-little-integer-unit:1 >>, M, PK = << _:?publickeybytes/binary >>, PHFlag, C) when is_binary(M) andalso (PHFlag =:= 0 orelse PHFlag =:= 1) andalso is_binary(C) -> Dom2 = dom2(PHFlag, C), A = decode_point(PK), << K:?HBits/unsigned-little-integer-unit:1 >> = ?H(<< Dom2/binary, R/binary, PK/binary, M/binary >>), edwards_equal(scalarmult(?B, S), edwards_add(decode_point(R), scalarmult(A, K))); verify_internal(Sig, M, _PK = << _:?publickeybytes/binary >>, PHFlag, C) when is_binary(Sig) andalso is_binary(M) andalso (PHFlag =:= 0 orelse PHFlag =:= 1) andalso is_binary(C) -> false. ed25519_verify(Sig, M, PK = << _:?publickeybytes/binary >>) when is_binary(Sig) andalso is_binary(M) -> verify_internal(Sig, M, PK, 0, <<>>). ed25519ctx_verify(Sig, M, PK = << _:?publickeybytes/binary >>, C) when is_binary(Sig) andalso is_binary(M) andalso is_binary(C) -> verify_internal(Sig, M, PK, 0, C). ed25519ph_verify(Sig, M, PK = << _:?publickeybytes/binary >>) when is_binary(Sig) andalso is_binary(M) -> verify_internal(Sig, ?PH(M), PK, 1, <<>>). ed25519ph_verify(Sig, M, PK = << _:?publickeybytes/binary >>, C) when is_binary(Sig) andalso is_binary(M) andalso is_binary(C) -> verify_internal(Sig, ?PH(M), PK, 1, C). potatosalad-erlang-jose-30ac019/src/jwa/jose_jwa_x25519.erl0000664000232200023220000001236715267717064023765 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc Elliptic Curves for Security - X25519 %%% See https://tools.ietf.org/html/rfc7748 %%% %%% SECURITY WARNING: this pure Erlang implementation is NOT constant-time. %%% %%% `curve25519/2' is operation-count balanced (every step performs exactly %%% one `montgomery_add/3' and one `montgomery_double/1', and clamping fixes %%% the recursion depth), so the coarse "skipped operation" leak is absent. %%% It is still not constant-time: the ladder branches on individual bits of %%% the secret scalar to route its operands, and the underlying Erlang bignum %%% `*' / `rem' operations and the `expmod/3'-based field inversion all run in %%% value-dependent time. Execution time is therefore correlated with the %%% private scalar. Pure Erlang integer arithmetic cannot be made %%% constant-time. %%% %%% This module is a last-resort fallback for platforms with no native %%% implementation. `jose' prefers a native module %%% (`libdecaf', `libsodium', or OTP `crypto') whenever one works, so this %%% code is not reached for X25519 on a stock OTP build with `crypto' x25519 %%% support. Do not select it explicitly for long-lived private keys in a %%% timing-observable context; use `jose_curve25519_crypto' (which calls %%% `crypto:generate_key/3' and `crypto:compute_key/4' with `x25519') or a %%% NIF-backed module instead. %%% @end %%% Created : 06 Jan 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwa_x25519). %% API -export([coordinate_to_edwards25519/1]). -export([curve25519/2]). -export([clamp_scalar/1]). -export([decode_scalar/1]). -export([montgomery_add/3]). -export([montgomery_double/1]). -export([scalarmult/2]). -export([scalarmult_base/1]). -export([x25519/2]). -export([x25519_base/1]). -export([keypair/0]). -export([keypair/1]). -export([sk_to_pk/1]). %% Macros -define(math, jose_jwa_math). -define(inv(Z), ?math:expmod(Z, ?p - 2, ?p)). % $= z^{-1} \mod p$, for z != 0 % 4.1. Curve25519 - https://tools.ietf.org/html/rfc7748#section-4.1 -define(p, 57896044618658097711785492504343953926634992332820282019728792003956564819949). % ?math:intpow(2, 255) - 19 -define(A, 486662). -define(order, 7237005577332262213973186563042994240857116359379907606001950938285454250989). % ?math:intpow(2, 252) + 16#14def9dea2f79cd65812631a5cf5d3ed -define(cofactor, 8). -define(u, 9). -define(v, 14781619447589544791020593568409986887264606134616475288964881837755586237401). -define(b, 256). -define(a24, 121665). % (?A - 2) div 4 -define(scalarbytes, 32). % (?b + 7) div 8 -define(coordinatebytes, 32). % (?b + 7) div 8 -define(publickeybytes, 32). % ?coordinatebytes -define(secretkeybytes, 32). % ?scalarbytes %%==================================================================== %% API %%==================================================================== coordinate_to_edwards25519(<< U:?b/unsigned-little-integer-unit:1 >>) -> Y = ?math:mod((U - 1) * ?inv(U + 1), ?p), Xp = jose_jwa_ed25519:xrecover(Y), X = case Xp band 1 of 0 -> Xp; _ -> ?p - Xp end, jose_jwa_ed25519:encode_point({X, Y, 1, X * Y}). curve25519(N, Base) -> One = {Base, 1}, Two = montgomery_double(One), % f(m) evaluates to a tuple containing the mth multiple and the % (m+1)th multiple of base. F = fun F(1) -> {One, Two}; F(M) when (M band 1) =:= 1 -> {Pm, Pm1} = F(M div 2), {montgomery_add(Pm, Pm1, One), montgomery_double(Pm1)}; F(M) -> {Pm, Pm1} = F(M div 2), {montgomery_double(Pm), montgomery_add(Pm, Pm1, One)} end, {{X, Z}, _} = F(N), (X * ?inv(Z)) rem ?p. % 5. The X25519 and X448 functions - https://tools.ietf.org/html/rfc7748#section-5 clamp_scalar(K0) -> K1 = K0 band (bnot 7), K2 = K1 band (bnot (128 bsl (?b - 8))), K3 = K2 bor (64 bsl (?b - 8)), K3. decode_scalar(<< K0:8/integer, KBody:(?b - 16)/bitstring, K31:8/integer >>) -> << K:?b/unsigned-little-integer-unit:1 >> = << (K0 band 248):8/integer, KBody/bitstring, ((K31 band 127) bor 64):8/integer >>, K. montgomery_add({Xn, Zn}, {Xm, Zm}, {Xd, Zd}) -> Z0 = (Xm * Xn) - (Zm * Zn), Z1 = (Xm * Zn) - (Zm * Xn), X = 4 * Z0 * Z0 * Zd, Z = 4 * Z1 * Z1 * Xd, {X rem ?p, Z rem ?p}. montgomery_double({Xn, Zn}) -> Xn2 = Xn * Xn, Zn2 = Zn * Zn, X = (Xn2 - Zn2), X2 = X * X, Z = 4 * Xn * Zn * (Xn2 + (?A * Xn * Zn) + Zn2), {X2 rem ?p, Z rem ?p}. scalarmult(<< Kb:?b/unsigned-little-integer-unit:1 >>, << U:?b/unsigned-little-integer-unit:1 >>) -> K = clamp_scalar(Kb), R = curve25519(K, U), << R:?b/unsigned-little-integer-unit:1 >>. scalarmult_base(<< Kb:?b/unsigned-little-integer-unit:1 >>) -> K = clamp_scalar(Kb), R = curve25519(K, ?u), << R:?b/unsigned-little-integer-unit:1 >>. x25519(SK = << _:?secretkeybytes/binary >>, PK = << _:?publickeybytes/binary >>) -> scalarmult(SK, PK). x25519_base(SK = << _:?secretkeybytes/binary >>) -> scalarmult_base(SK). keypair() -> keypair(crypto:strong_rand_bytes(?secretkeybytes)). keypair(SK = << _:?secretkeybytes/binary >>) -> PK = sk_to_pk(SK), {PK, SK}. sk_to_pk(SK = << _:?secretkeybytes/binary >>) -> x25519_base(SK). potatosalad-erlang-jose-30ac019/src/jwa/curve25519/0000775000232200023220000000000015267717064022244 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/src/jwa/curve25519/jose_curve25519.erl0000664000232200023220000001437115267717064025530 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 02 Jan 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_curve25519). %% Types -type eddsa_public_key() :: <<_:256>>. -type eddsa_secret_key() :: <<_:512>>. -type eddsa_seed() :: <<_:256>>. -type message() :: binary(). -type signature() :: <<_:512>>. -type maybe_invalid_signature() :: signature() | binary(). -type context() :: binary(). -type x25519_public_key() :: <<_:256>>. -type x25519_secret_key() :: <<_:256>>. -type x25519_seed() :: <<_:256>>. -type x25519_shared_secret() :: <<_:256>>. -export_type([ eddsa_public_key/0, eddsa_secret_key/0, eddsa_seed/0, message/0, signature/0, maybe_invalid_signature/0, context/0, x25519_public_key/0, x25519_secret_key/0, x25519_seed/0, x25519_shared_secret/0 ]). -callback eddsa_keypair() -> {PublicKey::eddsa_public_key(), SecretKey::eddsa_secret_key()}. -callback eddsa_keypair(Seed::eddsa_seed()) -> {PublicKey::eddsa_public_key(), SecretKey::eddsa_secret_key()}. -callback eddsa_secret_to_public(SecretKey::eddsa_secret_key()) -> PublicKey::eddsa_public_key(). -callback ed25519_sign(Message::message(), SecretKey::eddsa_secret_key()) -> Signature::signature(). -callback ed25519_verify(Signature::maybe_invalid_signature(), Message::message(), PublicKey::eddsa_public_key()) -> boolean(). -callback ed25519ctx_sign(Message::message(), SecretKey::eddsa_secret_key(), Context::context()) -> Signature::signature(). -callback ed25519ctx_verify(Signature::maybe_invalid_signature(), Message::message(), PublicKey::eddsa_public_key(), Context::context()) -> boolean(). -callback ed25519ph_sign(Message::message(), SecretKey::eddsa_secret_key()) -> Signature::signature(). -callback ed25519ph_sign(Message::message(), SecretKey::eddsa_secret_key(), Context::context()) -> Signature::signature(). -callback ed25519ph_verify(Signature::maybe_invalid_signature(), Message::message(), PublicKey::eddsa_public_key()) -> boolean(). -callback ed25519ph_verify(Signature::maybe_invalid_signature(), Message::message(), PublicKey::eddsa_public_key(), Context::context()) -> boolean(). -callback x25519_keypair() -> {PublicKey::x25519_public_key(), SecretKey::x25519_secret_key()}. -callback x25519_keypair(Seed::x25519_seed()) -> {PublicKey::x25519_public_key(), SecretKey::x25519_secret_key()}. -callback x25519_secret_to_public(SecretKey::x25519_secret_key()) -> PublicKey::x25519_public_key(). -callback x25519_shared_secret(MySecretKey::x25519_secret_key(), YourPublicKey::x25519_public_key()) -> SharedSecret::x25519_shared_secret(). %% jose_curve25519 callbacks -export([eddsa_keypair/0]). -export([eddsa_keypair/1]). -export([eddsa_secret_to_public/1]). -export([ed25519_sign/2]). -export([ed25519_verify/3]). -export([ed25519ctx_sign/3]). -export([ed25519ctx_verify/4]). -export([ed25519ph_sign/2]). -export([ed25519ph_sign/3]). -export([ed25519ph_verify/3]). -export([ed25519ph_verify/4]). -export([x25519_keypair/0]). -export([x25519_keypair/1]). -export([x25519_secret_to_public/1]). -export([x25519_shared_secret/2]). %% Macros -define(JOSE_CURVE25519, (jose:curve25519_module())). %%==================================================================== %% jose_curve25519 callbacks %%==================================================================== % EdDSA -spec eddsa_keypair() -> {eddsa_public_key(), eddsa_secret_key()}. eddsa_keypair() -> ?JOSE_CURVE25519:eddsa_keypair(). -spec eddsa_keypair(eddsa_seed()) -> {eddsa_public_key(), eddsa_secret_key()}. eddsa_keypair(Seed) -> ?JOSE_CURVE25519:eddsa_keypair(Seed). -spec eddsa_secret_to_public(eddsa_secret_key()) -> eddsa_public_key(). eddsa_secret_to_public(SecretKey) -> ?JOSE_CURVE25519:eddsa_secret_to_public(SecretKey). % Ed25519 -spec ed25519_sign(message(), eddsa_secret_key()) -> signature(). ed25519_sign(Message, SecretKey) -> ?JOSE_CURVE25519:ed25519_sign(Message, SecretKey). -spec ed25519_verify(maybe_invalid_signature(), message(), eddsa_public_key()) -> boolean(). ed25519_verify(Signature, Message, PublicKey) -> ?JOSE_CURVE25519:ed25519_verify(Signature, Message, PublicKey). % Ed25519ctx -spec ed25519ctx_sign(message(), eddsa_secret_key(), context()) -> signature(). ed25519ctx_sign(Message, SecretKey, Context) -> ?JOSE_CURVE25519:ed25519ctx_sign(Message, SecretKey, Context). -spec ed25519ctx_verify(maybe_invalid_signature(), message(), eddsa_public_key(), context()) -> boolean(). ed25519ctx_verify(Signature, Message, PublicKey, Context) -> ?JOSE_CURVE25519:ed25519ctx_verify(Signature, Message, PublicKey, Context). % Ed25519ph -spec ed25519ph_sign(message(), eddsa_secret_key()) -> signature(). ed25519ph_sign(Message, SecretKey) -> ?JOSE_CURVE25519:ed25519ph_sign(Message, SecretKey). -spec ed25519ph_sign(message(), eddsa_secret_key(), context()) -> signature(). ed25519ph_sign(Message, SecretKey, Context) -> ?JOSE_CURVE25519:ed25519ph_sign(Message, SecretKey, Context). -spec ed25519ph_verify(maybe_invalid_signature(), message(), eddsa_public_key()) -> boolean(). ed25519ph_verify(Signature, Message, PublicKey) -> ?JOSE_CURVE25519:ed25519ph_verify(Signature, Message, PublicKey). -spec ed25519ph_verify(maybe_invalid_signature(), message(), eddsa_public_key(), context()) -> boolean(). ed25519ph_verify(Signature, Message, PublicKey, Context) -> ?JOSE_CURVE25519:ed25519ph_verify(Signature, Message, PublicKey, Context). % X25519 -spec x25519_keypair() -> {x25519_public_key(), x25519_secret_key()}. x25519_keypair() -> ?JOSE_CURVE25519:x25519_keypair(). -spec x25519_keypair(x25519_seed()) -> {x25519_public_key(), x25519_secret_key()}. x25519_keypair(Seed) -> ?JOSE_CURVE25519:x25519_keypair(Seed). -spec x25519_secret_to_public(x25519_secret_key()) -> x25519_public_key(). x25519_secret_to_public(SecretKey) -> ?JOSE_CURVE25519:x25519_secret_to_public(SecretKey). -spec x25519_shared_secret(MySecretKey :: x25519_secret_key(), YourPublicKey :: x25519_public_key()) -> x25519_shared_secret(). x25519_shared_secret(MySecretKey, YourPublicKey) -> ?JOSE_CURVE25519:x25519_shared_secret(MySecretKey, YourPublicKey). potatosalad-erlang-jose-30ac019/src/jwa/curve25519/jose_curve25519_libdecaf.erl0000664000232200023220000000520015267717064027330 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 01 Mar 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_curve25519_libdecaf). -behaviour(jose_curve25519). %% jose_curve25519 callbacks -export([eddsa_keypair/0]). -export([eddsa_keypair/1]). -export([eddsa_secret_to_public/1]). -export([ed25519_sign/2]). -export([ed25519_verify/3]). -export([ed25519ctx_sign/3]). -export([ed25519ctx_verify/4]). -export([ed25519ph_sign/2]). -export([ed25519ph_sign/3]). -export([ed25519ph_verify/3]). -export([ed25519ph_verify/4]). -export([x25519_keypair/0]). -export([x25519_keypair/1]). -export([x25519_secret_to_public/1]). -export([x25519_shared_secret/2]). %%==================================================================== %% jose_curve25519 callbacks %%==================================================================== % EdDSA eddsa_keypair() -> libdecaf_curve25519:eddsa_keypair(). eddsa_keypair(Seed) -> libdecaf_curve25519:eddsa_keypair(Seed). eddsa_secret_to_public(SecretKey) -> libdecaf_curve25519:eddsa_secret_to_pk(SecretKey). % Ed25519 ed25519_sign(Message, SecretKey) -> libdecaf_curve25519:ed25519_sign(Message, SecretKey). ed25519_verify(Signature, Message, PublicKey) -> libdecaf_curve25519:ed25519_verify(Signature, Message, PublicKey). % Ed25519ctx ed25519ctx_sign(Message, SecretKey, Context) -> libdecaf_curve25519:ed25519ctx_sign(Message, SecretKey, Context). ed25519ctx_verify(Signature, Message, PublicKey, Context) -> libdecaf_curve25519:ed25519ctx_verify(Signature, Message, PublicKey, Context). % Ed25519ph ed25519ph_sign(Message, SecretKey) -> libdecaf_curve25519:ed25519ph_sign(Message, SecretKey). ed25519ph_sign(Message, SecretKey, Context) -> libdecaf_curve25519:ed25519ph_sign(Message, SecretKey, Context). ed25519ph_verify(Signature, Message, PublicKey) -> libdecaf_curve25519:ed25519ph_verify(Signature, Message, PublicKey). ed25519ph_verify(Signature, Message, PublicKey, Context) -> libdecaf_curve25519:ed25519ph_verify(Signature, Message, PublicKey, Context). % X25519 x25519_keypair() -> libdecaf_curve25519:x25519_keypair(). x25519_keypair(Seed) -> libdecaf_curve25519:x25519_keypair(Seed). x25519_secret_to_public(SecretKey) -> libdecaf_curve25519:x25519(SecretKey). x25519_shared_secret(MySecretKey, YourPublicKey) -> libdecaf_curve25519:x25519(MySecretKey, YourPublicKey). potatosalad-erlang-jose-30ac019/src/jwa/curve25519/jose_curve25519_libsodium.erl0000664000232200023220000000630315267717064027573 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 02 Jan 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_curve25519_libsodium). -behaviour(jose_curve25519). %% jose_curve25519 callbacks -export([eddsa_keypair/0]). -export([eddsa_keypair/1]). -export([eddsa_secret_to_public/1]). -export([ed25519_sign/2]). -export([ed25519_verify/3]). -export([ed25519ctx_sign/3]). -export([ed25519ctx_verify/4]). -export([ed25519ph_sign/2]). -export([ed25519ph_sign/3]). -export([ed25519ph_verify/3]). -export([ed25519ph_verify/4]). -export([x25519_keypair/0]). -export([x25519_keypair/1]). -export([x25519_secret_to_public/1]). -export([x25519_shared_secret/2]). %% Macros -define(FALLBACK_MOD, jose_curve25519_fallback). %%==================================================================== %% jose_curve25519 callbacks %%==================================================================== % EdDSA eddsa_keypair() -> libsodium_crypto_sign_ed25519:keypair(). eddsa_keypair(Seed) -> libsodium_crypto_sign_ed25519:seed_keypair(Seed). eddsa_secret_to_public(SecretKey) -> {PK, _} = libsodium_crypto_sign_ed25519:seed_keypair(SecretKey), PK. % Ed25519 ed25519_sign(Message, SecretKey) -> libsodium_crypto_sign_ed25519:detached(Message, SecretKey). ed25519_verify(Signature, Message, PublicKey) -> try libsodium_crypto_sign_ed25519:verify_detached(Signature, Message, PublicKey) of 0 -> true; _ -> false catch _:_:_ -> false end. % Ed25519ctx ed25519ctx_sign(Message, SecretKey, Context) -> ?FALLBACK_MOD:ed25519ctx_sign(Message, SecretKey, Context). ed25519ctx_verify(Signature, Message, PublicKey, Context) -> ?FALLBACK_MOD:ed25519ctx_verify(Signature, Message, PublicKey, Context). % Ed25519ph ed25519ph_sign(Message, SecretKey) -> State0 = libsodium_crypto_sign_ed25519ph:init(), State1 = libsodium_crypto_sign_ed25519ph:update(State0, Message), libsodium_crypto_sign_ed25519ph:final_create(State1, SecretKey). ed25519ph_sign(Message, SecretKey, Context) -> ?FALLBACK_MOD:ed25519ph_sign(Message, SecretKey, Context). ed25519ph_verify(Signature, Message, PublicKey) -> State0 = libsodium_crypto_sign_ed25519ph:init(), State1 = libsodium_crypto_sign_ed25519ph:update(State0, Message), try libsodium_crypto_sign_ed25519ph:final_verify(State1, Signature, PublicKey) of 0 -> true; _ -> false catch _:_:_ -> false end. ed25519ph_verify(Signature, Message, PublicKey, Context) -> ?FALLBACK_MOD:ed25519ph_verify(Signature, Message, PublicKey, Context). % X25519 x25519_keypair() -> libsodium_crypto_box_curve25519xchacha20poly1305:keypair(). x25519_keypair(SK = << _:32/binary >>) -> PK = x25519_secret_to_public(SK), {PK, SK}. x25519_secret_to_public(SecretKey) -> libsodium_crypto_scalarmult_curve25519:base(SecretKey). x25519_shared_secret(MySecretKey, YourPublicKey) -> libsodium_crypto_scalarmult_curve25519:crypto_scalarmult_curve25519(MySecretKey, YourPublicKey). potatosalad-erlang-jose-30ac019/src/jwa/curve25519/jose_curve25519_fallback.erl0000664000232200023220000001140115267717064027336 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 31 Aug 2022 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_curve25519_fallback). -behaviour(jose_curve25519). %% jose_curve25519 callbacks -export([eddsa_keypair/0]). -export([eddsa_keypair/1]). -export([eddsa_secret_to_public/1]). -export([ed25519_sign/2]). -export([ed25519_verify/3]). -export([ed25519ctx_sign/3]). -export([ed25519ctx_verify/4]). -export([ed25519ph_sign/2]). -export([ed25519ph_sign/3]). -export([ed25519ph_verify/3]). -export([ed25519ph_verify/4]). -export([x25519_keypair/0]). -export([x25519_keypair/1]). -export([x25519_secret_to_public/1]). -export([x25519_shared_secret/2]). %% Macros -define(MAYBE_FALLBACK_MOD, case jose:crypto_fallback() of true -> jose_jwa_curve25519; false -> jose_curve25519_unsupported end). %%==================================================================== %% jose_curve25519 callbacks %%==================================================================== % EdDSA -spec eddsa_keypair() -> {jose_curve25519:eddsa_public_key(), jose_curve25519:eddsa_secret_key()}. eddsa_keypair() -> ?MAYBE_FALLBACK_MOD:eddsa_keypair(). -spec eddsa_keypair(jose_curve25519:eddsa_seed()) -> {jose_curve25519:eddsa_public_key(), jose_curve25519:eddsa_secret_key()}. eddsa_keypair(Seed) -> ?MAYBE_FALLBACK_MOD:eddsa_keypair(Seed). -spec eddsa_secret_to_public(jose_curve25519:eddsa_secret_key()) -> jose_curve25519:eddsa_public_key(). eddsa_secret_to_public(SecretKey) -> ?MAYBE_FALLBACK_MOD:eddsa_secret_to_public(SecretKey). % Ed25519 -spec ed25519_sign(jose_curve25519:message(), jose_curve25519:eddsa_secret_key()) -> jose_curve25519:signature(). ed25519_sign(Message, SecretKey) -> ?MAYBE_FALLBACK_MOD:ed25519_sign(Message, SecretKey). -spec ed25519_verify(jose_curve25519:maybe_invalid_signature(), jose_curve25519:message(), jose_curve25519:eddsa_public_key()) -> boolean(). ed25519_verify(Signature, Message, PublicKey) -> ?MAYBE_FALLBACK_MOD:ed25519_verify(Signature, Message, PublicKey). % Ed25519ctx -spec ed25519ctx_sign(jose_curve25519:message(), jose_curve25519:eddsa_secret_key(), jose_curve25519:context()) -> jose_curve25519:signature(). ed25519ctx_sign(Message, SecretKey, Context) -> ?MAYBE_FALLBACK_MOD:ed25519ctx_sign(Message, SecretKey, Context). -spec ed25519ctx_verify(jose_curve25519:maybe_invalid_signature(), jose_curve25519:message(), jose_curve25519:eddsa_public_key(), jose_curve25519:context()) -> boolean(). ed25519ctx_verify(Signature, Message, PublicKey, Context) -> ?MAYBE_FALLBACK_MOD:ed25519ctx_verify(Signature, Message, PublicKey, Context). % Ed25519ph -spec ed25519ph_sign(jose_curve25519:message(), jose_curve25519:eddsa_secret_key()) -> jose_curve25519:signature(). ed25519ph_sign(Message, SecretKey) -> ?MAYBE_FALLBACK_MOD:ed25519ph_sign(Message, SecretKey). -spec ed25519ph_sign(jose_curve25519:message(), jose_curve25519:eddsa_secret_key(), jose_curve25519:context()) -> jose_curve25519:signature(). ed25519ph_sign(Message, SecretKey, Context) -> ?MAYBE_FALLBACK_MOD:ed25519ph_sign(Message, SecretKey, Context). -spec ed25519ph_verify(jose_curve25519:maybe_invalid_signature(), jose_curve25519:message(), jose_curve25519:eddsa_public_key()) -> boolean(). ed25519ph_verify(Signature, Message, PublicKey) -> ?MAYBE_FALLBACK_MOD:ed25519ph_verify(Signature, Message, PublicKey). -spec ed25519ph_verify(jose_curve25519:maybe_invalid_signature(), jose_curve25519:message(), jose_curve25519:eddsa_public_key(), jose_curve25519:context()) -> boolean(). ed25519ph_verify(Signature, Message, PublicKey, Context) -> ?MAYBE_FALLBACK_MOD:ed25519ph_verify(Signature, Message, PublicKey, Context). % X25519 -spec x25519_keypair() -> {jose_curve25519:x25519_public_key(), jose_curve25519:x25519_secret_key()}. x25519_keypair() -> ?MAYBE_FALLBACK_MOD:x25519_keypair(). -spec x25519_keypair(jose_curve25519:x25519_seed()) -> {jose_curve25519:x25519_public_key(), jose_curve25519:x25519_secret_key()}. x25519_keypair(Seed) -> ?MAYBE_FALLBACK_MOD:x25519_keypair(Seed). -spec x25519_secret_to_public(jose_curve25519:x25519_secret_key()) -> jose_curve25519:x25519_public_key(). x25519_secret_to_public(SecretKey) -> ?MAYBE_FALLBACK_MOD:x25519_secret_to_public(SecretKey). -spec x25519_shared_secret(MySecretKey :: jose_curve25519:x25519_secret_key(), YourPublicKey :: jose_curve25519:x25519_public_key()) -> jose_curve25519:x25519_shared_secret(). x25519_shared_secret(MySecretKey, YourPublicKey) -> ?MAYBE_FALLBACK_MOD:x25519_shared_secret(MySecretKey, YourPublicKey). potatosalad-erlang-jose-30ac019/src/jwa/curve25519/jose_curve25519_crypto.erl0000664000232200023220000000572515267717064027133 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Brett Beatty %%% @copyright 2014-2022, Andrew Bennett, 2021, Brett Beatty %%% @doc %%% %%% @end %%% Created : 22 Oct 2021 by Brett Beatty %%%------------------------------------------------------------------- -module(jose_curve25519_crypto). -behaviour(jose_curve25519). %% jose_curve25519 callbacks -export([eddsa_keypair/0]). -export([eddsa_keypair/1]). -export([eddsa_secret_to_public/1]). -export([ed25519_sign/2]). -export([ed25519_verify/3]). -export([ed25519ctx_sign/3]). -export([ed25519ctx_verify/4]). -export([ed25519ph_sign/2]). -export([ed25519ph_sign/3]). -export([ed25519ph_verify/3]). -export([ed25519ph_verify/4]). -export([x25519_keypair/0]). -export([x25519_keypair/1]). -export([x25519_secret_to_public/1]). -export([x25519_shared_secret/2]). %% Macros -define(FALLBACK_MOD, jose_curve25519_fallback). %%==================================================================== %% jose_curve25519 callbacks %%==================================================================== % EdDSA eddsa_keypair() -> {PublicKey, Secret} = crypto:generate_key(eddsa, ed25519), {PublicKey, <>}. eddsa_keypair(<>) -> {PublicKey, Secret} = crypto:generate_key(eddsa, ed25519, Secret), {PublicKey, <>}. eddsa_secret_to_public(<>) -> {PublicKey, _} = crypto:generate_key(eddsa, ed25519, Secret), PublicKey. % Ed25519 ed25519_sign(Message, <>) -> crypto:sign(eddsa, none, Message, [Secret, ed25519]). ed25519_verify(Signature, Message, <>) -> crypto:verify(eddsa, none, Message, Signature, [PublicKey, ed25519]). % Ed25519ctx ed25519ctx_sign(Message, SecretKey, Context) -> ?FALLBACK_MOD:ed25519ctx_sign(Message, SecretKey, Context). ed25519ctx_verify(Signature, Message, PublicKey, Context) -> ?FALLBACK_MOD:ed25519ctx_verify(Signature, Message, PublicKey, Context). % Ed25519ph ed25519ph_sign(Message, SecretKey) -> ?FALLBACK_MOD:ed25519ph_sign(Message, SecretKey). ed25519ph_sign(Message, SecretKey, Context) -> ?FALLBACK_MOD:ed25519ph_sign(Message, SecretKey, Context). ed25519ph_verify(Signature, Message, PublicKey) -> ?FALLBACK_MOD:ed25519ph_verify(Signature, Message, PublicKey). ed25519ph_verify(Signature, Message, PublicKey, Context) -> ?FALLBACK_MOD:ed25519ph_verify(Signature, Message, PublicKey, Context). % X25519 x25519_keypair() -> crypto:generate_key(ecdh, x25519). x25519_keypair(<>) -> crypto:generate_key(ecdh, x25519, Secret). x25519_secret_to_public(<>) -> {PublicKey, _} = crypto:generate_key(ecdh, x25519, Secret), PublicKey. x25519_shared_secret(MySecretKey, YourPublicKey) -> crypto:compute_key(ecdh, YourPublicKey, MySecretKey, x25519). potatosalad-erlang-jose-30ac019/src/jwa/curve25519/jose_curve25519_unsupported.erl0000664000232200023220000000416515267717064030200 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 02 Jan 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_curve25519_unsupported). -behaviour(jose_curve25519). %% jose_curve25519 callbacks -export([eddsa_keypair/0]). -export([eddsa_keypair/1]). -export([eddsa_secret_to_public/1]). -export([ed25519_sign/2]). -export([ed25519_verify/3]). -export([ed25519ctx_sign/3]). -export([ed25519ctx_verify/4]). -export([ed25519ph_sign/2]). -export([ed25519ph_sign/3]). -export([ed25519ph_verify/3]). -export([ed25519ph_verify/4]). -export([x25519_keypair/0]). -export([x25519_keypair/1]). -export([x25519_secret_to_public/1]). -export([x25519_shared_secret/2]). %% Macros -define(unsupported, erlang:error(operation_not_supported)). %%==================================================================== %% jose_curve25519 callbacks %%==================================================================== % EdDSA eddsa_keypair() -> ?unsupported. eddsa_keypair(_Seed) -> ?unsupported. eddsa_secret_to_public(_SecretKey) -> ?unsupported. % Ed25519 ed25519_sign(_Message, _SecretKey) -> ?unsupported. ed25519_verify(_Signature, _Message, _PublicKey) -> ?unsupported. % Ed25519ctx ed25519ctx_sign(_Message, _SecretKey, _Context) -> ?unsupported. ed25519ctx_verify(_Signature, _Message, _PublicKey, _Context) -> ?unsupported. % Ed25519ph ed25519ph_sign(_Message, _SecretKey) -> ?unsupported. ed25519ph_sign(_Message, _SecretKey, _Context) -> ?unsupported. ed25519ph_verify(_Signature, _Message, _PublicKey) -> ?unsupported. ed25519ph_verify(_Signature, _Message, _PublicKey, _Context) -> ?unsupported. % X25519 x25519_keypair() -> ?unsupported. x25519_keypair(_Seed) -> ?unsupported. x25519_secret_to_public(_SecretKey) -> ?unsupported. x25519_shared_secret(_MySecretKey, _YourPublicKey) -> ?unsupported. potatosalad-erlang-jose-30ac019/src/jwa/jose_jwa_bench.erl0000664000232200023220000000761115267717064024163 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: nil; st-rulers: [132] -*- %% vim: ts=4 sw=4 ft=erlang et %%% % @format %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 06 Jan 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwa_bench). %% API -export([bench/2]). -export([bench/3]). -export([compare/3]). %% Records -record(stat, { acc = 0 :: non_neg_integer(), min = 0 :: non_neg_integer(), max = 0 :: non_neg_integer() }). %% Types -type arguments_list(Type) :: [Type]. -type arguments_function(Type) :: fun(() -> arguments_list(Type)). -type arguments(Type) :: arguments_function(Type) | arguments_list(Type). -type arguments() :: arguments(term()). -export_type([arguments/1]). -export_type([arguments/0]). -type metric() :: #{ acc := non_neg_integer(), avg := float(), min := non_neg_integer(), max := non_neg_integer() }. -export_type([metric/0]). -type stats() :: #{ reds := metric(), time := metric() }. -export_type([stats/0]). %%==================================================================== %% API %%==================================================================== -spec bench(function(), arguments()) -> stats(). bench(Function, Arguments) -> bench(Function, Arguments, 1). -spec bench(function(), arguments(), non_neg_integer()) -> stats(). bench(Function, Arguments, N) when is_function(Function) andalso (is_list(Arguments) orelse is_function(Arguments, 0)) andalso (is_integer(N) andalso N > 0) -> {Time, Reds} = bench_loop(N, erlang:self(), #stat{}, #stat{}, Function, Arguments), #{ time => stat_final(Time, N), reds => stat_final(Reds, N) }. -spec compare([{atom(), function()}], arguments(), non_neg_integer()) -> [{atom(), stats()}]. compare(Groups, Arguments, N) when is_list(Groups) andalso (is_list(Arguments) orelse is_function(Arguments, 0)) andalso (is_integer(N) andalso N > 0) -> ResolvedArguments = resolve(Arguments), [ begin {Label, bench(Function, ResolvedArguments, N)} end || {Label, Function} <- Groups, is_atom(Label) andalso is_function(Function) ]. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private bench_loop(0, _Self, Time, Reds, _Function, _Arguments) -> {Time, Reds}; bench_loop(I, Self, Time0, Reds0, Function, Arguments) -> Args = resolve(Arguments), T1 = erlang:monotonic_time(microsecond), {reductions, R1} = erlang:process_info(Self, reductions), _ = erlang:apply(Function, Args), {reductions, R2} = erlang:process_info(Self, reductions), T2 = erlang:monotonic_time(microsecond), Time1 = stat_update(Time0, T2 - T1), Reds1 = stat_update(Reds0, R2 - R1), bench_loop(I - 1, Self, Time1, Reds1, Function, Arguments). %% @private resolve(Arguments) when is_function(Arguments, 0) -> resolve(Arguments()); resolve(Arguments) when is_list(Arguments) -> Arguments. %% @private stat_final(#stat{acc = Acc, min = Min, max = Max}, N) -> #{ acc => Acc, avg => (Acc / N), min => Min, max => Max }. %% @private stat_update(Stat = #stat{acc = Acc, min = Min, max = Max}, Val) -> Stat#stat{ acc = Acc + Val, min = case Val < Min of _ when Min =:= 0 -> Val; true -> Val; false -> Min end, max = case Val > Max of true -> Val; false -> Max end }. potatosalad-erlang-jose-30ac019/src/jwa/jose_jwa_math.erl0000664000232200023220000000451015267717064024030 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 06 Jan 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwa_math). %% Public API -export([expmod/3]). -export([exprem/3]). -export([intpow/2]). -export([mod/2]). -export([mod_pow/3]). %% Private API -export([expmod_fast/3]). -export([expmod_slow/3]). -export([exprem_fast/3]). -export([exprem_slow/3]). %%==================================================================== %% Public API %%==================================================================== expmod(B, E, M) -> expmod_fast(B, E, M). exprem(B, E, M) -> exprem_fast(B, E, M). intpow(B, E) when is_integer(B) andalso is_integer(E) andalso E >= 0 -> case B of 0 -> 0; 1 -> 1; 2 -> 1 bsl E; _ -> intpow(B, E, 1) end. mod(B, M) -> (B rem M + M) rem M. mod_pow(B, E, M) -> Bytes = crypto:mod_pow(B, E, M), Size = byte_size(Bytes), << ((crypto:bytes_to_integer(Bytes) + M) rem M):Size/signed-big-integer-unit:8 >>. %%==================================================================== %% Private API %%==================================================================== % @private expmod_fast(B, E, M) -> (exprem_fast(B, E, M) + M) rem M. % @private expmod_slow(B, E, M) -> (exprem_slow(B, E, M) + M) rem M. % @private exprem_fast(B, E, M) when B < 0 andalso E rem 2 =/= 0 -> -exprem_fast(abs(B), E, M); exprem_fast(B, E, M) when B < 0 -> exprem_fast(abs(B), E, M); exprem_fast(B, E, M) -> crypto:bytes_to_integer(crypto:mod_pow(B, E, M)). %% @private exprem_slow(_B, 0, _M) -> 1; exprem_slow(B, E, M) -> T0 = exprem_slow(B, E div 2, M), T = (T0 * T0) rem M, case E rem 2 of 0 -> T band M; _ -> (T * B) rem M end. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private intpow(B, E, R) when (E rem 2) =:= 0 -> intpow(B * B, E div 2, R); intpow(B, E, R) when (E div 2) =:= 0 -> B * R; intpow(B, E, R) -> intpow(B * B, E div 2, B * R). potatosalad-erlang-jose-30ac019/src/jwa/jose_jwa_xchacha20_poly1305.erl0000664000232200023220000000351015267717064026213 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc XChaCha: eXtended-nonce ChaCha and AEAD_XChaCha20_Poly1305 %%% See https://tools.ietf.org/html/draft-irtf-cfrg-xchacha %%% @end %%% Created : 14 Sep 2019 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwa_xchacha20_poly1305). -behaviour(jose_xchacha20_poly1305). %% jose_xchacha20_poly1305 callbacks -export([decrypt/5]). -export([encrypt/4]). -export([authenticate/3]). -export([verify/4]). %%==================================================================== %% jose_chacha20_poly1305 callbacks %%==================================================================== decrypt(CipherText, CipherTag, AAD, IV, CEK) -> {Subkey, Nonce} = jose_jwa_xchacha20:subkey_and_nonce(CEK, IV), jose_chacha20_poly1305:block_decrypt({chacha20_poly1305, 256}, Subkey, Nonce, {AAD, CipherText, CipherTag}). encrypt(PlainText, AAD, IV, CEK) -> {Subkey, Nonce} = jose_jwa_xchacha20:subkey_and_nonce(CEK, IV), jose_chacha20_poly1305:block_encrypt({chacha20_poly1305, 256}, Subkey, Nonce, {AAD, PlainText}). authenticate(Message, Key, Nonce0) -> {Subkey, Nonce} = jose_jwa_xchacha20:subkey_and_nonce(Key, Nonce0), jose_chacha20_poly1305:authenticate(Message, Subkey, Nonce). verify(MAC, Message, Key, Nonce0) -> {Subkey, Nonce} = jose_jwa_xchacha20:subkey_and_nonce(Key, Nonce0), jose_chacha20_poly1305:verify(MAC, Message, Subkey, Nonce). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jwa/jose_jwa_chacha20.erl0000664000232200023220000001135115267717064024451 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc ChaCha20 and Poly1305 for IETF Protocols %%% See https://tools.ietf.org/html/rfc7539 %%% @end %%% Created : 31 May 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwa_chacha20). %% API -export([quarter_round/1]). -export([column_round/1]). -export([diagonal_round/1]). -export([block/3]). -export([encrypt/4]). %% Macros -define(math, jose_jwa_math). -define(p, 16#100000000). -define(rotl(X, R), ?math:mod((X bsl R) bor (X bsr (32 - R)), ?p)). %%==================================================================== %% API functions %%==================================================================== quarter_round({A0, B0, C0, D0}) -> A1 = ?math:mod(A0 + B0, ?p), D1 = ?rotl(D0 bxor A1, 16), C1 = ?math:mod(C0 + D1, ?p), B1 = ?rotl(B0 bxor C1, 12), A = ?math:mod(A1 + B1, ?p), D = ?rotl(D1 bxor A, 8), C = ?math:mod(C1 + D, ?p), B = ?rotl(B1 bxor C, 7), {A, B, C, D}. column_round({X00, X01, X02, X03, X04, X05, X06, X07, X08, X09, X10, X11, X12, X13, X14, X15}) -> {Y00, Y04, Y08, Y12} = quarter_round({X00, X04, X08, X12}), {Y01, Y05, Y09, Y13} = quarter_round({X01, X05, X09, X13}), {Y02, Y06, Y10, Y14} = quarter_round({X02, X06, X10, X14}), {Y03, Y07, Y11, Y15} = quarter_round({X03, X07, X11, X15}), {Y00, Y01, Y02, Y03, Y04, Y05, Y06, Y07, Y08, Y09, Y10, Y11, Y12, Y13, Y14, Y15}. diagonal_round({Y00, Y01, Y02, Y03, Y04, Y05, Y06, Y07, Y08, Y09, Y10, Y11, Y12, Y13, Y14, Y15}) -> {Z00, Z05, Z10, Z15} = quarter_round({Y00, Y05, Y10, Y15}), {Z01, Z06, Z11, Z12} = quarter_round({Y01, Y06, Y11, Y12}), {Z02, Z07, Z08, Z13} = quarter_round({Y02, Y07, Y08, Y13}), {Z03, Z04, Z09, Z14} = quarter_round({Y03, Y04, Y09, Y14}), {Z00, Z01, Z02, Z03, Z04, Z05, Z06, Z07, Z08, Z09, Z10, Z11, Z12, Z13, Z14, Z15}. block(Key, Counter, Nonce) when is_binary(Key) andalso bit_size(Key) =:= 256 andalso is_integer(Counter) andalso Counter >= 0 andalso is_binary(Nonce) andalso bit_size(Nonce) =:= 96 -> State = << "expand 32-byte k", Key:256/bitstring, Counter:32/unsigned-little-integer-unit:1, Nonce:96/bitstring >>, WS0 = list_to_tuple([Word || << Word:32/unsigned-little-integer-unit:1 >> <= State]), WS1 = rounds(WS0, 10), WS2 = add(WS1, WS0), serialize(WS2). encrypt(Key, Counter, Nonce, Plaintext) -> encrypt(Key, Counter, Nonce, Plaintext, 0, <<>>). encrypt(_Key, _Counter, _Nonce, <<>>, _J, EncryptedMessage) -> EncryptedMessage; encrypt(Key, Counter, Nonce, << Block:64/binary, Rest/binary >>, J, EncryptedMessage) -> KeyStream = block(Key, Counter + J, Nonce), encrypt(Key, Counter, Nonce, Rest, J + 1, << EncryptedMessage/binary, (crypto:exor(Block, KeyStream))/binary >>); encrypt(Key, Counter, Nonce, Block, J, EncryptedMessage) -> BlockBytes = byte_size(Block), << KeyStream:BlockBytes/binary, _/binary >> = block(Key, Counter + J, Nonce), << EncryptedMessage/binary, (crypto:exor(Block, KeyStream))/binary >>. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private inner_block(State0) when is_tuple(State0) andalso tuple_size(State0) =:= 16 -> State1 = column_round(State0), State2 = diagonal_round(State1), State2. %% @private rounds(S, 0) -> S; rounds(S, N) when is_integer(N) andalso N > 0 -> rounds(inner_block(S), N - 1). %% @private add({X00, X01, X02, X03, X04, X05, X06, X07, X08, X09, X10, X11, X12, X13, X14, X15}, {Y00, Y01, Y02, Y03, Y04, Y05, Y06, Y07, Y08, Y09, Y10, Y11, Y12, Y13, Y14, Y15}) -> {X00 + Y00, X01 + Y01, X02 + Y02, X03 + Y03, X04 + Y04, X05 + Y05, X06 + Y06, X07 + Y07, X08 + Y08, X09 + Y09, X10 + Y10, X11 + Y11, X12 + Y12, X13 + Y13, X14 + Y14, X15 + Y15}. %% @private serialize({Z00, Z01, Z02, Z03, Z04, Z05, Z06, Z07, Z08, Z09, Z10, Z11, Z12, Z13, Z14, Z15}) -> << Z00:32/unsigned-little-integer-unit:1, Z01:32/unsigned-little-integer-unit:1, Z02:32/unsigned-little-integer-unit:1, Z03:32/unsigned-little-integer-unit:1, Z04:32/unsigned-little-integer-unit:1, Z05:32/unsigned-little-integer-unit:1, Z06:32/unsigned-little-integer-unit:1, Z07:32/unsigned-little-integer-unit:1, Z08:32/unsigned-little-integer-unit:1, Z09:32/unsigned-little-integer-unit:1, Z10:32/unsigned-little-integer-unit:1, Z11:32/unsigned-little-integer-unit:1, Z12:32/unsigned-little-integer-unit:1, Z13:32/unsigned-little-integer-unit:1, Z14:32/unsigned-little-integer-unit:1, Z15:32/unsigned-little-integer-unit:1 >>. potatosalad-erlang-jose-30ac019/src/jwa/jose_jwa_pkcs5.erl0000664000232200023220000001204115267717064024122 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc PKCS #5: Password-Based Cryptography Specification Version 2.0 %%% See RFC 2898: https://tools.ietf.org/html/rfc2898 %%% @end %%% Created : 27 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwa_pkcs5). %% API -export([pbkdf1/3]). -export([pbkdf1/4]). -export([pbkdf1/5]). -export([pbkdf2/3]). -export([pbkdf2/4]). -export([pbkdf2/5]). %%==================================================================== %% API functions %%==================================================================== pbkdf1(Hash, Password, Salt) -> pbkdf1(Hash, Password, Salt, 1). pbkdf1(Hash, Password, Salt, Iterations) -> HashFun = resolve_hash(Hash), DerivedKeyLen = byte_size(HashFun(<<>>)), pbkdf1(HashFun, Password, Salt, Iterations, DerivedKeyLen). pbkdf1(Hash, Password, Salt, Iterations, DerivedKeyLen) when is_function(Hash, 1) andalso is_binary(Password) andalso is_binary(Salt) andalso Iterations >= 1 andalso is_integer(DerivedKeyLen) andalso DerivedKeyLen >= 0 -> HashLen = byte_size(Hash(<<>>)), case DerivedKeyLen > HashLen of false -> {ok, derive_pbkdf1(Hash, 1, Iterations, DerivedKeyLen, << Password/binary, Salt/binary >>)}; true -> {error, derived_key_too_long} end; pbkdf1(Hash, Password, Salt, Iterations, DerivedKeyLen) when is_tuple(Hash) orelse is_atom(Hash) -> pbkdf1(resolve_hash(Hash), Password, Salt, Iterations, DerivedKeyLen). pbkdf2(Mac, Password, Salt) -> pbkdf2(Mac, Password, Salt, 1). pbkdf2(Mac, Password, Salt, Iterations) -> MacFun = resolve_mac(Mac), DerivedKeyLen = byte_size(MacFun(<<>>, <<>>)), pbkdf2(MacFun, Password, Salt, Iterations, DerivedKeyLen). pbkdf2(Mac, Password, Salt, Iterations, DerivedKeyLen) when is_function(Mac, 2) andalso is_binary(Password) andalso is_binary(Salt) andalso is_integer(Iterations) andalso Iterations >= 1 andalso is_integer(DerivedKeyLen) andalso DerivedKeyLen >= 0 -> MacLen = byte_size(Mac(<<>>, <<>>)), case DerivedKeyLen > (16#FFFFFFFF * MacLen) of false -> Reps = ceiling(DerivedKeyLen / MacLen), {ok, derive_pbkdf2(Mac, 1, Reps, Iterations, DerivedKeyLen, Password, Salt, <<>>)}; true -> {error, derived_key_too_long} end; pbkdf2(Mac, Password, Salt, Iterations, DerivedKeyLen) when is_tuple(Mac) orelse is_atom(Mac) -> pbkdf2(resolve_mac(Mac), Password, Salt, Iterations, DerivedKeyLen). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private ceiling(X) when X < 0 -> trunc(X); ceiling(X) -> T = trunc(X), case X - T == 0 of false -> T + 1; true -> T end. %% @private derive_pbkdf1(Hash, Reps, Reps, DerivedKeyLen, DerivedKeyingMaterial) -> << DerivedKey:DerivedKeyLen/binary, _/binary >> = Hash(DerivedKeyingMaterial), DerivedKey; derive_pbkdf1(Hash, Counter, Reps, DerivedKeyLen, DerivedKeyingMaterial) -> derive_pbkdf1(Hash, Counter + 1, Reps, DerivedKeyLen, Hash(DerivedKeyingMaterial)). %% @private derive_pbkdf2(Mac, Reps, Reps, Iterations, DerivedKeyLen, Password, Salt, DerivedKeyingMaterial) -> << DerivedKey:DerivedKeyLen/binary, _/binary >> = << DerivedKeyingMaterial/binary, (derive_pbkdf2_exor(Mac, Password, Salt, 1, Iterations, Reps, <<>>, <<>>))/binary >>, DerivedKey; derive_pbkdf2(Mac, Counter, Reps, Iterations, DerivedKeyLen, Password, Salt, DerivedKeyingMaterial) -> derive_pbkdf2(Mac, Counter + 1, Reps, Iterations, DerivedKeyLen, Password, Salt, << DerivedKeyingMaterial/binary, (derive_pbkdf2_exor(Mac, Password, Salt, 1, Iterations, Counter, <<>>, <<>>))/binary >>). %% @private derive_pbkdf2_exor(_Mac, _Password, _Salt, I, Iterations, _Counter, _Prev, Acc) when I > Iterations -> Acc; derive_pbkdf2_exor(Mac, Password, Salt, I = 1, Iterations, Counter, <<>>, <<>>) -> Next = Mac(Password, << Salt/binary, Counter:1/unsigned-big-integer-unit:32 >>), derive_pbkdf2_exor(Mac, Password, Salt, I + 1, Iterations, Counter, Next, Next); derive_pbkdf2_exor(Mac, Password, Salt, I, Iterations, Counter, Prev, Acc) -> Next = Mac(Password, Prev), derive_pbkdf2_exor(Mac, Password, Salt, I + 1, Iterations, Counter, Next, crypto:exor(Next, Acc)). %% @private resolve_hash(HashFun) when is_function(HashFun, 1) -> HashFun; resolve_hash(DigestType) when is_atom(DigestType) -> fun(Data) -> crypto:hash(DigestType, Data) end; resolve_hash({hmac, DigestType, Key}) when is_atom(DigestType) -> fun(Data) -> jose_crypto_compat:mac(hmac, DigestType, Key, Data) end. %% @private resolve_mac(MacFun) when is_function(MacFun, 2) -> MacFun; resolve_mac(DigestType) when is_atom(DigestType) -> resolve_mac({hmac, DigestType}); resolve_mac({hmac, DigestType}) when is_atom(DigestType) -> fun(Key, Data) -> jose_crypto_compat:mac(hmac, DigestType, Key, Data) end. potatosalad-erlang-jose-30ac019/src/jwa/jose_jwa_aes.erl0000664000232200023220000020303515267717064023652 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc Advanced Encryption Standard (AES) %%% Cipher Block Chaining (CBC), as defined in NIST.800-38A %%% Electronic Codebook (ECB), as defined in NIST.800-38A %%% Galois/Counter Mode (GCM) and GMAC, as defined in NIST.800-38D %%% See NIST.800-38A: http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf %%% See NIST.800-38D: http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf %%% @end %%% Created : 28 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwa_aes). -behaviour(jose_block_encryptor). %% jose_block_encryptor callbacks -export([block_decrypt/3]). -export([block_decrypt/4]). -export([block_encrypt/3]). -export([block_encrypt/4]). %%==================================================================== %% jose_block_encryptor callbacks %%==================================================================== block_decrypt({aes_ecb, Bits}, Key, CipherText) when (Bits =:= 128 orelse Bits =:= 192 orelse Bits =:= 256) andalso bit_size(Key) =:= Bits andalso is_binary(CipherText) -> {St, RoundKey} = aes_key_expansion(Bits, Key), ecb_block_decrypt(St, RoundKey, CipherText, <<>>). block_decrypt({aes_cbc, Bits}, Key, IV, CipherText) when (Bits =:= 128 orelse Bits =:= 192 orelse Bits =:= 256) andalso bit_size(Key) =:= Bits andalso bit_size(IV) =:= 128 andalso is_binary(CipherText) -> {St, RoundKey} = aes_key_expansion(Bits, Key), cbc_block_decrypt(St, RoundKey, IV, CipherText, <<>>); block_decrypt({aes_gcm, Bits}, Key, IV, {AAD, CipherText, CipherTag}) when (Bits =:= 128 orelse Bits =:= 192 orelse Bits =:= 256) andalso bit_size(Key) =:= Bits andalso bit_size(IV) > 0 andalso is_binary(AAD) andalso is_binary(CipherText) andalso is_binary(CipherTag) %% GCM tags are always 128 bits here. Verifying a shorter tag %% would compare only bit_size(CipherTag) bits of the real tag %% (tag truncation forgery), and a longer one would crash the %% bitstring match in gcm_block_decrypt/6. andalso bit_size(CipherTag) =:= 128 -> MasterKey = block_encrypt({aes_ecb, Bits}, Key, << 0:128 >>), gcm_block_decrypt(MasterKey, Key, IV, AAD, CipherText, CipherTag); block_decrypt({aes_gcm, Bits}, Key, IV, {AAD, CipherText, CipherTag}) when (Bits =:= 128 orelse Bits =:= 192 orelse Bits =:= 256) andalso bit_size(Key) =:= Bits andalso bit_size(IV) > 0 andalso is_binary(AAD) andalso is_binary(CipherText) andalso is_binary(CipherTag) -> error. block_encrypt({aes_ecb, Bits}, Key, PlainText) when (Bits =:= 128 orelse Bits =:= 192 orelse Bits =:= 256) andalso bit_size(Key) =:= Bits andalso is_binary(PlainText) -> {St, RoundKey} = aes_key_expansion(Bits, Key), ecb_block_encrypt(St, RoundKey, PlainText, <<>>). block_encrypt({aes_cbc, Bits}, Key, IV, PlainText) when (Bits =:= 128 orelse Bits =:= 192 orelse Bits =:= 256) andalso bit_size(Key) =:= Bits andalso bit_size(IV) =:= 128 andalso is_binary(PlainText) -> {St, RoundKey} = aes_key_expansion(Bits, Key), cbc_block_encrypt(St, RoundKey, IV, PlainText, <<>>); block_encrypt({aes_gcm, Bits}, Key, IV, {AAD, PlainText}) when (Bits =:= 128 orelse Bits =:= 192 orelse Bits =:= 256) andalso bit_size(Key) =:= Bits andalso bit_size(IV) > 0 andalso is_binary(AAD) andalso is_binary(PlainText) -> MasterKey = block_encrypt({aes_ecb, Bits}, Key, << 0:128 >>), gcm_block_encrypt(MasterKey, Key, IV, AAD, PlainText). %%%------------------------------------------------------------------- %%% Internal AES functions %%%------------------------------------------------------------------- %% @private aes_add_round_key(B0, RoundKey, {Nb, _, _}, Round) -> B1 = aes_add_round_key(0, 0, B0, RoundKey, Nb, Round), B2 = aes_add_round_key(1, 0, B1, RoundKey, Nb, Round), B3 = aes_add_round_key(2, 0, B2, RoundKey, Nb, Round), B4 = aes_add_round_key(3, 0, B3, RoundKey, Nb, Round), B4. %% @private aes_add_round_key(_I, 4, Block, _RoundKey, _Nb, _Round) -> Block; aes_add_round_key(I, J, B0, RoundKey, Nb, Round) -> RK = bget(RoundKey, Round * Nb * 4 + I * Nb + J), BK = bget(B0, I * 4 + J), B1 = bset(B0, I * 4 + J, BK bxor RK), aes_add_round_key(I, J + 1, B1, RoundKey, Nb, Round). %% @private aes_key_expansion(Bits, Key) -> % The number of columns comprising a state in AES. Nb = 4, % The number of 32 bit words in a key. Nk = Bits div 32, % The number of rounds in AES Cipher. Nr = case Bits of 128 -> 10; 192 -> 12; 256 -> 14 end, KeyLen = bit_size(Key), KeysLen = Nb * (Nr + 1) * Nk * 8, Keys = << Key/binary, 0:(KeysLen - KeyLen) >>, St = {Nb, Nk, Nr}, {St, aes_key_expansion((KeyLen div 32), (Nb * (Nr + 1)), St, Keys)}. aes_key_expansion(Rs, Rs, _, RoundKey) -> RoundKey; aes_key_expansion(I, Rs, St={Nb, Nk, _}, RoundKey) -> T0 = bget(RoundKey, (I - 1) * Nb + 0), T1 = bget(RoundKey, (I - 1) * Nb + 1), T2 = bget(RoundKey, (I - 1) * Nb + 2), T3 = bget(RoundKey, (I - 1) * Nb + 3), {V0, V1, V2, V3} = case I rem Nk of 0 -> % RotWord RW0 = T1, RW1 = T2, RW2 = T3, RW3 = T0, % SubWord SW0 = sBox(RW0) bxor rcon(I div Nk), SW1 = sBox(RW1), SW2 = sBox(RW2), SW3 = sBox(RW3), { SW0, SW1, SW2, SW3 }; 4 when Nk > 6 -> % SubWord SW0 = sBox(T0), SW1 = sBox(T1), SW2 = sBox(T2), SW3 = sBox(T3), { SW0, SW1, SW2, SW3 }; _ -> { T0, T1, T2, T3 } end, A0 = bget(RoundKey, (I - Nk) * Nb + 0) bxor V0, A1 = bget(RoundKey, (I - Nk) * Nb + 1) bxor V1, A2 = bget(RoundKey, (I - Nk) * Nb + 2) bxor V2, A3 = bget(RoundKey, (I - Nk) * Nb + 3) bxor V3, RK0 = bset(RoundKey, I * Nb + 0, A0), RK1 = bset(RK0, I * Nb + 1, A1), RK2 = bset(RK1, I * Nb + 2, A2), RK3 = bset(RK2, I * Nb + 3, A3), aes_key_expansion(I + 1, Rs, St, RK3). %%%------------------------------------------------------------------- %%% Internal AES decrypt functions %%%------------------------------------------------------------------- %% @private aes_inverse_cipher(St={_, _, Nr}, RoundKey, B0) -> B1 = aes_add_round_key(B0, RoundKey, St, Nr), B2 = aes_inverse_cipher_rounds(Nr - 1, St, RoundKey, B1), B3 = aes_inverse_shift_rows(B2), B4 = aes_inverse_sub_bytes(B3), B5 = aes_add_round_key(B4, RoundKey, St, 0), B5. %% @private aes_inverse_cipher_rounds(0, _St, _RoundKey, B) -> B; aes_inverse_cipher_rounds(Round, St, RoundKey, B0) -> B1 = aes_inverse_shift_rows(B0), B2 = aes_inverse_sub_bytes(B1), B3 = aes_add_round_key(B2, RoundKey, St, Round), B4 = aes_inverse_mix_columns(B3), aes_inverse_cipher_rounds(Round - 1, St, RoundKey, B4). %% @private aes_inverse_mix_columns(B) -> aes_inverse_mix_columns(0, B). %% @private aes_inverse_mix_columns(4, B) -> B; aes_inverse_mix_columns(I, B0) -> A = bget(B0, I * 4 + 0), B = bget(B0, I * 4 + 1), C = bget(B0, I * 4 + 2), D = bget(B0, I * 4 + 3), B1 = bset(B0, I * 4 + 0, gex(A) bxor gbx(B) bxor gdx(C) bxor g9x(D)), B2 = bset(B1, I * 4 + 1, g9x(A) bxor gex(B) bxor gbx(C) bxor gdx(D)), B3 = bset(B2, I * 4 + 2, gdx(A) bxor g9x(B) bxor gex(C) bxor gbx(D)), B4 = bset(B3, I * 4 + 3, gbx(A) bxor gdx(B) bxor g9x(C) bxor gex(D)), aes_inverse_mix_columns(I + 1, B4). %% @private aes_inverse_shift_rows(B0) -> % Rotate first row 1 columns to right T0 = bget(B0, 3 * 4 + 1), B1 = bset(B0, 3 * 4 + 1, bget(B0, 2 * 4 + 1)), B2 = bset(B1, 2 * 4 + 1, bget(B1, 1 * 4 + 1)), B3 = bset(B2, 1 * 4 + 1, bget(B2, 0 * 4 + 1)), B4 = bset(B3, 0 * 4 + 1, T0), % Rotate second row 2 columns to right T1 = bget(B4, 0 * 4 + 2), B5 = bset(B4, 0 * 4 + 2, bget(B4, 2 * 4 + 2)), B6 = bset(B5, 2 * 4 + 2, T1), T2 = bget(B6, 1 * 4 + 2), B7 = bset(B6, 1 * 4 + 2, bget(B6, 3 * 4 + 2)), B8 = bset(B7, 3 * 4 + 2, T2), % Rotate third row 3 columns to right T3 = bget(B8, 0 * 4 + 3), B9 = bset(B8, 0 * 4 + 3, bget(B8, 1 * 4 + 3)), BA = bset(B9, 1 * 4 + 3, bget(B9, 2 * 4 + 3)), BB = bset(BA, 2 * 4 + 3, bget(BA, 3 * 4 + 3)), BC = bset(BB, 3 * 4 + 3, T3), BC. %% @private aes_inverse_sub_bytes(B0) -> B1 = aes_inverse_sub_bytes(0, 0, B0), B2 = aes_inverse_sub_bytes(1, 0, B1), B3 = aes_inverse_sub_bytes(2, 0, B2), B4 = aes_inverse_sub_bytes(3, 0, B3), B4. %% @private aes_inverse_sub_bytes(_I, 4, B) -> B; aes_inverse_sub_bytes(I, J, B0) -> T0 = bget(B0, J * 4 + I), B1 = bset(B0, J * 4 + I, isBox(T0)), aes_inverse_sub_bytes(I, J + 1, B1). %%%------------------------------------------------------------------- %%% Internal AES encrypt functions %%%------------------------------------------------------------------- %% @private aes_cipher(St={_, _, Nr}, RoundKey, B0) -> B1 = aes_add_round_key(B0, RoundKey, St, 0), B2 = aes_cipher_rounds(1, St, RoundKey, B1), B3 = aes_sub_bytes(B2), B4 = aes_shift_rows(B3), B5 = aes_add_round_key(B4, RoundKey, St, Nr), B5. %% @private aes_cipher_rounds(Nr, _St={_, _, Nr}, _RoundKey, B) -> B; aes_cipher_rounds(Round, St, RoundKey, B0) -> B1 = aes_sub_bytes(B0), B2 = aes_shift_rows(B1), B3 = aes_mix_columns(B2), B4 = aes_add_round_key(B3, RoundKey, St, Round), aes_cipher_rounds(Round + 1, St, RoundKey, B4). %% @private aes_mix_columns(B) -> aes_mix_columns(0, B). %% @private aes_mix_columns(4, B) -> B; aes_mix_columns(I, B0) -> A = bget(B0, I * 4 + 0), B = bget(B0, I * 4 + 1), C = bget(B0, I * 4 + 2), D = bget(B0, I * 4 + 3), B1 = bset(B0, I * 4 + 0, g2x(A) bxor g3x(B) bxor C bxor D), B2 = bset(B1, I * 4 + 1, A bxor g2x(B) bxor g3x(C) bxor D), B3 = bset(B2, I * 4 + 2, A bxor B bxor g2x(C) bxor g3x(D)), B4 = bset(B3, I * 4 + 3, g3x(A) bxor B bxor C bxor g2x(D)), aes_mix_columns(I + 1, B4). %% @private aes_shift_rows(B0) -> % Rotate first row 1 columns to left T0 = bget(B0, 0 * 4 + 1), B1 = bset(B0, 0 * 4 + 1, bget(B0, 1 * 4 + 1)), B2 = bset(B1, 1 * 4 + 1, bget(B1, 2 * 4 + 1)), B3 = bset(B2, 2 * 4 + 1, bget(B2, 3 * 4 + 1)), B4 = bset(B3, 3 * 4 + 1, T0), % Rotate second row 2 columns to left T1 = bget(B4, 0 * 4 + 2), B5 = bset(B4, 0 * 4 + 2, bget(B4, 2 * 4 + 2)), B6 = bset(B5, 2 * 4 + 2, T1), T2 = bget(B6, 1 * 4 + 2), B7 = bset(B6, 1 * 4 + 2, bget(B6, 3 * 4 + 2)), B8 = bset(B7, 3 * 4 + 2, T2), % Rotate third row 3 columns to left T3 = bget(B8, 0 * 4 + 3), B9 = bset(B8, 0 * 4 + 3, bget(B8, 3 * 4 + 3)), BA = bset(B9, 3 * 4 + 3, bget(B9, 2 * 4 + 3)), BB = bset(BA, 2 * 4 + 3, bget(BA, 1 * 4 + 3)), BC = bset(BB, 1 * 4 + 3, T3), BC. %% @private aes_sub_bytes(B0) -> B1 = aes_sub_bytes(0, 0, B0), B2 = aes_sub_bytes(1, 0, B1), B3 = aes_sub_bytes(2, 0, B2), B4 = aes_sub_bytes(3, 0, B3), B4. %% @private aes_sub_bytes(_I, 4, B) -> B; aes_sub_bytes(I, J, B0) -> T0 = bget(B0, J * 4 + I), B1 = bset(B0, J * 4 + I, sBox(T0)), aes_sub_bytes(I, J + 1, B1). %%%------------------------------------------------------------------- %%% Internal CBC decrypt functions %%%------------------------------------------------------------------- %% @private cbc_block_decrypt(_St, _RoundKey, _IV, <<>>, PlainText) -> PlainText; cbc_block_decrypt(St, RoundKey, IV, << Block:128/bitstring, CipherText/bitstring >>, PlainText) -> Decrypted = crypto:exor(aes_inverse_cipher(St, RoundKey, Block), IV), cbc_block_decrypt(St, RoundKey, Block, CipherText, << PlainText/binary, Decrypted/binary >>). %%%------------------------------------------------------------------- %%% Internal CBC encrypt functions %%%------------------------------------------------------------------- %% @private cbc_block_encrypt(_St, _RoundKey, _IV, <<>>, CipherText) -> CipherText; cbc_block_encrypt(St, RoundKey, IV, << Block:128/bitstring, PlainText/bitstring >>, CipherText) -> Encrypted = aes_cipher(St, RoundKey, crypto:exor(Block, IV)), cbc_block_encrypt(St, RoundKey, Encrypted, PlainText, << CipherText/binary, Encrypted/binary >>). %%%------------------------------------------------------------------- %%% Internal GCM functions %%%------------------------------------------------------------------- %% @private gcm_block_decrypt(H, K, IV, A, C, T) -> Y0 = case bit_size(IV) of 96 -> << IV/binary, 1:32/unsigned-big-integer-unit:1 >>; _ -> gcm_ghash(H, <<>>, IV) end, KeyLen = bit_size(K), Cipher = list_to_atom("aes_" ++ integer_to_list(KeyLen) ++ "_ctr"), S0 = jose_crypto_compat:crypto_init(Cipher, K, Y0, true), {S1, EKY0xor} = jose_crypto_compat:crypto_update_encrypt(S0, Y0), EKY0 = crypto:exor(EKY0xor, Y0), << Y0int:128/unsigned-big-integer-unit:1 >> = Y0, Y1 = << (Y0int + 1):128/unsigned-big-integer-unit:1 >>, GHASH = gcm_ghash(H, A, C), TBits = bit_size(T), << TPrime:TBits/bitstring, _/bitstring >> = crypto:exor(GHASH, EKY0), case jose_jwa:constant_time_compare(T, TPrime) of false -> error; true -> P = gcm_exor(S1, Y1, C, <<>>), P end. %% @private gcm_block_encrypt(H, K, IV, A, P) -> Y0 = case bit_size(IV) of 96 -> << IV/binary, 1:32/unsigned-big-integer-unit:1 >>; _ -> gcm_ghash(H, <<>>, IV) end, KeyLen = bit_size(K), Cipher = list_to_atom("aes_" ++ integer_to_list(KeyLen) ++ "_ctr"), S0 = jose_crypto_compat:crypto_init(Cipher, K, Y0, true), {S1, EKY0xor} = jose_crypto_compat:crypto_update_encrypt(S0, Y0), EKY0 = crypto:exor(EKY0xor, Y0), << Y0int:128/unsigned-big-integer-unit:1 >> = Y0, Y1 = << (Y0int + 1):128/unsigned-big-integer-unit:1 >>, C = gcm_exor(S1, Y1, P, <<>>), GHASH = gcm_ghash(H, A, C), T = crypto:exor(GHASH, EKY0), {C, T}. %% @private gcm_exor(_S, _Y, <<>>, C) -> C; gcm_exor(S0, Y0, << B:128/bitstring, P/bitstring >>, C0) -> {S1, EKY0xor} = jose_crypto_compat:crypto_update_encrypt(S0, Y0), EKY0 = crypto:exor(EKY0xor, Y0), << Y0int:128/unsigned-big-integer-unit:1 >> = Y0, Y1 = << (Y0int + 1):128/unsigned-big-integer-unit:1 >>, C1 = << C0/binary, (crypto:exor(B, EKY0))/binary >>, gcm_exor(S1, Y1, P, C1); gcm_exor(S0, Y0, P, C0) -> PBits = bit_size(P), {_S1, EKY0xor} = jose_crypto_compat:crypto_update_encrypt(S0, Y0), << EKY0:PBits/bitstring, _/bitstring >> = crypto:exor(EKY0xor, Y0), C1 = << C0/binary, (crypto:exor(P, EKY0))/binary >>, C1. %% @private gcm_ghash(Key, AAD, CipherText) -> Data = << (gcm_pad(AAD))/binary, (gcm_pad(CipherText))/binary >>, K = crypto:bytes_to_integer(Key), gcm_ghash_block(K, AAD, CipherText, Data, << 0:128/unsigned-big-integer-unit:1 >>). %% @private gcm_ghash_block(K, AAD, CipherText, <<>>, GHash) -> AADBits = bit_size(AAD), CipherTextBits = bit_size(CipherText), GHashMask = << ((AADBits bsl 64) bor CipherTextBits):128/unsigned-big-integer-unit:1 >>, gcm_ghash_multiply(crypto:exor(GHash, GHashMask), K); gcm_ghash_block(K, AAD, CipherText, << Block:128/bitstring, Data/bitstring >>, GHash) -> gcm_ghash_block(K, AAD, CipherText, Data, gcm_ghash_multiply(crypto:exor(GHash, Block), K)). %% @private gcm_ghash_multiply(GHash, K) -> gcm_ghash_multiply(0, K, crypto:bytes_to_integer(GHash), 0). %% @private gcm_ghash_multiply(16, _K, _GHash, Result) -> << Result:128/unsigned-big-integer-unit:1 >>; gcm_ghash_multiply(I, K, GHash, Result) -> J = (GHash band 16#FF), Val = gf_2_128_mul(K, (J bsl (I * 8))), gcm_ghash_multiply(I + 1, K, GHash bsr 8, Result bxor Val). %% @private gcm_pad(Binary) when (byte_size(Binary) rem 16) =/= 0 -> PadBits = (16 - (byte_size(Binary) rem 16)) * 8, << Binary/binary, 0:PadBits >>; gcm_pad(Binary) -> Binary. %% @private gf_2_128_mul(X, Y) -> gf_2_128_mul(127, X, Y, 0). %% @private gf_2_128_mul(-1, _X, _Y, R) -> R; gf_2_128_mul(I, X0, Y, R0) -> R1 = (R0 bxor (X0 * ((Y bsr I) band 1))), X1 = (X0 bsr 1) bxor ((X0 band 1) * 16#E1000000000000000000000000000000), gf_2_128_mul(I - 1, X1, Y, R1). %%%------------------------------------------------------------------- %%% Internal ECB decrypt functions %%%------------------------------------------------------------------- %% @private ecb_block_decrypt(_St, _RoundKey, <<>>, PlainText) -> PlainText; ecb_block_decrypt(St, RoundKey, << Block:128/bitstring, CipherText/bitstring >>, PlainText) -> Decrypted = aes_inverse_cipher(St, RoundKey, Block), ecb_block_decrypt(St, RoundKey, CipherText, << PlainText/binary, Decrypted/binary >>). %%%------------------------------------------------------------------- %%% Internal ECB encrypt functions %%%------------------------------------------------------------------- %% @private ecb_block_encrypt(_St, _RoundKey, <<>>, CipherText) -> CipherText; ecb_block_encrypt(St, RoundKey, << Block:128/bitstring, PlainText/bitstring >>, CipherText) -> Encrypted = aes_cipher(St, RoundKey, Block), ecb_block_encrypt(St, RoundKey, PlainText, << CipherText/binary, Encrypted/binary >>). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private bget(B, Pos) when Pos >= 0 -> binary:at(B, Pos); bget(B, Pos) -> bget(B, byte_size(B) + Pos). %% @private bset(B, Pos, Val) when Pos =:= byte_size(B) -> << B/binary, Val >>; bset(<< _, B/binary >>, 0, Val) -> << Val, B/binary >>; bset(B, Pos, Val) when Pos >= 0 -> << Head:Pos/binary, _, Tail/binary >> = B, << Head/binary, Val, Tail/binary >>; bset(B, Pos, Val) -> bset(B, byte_size(B) + Pos, Val). %%%------------------------------------------------------------------- %%% AES constant functions %%%------------------------------------------------------------------- %% @private g2x(16#00) -> 16#00; g2x(16#01) -> 16#02; g2x(16#02) -> 16#04; g2x(16#03) -> 16#06; g2x(16#04) -> 16#08; g2x(16#05) -> 16#0A; g2x(16#06) -> 16#0C; g2x(16#07) -> 16#0E; g2x(16#08) -> 16#10; g2x(16#09) -> 16#12; g2x(16#0A) -> 16#14; g2x(16#0B) -> 16#16; g2x(16#0C) -> 16#18; g2x(16#0D) -> 16#1A; g2x(16#0E) -> 16#1C; g2x(16#0F) -> 16#1E; g2x(16#10) -> 16#20; g2x(16#11) -> 16#22; g2x(16#12) -> 16#24; g2x(16#13) -> 16#26; g2x(16#14) -> 16#28; g2x(16#15) -> 16#2A; g2x(16#16) -> 16#2C; g2x(16#17) -> 16#2E; g2x(16#18) -> 16#30; g2x(16#19) -> 16#32; g2x(16#1A) -> 16#34; g2x(16#1B) -> 16#36; g2x(16#1C) -> 16#38; g2x(16#1D) -> 16#3A; g2x(16#1E) -> 16#3C; g2x(16#1F) -> 16#3E; g2x(16#20) -> 16#40; g2x(16#21) -> 16#42; g2x(16#22) -> 16#44; g2x(16#23) -> 16#46; g2x(16#24) -> 16#48; g2x(16#25) -> 16#4A; g2x(16#26) -> 16#4C; g2x(16#27) -> 16#4E; g2x(16#28) -> 16#50; g2x(16#29) -> 16#52; g2x(16#2A) -> 16#54; g2x(16#2B) -> 16#56; g2x(16#2C) -> 16#58; g2x(16#2D) -> 16#5A; g2x(16#2E) -> 16#5C; g2x(16#2F) -> 16#5E; g2x(16#30) -> 16#60; g2x(16#31) -> 16#62; g2x(16#32) -> 16#64; g2x(16#33) -> 16#66; g2x(16#34) -> 16#68; g2x(16#35) -> 16#6A; g2x(16#36) -> 16#6C; g2x(16#37) -> 16#6E; g2x(16#38) -> 16#70; g2x(16#39) -> 16#72; g2x(16#3A) -> 16#74; g2x(16#3B) -> 16#76; g2x(16#3C) -> 16#78; g2x(16#3D) -> 16#7A; g2x(16#3E) -> 16#7C; g2x(16#3F) -> 16#7E; g2x(16#40) -> 16#80; g2x(16#41) -> 16#82; g2x(16#42) -> 16#84; g2x(16#43) -> 16#86; g2x(16#44) -> 16#88; g2x(16#45) -> 16#8A; g2x(16#46) -> 16#8C; g2x(16#47) -> 16#8E; g2x(16#48) -> 16#90; g2x(16#49) -> 16#92; g2x(16#4A) -> 16#94; g2x(16#4B) -> 16#96; g2x(16#4C) -> 16#98; g2x(16#4D) -> 16#9A; g2x(16#4E) -> 16#9C; g2x(16#4F) -> 16#9E; g2x(16#50) -> 16#A0; g2x(16#51) -> 16#A2; g2x(16#52) -> 16#A4; g2x(16#53) -> 16#A6; g2x(16#54) -> 16#A8; g2x(16#55) -> 16#AA; g2x(16#56) -> 16#AC; g2x(16#57) -> 16#AE; g2x(16#58) -> 16#B0; g2x(16#59) -> 16#B2; g2x(16#5A) -> 16#B4; g2x(16#5B) -> 16#B6; g2x(16#5C) -> 16#B8; g2x(16#5D) -> 16#BA; g2x(16#5E) -> 16#BC; g2x(16#5F) -> 16#BE; g2x(16#60) -> 16#C0; g2x(16#61) -> 16#C2; g2x(16#62) -> 16#C4; g2x(16#63) -> 16#C6; g2x(16#64) -> 16#C8; g2x(16#65) -> 16#CA; g2x(16#66) -> 16#CC; g2x(16#67) -> 16#CE; g2x(16#68) -> 16#D0; g2x(16#69) -> 16#D2; g2x(16#6A) -> 16#D4; g2x(16#6B) -> 16#D6; g2x(16#6C) -> 16#D8; g2x(16#6D) -> 16#DA; g2x(16#6E) -> 16#DC; g2x(16#6F) -> 16#DE; g2x(16#70) -> 16#E0; g2x(16#71) -> 16#E2; g2x(16#72) -> 16#E4; g2x(16#73) -> 16#E6; g2x(16#74) -> 16#E8; g2x(16#75) -> 16#EA; g2x(16#76) -> 16#EC; g2x(16#77) -> 16#EE; g2x(16#78) -> 16#F0; g2x(16#79) -> 16#F2; g2x(16#7A) -> 16#F4; g2x(16#7B) -> 16#F6; g2x(16#7C) -> 16#F8; g2x(16#7D) -> 16#FA; g2x(16#7E) -> 16#FC; g2x(16#7F) -> 16#FE; g2x(16#80) -> 16#1B; g2x(16#81) -> 16#19; g2x(16#82) -> 16#1F; g2x(16#83) -> 16#1D; g2x(16#84) -> 16#13; g2x(16#85) -> 16#11; g2x(16#86) -> 16#17; g2x(16#87) -> 16#15; g2x(16#88) -> 16#0B; g2x(16#89) -> 16#09; g2x(16#8A) -> 16#0F; g2x(16#8B) -> 16#0D; g2x(16#8C) -> 16#03; g2x(16#8D) -> 16#01; g2x(16#8E) -> 16#07; g2x(16#8F) -> 16#05; g2x(16#90) -> 16#3B; g2x(16#91) -> 16#39; g2x(16#92) -> 16#3F; g2x(16#93) -> 16#3D; g2x(16#94) -> 16#33; g2x(16#95) -> 16#31; g2x(16#96) -> 16#37; g2x(16#97) -> 16#35; g2x(16#98) -> 16#2B; g2x(16#99) -> 16#29; g2x(16#9A) -> 16#2F; g2x(16#9B) -> 16#2D; g2x(16#9C) -> 16#23; g2x(16#9D) -> 16#21; g2x(16#9E) -> 16#27; g2x(16#9F) -> 16#25; g2x(16#A0) -> 16#5B; g2x(16#A1) -> 16#59; g2x(16#A2) -> 16#5F; g2x(16#A3) -> 16#5D; g2x(16#A4) -> 16#53; g2x(16#A5) -> 16#51; g2x(16#A6) -> 16#57; g2x(16#A7) -> 16#55; g2x(16#A8) -> 16#4B; g2x(16#A9) -> 16#49; g2x(16#AA) -> 16#4F; g2x(16#AB) -> 16#4D; g2x(16#AC) -> 16#43; g2x(16#AD) -> 16#41; g2x(16#AE) -> 16#47; g2x(16#AF) -> 16#45; g2x(16#B0) -> 16#7B; g2x(16#B1) -> 16#79; g2x(16#B2) -> 16#7F; g2x(16#B3) -> 16#7D; g2x(16#B4) -> 16#73; g2x(16#B5) -> 16#71; g2x(16#B6) -> 16#77; g2x(16#B7) -> 16#75; g2x(16#B8) -> 16#6B; g2x(16#B9) -> 16#69; g2x(16#BA) -> 16#6F; g2x(16#BB) -> 16#6D; g2x(16#BC) -> 16#63; g2x(16#BD) -> 16#61; g2x(16#BE) -> 16#67; g2x(16#BF) -> 16#65; g2x(16#C0) -> 16#9B; g2x(16#C1) -> 16#99; g2x(16#C2) -> 16#9F; g2x(16#C3) -> 16#9D; g2x(16#C4) -> 16#93; g2x(16#C5) -> 16#91; g2x(16#C6) -> 16#97; g2x(16#C7) -> 16#95; g2x(16#C8) -> 16#8B; g2x(16#C9) -> 16#89; g2x(16#CA) -> 16#8F; g2x(16#CB) -> 16#8D; g2x(16#CC) -> 16#83; g2x(16#CD) -> 16#81; g2x(16#CE) -> 16#87; g2x(16#CF) -> 16#85; g2x(16#D0) -> 16#BB; g2x(16#D1) -> 16#B9; g2x(16#D2) -> 16#BF; g2x(16#D3) -> 16#BD; g2x(16#D4) -> 16#B3; g2x(16#D5) -> 16#B1; g2x(16#D6) -> 16#B7; g2x(16#D7) -> 16#B5; g2x(16#D8) -> 16#AB; g2x(16#D9) -> 16#A9; g2x(16#DA) -> 16#AF; g2x(16#DB) -> 16#AD; g2x(16#DC) -> 16#A3; g2x(16#DD) -> 16#A1; g2x(16#DE) -> 16#A7; g2x(16#DF) -> 16#A5; g2x(16#E0) -> 16#DB; g2x(16#E1) -> 16#D9; g2x(16#E2) -> 16#DF; g2x(16#E3) -> 16#DD; g2x(16#E4) -> 16#D3; g2x(16#E5) -> 16#D1; g2x(16#E6) -> 16#D7; g2x(16#E7) -> 16#D5; g2x(16#E8) -> 16#CB; g2x(16#E9) -> 16#C9; g2x(16#EA) -> 16#CF; g2x(16#EB) -> 16#CD; g2x(16#EC) -> 16#C3; g2x(16#ED) -> 16#C1; g2x(16#EE) -> 16#C7; g2x(16#EF) -> 16#C5; g2x(16#F0) -> 16#FB; g2x(16#F1) -> 16#F9; g2x(16#F2) -> 16#FF; g2x(16#F3) -> 16#FD; g2x(16#F4) -> 16#F3; g2x(16#F5) -> 16#F1; g2x(16#F6) -> 16#F7; g2x(16#F7) -> 16#F5; g2x(16#F8) -> 16#EB; g2x(16#F9) -> 16#E9; g2x(16#FA) -> 16#EF; g2x(16#FB) -> 16#ED; g2x(16#FC) -> 16#E3; g2x(16#FD) -> 16#E1; g2x(16#FE) -> 16#E7; g2x(16#FF) -> 16#E5. %% @private g3x(16#00) -> 16#00; g3x(16#01) -> 16#03; g3x(16#02) -> 16#06; g3x(16#03) -> 16#05; g3x(16#04) -> 16#0C; g3x(16#05) -> 16#0F; g3x(16#06) -> 16#0A; g3x(16#07) -> 16#09; g3x(16#08) -> 16#18; g3x(16#09) -> 16#1B; g3x(16#0A) -> 16#1E; g3x(16#0B) -> 16#1D; g3x(16#0C) -> 16#14; g3x(16#0D) -> 16#17; g3x(16#0E) -> 16#12; g3x(16#0F) -> 16#11; g3x(16#10) -> 16#30; g3x(16#11) -> 16#33; g3x(16#12) -> 16#36; g3x(16#13) -> 16#35; g3x(16#14) -> 16#3C; g3x(16#15) -> 16#3F; g3x(16#16) -> 16#3A; g3x(16#17) -> 16#39; g3x(16#18) -> 16#28; g3x(16#19) -> 16#2B; g3x(16#1A) -> 16#2E; g3x(16#1B) -> 16#2D; g3x(16#1C) -> 16#24; g3x(16#1D) -> 16#27; g3x(16#1E) -> 16#22; g3x(16#1F) -> 16#21; g3x(16#20) -> 16#60; g3x(16#21) -> 16#63; g3x(16#22) -> 16#66; g3x(16#23) -> 16#65; g3x(16#24) -> 16#6C; g3x(16#25) -> 16#6F; g3x(16#26) -> 16#6A; g3x(16#27) -> 16#69; g3x(16#28) -> 16#78; g3x(16#29) -> 16#7B; g3x(16#2A) -> 16#7E; g3x(16#2B) -> 16#7D; g3x(16#2C) -> 16#74; g3x(16#2D) -> 16#77; g3x(16#2E) -> 16#72; g3x(16#2F) -> 16#71; g3x(16#30) -> 16#50; g3x(16#31) -> 16#53; g3x(16#32) -> 16#56; g3x(16#33) -> 16#55; g3x(16#34) -> 16#5C; g3x(16#35) -> 16#5F; g3x(16#36) -> 16#5A; g3x(16#37) -> 16#59; g3x(16#38) -> 16#48; g3x(16#39) -> 16#4B; g3x(16#3A) -> 16#4E; g3x(16#3B) -> 16#4D; g3x(16#3C) -> 16#44; g3x(16#3D) -> 16#47; g3x(16#3E) -> 16#42; g3x(16#3F) -> 16#41; g3x(16#40) -> 16#C0; g3x(16#41) -> 16#C3; g3x(16#42) -> 16#C6; g3x(16#43) -> 16#C5; g3x(16#44) -> 16#CC; g3x(16#45) -> 16#CF; g3x(16#46) -> 16#CA; g3x(16#47) -> 16#C9; g3x(16#48) -> 16#D8; g3x(16#49) -> 16#DB; g3x(16#4A) -> 16#DE; g3x(16#4B) -> 16#DD; g3x(16#4C) -> 16#D4; g3x(16#4D) -> 16#D7; g3x(16#4E) -> 16#D2; g3x(16#4F) -> 16#D1; g3x(16#50) -> 16#F0; g3x(16#51) -> 16#F3; g3x(16#52) -> 16#F6; g3x(16#53) -> 16#F5; g3x(16#54) -> 16#FC; g3x(16#55) -> 16#FF; g3x(16#56) -> 16#FA; g3x(16#57) -> 16#F9; g3x(16#58) -> 16#E8; g3x(16#59) -> 16#EB; g3x(16#5A) -> 16#EE; g3x(16#5B) -> 16#ED; g3x(16#5C) -> 16#E4; g3x(16#5D) -> 16#E7; g3x(16#5E) -> 16#E2; g3x(16#5F) -> 16#E1; g3x(16#60) -> 16#A0; g3x(16#61) -> 16#A3; g3x(16#62) -> 16#A6; g3x(16#63) -> 16#A5; g3x(16#64) -> 16#AC; g3x(16#65) -> 16#AF; g3x(16#66) -> 16#AA; g3x(16#67) -> 16#A9; g3x(16#68) -> 16#B8; g3x(16#69) -> 16#BB; g3x(16#6A) -> 16#BE; g3x(16#6B) -> 16#BD; g3x(16#6C) -> 16#B4; g3x(16#6D) -> 16#B7; g3x(16#6E) -> 16#B2; g3x(16#6F) -> 16#B1; g3x(16#70) -> 16#90; g3x(16#71) -> 16#93; g3x(16#72) -> 16#96; g3x(16#73) -> 16#95; g3x(16#74) -> 16#9C; g3x(16#75) -> 16#9F; g3x(16#76) -> 16#9A; g3x(16#77) -> 16#99; g3x(16#78) -> 16#88; g3x(16#79) -> 16#8B; g3x(16#7A) -> 16#8E; g3x(16#7B) -> 16#8D; g3x(16#7C) -> 16#84; g3x(16#7D) -> 16#87; g3x(16#7E) -> 16#82; g3x(16#7F) -> 16#81; g3x(16#80) -> 16#9B; g3x(16#81) -> 16#98; g3x(16#82) -> 16#9D; g3x(16#83) -> 16#9E; g3x(16#84) -> 16#97; g3x(16#85) -> 16#94; g3x(16#86) -> 16#91; g3x(16#87) -> 16#92; g3x(16#88) -> 16#83; g3x(16#89) -> 16#80; g3x(16#8A) -> 16#85; g3x(16#8B) -> 16#86; g3x(16#8C) -> 16#8F; g3x(16#8D) -> 16#8C; g3x(16#8E) -> 16#89; g3x(16#8F) -> 16#8A; g3x(16#90) -> 16#AB; g3x(16#91) -> 16#A8; g3x(16#92) -> 16#AD; g3x(16#93) -> 16#AE; g3x(16#94) -> 16#A7; g3x(16#95) -> 16#A4; g3x(16#96) -> 16#A1; g3x(16#97) -> 16#A2; g3x(16#98) -> 16#B3; g3x(16#99) -> 16#B0; g3x(16#9A) -> 16#B5; g3x(16#9B) -> 16#B6; g3x(16#9C) -> 16#BF; g3x(16#9D) -> 16#BC; g3x(16#9E) -> 16#B9; g3x(16#9F) -> 16#BA; g3x(16#A0) -> 16#FB; g3x(16#A1) -> 16#F8; g3x(16#A2) -> 16#FD; g3x(16#A3) -> 16#FE; g3x(16#A4) -> 16#F7; g3x(16#A5) -> 16#F4; g3x(16#A6) -> 16#F1; g3x(16#A7) -> 16#F2; g3x(16#A8) -> 16#E3; g3x(16#A9) -> 16#E0; g3x(16#AA) -> 16#E5; g3x(16#AB) -> 16#E6; g3x(16#AC) -> 16#EF; g3x(16#AD) -> 16#EC; g3x(16#AE) -> 16#E9; g3x(16#AF) -> 16#EA; g3x(16#B0) -> 16#CB; g3x(16#B1) -> 16#C8; g3x(16#B2) -> 16#CD; g3x(16#B3) -> 16#CE; g3x(16#B4) -> 16#C7; g3x(16#B5) -> 16#C4; g3x(16#B6) -> 16#C1; g3x(16#B7) -> 16#C2; g3x(16#B8) -> 16#D3; g3x(16#B9) -> 16#D0; g3x(16#BA) -> 16#D5; g3x(16#BB) -> 16#D6; g3x(16#BC) -> 16#DF; g3x(16#BD) -> 16#DC; g3x(16#BE) -> 16#D9; g3x(16#BF) -> 16#DA; g3x(16#C0) -> 16#5B; g3x(16#C1) -> 16#58; g3x(16#C2) -> 16#5D; g3x(16#C3) -> 16#5E; g3x(16#C4) -> 16#57; g3x(16#C5) -> 16#54; g3x(16#C6) -> 16#51; g3x(16#C7) -> 16#52; g3x(16#C8) -> 16#43; g3x(16#C9) -> 16#40; g3x(16#CA) -> 16#45; g3x(16#CB) -> 16#46; g3x(16#CC) -> 16#4F; g3x(16#CD) -> 16#4C; g3x(16#CE) -> 16#49; g3x(16#CF) -> 16#4A; g3x(16#D0) -> 16#6B; g3x(16#D1) -> 16#68; g3x(16#D2) -> 16#6D; g3x(16#D3) -> 16#6E; g3x(16#D4) -> 16#67; g3x(16#D5) -> 16#64; g3x(16#D6) -> 16#61; g3x(16#D7) -> 16#62; g3x(16#D8) -> 16#73; g3x(16#D9) -> 16#70; g3x(16#DA) -> 16#75; g3x(16#DB) -> 16#76; g3x(16#DC) -> 16#7F; g3x(16#DD) -> 16#7C; g3x(16#DE) -> 16#79; g3x(16#DF) -> 16#7A; g3x(16#E0) -> 16#3B; g3x(16#E1) -> 16#38; g3x(16#E2) -> 16#3D; g3x(16#E3) -> 16#3E; g3x(16#E4) -> 16#37; g3x(16#E5) -> 16#34; g3x(16#E6) -> 16#31; g3x(16#E7) -> 16#32; g3x(16#E8) -> 16#23; g3x(16#E9) -> 16#20; g3x(16#EA) -> 16#25; g3x(16#EB) -> 16#26; g3x(16#EC) -> 16#2F; g3x(16#ED) -> 16#2C; g3x(16#EE) -> 16#29; g3x(16#EF) -> 16#2A; g3x(16#F0) -> 16#0B; g3x(16#F1) -> 16#08; g3x(16#F2) -> 16#0D; g3x(16#F3) -> 16#0E; g3x(16#F4) -> 16#07; g3x(16#F5) -> 16#04; g3x(16#F6) -> 16#01; g3x(16#F7) -> 16#02; g3x(16#F8) -> 16#13; g3x(16#F9) -> 16#10; g3x(16#FA) -> 16#15; g3x(16#FB) -> 16#16; g3x(16#FC) -> 16#1F; g3x(16#FD) -> 16#1C; g3x(16#FE) -> 16#19; g3x(16#FF) -> 16#1A. %% @private g9x(16#00) -> 16#00; g9x(16#01) -> 16#09; g9x(16#02) -> 16#12; g9x(16#03) -> 16#1B; g9x(16#04) -> 16#24; g9x(16#05) -> 16#2D; g9x(16#06) -> 16#36; g9x(16#07) -> 16#3F; g9x(16#08) -> 16#48; g9x(16#09) -> 16#41; g9x(16#0A) -> 16#5A; g9x(16#0B) -> 16#53; g9x(16#0C) -> 16#6C; g9x(16#0D) -> 16#65; g9x(16#0E) -> 16#7E; g9x(16#0F) -> 16#77; g9x(16#10) -> 16#90; g9x(16#11) -> 16#99; g9x(16#12) -> 16#82; g9x(16#13) -> 16#8B; g9x(16#14) -> 16#B4; g9x(16#15) -> 16#BD; g9x(16#16) -> 16#A6; g9x(16#17) -> 16#AF; g9x(16#18) -> 16#D8; g9x(16#19) -> 16#D1; g9x(16#1A) -> 16#CA; g9x(16#1B) -> 16#C3; g9x(16#1C) -> 16#FC; g9x(16#1D) -> 16#F5; g9x(16#1E) -> 16#EE; g9x(16#1F) -> 16#E7; g9x(16#20) -> 16#3B; g9x(16#21) -> 16#32; g9x(16#22) -> 16#29; g9x(16#23) -> 16#20; g9x(16#24) -> 16#1F; g9x(16#25) -> 16#16; g9x(16#26) -> 16#0D; g9x(16#27) -> 16#04; g9x(16#28) -> 16#73; g9x(16#29) -> 16#7A; g9x(16#2A) -> 16#61; g9x(16#2B) -> 16#68; g9x(16#2C) -> 16#57; g9x(16#2D) -> 16#5E; g9x(16#2E) -> 16#45; g9x(16#2F) -> 16#4C; g9x(16#30) -> 16#AB; g9x(16#31) -> 16#A2; g9x(16#32) -> 16#B9; g9x(16#33) -> 16#B0; g9x(16#34) -> 16#8F; g9x(16#35) -> 16#86; g9x(16#36) -> 16#9D; g9x(16#37) -> 16#94; g9x(16#38) -> 16#E3; g9x(16#39) -> 16#EA; g9x(16#3A) -> 16#F1; g9x(16#3B) -> 16#F8; g9x(16#3C) -> 16#C7; g9x(16#3D) -> 16#CE; g9x(16#3E) -> 16#D5; g9x(16#3F) -> 16#DC; g9x(16#40) -> 16#76; g9x(16#41) -> 16#7F; g9x(16#42) -> 16#64; g9x(16#43) -> 16#6D; g9x(16#44) -> 16#52; g9x(16#45) -> 16#5B; g9x(16#46) -> 16#40; g9x(16#47) -> 16#49; g9x(16#48) -> 16#3E; g9x(16#49) -> 16#37; g9x(16#4A) -> 16#2C; g9x(16#4B) -> 16#25; g9x(16#4C) -> 16#1A; g9x(16#4D) -> 16#13; g9x(16#4E) -> 16#08; g9x(16#4F) -> 16#01; g9x(16#50) -> 16#E6; g9x(16#51) -> 16#EF; g9x(16#52) -> 16#F4; g9x(16#53) -> 16#FD; g9x(16#54) -> 16#C2; g9x(16#55) -> 16#CB; g9x(16#56) -> 16#D0; g9x(16#57) -> 16#D9; g9x(16#58) -> 16#AE; g9x(16#59) -> 16#A7; g9x(16#5A) -> 16#BC; g9x(16#5B) -> 16#B5; g9x(16#5C) -> 16#8A; g9x(16#5D) -> 16#83; g9x(16#5E) -> 16#98; g9x(16#5F) -> 16#91; g9x(16#60) -> 16#4D; g9x(16#61) -> 16#44; g9x(16#62) -> 16#5F; g9x(16#63) -> 16#56; g9x(16#64) -> 16#69; g9x(16#65) -> 16#60; g9x(16#66) -> 16#7B; g9x(16#67) -> 16#72; g9x(16#68) -> 16#05; g9x(16#69) -> 16#0C; g9x(16#6A) -> 16#17; g9x(16#6B) -> 16#1E; g9x(16#6C) -> 16#21; g9x(16#6D) -> 16#28; g9x(16#6E) -> 16#33; g9x(16#6F) -> 16#3A; g9x(16#70) -> 16#DD; g9x(16#71) -> 16#D4; g9x(16#72) -> 16#CF; g9x(16#73) -> 16#C6; g9x(16#74) -> 16#F9; g9x(16#75) -> 16#F0; g9x(16#76) -> 16#EB; g9x(16#77) -> 16#E2; g9x(16#78) -> 16#95; g9x(16#79) -> 16#9C; g9x(16#7A) -> 16#87; g9x(16#7B) -> 16#8E; g9x(16#7C) -> 16#B1; g9x(16#7D) -> 16#B8; g9x(16#7E) -> 16#A3; g9x(16#7F) -> 16#AA; g9x(16#80) -> 16#EC; g9x(16#81) -> 16#E5; g9x(16#82) -> 16#FE; g9x(16#83) -> 16#F7; g9x(16#84) -> 16#C8; g9x(16#85) -> 16#C1; g9x(16#86) -> 16#DA; g9x(16#87) -> 16#D3; g9x(16#88) -> 16#A4; g9x(16#89) -> 16#AD; g9x(16#8A) -> 16#B6; g9x(16#8B) -> 16#BF; g9x(16#8C) -> 16#80; g9x(16#8D) -> 16#89; g9x(16#8E) -> 16#92; g9x(16#8F) -> 16#9B; g9x(16#90) -> 16#7C; g9x(16#91) -> 16#75; g9x(16#92) -> 16#6E; g9x(16#93) -> 16#67; g9x(16#94) -> 16#58; g9x(16#95) -> 16#51; g9x(16#96) -> 16#4A; g9x(16#97) -> 16#43; g9x(16#98) -> 16#34; g9x(16#99) -> 16#3D; g9x(16#9A) -> 16#26; g9x(16#9B) -> 16#2F; g9x(16#9C) -> 16#10; g9x(16#9D) -> 16#19; g9x(16#9E) -> 16#02; g9x(16#9F) -> 16#0B; g9x(16#A0) -> 16#D7; g9x(16#A1) -> 16#DE; g9x(16#A2) -> 16#C5; g9x(16#A3) -> 16#CC; g9x(16#A4) -> 16#F3; g9x(16#A5) -> 16#FA; g9x(16#A6) -> 16#E1; g9x(16#A7) -> 16#E8; g9x(16#A8) -> 16#9F; g9x(16#A9) -> 16#96; g9x(16#AA) -> 16#8D; g9x(16#AB) -> 16#84; g9x(16#AC) -> 16#BB; g9x(16#AD) -> 16#B2; g9x(16#AE) -> 16#A9; g9x(16#AF) -> 16#A0; g9x(16#B0) -> 16#47; g9x(16#B1) -> 16#4E; g9x(16#B2) -> 16#55; g9x(16#B3) -> 16#5C; g9x(16#B4) -> 16#63; g9x(16#B5) -> 16#6A; g9x(16#B6) -> 16#71; g9x(16#B7) -> 16#78; g9x(16#B8) -> 16#0F; g9x(16#B9) -> 16#06; g9x(16#BA) -> 16#1D; g9x(16#BB) -> 16#14; g9x(16#BC) -> 16#2B; g9x(16#BD) -> 16#22; g9x(16#BE) -> 16#39; g9x(16#BF) -> 16#30; g9x(16#C0) -> 16#9A; g9x(16#C1) -> 16#93; g9x(16#C2) -> 16#88; g9x(16#C3) -> 16#81; g9x(16#C4) -> 16#BE; g9x(16#C5) -> 16#B7; g9x(16#C6) -> 16#AC; g9x(16#C7) -> 16#A5; g9x(16#C8) -> 16#D2; g9x(16#C9) -> 16#DB; g9x(16#CA) -> 16#C0; g9x(16#CB) -> 16#C9; g9x(16#CC) -> 16#F6; g9x(16#CD) -> 16#FF; g9x(16#CE) -> 16#E4; g9x(16#CF) -> 16#ED; g9x(16#D0) -> 16#0A; g9x(16#D1) -> 16#03; g9x(16#D2) -> 16#18; g9x(16#D3) -> 16#11; g9x(16#D4) -> 16#2E; g9x(16#D5) -> 16#27; g9x(16#D6) -> 16#3C; g9x(16#D7) -> 16#35; g9x(16#D8) -> 16#42; g9x(16#D9) -> 16#4B; g9x(16#DA) -> 16#50; g9x(16#DB) -> 16#59; g9x(16#DC) -> 16#66; g9x(16#DD) -> 16#6F; g9x(16#DE) -> 16#74; g9x(16#DF) -> 16#7D; g9x(16#E0) -> 16#A1; g9x(16#E1) -> 16#A8; g9x(16#E2) -> 16#B3; g9x(16#E3) -> 16#BA; g9x(16#E4) -> 16#85; g9x(16#E5) -> 16#8C; g9x(16#E6) -> 16#97; g9x(16#E7) -> 16#9E; g9x(16#E8) -> 16#E9; g9x(16#E9) -> 16#E0; g9x(16#EA) -> 16#FB; g9x(16#EB) -> 16#F2; g9x(16#EC) -> 16#CD; g9x(16#ED) -> 16#C4; g9x(16#EE) -> 16#DF; g9x(16#EF) -> 16#D6; g9x(16#F0) -> 16#31; g9x(16#F1) -> 16#38; g9x(16#F2) -> 16#23; g9x(16#F3) -> 16#2A; g9x(16#F4) -> 16#15; g9x(16#F5) -> 16#1C; g9x(16#F6) -> 16#07; g9x(16#F7) -> 16#0E; g9x(16#F8) -> 16#79; g9x(16#F9) -> 16#70; g9x(16#FA) -> 16#6B; g9x(16#FB) -> 16#62; g9x(16#FC) -> 16#5D; g9x(16#FD) -> 16#54; g9x(16#FE) -> 16#4F; g9x(16#FF) -> 16#46. %% @private gbx(16#00) -> 16#00; gbx(16#01) -> 16#0B; gbx(16#02) -> 16#16; gbx(16#03) -> 16#1D; gbx(16#04) -> 16#2C; gbx(16#05) -> 16#27; gbx(16#06) -> 16#3A; gbx(16#07) -> 16#31; gbx(16#08) -> 16#58; gbx(16#09) -> 16#53; gbx(16#0A) -> 16#4E; gbx(16#0B) -> 16#45; gbx(16#0C) -> 16#74; gbx(16#0D) -> 16#7F; gbx(16#0E) -> 16#62; gbx(16#0F) -> 16#69; gbx(16#10) -> 16#B0; gbx(16#11) -> 16#BB; gbx(16#12) -> 16#A6; gbx(16#13) -> 16#AD; gbx(16#14) -> 16#9C; gbx(16#15) -> 16#97; gbx(16#16) -> 16#8A; gbx(16#17) -> 16#81; gbx(16#18) -> 16#E8; gbx(16#19) -> 16#E3; gbx(16#1A) -> 16#FE; gbx(16#1B) -> 16#F5; gbx(16#1C) -> 16#C4; gbx(16#1D) -> 16#CF; gbx(16#1E) -> 16#D2; gbx(16#1F) -> 16#D9; gbx(16#20) -> 16#7B; gbx(16#21) -> 16#70; gbx(16#22) -> 16#6D; gbx(16#23) -> 16#66; gbx(16#24) -> 16#57; gbx(16#25) -> 16#5C; gbx(16#26) -> 16#41; gbx(16#27) -> 16#4A; gbx(16#28) -> 16#23; gbx(16#29) -> 16#28; gbx(16#2A) -> 16#35; gbx(16#2B) -> 16#3E; gbx(16#2C) -> 16#0F; gbx(16#2D) -> 16#04; gbx(16#2E) -> 16#19; gbx(16#2F) -> 16#12; gbx(16#30) -> 16#CB; gbx(16#31) -> 16#C0; gbx(16#32) -> 16#DD; gbx(16#33) -> 16#D6; gbx(16#34) -> 16#E7; gbx(16#35) -> 16#EC; gbx(16#36) -> 16#F1; gbx(16#37) -> 16#FA; gbx(16#38) -> 16#93; gbx(16#39) -> 16#98; gbx(16#3A) -> 16#85; gbx(16#3B) -> 16#8E; gbx(16#3C) -> 16#BF; gbx(16#3D) -> 16#B4; gbx(16#3E) -> 16#A9; gbx(16#3F) -> 16#A2; gbx(16#40) -> 16#F6; gbx(16#41) -> 16#FD; gbx(16#42) -> 16#E0; gbx(16#43) -> 16#EB; gbx(16#44) -> 16#DA; gbx(16#45) -> 16#D1; gbx(16#46) -> 16#CC; gbx(16#47) -> 16#C7; gbx(16#48) -> 16#AE; gbx(16#49) -> 16#A5; gbx(16#4A) -> 16#B8; gbx(16#4B) -> 16#B3; gbx(16#4C) -> 16#82; gbx(16#4D) -> 16#89; gbx(16#4E) -> 16#94; gbx(16#4F) -> 16#9F; gbx(16#50) -> 16#46; gbx(16#51) -> 16#4D; gbx(16#52) -> 16#50; gbx(16#53) -> 16#5B; gbx(16#54) -> 16#6A; gbx(16#55) -> 16#61; gbx(16#56) -> 16#7C; gbx(16#57) -> 16#77; gbx(16#58) -> 16#1E; gbx(16#59) -> 16#15; gbx(16#5A) -> 16#08; gbx(16#5B) -> 16#03; gbx(16#5C) -> 16#32; gbx(16#5D) -> 16#39; gbx(16#5E) -> 16#24; gbx(16#5F) -> 16#2F; gbx(16#60) -> 16#8D; gbx(16#61) -> 16#86; gbx(16#62) -> 16#9B; gbx(16#63) -> 16#90; gbx(16#64) -> 16#A1; gbx(16#65) -> 16#AA; gbx(16#66) -> 16#B7; gbx(16#67) -> 16#BC; gbx(16#68) -> 16#D5; gbx(16#69) -> 16#DE; gbx(16#6A) -> 16#C3; gbx(16#6B) -> 16#C8; gbx(16#6C) -> 16#F9; gbx(16#6D) -> 16#F2; gbx(16#6E) -> 16#EF; gbx(16#6F) -> 16#E4; gbx(16#70) -> 16#3D; gbx(16#71) -> 16#36; gbx(16#72) -> 16#2B; gbx(16#73) -> 16#20; gbx(16#74) -> 16#11; gbx(16#75) -> 16#1A; gbx(16#76) -> 16#07; gbx(16#77) -> 16#0C; gbx(16#78) -> 16#65; gbx(16#79) -> 16#6E; gbx(16#7A) -> 16#73; gbx(16#7B) -> 16#78; gbx(16#7C) -> 16#49; gbx(16#7D) -> 16#42; gbx(16#7E) -> 16#5F; gbx(16#7F) -> 16#54; gbx(16#80) -> 16#F7; gbx(16#81) -> 16#FC; gbx(16#82) -> 16#E1; gbx(16#83) -> 16#EA; gbx(16#84) -> 16#DB; gbx(16#85) -> 16#D0; gbx(16#86) -> 16#CD; gbx(16#87) -> 16#C6; gbx(16#88) -> 16#AF; gbx(16#89) -> 16#A4; gbx(16#8A) -> 16#B9; gbx(16#8B) -> 16#B2; gbx(16#8C) -> 16#83; gbx(16#8D) -> 16#88; gbx(16#8E) -> 16#95; gbx(16#8F) -> 16#9E; gbx(16#90) -> 16#47; gbx(16#91) -> 16#4C; gbx(16#92) -> 16#51; gbx(16#93) -> 16#5A; gbx(16#94) -> 16#6B; gbx(16#95) -> 16#60; gbx(16#96) -> 16#7D; gbx(16#97) -> 16#76; gbx(16#98) -> 16#1F; gbx(16#99) -> 16#14; gbx(16#9A) -> 16#09; gbx(16#9B) -> 16#02; gbx(16#9C) -> 16#33; gbx(16#9D) -> 16#38; gbx(16#9E) -> 16#25; gbx(16#9F) -> 16#2E; gbx(16#A0) -> 16#8C; gbx(16#A1) -> 16#87; gbx(16#A2) -> 16#9A; gbx(16#A3) -> 16#91; gbx(16#A4) -> 16#A0; gbx(16#A5) -> 16#AB; gbx(16#A6) -> 16#B6; gbx(16#A7) -> 16#BD; gbx(16#A8) -> 16#D4; gbx(16#A9) -> 16#DF; gbx(16#AA) -> 16#C2; gbx(16#AB) -> 16#C9; gbx(16#AC) -> 16#F8; gbx(16#AD) -> 16#F3; gbx(16#AE) -> 16#EE; gbx(16#AF) -> 16#E5; gbx(16#B0) -> 16#3C; gbx(16#B1) -> 16#37; gbx(16#B2) -> 16#2A; gbx(16#B3) -> 16#21; gbx(16#B4) -> 16#10; gbx(16#B5) -> 16#1B; gbx(16#B6) -> 16#06; gbx(16#B7) -> 16#0D; gbx(16#B8) -> 16#64; gbx(16#B9) -> 16#6F; gbx(16#BA) -> 16#72; gbx(16#BB) -> 16#79; gbx(16#BC) -> 16#48; gbx(16#BD) -> 16#43; gbx(16#BE) -> 16#5E; gbx(16#BF) -> 16#55; gbx(16#C0) -> 16#01; gbx(16#C1) -> 16#0A; gbx(16#C2) -> 16#17; gbx(16#C3) -> 16#1C; gbx(16#C4) -> 16#2D; gbx(16#C5) -> 16#26; gbx(16#C6) -> 16#3B; gbx(16#C7) -> 16#30; gbx(16#C8) -> 16#59; gbx(16#C9) -> 16#52; gbx(16#CA) -> 16#4F; gbx(16#CB) -> 16#44; gbx(16#CC) -> 16#75; gbx(16#CD) -> 16#7E; gbx(16#CE) -> 16#63; gbx(16#CF) -> 16#68; gbx(16#D0) -> 16#B1; gbx(16#D1) -> 16#BA; gbx(16#D2) -> 16#A7; gbx(16#D3) -> 16#AC; gbx(16#D4) -> 16#9D; gbx(16#D5) -> 16#96; gbx(16#D6) -> 16#8B; gbx(16#D7) -> 16#80; gbx(16#D8) -> 16#E9; gbx(16#D9) -> 16#E2; gbx(16#DA) -> 16#FF; gbx(16#DB) -> 16#F4; gbx(16#DC) -> 16#C5; gbx(16#DD) -> 16#CE; gbx(16#DE) -> 16#D3; gbx(16#DF) -> 16#D8; gbx(16#E0) -> 16#7A; gbx(16#E1) -> 16#71; gbx(16#E2) -> 16#6C; gbx(16#E3) -> 16#67; gbx(16#E4) -> 16#56; gbx(16#E5) -> 16#5D; gbx(16#E6) -> 16#40; gbx(16#E7) -> 16#4B; gbx(16#E8) -> 16#22; gbx(16#E9) -> 16#29; gbx(16#EA) -> 16#34; gbx(16#EB) -> 16#3F; gbx(16#EC) -> 16#0E; gbx(16#ED) -> 16#05; gbx(16#EE) -> 16#18; gbx(16#EF) -> 16#13; gbx(16#F0) -> 16#CA; gbx(16#F1) -> 16#C1; gbx(16#F2) -> 16#DC; gbx(16#F3) -> 16#D7; gbx(16#F4) -> 16#E6; gbx(16#F5) -> 16#ED; gbx(16#F6) -> 16#F0; gbx(16#F7) -> 16#FB; gbx(16#F8) -> 16#92; gbx(16#F9) -> 16#99; gbx(16#FA) -> 16#84; gbx(16#FB) -> 16#8F; gbx(16#FC) -> 16#BE; gbx(16#FD) -> 16#B5; gbx(16#FE) -> 16#A8; gbx(16#FF) -> 16#A3. %% @private gdx(16#00) -> 16#00; gdx(16#01) -> 16#0D; gdx(16#02) -> 16#1A; gdx(16#03) -> 16#17; gdx(16#04) -> 16#34; gdx(16#05) -> 16#39; gdx(16#06) -> 16#2E; gdx(16#07) -> 16#23; gdx(16#08) -> 16#68; gdx(16#09) -> 16#65; gdx(16#0A) -> 16#72; gdx(16#0B) -> 16#7F; gdx(16#0C) -> 16#5C; gdx(16#0D) -> 16#51; gdx(16#0E) -> 16#46; gdx(16#0F) -> 16#4B; gdx(16#10) -> 16#D0; gdx(16#11) -> 16#DD; gdx(16#12) -> 16#CA; gdx(16#13) -> 16#C7; gdx(16#14) -> 16#E4; gdx(16#15) -> 16#E9; gdx(16#16) -> 16#FE; gdx(16#17) -> 16#F3; gdx(16#18) -> 16#B8; gdx(16#19) -> 16#B5; gdx(16#1A) -> 16#A2; gdx(16#1B) -> 16#AF; gdx(16#1C) -> 16#8C; gdx(16#1D) -> 16#81; gdx(16#1E) -> 16#96; gdx(16#1F) -> 16#9B; gdx(16#20) -> 16#BB; gdx(16#21) -> 16#B6; gdx(16#22) -> 16#A1; gdx(16#23) -> 16#AC; gdx(16#24) -> 16#8F; gdx(16#25) -> 16#82; gdx(16#26) -> 16#95; gdx(16#27) -> 16#98; gdx(16#28) -> 16#D3; gdx(16#29) -> 16#DE; gdx(16#2A) -> 16#C9; gdx(16#2B) -> 16#C4; gdx(16#2C) -> 16#E7; gdx(16#2D) -> 16#EA; gdx(16#2E) -> 16#FD; gdx(16#2F) -> 16#F0; gdx(16#30) -> 16#6B; gdx(16#31) -> 16#66; gdx(16#32) -> 16#71; gdx(16#33) -> 16#7C; gdx(16#34) -> 16#5F; gdx(16#35) -> 16#52; gdx(16#36) -> 16#45; gdx(16#37) -> 16#48; gdx(16#38) -> 16#03; gdx(16#39) -> 16#0E; gdx(16#3A) -> 16#19; gdx(16#3B) -> 16#14; gdx(16#3C) -> 16#37; gdx(16#3D) -> 16#3A; gdx(16#3E) -> 16#2D; gdx(16#3F) -> 16#20; gdx(16#40) -> 16#6D; gdx(16#41) -> 16#60; gdx(16#42) -> 16#77; gdx(16#43) -> 16#7A; gdx(16#44) -> 16#59; gdx(16#45) -> 16#54; gdx(16#46) -> 16#43; gdx(16#47) -> 16#4E; gdx(16#48) -> 16#05; gdx(16#49) -> 16#08; gdx(16#4A) -> 16#1F; gdx(16#4B) -> 16#12; gdx(16#4C) -> 16#31; gdx(16#4D) -> 16#3C; gdx(16#4E) -> 16#2B; gdx(16#4F) -> 16#26; gdx(16#50) -> 16#BD; gdx(16#51) -> 16#B0; gdx(16#52) -> 16#A7; gdx(16#53) -> 16#AA; gdx(16#54) -> 16#89; gdx(16#55) -> 16#84; gdx(16#56) -> 16#93; gdx(16#57) -> 16#9E; gdx(16#58) -> 16#D5; gdx(16#59) -> 16#D8; gdx(16#5A) -> 16#CF; gdx(16#5B) -> 16#C2; gdx(16#5C) -> 16#E1; gdx(16#5D) -> 16#EC; gdx(16#5E) -> 16#FB; gdx(16#5F) -> 16#F6; gdx(16#60) -> 16#D6; gdx(16#61) -> 16#DB; gdx(16#62) -> 16#CC; gdx(16#63) -> 16#C1; gdx(16#64) -> 16#E2; gdx(16#65) -> 16#EF; gdx(16#66) -> 16#F8; gdx(16#67) -> 16#F5; gdx(16#68) -> 16#BE; gdx(16#69) -> 16#B3; gdx(16#6A) -> 16#A4; gdx(16#6B) -> 16#A9; gdx(16#6C) -> 16#8A; gdx(16#6D) -> 16#87; gdx(16#6E) -> 16#90; gdx(16#6F) -> 16#9D; gdx(16#70) -> 16#06; gdx(16#71) -> 16#0B; gdx(16#72) -> 16#1C; gdx(16#73) -> 16#11; gdx(16#74) -> 16#32; gdx(16#75) -> 16#3F; gdx(16#76) -> 16#28; gdx(16#77) -> 16#25; gdx(16#78) -> 16#6E; gdx(16#79) -> 16#63; gdx(16#7A) -> 16#74; gdx(16#7B) -> 16#79; gdx(16#7C) -> 16#5A; gdx(16#7D) -> 16#57; gdx(16#7E) -> 16#40; gdx(16#7F) -> 16#4D; gdx(16#80) -> 16#DA; gdx(16#81) -> 16#D7; gdx(16#82) -> 16#C0; gdx(16#83) -> 16#CD; gdx(16#84) -> 16#EE; gdx(16#85) -> 16#E3; gdx(16#86) -> 16#F4; gdx(16#87) -> 16#F9; gdx(16#88) -> 16#B2; gdx(16#89) -> 16#BF; gdx(16#8A) -> 16#A8; gdx(16#8B) -> 16#A5; gdx(16#8C) -> 16#86; gdx(16#8D) -> 16#8B; gdx(16#8E) -> 16#9C; gdx(16#8F) -> 16#91; gdx(16#90) -> 16#0A; gdx(16#91) -> 16#07; gdx(16#92) -> 16#10; gdx(16#93) -> 16#1D; gdx(16#94) -> 16#3E; gdx(16#95) -> 16#33; gdx(16#96) -> 16#24; gdx(16#97) -> 16#29; gdx(16#98) -> 16#62; gdx(16#99) -> 16#6F; gdx(16#9A) -> 16#78; gdx(16#9B) -> 16#75; gdx(16#9C) -> 16#56; gdx(16#9D) -> 16#5B; gdx(16#9E) -> 16#4C; gdx(16#9F) -> 16#41; gdx(16#A0) -> 16#61; gdx(16#A1) -> 16#6C; gdx(16#A2) -> 16#7B; gdx(16#A3) -> 16#76; gdx(16#A4) -> 16#55; gdx(16#A5) -> 16#58; gdx(16#A6) -> 16#4F; gdx(16#A7) -> 16#42; gdx(16#A8) -> 16#09; gdx(16#A9) -> 16#04; gdx(16#AA) -> 16#13; gdx(16#AB) -> 16#1E; gdx(16#AC) -> 16#3D; gdx(16#AD) -> 16#30; gdx(16#AE) -> 16#27; gdx(16#AF) -> 16#2A; gdx(16#B0) -> 16#B1; gdx(16#B1) -> 16#BC; gdx(16#B2) -> 16#AB; gdx(16#B3) -> 16#A6; gdx(16#B4) -> 16#85; gdx(16#B5) -> 16#88; gdx(16#B6) -> 16#9F; gdx(16#B7) -> 16#92; gdx(16#B8) -> 16#D9; gdx(16#B9) -> 16#D4; gdx(16#BA) -> 16#C3; gdx(16#BB) -> 16#CE; gdx(16#BC) -> 16#ED; gdx(16#BD) -> 16#E0; gdx(16#BE) -> 16#F7; gdx(16#BF) -> 16#FA; gdx(16#C0) -> 16#B7; gdx(16#C1) -> 16#BA; gdx(16#C2) -> 16#AD; gdx(16#C3) -> 16#A0; gdx(16#C4) -> 16#83; gdx(16#C5) -> 16#8E; gdx(16#C6) -> 16#99; gdx(16#C7) -> 16#94; gdx(16#C8) -> 16#DF; gdx(16#C9) -> 16#D2; gdx(16#CA) -> 16#C5; gdx(16#CB) -> 16#C8; gdx(16#CC) -> 16#EB; gdx(16#CD) -> 16#E6; gdx(16#CE) -> 16#F1; gdx(16#CF) -> 16#FC; gdx(16#D0) -> 16#67; gdx(16#D1) -> 16#6A; gdx(16#D2) -> 16#7D; gdx(16#D3) -> 16#70; gdx(16#D4) -> 16#53; gdx(16#D5) -> 16#5E; gdx(16#D6) -> 16#49; gdx(16#D7) -> 16#44; gdx(16#D8) -> 16#0F; gdx(16#D9) -> 16#02; gdx(16#DA) -> 16#15; gdx(16#DB) -> 16#18; gdx(16#DC) -> 16#3B; gdx(16#DD) -> 16#36; gdx(16#DE) -> 16#21; gdx(16#DF) -> 16#2C; gdx(16#E0) -> 16#0C; gdx(16#E1) -> 16#01; gdx(16#E2) -> 16#16; gdx(16#E3) -> 16#1B; gdx(16#E4) -> 16#38; gdx(16#E5) -> 16#35; gdx(16#E6) -> 16#22; gdx(16#E7) -> 16#2F; gdx(16#E8) -> 16#64; gdx(16#E9) -> 16#69; gdx(16#EA) -> 16#7E; gdx(16#EB) -> 16#73; gdx(16#EC) -> 16#50; gdx(16#ED) -> 16#5D; gdx(16#EE) -> 16#4A; gdx(16#EF) -> 16#47; gdx(16#F0) -> 16#DC; gdx(16#F1) -> 16#D1; gdx(16#F2) -> 16#C6; gdx(16#F3) -> 16#CB; gdx(16#F4) -> 16#E8; gdx(16#F5) -> 16#E5; gdx(16#F6) -> 16#F2; gdx(16#F7) -> 16#FF; gdx(16#F8) -> 16#B4; gdx(16#F9) -> 16#B9; gdx(16#FA) -> 16#AE; gdx(16#FB) -> 16#A3; gdx(16#FC) -> 16#80; gdx(16#FD) -> 16#8D; gdx(16#FE) -> 16#9A; gdx(16#FF) -> 16#97. %% @private gex(16#00) -> 16#00; gex(16#01) -> 16#0E; gex(16#02) -> 16#1C; gex(16#03) -> 16#12; gex(16#04) -> 16#38; gex(16#05) -> 16#36; gex(16#06) -> 16#24; gex(16#07) -> 16#2A; gex(16#08) -> 16#70; gex(16#09) -> 16#7E; gex(16#0A) -> 16#6C; gex(16#0B) -> 16#62; gex(16#0C) -> 16#48; gex(16#0D) -> 16#46; gex(16#0E) -> 16#54; gex(16#0F) -> 16#5A; gex(16#10) -> 16#E0; gex(16#11) -> 16#EE; gex(16#12) -> 16#FC; gex(16#13) -> 16#F2; gex(16#14) -> 16#D8; gex(16#15) -> 16#D6; gex(16#16) -> 16#C4; gex(16#17) -> 16#CA; gex(16#18) -> 16#90; gex(16#19) -> 16#9E; gex(16#1A) -> 16#8C; gex(16#1B) -> 16#82; gex(16#1C) -> 16#A8; gex(16#1D) -> 16#A6; gex(16#1E) -> 16#B4; gex(16#1F) -> 16#BA; gex(16#20) -> 16#DB; gex(16#21) -> 16#D5; gex(16#22) -> 16#C7; gex(16#23) -> 16#C9; gex(16#24) -> 16#E3; gex(16#25) -> 16#ED; gex(16#26) -> 16#FF; gex(16#27) -> 16#F1; gex(16#28) -> 16#AB; gex(16#29) -> 16#A5; gex(16#2A) -> 16#B7; gex(16#2B) -> 16#B9; gex(16#2C) -> 16#93; gex(16#2D) -> 16#9D; gex(16#2E) -> 16#8F; gex(16#2F) -> 16#81; gex(16#30) -> 16#3B; gex(16#31) -> 16#35; gex(16#32) -> 16#27; gex(16#33) -> 16#29; gex(16#34) -> 16#03; gex(16#35) -> 16#0D; gex(16#36) -> 16#1F; gex(16#37) -> 16#11; gex(16#38) -> 16#4B; gex(16#39) -> 16#45; gex(16#3A) -> 16#57; gex(16#3B) -> 16#59; gex(16#3C) -> 16#73; gex(16#3D) -> 16#7D; gex(16#3E) -> 16#6F; gex(16#3F) -> 16#61; gex(16#40) -> 16#AD; gex(16#41) -> 16#A3; gex(16#42) -> 16#B1; gex(16#43) -> 16#BF; gex(16#44) -> 16#95; gex(16#45) -> 16#9B; gex(16#46) -> 16#89; gex(16#47) -> 16#87; gex(16#48) -> 16#DD; gex(16#49) -> 16#D3; gex(16#4A) -> 16#C1; gex(16#4B) -> 16#CF; gex(16#4C) -> 16#E5; gex(16#4D) -> 16#EB; gex(16#4E) -> 16#F9; gex(16#4F) -> 16#F7; gex(16#50) -> 16#4D; gex(16#51) -> 16#43; gex(16#52) -> 16#51; gex(16#53) -> 16#5F; gex(16#54) -> 16#75; gex(16#55) -> 16#7B; gex(16#56) -> 16#69; gex(16#57) -> 16#67; gex(16#58) -> 16#3D; gex(16#59) -> 16#33; gex(16#5A) -> 16#21; gex(16#5B) -> 16#2F; gex(16#5C) -> 16#05; gex(16#5D) -> 16#0B; gex(16#5E) -> 16#19; gex(16#5F) -> 16#17; gex(16#60) -> 16#76; gex(16#61) -> 16#78; gex(16#62) -> 16#6A; gex(16#63) -> 16#64; gex(16#64) -> 16#4E; gex(16#65) -> 16#40; gex(16#66) -> 16#52; gex(16#67) -> 16#5C; gex(16#68) -> 16#06; gex(16#69) -> 16#08; gex(16#6A) -> 16#1A; gex(16#6B) -> 16#14; gex(16#6C) -> 16#3E; gex(16#6D) -> 16#30; gex(16#6E) -> 16#22; gex(16#6F) -> 16#2C; gex(16#70) -> 16#96; gex(16#71) -> 16#98; gex(16#72) -> 16#8A; gex(16#73) -> 16#84; gex(16#74) -> 16#AE; gex(16#75) -> 16#A0; gex(16#76) -> 16#B2; gex(16#77) -> 16#BC; gex(16#78) -> 16#E6; gex(16#79) -> 16#E8; gex(16#7A) -> 16#FA; gex(16#7B) -> 16#F4; gex(16#7C) -> 16#DE; gex(16#7D) -> 16#D0; gex(16#7E) -> 16#C2; gex(16#7F) -> 16#CC; gex(16#80) -> 16#41; gex(16#81) -> 16#4F; gex(16#82) -> 16#5D; gex(16#83) -> 16#53; gex(16#84) -> 16#79; gex(16#85) -> 16#77; gex(16#86) -> 16#65; gex(16#87) -> 16#6B; gex(16#88) -> 16#31; gex(16#89) -> 16#3F; gex(16#8A) -> 16#2D; gex(16#8B) -> 16#23; gex(16#8C) -> 16#09; gex(16#8D) -> 16#07; gex(16#8E) -> 16#15; gex(16#8F) -> 16#1B; gex(16#90) -> 16#A1; gex(16#91) -> 16#AF; gex(16#92) -> 16#BD; gex(16#93) -> 16#B3; gex(16#94) -> 16#99; gex(16#95) -> 16#97; gex(16#96) -> 16#85; gex(16#97) -> 16#8B; gex(16#98) -> 16#D1; gex(16#99) -> 16#DF; gex(16#9A) -> 16#CD; gex(16#9B) -> 16#C3; gex(16#9C) -> 16#E9; gex(16#9D) -> 16#E7; gex(16#9E) -> 16#F5; gex(16#9F) -> 16#FB; gex(16#A0) -> 16#9A; gex(16#A1) -> 16#94; gex(16#A2) -> 16#86; gex(16#A3) -> 16#88; gex(16#A4) -> 16#A2; gex(16#A5) -> 16#AC; gex(16#A6) -> 16#BE; gex(16#A7) -> 16#B0; gex(16#A8) -> 16#EA; gex(16#A9) -> 16#E4; gex(16#AA) -> 16#F6; gex(16#AB) -> 16#F8; gex(16#AC) -> 16#D2; gex(16#AD) -> 16#DC; gex(16#AE) -> 16#CE; gex(16#AF) -> 16#C0; gex(16#B0) -> 16#7A; gex(16#B1) -> 16#74; gex(16#B2) -> 16#66; gex(16#B3) -> 16#68; gex(16#B4) -> 16#42; gex(16#B5) -> 16#4C; gex(16#B6) -> 16#5E; gex(16#B7) -> 16#50; gex(16#B8) -> 16#0A; gex(16#B9) -> 16#04; gex(16#BA) -> 16#16; gex(16#BB) -> 16#18; gex(16#BC) -> 16#32; gex(16#BD) -> 16#3C; gex(16#BE) -> 16#2E; gex(16#BF) -> 16#20; gex(16#C0) -> 16#EC; gex(16#C1) -> 16#E2; gex(16#C2) -> 16#F0; gex(16#C3) -> 16#FE; gex(16#C4) -> 16#D4; gex(16#C5) -> 16#DA; gex(16#C6) -> 16#C8; gex(16#C7) -> 16#C6; gex(16#C8) -> 16#9C; gex(16#C9) -> 16#92; gex(16#CA) -> 16#80; gex(16#CB) -> 16#8E; gex(16#CC) -> 16#A4; gex(16#CD) -> 16#AA; gex(16#CE) -> 16#B8; gex(16#CF) -> 16#B6; gex(16#D0) -> 16#0C; gex(16#D1) -> 16#02; gex(16#D2) -> 16#10; gex(16#D3) -> 16#1E; gex(16#D4) -> 16#34; gex(16#D5) -> 16#3A; gex(16#D6) -> 16#28; gex(16#D7) -> 16#26; gex(16#D8) -> 16#7C; gex(16#D9) -> 16#72; gex(16#DA) -> 16#60; gex(16#DB) -> 16#6E; gex(16#DC) -> 16#44; gex(16#DD) -> 16#4A; gex(16#DE) -> 16#58; gex(16#DF) -> 16#56; gex(16#E0) -> 16#37; gex(16#E1) -> 16#39; gex(16#E2) -> 16#2B; gex(16#E3) -> 16#25; gex(16#E4) -> 16#0F; gex(16#E5) -> 16#01; gex(16#E6) -> 16#13; gex(16#E7) -> 16#1D; gex(16#E8) -> 16#47; gex(16#E9) -> 16#49; gex(16#EA) -> 16#5B; gex(16#EB) -> 16#55; gex(16#EC) -> 16#7F; gex(16#ED) -> 16#71; gex(16#EE) -> 16#63; gex(16#EF) -> 16#6D; gex(16#F0) -> 16#D7; gex(16#F1) -> 16#D9; gex(16#F2) -> 16#CB; gex(16#F3) -> 16#C5; gex(16#F4) -> 16#EF; gex(16#F5) -> 16#E1; gex(16#F6) -> 16#F3; gex(16#F7) -> 16#FD; gex(16#F8) -> 16#A7; gex(16#F9) -> 16#A9; gex(16#FA) -> 16#BB; gex(16#FB) -> 16#B5; gex(16#FC) -> 16#9F; gex(16#FD) -> 16#91; gex(16#FE) -> 16#83; gex(16#FF) -> 16#8D. %% @private isBox(16#00) -> 16#52; isBox(16#01) -> 16#09; isBox(16#02) -> 16#6A; isBox(16#03) -> 16#D5; isBox(16#04) -> 16#30; isBox(16#05) -> 16#36; isBox(16#06) -> 16#A5; isBox(16#07) -> 16#38; isBox(16#08) -> 16#BF; isBox(16#09) -> 16#40; isBox(16#0A) -> 16#A3; isBox(16#0B) -> 16#9E; isBox(16#0C) -> 16#81; isBox(16#0D) -> 16#F3; isBox(16#0E) -> 16#D7; isBox(16#0F) -> 16#FB; isBox(16#10) -> 16#7C; isBox(16#11) -> 16#E3; isBox(16#12) -> 16#39; isBox(16#13) -> 16#82; isBox(16#14) -> 16#9B; isBox(16#15) -> 16#2F; isBox(16#16) -> 16#FF; isBox(16#17) -> 16#87; isBox(16#18) -> 16#34; isBox(16#19) -> 16#8E; isBox(16#1A) -> 16#43; isBox(16#1B) -> 16#44; isBox(16#1C) -> 16#C4; isBox(16#1D) -> 16#DE; isBox(16#1E) -> 16#E9; isBox(16#1F) -> 16#CB; isBox(16#20) -> 16#54; isBox(16#21) -> 16#7B; isBox(16#22) -> 16#94; isBox(16#23) -> 16#32; isBox(16#24) -> 16#A6; isBox(16#25) -> 16#C2; isBox(16#26) -> 16#23; isBox(16#27) -> 16#3D; isBox(16#28) -> 16#EE; isBox(16#29) -> 16#4C; isBox(16#2A) -> 16#95; isBox(16#2B) -> 16#0B; isBox(16#2C) -> 16#42; isBox(16#2D) -> 16#FA; isBox(16#2E) -> 16#C3; isBox(16#2F) -> 16#4E; isBox(16#30) -> 16#08; isBox(16#31) -> 16#2E; isBox(16#32) -> 16#A1; isBox(16#33) -> 16#66; isBox(16#34) -> 16#28; isBox(16#35) -> 16#D9; isBox(16#36) -> 16#24; isBox(16#37) -> 16#B2; isBox(16#38) -> 16#76; isBox(16#39) -> 16#5B; isBox(16#3A) -> 16#A2; isBox(16#3B) -> 16#49; isBox(16#3C) -> 16#6D; isBox(16#3D) -> 16#8B; isBox(16#3E) -> 16#D1; isBox(16#3F) -> 16#25; isBox(16#40) -> 16#72; isBox(16#41) -> 16#F8; isBox(16#42) -> 16#F6; isBox(16#43) -> 16#64; isBox(16#44) -> 16#86; isBox(16#45) -> 16#68; isBox(16#46) -> 16#98; isBox(16#47) -> 16#16; isBox(16#48) -> 16#D4; isBox(16#49) -> 16#A4; isBox(16#4A) -> 16#5C; isBox(16#4B) -> 16#CC; isBox(16#4C) -> 16#5D; isBox(16#4D) -> 16#65; isBox(16#4E) -> 16#B6; isBox(16#4F) -> 16#92; isBox(16#50) -> 16#6C; isBox(16#51) -> 16#70; isBox(16#52) -> 16#48; isBox(16#53) -> 16#50; isBox(16#54) -> 16#FD; isBox(16#55) -> 16#ED; isBox(16#56) -> 16#B9; isBox(16#57) -> 16#DA; isBox(16#58) -> 16#5E; isBox(16#59) -> 16#15; isBox(16#5A) -> 16#46; isBox(16#5B) -> 16#57; isBox(16#5C) -> 16#A7; isBox(16#5D) -> 16#8D; isBox(16#5E) -> 16#9D; isBox(16#5F) -> 16#84; isBox(16#60) -> 16#90; isBox(16#61) -> 16#D8; isBox(16#62) -> 16#AB; isBox(16#63) -> 16#00; isBox(16#64) -> 16#8C; isBox(16#65) -> 16#BC; isBox(16#66) -> 16#D3; isBox(16#67) -> 16#0A; isBox(16#68) -> 16#F7; isBox(16#69) -> 16#E4; isBox(16#6A) -> 16#58; isBox(16#6B) -> 16#05; isBox(16#6C) -> 16#B8; isBox(16#6D) -> 16#B3; isBox(16#6E) -> 16#45; isBox(16#6F) -> 16#06; isBox(16#70) -> 16#D0; isBox(16#71) -> 16#2C; isBox(16#72) -> 16#1E; isBox(16#73) -> 16#8F; isBox(16#74) -> 16#CA; isBox(16#75) -> 16#3F; isBox(16#76) -> 16#0F; isBox(16#77) -> 16#02; isBox(16#78) -> 16#C1; isBox(16#79) -> 16#AF; isBox(16#7A) -> 16#BD; isBox(16#7B) -> 16#03; isBox(16#7C) -> 16#01; isBox(16#7D) -> 16#13; isBox(16#7E) -> 16#8A; isBox(16#7F) -> 16#6B; isBox(16#80) -> 16#3A; isBox(16#81) -> 16#91; isBox(16#82) -> 16#11; isBox(16#83) -> 16#41; isBox(16#84) -> 16#4F; isBox(16#85) -> 16#67; isBox(16#86) -> 16#DC; isBox(16#87) -> 16#EA; isBox(16#88) -> 16#97; isBox(16#89) -> 16#F2; isBox(16#8A) -> 16#CF; isBox(16#8B) -> 16#CE; isBox(16#8C) -> 16#F0; isBox(16#8D) -> 16#B4; isBox(16#8E) -> 16#E6; isBox(16#8F) -> 16#73; isBox(16#90) -> 16#96; isBox(16#91) -> 16#AC; isBox(16#92) -> 16#74; isBox(16#93) -> 16#22; isBox(16#94) -> 16#E7; isBox(16#95) -> 16#AD; isBox(16#96) -> 16#35; isBox(16#97) -> 16#85; isBox(16#98) -> 16#E2; isBox(16#99) -> 16#F9; isBox(16#9A) -> 16#37; isBox(16#9B) -> 16#E8; isBox(16#9C) -> 16#1C; isBox(16#9D) -> 16#75; isBox(16#9E) -> 16#DF; isBox(16#9F) -> 16#6E; isBox(16#A0) -> 16#47; isBox(16#A1) -> 16#F1; isBox(16#A2) -> 16#1A; isBox(16#A3) -> 16#71; isBox(16#A4) -> 16#1D; isBox(16#A5) -> 16#29; isBox(16#A6) -> 16#C5; isBox(16#A7) -> 16#89; isBox(16#A8) -> 16#6F; isBox(16#A9) -> 16#B7; isBox(16#AA) -> 16#62; isBox(16#AB) -> 16#0E; isBox(16#AC) -> 16#AA; isBox(16#AD) -> 16#18; isBox(16#AE) -> 16#BE; isBox(16#AF) -> 16#1B; isBox(16#B0) -> 16#FC; isBox(16#B1) -> 16#56; isBox(16#B2) -> 16#3E; isBox(16#B3) -> 16#4B; isBox(16#B4) -> 16#C6; isBox(16#B5) -> 16#D2; isBox(16#B6) -> 16#79; isBox(16#B7) -> 16#20; isBox(16#B8) -> 16#9A; isBox(16#B9) -> 16#DB; isBox(16#BA) -> 16#C0; isBox(16#BB) -> 16#FE; isBox(16#BC) -> 16#78; isBox(16#BD) -> 16#CD; isBox(16#BE) -> 16#5A; isBox(16#BF) -> 16#F4; isBox(16#C0) -> 16#1F; isBox(16#C1) -> 16#DD; isBox(16#C2) -> 16#A8; isBox(16#C3) -> 16#33; isBox(16#C4) -> 16#88; isBox(16#C5) -> 16#07; isBox(16#C6) -> 16#C7; isBox(16#C7) -> 16#31; isBox(16#C8) -> 16#B1; isBox(16#C9) -> 16#12; isBox(16#CA) -> 16#10; isBox(16#CB) -> 16#59; isBox(16#CC) -> 16#27; isBox(16#CD) -> 16#80; isBox(16#CE) -> 16#EC; isBox(16#CF) -> 16#5F; isBox(16#D0) -> 16#60; isBox(16#D1) -> 16#51; isBox(16#D2) -> 16#7F; isBox(16#D3) -> 16#A9; isBox(16#D4) -> 16#19; isBox(16#D5) -> 16#B5; isBox(16#D6) -> 16#4A; isBox(16#D7) -> 16#0D; isBox(16#D8) -> 16#2D; isBox(16#D9) -> 16#E5; isBox(16#DA) -> 16#7A; isBox(16#DB) -> 16#9F; isBox(16#DC) -> 16#93; isBox(16#DD) -> 16#C9; isBox(16#DE) -> 16#9C; isBox(16#DF) -> 16#EF; isBox(16#E0) -> 16#A0; isBox(16#E1) -> 16#E0; isBox(16#E2) -> 16#3B; isBox(16#E3) -> 16#4D; isBox(16#E4) -> 16#AE; isBox(16#E5) -> 16#2A; isBox(16#E6) -> 16#F5; isBox(16#E7) -> 16#B0; isBox(16#E8) -> 16#C8; isBox(16#E9) -> 16#EB; isBox(16#EA) -> 16#BB; isBox(16#EB) -> 16#3C; isBox(16#EC) -> 16#83; isBox(16#ED) -> 16#53; isBox(16#EE) -> 16#99; isBox(16#EF) -> 16#61; isBox(16#F0) -> 16#17; isBox(16#F1) -> 16#2B; isBox(16#F2) -> 16#04; isBox(16#F3) -> 16#7E; isBox(16#F4) -> 16#BA; isBox(16#F5) -> 16#77; isBox(16#F6) -> 16#D6; isBox(16#F7) -> 16#26; isBox(16#F8) -> 16#E1; isBox(16#F9) -> 16#69; isBox(16#FA) -> 16#14; isBox(16#FB) -> 16#63; isBox(16#FC) -> 16#55; isBox(16#FD) -> 16#21; isBox(16#FE) -> 16#0C; isBox(16#FF) -> 16#7D. %% @private rcon(16#00) -> 16#8D; rcon(16#01) -> 16#01; rcon(16#02) -> 16#02; rcon(16#03) -> 16#04; rcon(16#04) -> 16#08; rcon(16#05) -> 16#10; rcon(16#06) -> 16#20; rcon(16#07) -> 16#40; rcon(16#08) -> 16#80; rcon(16#09) -> 16#1B; rcon(16#0A) -> 16#36; rcon(16#0B) -> 16#6C; rcon(16#0C) -> 16#D8; rcon(16#0D) -> 16#AB; rcon(16#0E) -> 16#4D; rcon(16#0F) -> 16#9A; rcon(16#10) -> 16#2F; rcon(16#11) -> 16#5E; rcon(16#12) -> 16#BC; rcon(16#13) -> 16#63; rcon(16#14) -> 16#C6; rcon(16#15) -> 16#97; rcon(16#16) -> 16#35; rcon(16#17) -> 16#6A; rcon(16#18) -> 16#D4; rcon(16#19) -> 16#B3; rcon(16#1A) -> 16#7D; rcon(16#1B) -> 16#FA; rcon(16#1C) -> 16#EF; rcon(16#1D) -> 16#C5; rcon(16#1E) -> 16#91; rcon(16#1F) -> 16#39; rcon(16#20) -> 16#72; rcon(16#21) -> 16#E4; rcon(16#22) -> 16#D3; rcon(16#23) -> 16#BD; rcon(16#24) -> 16#61; rcon(16#25) -> 16#C2; rcon(16#26) -> 16#9F; rcon(16#27) -> 16#25; rcon(16#28) -> 16#4A; rcon(16#29) -> 16#94; rcon(16#2A) -> 16#33; rcon(16#2B) -> 16#66; rcon(16#2C) -> 16#CC; rcon(16#2D) -> 16#83; rcon(16#2E) -> 16#1D; rcon(16#2F) -> 16#3A; rcon(16#30) -> 16#74; rcon(16#31) -> 16#E8; rcon(16#32) -> 16#CB; rcon(16#33) -> 16#8D; rcon(16#34) -> 16#01; rcon(16#35) -> 16#02; rcon(16#36) -> 16#04; rcon(16#37) -> 16#08; rcon(16#38) -> 16#10; rcon(16#39) -> 16#20; rcon(16#3A) -> 16#40; rcon(16#3B) -> 16#80; rcon(16#3C) -> 16#1B; rcon(16#3D) -> 16#36; rcon(16#3E) -> 16#6C; rcon(16#3F) -> 16#D8; rcon(16#40) -> 16#AB; rcon(16#41) -> 16#4D; rcon(16#42) -> 16#9A; rcon(16#43) -> 16#2F; rcon(16#44) -> 16#5E; rcon(16#45) -> 16#BC; rcon(16#46) -> 16#63; rcon(16#47) -> 16#C6; rcon(16#48) -> 16#97; rcon(16#49) -> 16#35; rcon(16#4A) -> 16#6A; rcon(16#4B) -> 16#D4; rcon(16#4C) -> 16#B3; rcon(16#4D) -> 16#7D; rcon(16#4E) -> 16#FA; rcon(16#4F) -> 16#EF; rcon(16#50) -> 16#C5; rcon(16#51) -> 16#91; rcon(16#52) -> 16#39; rcon(16#53) -> 16#72; rcon(16#54) -> 16#E4; rcon(16#55) -> 16#D3; rcon(16#56) -> 16#BD; rcon(16#57) -> 16#61; rcon(16#58) -> 16#C2; rcon(16#59) -> 16#9F; rcon(16#5A) -> 16#25; rcon(16#5B) -> 16#4A; rcon(16#5C) -> 16#94; rcon(16#5D) -> 16#33; rcon(16#5E) -> 16#66; rcon(16#5F) -> 16#CC; rcon(16#60) -> 16#83; rcon(16#61) -> 16#1D; rcon(16#62) -> 16#3A; rcon(16#63) -> 16#74; rcon(16#64) -> 16#E8; rcon(16#65) -> 16#CB; rcon(16#66) -> 16#8D; rcon(16#67) -> 16#01; rcon(16#68) -> 16#02; rcon(16#69) -> 16#04; rcon(16#6A) -> 16#08; rcon(16#6B) -> 16#10; rcon(16#6C) -> 16#20; rcon(16#6D) -> 16#40; rcon(16#6E) -> 16#80; rcon(16#6F) -> 16#1B; rcon(16#70) -> 16#36; rcon(16#71) -> 16#6C; rcon(16#72) -> 16#D8; rcon(16#73) -> 16#AB; rcon(16#74) -> 16#4D; rcon(16#75) -> 16#9A; rcon(16#76) -> 16#2F; rcon(16#77) -> 16#5E; rcon(16#78) -> 16#BC; rcon(16#79) -> 16#63; rcon(16#7A) -> 16#C6; rcon(16#7B) -> 16#97; rcon(16#7C) -> 16#35; rcon(16#7D) -> 16#6A; rcon(16#7E) -> 16#D4; rcon(16#7F) -> 16#B3; rcon(16#80) -> 16#7D; rcon(16#81) -> 16#FA; rcon(16#82) -> 16#EF; rcon(16#83) -> 16#C5; rcon(16#84) -> 16#91; rcon(16#85) -> 16#39; rcon(16#86) -> 16#72; rcon(16#87) -> 16#E4; rcon(16#88) -> 16#D3; rcon(16#89) -> 16#BD; rcon(16#8A) -> 16#61; rcon(16#8B) -> 16#C2; rcon(16#8C) -> 16#9F; rcon(16#8D) -> 16#25; rcon(16#8E) -> 16#4A; rcon(16#8F) -> 16#94; rcon(16#90) -> 16#33; rcon(16#91) -> 16#66; rcon(16#92) -> 16#CC; rcon(16#93) -> 16#83; rcon(16#94) -> 16#1D; rcon(16#95) -> 16#3A; rcon(16#96) -> 16#74; rcon(16#97) -> 16#E8; rcon(16#98) -> 16#CB; rcon(16#99) -> 16#8D; rcon(16#9A) -> 16#01; rcon(16#9B) -> 16#02; rcon(16#9C) -> 16#04; rcon(16#9D) -> 16#08; rcon(16#9E) -> 16#10; rcon(16#9F) -> 16#20; rcon(16#A0) -> 16#40; rcon(16#A1) -> 16#80; rcon(16#A2) -> 16#1B; rcon(16#A3) -> 16#36; rcon(16#A4) -> 16#6C; rcon(16#A5) -> 16#D8; rcon(16#A6) -> 16#AB; rcon(16#A7) -> 16#4D; rcon(16#A8) -> 16#9A; rcon(16#A9) -> 16#2F; rcon(16#AA) -> 16#5E; rcon(16#AB) -> 16#BC; rcon(16#AC) -> 16#63; rcon(16#AD) -> 16#C6; rcon(16#AE) -> 16#97; rcon(16#AF) -> 16#35; rcon(16#B0) -> 16#6A; rcon(16#B1) -> 16#D4; rcon(16#B2) -> 16#B3; rcon(16#B3) -> 16#7D; rcon(16#B4) -> 16#FA; rcon(16#B5) -> 16#EF; rcon(16#B6) -> 16#C5; rcon(16#B7) -> 16#91; rcon(16#B8) -> 16#39; rcon(16#B9) -> 16#72; rcon(16#BA) -> 16#E4; rcon(16#BB) -> 16#D3; rcon(16#BC) -> 16#BD; rcon(16#BD) -> 16#61; rcon(16#BE) -> 16#C2; rcon(16#BF) -> 16#9F; rcon(16#C0) -> 16#25; rcon(16#C1) -> 16#4A; rcon(16#C2) -> 16#94; rcon(16#C3) -> 16#33; rcon(16#C4) -> 16#66; rcon(16#C5) -> 16#CC; rcon(16#C6) -> 16#83; rcon(16#C7) -> 16#1D; rcon(16#C8) -> 16#3A; rcon(16#C9) -> 16#74; rcon(16#CA) -> 16#E8; rcon(16#CB) -> 16#CB; rcon(16#CC) -> 16#8D; rcon(16#CD) -> 16#01; rcon(16#CE) -> 16#02; rcon(16#CF) -> 16#04; rcon(16#D0) -> 16#08; rcon(16#D1) -> 16#10; rcon(16#D2) -> 16#20; rcon(16#D3) -> 16#40; rcon(16#D4) -> 16#80; rcon(16#D5) -> 16#1B; rcon(16#D6) -> 16#36; rcon(16#D7) -> 16#6C; rcon(16#D8) -> 16#D8; rcon(16#D9) -> 16#AB; rcon(16#DA) -> 16#4D; rcon(16#DB) -> 16#9A; rcon(16#DC) -> 16#2F; rcon(16#DD) -> 16#5E; rcon(16#DE) -> 16#BC; rcon(16#DF) -> 16#63; rcon(16#E0) -> 16#C6; rcon(16#E1) -> 16#97; rcon(16#E2) -> 16#35; rcon(16#E3) -> 16#6A; rcon(16#E4) -> 16#D4; rcon(16#E5) -> 16#B3; rcon(16#E6) -> 16#7D; rcon(16#E7) -> 16#FA; rcon(16#E8) -> 16#EF; rcon(16#E9) -> 16#C5; rcon(16#EA) -> 16#91; rcon(16#EB) -> 16#39; rcon(16#EC) -> 16#72; rcon(16#ED) -> 16#E4; rcon(16#EE) -> 16#D3; rcon(16#EF) -> 16#BD; rcon(16#F0) -> 16#61; rcon(16#F1) -> 16#C2; rcon(16#F2) -> 16#9F; rcon(16#F3) -> 16#25; rcon(16#F4) -> 16#4A; rcon(16#F5) -> 16#94; rcon(16#F6) -> 16#33; rcon(16#F7) -> 16#66; rcon(16#F8) -> 16#CC; rcon(16#F9) -> 16#83; rcon(16#FA) -> 16#1D; rcon(16#FB) -> 16#3A; rcon(16#FC) -> 16#74; rcon(16#FD) -> 16#E8; rcon(16#FE) -> 16#CB. %% @private sBox(16#00) -> 16#63; sBox(16#01) -> 16#7C; sBox(16#02) -> 16#77; sBox(16#03) -> 16#7B; sBox(16#04) -> 16#F2; sBox(16#05) -> 16#6B; sBox(16#06) -> 16#6F; sBox(16#07) -> 16#C5; sBox(16#08) -> 16#30; sBox(16#09) -> 16#01; sBox(16#0A) -> 16#67; sBox(16#0B) -> 16#2B; sBox(16#0C) -> 16#FE; sBox(16#0D) -> 16#D7; sBox(16#0E) -> 16#AB; sBox(16#0F) -> 16#76; sBox(16#10) -> 16#CA; sBox(16#11) -> 16#82; sBox(16#12) -> 16#C9; sBox(16#13) -> 16#7D; sBox(16#14) -> 16#FA; sBox(16#15) -> 16#59; sBox(16#16) -> 16#47; sBox(16#17) -> 16#F0; sBox(16#18) -> 16#AD; sBox(16#19) -> 16#D4; sBox(16#1A) -> 16#A2; sBox(16#1B) -> 16#AF; sBox(16#1C) -> 16#9C; sBox(16#1D) -> 16#A4; sBox(16#1E) -> 16#72; sBox(16#1F) -> 16#C0; sBox(16#20) -> 16#B7; sBox(16#21) -> 16#FD; sBox(16#22) -> 16#93; sBox(16#23) -> 16#26; sBox(16#24) -> 16#36; sBox(16#25) -> 16#3F; sBox(16#26) -> 16#F7; sBox(16#27) -> 16#CC; sBox(16#28) -> 16#34; sBox(16#29) -> 16#A5; sBox(16#2A) -> 16#E5; sBox(16#2B) -> 16#F1; sBox(16#2C) -> 16#71; sBox(16#2D) -> 16#D8; sBox(16#2E) -> 16#31; sBox(16#2F) -> 16#15; sBox(16#30) -> 16#04; sBox(16#31) -> 16#C7; sBox(16#32) -> 16#23; sBox(16#33) -> 16#C3; sBox(16#34) -> 16#18; sBox(16#35) -> 16#96; sBox(16#36) -> 16#05; sBox(16#37) -> 16#9A; sBox(16#38) -> 16#07; sBox(16#39) -> 16#12; sBox(16#3A) -> 16#80; sBox(16#3B) -> 16#E2; sBox(16#3C) -> 16#EB; sBox(16#3D) -> 16#27; sBox(16#3E) -> 16#B2; sBox(16#3F) -> 16#75; sBox(16#40) -> 16#09; sBox(16#41) -> 16#83; sBox(16#42) -> 16#2C; sBox(16#43) -> 16#1A; sBox(16#44) -> 16#1B; sBox(16#45) -> 16#6E; sBox(16#46) -> 16#5A; sBox(16#47) -> 16#A0; sBox(16#48) -> 16#52; sBox(16#49) -> 16#3B; sBox(16#4A) -> 16#D6; sBox(16#4B) -> 16#B3; sBox(16#4C) -> 16#29; sBox(16#4D) -> 16#E3; sBox(16#4E) -> 16#2F; sBox(16#4F) -> 16#84; sBox(16#50) -> 16#53; sBox(16#51) -> 16#D1; sBox(16#52) -> 16#00; sBox(16#53) -> 16#ED; sBox(16#54) -> 16#20; sBox(16#55) -> 16#FC; sBox(16#56) -> 16#B1; sBox(16#57) -> 16#5B; sBox(16#58) -> 16#6A; sBox(16#59) -> 16#CB; sBox(16#5A) -> 16#BE; sBox(16#5B) -> 16#39; sBox(16#5C) -> 16#4A; sBox(16#5D) -> 16#4C; sBox(16#5E) -> 16#58; sBox(16#5F) -> 16#CF; sBox(16#60) -> 16#D0; sBox(16#61) -> 16#EF; sBox(16#62) -> 16#AA; sBox(16#63) -> 16#FB; sBox(16#64) -> 16#43; sBox(16#65) -> 16#4D; sBox(16#66) -> 16#33; sBox(16#67) -> 16#85; sBox(16#68) -> 16#45; sBox(16#69) -> 16#F9; sBox(16#6A) -> 16#02; sBox(16#6B) -> 16#7F; sBox(16#6C) -> 16#50; sBox(16#6D) -> 16#3C; sBox(16#6E) -> 16#9F; sBox(16#6F) -> 16#A8; sBox(16#70) -> 16#51; sBox(16#71) -> 16#A3; sBox(16#72) -> 16#40; sBox(16#73) -> 16#8F; sBox(16#74) -> 16#92; sBox(16#75) -> 16#9D; sBox(16#76) -> 16#38; sBox(16#77) -> 16#F5; sBox(16#78) -> 16#BC; sBox(16#79) -> 16#B6; sBox(16#7A) -> 16#DA; sBox(16#7B) -> 16#21; sBox(16#7C) -> 16#10; sBox(16#7D) -> 16#FF; sBox(16#7E) -> 16#F3; sBox(16#7F) -> 16#D2; sBox(16#80) -> 16#CD; sBox(16#81) -> 16#0C; sBox(16#82) -> 16#13; sBox(16#83) -> 16#EC; sBox(16#84) -> 16#5F; sBox(16#85) -> 16#97; sBox(16#86) -> 16#44; sBox(16#87) -> 16#17; sBox(16#88) -> 16#C4; sBox(16#89) -> 16#A7; sBox(16#8A) -> 16#7E; sBox(16#8B) -> 16#3D; sBox(16#8C) -> 16#64; sBox(16#8D) -> 16#5D; sBox(16#8E) -> 16#19; sBox(16#8F) -> 16#73; sBox(16#90) -> 16#60; sBox(16#91) -> 16#81; sBox(16#92) -> 16#4F; sBox(16#93) -> 16#DC; sBox(16#94) -> 16#22; sBox(16#95) -> 16#2A; sBox(16#96) -> 16#90; sBox(16#97) -> 16#88; sBox(16#98) -> 16#46; sBox(16#99) -> 16#EE; sBox(16#9A) -> 16#B8; sBox(16#9B) -> 16#14; sBox(16#9C) -> 16#DE; sBox(16#9D) -> 16#5E; sBox(16#9E) -> 16#0B; sBox(16#9F) -> 16#DB; sBox(16#A0) -> 16#E0; sBox(16#A1) -> 16#32; sBox(16#A2) -> 16#3A; sBox(16#A3) -> 16#0A; sBox(16#A4) -> 16#49; sBox(16#A5) -> 16#06; sBox(16#A6) -> 16#24; sBox(16#A7) -> 16#5C; sBox(16#A8) -> 16#C2; sBox(16#A9) -> 16#D3; sBox(16#AA) -> 16#AC; sBox(16#AB) -> 16#62; sBox(16#AC) -> 16#91; sBox(16#AD) -> 16#95; sBox(16#AE) -> 16#E4; sBox(16#AF) -> 16#79; sBox(16#B0) -> 16#E7; sBox(16#B1) -> 16#C8; sBox(16#B2) -> 16#37; sBox(16#B3) -> 16#6D; sBox(16#B4) -> 16#8D; sBox(16#B5) -> 16#D5; sBox(16#B6) -> 16#4E; sBox(16#B7) -> 16#A9; sBox(16#B8) -> 16#6C; sBox(16#B9) -> 16#56; sBox(16#BA) -> 16#F4; sBox(16#BB) -> 16#EA; sBox(16#BC) -> 16#65; sBox(16#BD) -> 16#7A; sBox(16#BE) -> 16#AE; sBox(16#BF) -> 16#08; sBox(16#C0) -> 16#BA; sBox(16#C1) -> 16#78; sBox(16#C2) -> 16#25; sBox(16#C3) -> 16#2E; sBox(16#C4) -> 16#1C; sBox(16#C5) -> 16#A6; sBox(16#C6) -> 16#B4; sBox(16#C7) -> 16#C6; sBox(16#C8) -> 16#E8; sBox(16#C9) -> 16#DD; sBox(16#CA) -> 16#74; sBox(16#CB) -> 16#1F; sBox(16#CC) -> 16#4B; sBox(16#CD) -> 16#BD; sBox(16#CE) -> 16#8B; sBox(16#CF) -> 16#8A; sBox(16#D0) -> 16#70; sBox(16#D1) -> 16#3E; sBox(16#D2) -> 16#B5; sBox(16#D3) -> 16#66; sBox(16#D4) -> 16#48; sBox(16#D5) -> 16#03; sBox(16#D6) -> 16#F6; sBox(16#D7) -> 16#0E; sBox(16#D8) -> 16#61; sBox(16#D9) -> 16#35; sBox(16#DA) -> 16#57; sBox(16#DB) -> 16#B9; sBox(16#DC) -> 16#86; sBox(16#DD) -> 16#C1; sBox(16#DE) -> 16#1D; sBox(16#DF) -> 16#9E; sBox(16#E0) -> 16#E1; sBox(16#E1) -> 16#F8; sBox(16#E2) -> 16#98; sBox(16#E3) -> 16#11; sBox(16#E4) -> 16#69; sBox(16#E5) -> 16#D9; sBox(16#E6) -> 16#8E; sBox(16#E7) -> 16#94; sBox(16#E8) -> 16#9B; sBox(16#E9) -> 16#1E; sBox(16#EA) -> 16#87; sBox(16#EB) -> 16#E9; sBox(16#EC) -> 16#CE; sBox(16#ED) -> 16#55; sBox(16#EE) -> 16#28; sBox(16#EF) -> 16#DF; sBox(16#F0) -> 16#8C; sBox(16#F1) -> 16#A1; sBox(16#F2) -> 16#89; sBox(16#F3) -> 16#0D; sBox(16#F4) -> 16#BF; sBox(16#F5) -> 16#E6; sBox(16#F6) -> 16#42; sBox(16#F7) -> 16#68; sBox(16#F8) -> 16#41; sBox(16#F9) -> 16#99; sBox(16#FA) -> 16#2D; sBox(16#FB) -> 16#0F; sBox(16#FC) -> 16#B0; sBox(16#FD) -> 16#54; sBox(16#FE) -> 16#BB; sBox(16#FF) -> 16#16. potatosalad-erlang-jose-30ac019/src/jwa/curve448/0000775000232200023220000000000015267717064022076 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/src/jwa/curve448/jose_curve448_unsupported.erl0000664000232200023220000000403615267717064027661 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 02 Jan 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_curve448_unsupported). -behaviour(jose_curve448). %% jose_curve448 callbacks -export([eddsa_keypair/0]). -export([eddsa_keypair/1]). -export([eddsa_secret_to_public/1]). -export([ed448_sign/2]). -export([ed448_sign/3]). -export([ed448_verify/3]). -export([ed448_verify/4]). -export([ed448ph_sign/2]). -export([ed448ph_sign/3]). -export([ed448ph_verify/3]). -export([ed448ph_verify/4]). -export([x448_keypair/0]). -export([x448_keypair/1]). -export([x448_secret_to_public/1]). -export([x448_shared_secret/2]). %% Macros -define(unsupported, erlang:error(operation_not_supported)). %%==================================================================== %% jose_curve448 callbacks %%==================================================================== % EdDSA eddsa_keypair() -> ?unsupported. eddsa_keypair(_Seed) -> ?unsupported. eddsa_secret_to_public(_SecretKey) -> ?unsupported. % Ed448 ed448_sign(_Message, _SecretKey) -> ?unsupported. ed448_sign(_Message, _SecretKey, _Context) -> ?unsupported. ed448_verify(_Signature, _Message, _PublicKey) -> ?unsupported. ed448_verify(_Signature, _Message, _PublicKey, _Context) -> ?unsupported. % Ed448ph ed448ph_sign(_Message, _SecretKey) -> ?unsupported. ed448ph_sign(_Message, _SecretKey, _Context) -> ?unsupported. ed448ph_verify(_Signature, _Message, _PublicKey) -> ?unsupported. ed448ph_verify(_Signature, _Message, _PublicKey, _Context) -> ?unsupported. % X448 x448_keypair() -> ?unsupported. x448_keypair(_Seed) -> ?unsupported. x448_secret_to_public(_SecretKey) -> ?unsupported. x448_shared_secret(_MySecretKey, _YourPublicKey) -> ?unsupported. potatosalad-erlang-jose-30ac019/src/jwa/curve448/jose_curve448_fallback.erl0000664000232200023220000001077415267717064027036 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 31 Aug 2022 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_curve448_fallback). -behaviour(jose_curve448). %% jose_curve448 callbacks -export([eddsa_keypair/0]). -export([eddsa_keypair/1]). -export([eddsa_secret_to_public/1]). -export([ed448_sign/2]). -export([ed448_sign/3]). -export([ed448_verify/3]). -export([ed448_verify/4]). -export([ed448ph_sign/2]). -export([ed448ph_sign/3]). -export([ed448ph_verify/3]). -export([ed448ph_verify/4]). -export([x448_keypair/0]). -export([x448_keypair/1]). -export([x448_secret_to_public/1]). -export([x448_shared_secret/2]). %% Macros -define(MAYBE_FALLBACK_MOD, case jose:crypto_fallback() of true -> jose_jwa_curve448; false -> jose_curve448_unsupported end). %%==================================================================== %% jose_curve448 callbacks %%==================================================================== % EdDSA -spec eddsa_keypair() -> {jose_curve448:eddsa_public_key(), jose_curve448:eddsa_secret_key()}. eddsa_keypair() -> ?MAYBE_FALLBACK_MOD:eddsa_keypair(). -spec eddsa_keypair(jose_curve448:eddsa_seed()) -> {jose_curve448:eddsa_public_key(), jose_curve448:eddsa_secret_key()}. eddsa_keypair(Seed) -> ?MAYBE_FALLBACK_MOD:eddsa_keypair(Seed). -spec eddsa_secret_to_public(jose_curve448:eddsa_secret_key()) -> jose_curve448:eddsa_public_key(). eddsa_secret_to_public(SecretKey) -> ?MAYBE_FALLBACK_MOD:eddsa_secret_to_public(SecretKey). % Ed448 -spec ed448_sign(jose_curve448:message(), jose_curve448:eddsa_secret_key()) -> jose_curve448:signature(). ed448_sign(Message, SecretKey) -> ?MAYBE_FALLBACK_MOD:ed448_sign(Message, SecretKey). -spec ed448_sign(jose_curve448:message(), jose_curve448:eddsa_secret_key(), jose_curve448:context()) -> jose_curve448:signature(). ed448_sign(Message, SecretKey, Context) -> ?MAYBE_FALLBACK_MOD:ed448_sign(Message, SecretKey, Context). -spec ed448_verify(jose_curve448:maybe_invalid_signature(), jose_curve448:message(), jose_curve448:eddsa_public_key()) -> boolean(). ed448_verify(Signature, Message, PublicKey) -> ?MAYBE_FALLBACK_MOD:ed448_verify(Signature, Message, PublicKey). -spec ed448_verify(jose_curve448:maybe_invalid_signature(), jose_curve448:message(), jose_curve448:eddsa_public_key(), jose_curve448:context()) -> boolean(). ed448_verify(Signature, Message, PublicKey, Context) -> ?MAYBE_FALLBACK_MOD:ed448_verify(Signature, Message, PublicKey, Context). % Ed448ph -spec ed448ph_sign(jose_curve448:message(), jose_curve448:eddsa_secret_key()) -> jose_curve448:signature(). ed448ph_sign(Message, SecretKey) -> ?MAYBE_FALLBACK_MOD:ed448ph_sign(Message, SecretKey). -spec ed448ph_sign(jose_curve448:message(), jose_curve448:eddsa_secret_key(), jose_curve448:context()) -> jose_curve448:signature(). ed448ph_sign(Message, SecretKey, Context) -> ?MAYBE_FALLBACK_MOD:ed448ph_sign(Message, SecretKey, Context). -spec ed448ph_verify(jose_curve448:maybe_invalid_signature(), jose_curve448:message(), jose_curve448:eddsa_secret_key()) -> boolean(). ed448ph_verify(Signature, Message, PublicKey) -> ?MAYBE_FALLBACK_MOD:ed448ph_verify(Signature, Message, PublicKey). -spec ed448ph_verify(jose_curve448:maybe_invalid_signature(), jose_curve448:message(), jose_curve448:eddsa_secret_key(), jose_curve448:context()) -> boolean(). ed448ph_verify(Signature, Message, PublicKey, Context) -> ?MAYBE_FALLBACK_MOD:ed448ph_verify(Signature, Message, PublicKey, Context). % X448 -spec x448_keypair() -> {jose_curve448:x448_public_key(), jose_curve448:x448_secret_key()}. x448_keypair() -> ?MAYBE_FALLBACK_MOD:x448_keypair(). -spec x448_keypair(jose_curve448:x448_seed()) -> {jose_curve448:x448_public_key(), jose_curve448:x448_secret_key()}. x448_keypair(Seed) -> ?MAYBE_FALLBACK_MOD:x448_keypair(Seed). -spec x448_secret_to_public(jose_curve448:x448_secret_key()) -> jose_curve448:x448_public_key(). x448_secret_to_public(SecretKey) -> ?MAYBE_FALLBACK_MOD:x448_secret_to_public(SecretKey). -spec x448_shared_secret(MySecretKey :: jose_curve448:x448_secret_key(), YourPublicKey :: jose_curve448:x448_public_key()) -> jose_curve448:x448_shared_secret(). x448_shared_secret(MySecretKey, YourPublicKey) -> ?MAYBE_FALLBACK_MOD:x448_shared_secret(MySecretKey, YourPublicKey). potatosalad-erlang-jose-30ac019/src/jwa/curve448/jose_curve448.erl0000664000232200023220000001376215267717064025217 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 02 Jan 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_curve448). %% Types -type eddsa_public_key() :: <<_:456>>. -type eddsa_secret_key() :: <<_:912>>. -type eddsa_seed() :: <<_:456>>. -type message() :: binary(). -type signature() :: <<_:912>>. -type maybe_invalid_signature() :: signature() | binary(). -type context() :: binary(). -type x448_public_key() :: <<_:448>>. -type x448_secret_key() :: <<_:448>>. -type x448_seed() :: <<_:448>>. -type x448_shared_secret() :: <<_:448>>. -export_type([ eddsa_public_key/0, eddsa_secret_key/0, eddsa_seed/0, message/0, signature/0, maybe_invalid_signature/0, context/0, x448_public_key/0, x448_secret_key/0, x448_seed/0, x448_shared_secret/0 ]). -callback eddsa_keypair() -> {PublicKey::eddsa_public_key(), SecretKey::eddsa_secret_key()}. -callback eddsa_keypair(Seed::eddsa_seed()) -> {PublicKey::eddsa_public_key(), SecretKey::eddsa_secret_key()}. -callback eddsa_secret_to_public(SecretKey::eddsa_secret_key()) -> PublicKey::eddsa_public_key(). -callback ed448_sign(Message::message(), SecretKey::eddsa_secret_key()) -> Signature::signature(). -callback ed448_sign(Message::message(), SecretKey::eddsa_secret_key(), Context::context()) -> Signature::signature(). -callback ed448_verify(Signature::maybe_invalid_signature(), Message::message(), PublicKey::eddsa_public_key()) -> boolean(). -callback ed448_verify(Signature::maybe_invalid_signature(), Message::message(), PublicKey::eddsa_public_key(), Context::context()) -> boolean(). -callback ed448ph_sign(Message::message(), SecretKey::eddsa_secret_key()) -> Signature::signature(). -callback ed448ph_sign(Message::message(), SecretKey::eddsa_secret_key(), Context::context()) -> Signature::signature(). -callback ed448ph_verify(Signature::maybe_invalid_signature(), Message::message(), PublicKey::eddsa_public_key()) -> boolean(). -callback ed448ph_verify(Signature::maybe_invalid_signature(), Message::message(), PublicKey::eddsa_public_key(), Context::context()) -> boolean(). -callback x448_keypair() -> {PublicKey::eddsa_public_key(), SecretKey::eddsa_secret_key()}. -callback x448_keypair(Seed::x448_seed()) -> {PublicKey::x448_public_key(), SecretKey::x448_secret_key()}. -callback x448_secret_to_public(SecretKey::x448_secret_key()) -> PublicKey::x448_public_key(). -callback x448_shared_secret(MySecretKey::x448_secret_key(), YourPublicKey::x448_public_key()) -> SharedSecret::x448_shared_secret(). %% jose_curve448 callbacks -export([eddsa_keypair/0]). -export([eddsa_keypair/1]). -export([eddsa_secret_to_public/1]). -export([ed448_sign/2]). -export([ed448_sign/3]). -export([ed448_verify/3]). -export([ed448_verify/4]). -export([ed448ph_sign/2]). -export([ed448ph_sign/3]). -export([ed448ph_verify/3]). -export([ed448ph_verify/4]). -export([x448_keypair/0]). -export([x448_keypair/1]). -export([x448_secret_to_public/1]). -export([x448_shared_secret/2]). %% Macros -define(JOSE_CURVE448, (jose:curve448_module())). %%==================================================================== %% jose_curve448 callbacks %%==================================================================== % EdDSA -spec eddsa_keypair() -> {eddsa_public_key(), eddsa_secret_key()}. eddsa_keypair() -> ?JOSE_CURVE448:eddsa_keypair(). -spec eddsa_keypair(eddsa_seed()) -> {eddsa_public_key(), eddsa_secret_key()}. eddsa_keypair(Seed) -> ?JOSE_CURVE448:eddsa_keypair(Seed). -spec eddsa_secret_to_public(eddsa_secret_key()) -> eddsa_public_key(). eddsa_secret_to_public(SecretKey) -> ?JOSE_CURVE448:eddsa_secret_to_public(SecretKey). % Ed448 -spec ed448_sign(message(), eddsa_secret_key()) -> signature(). ed448_sign(Message, SecretKey) -> ?JOSE_CURVE448:ed448_sign(Message, SecretKey). -spec ed448_sign(message(), eddsa_secret_key(), context()) -> signature(). ed448_sign(Message, SecretKey, Context) -> ?JOSE_CURVE448:ed448_sign(Message, SecretKey, Context). -spec ed448_verify(maybe_invalid_signature(), message(), eddsa_public_key()) -> boolean(). ed448_verify(Signature, Message, PublicKey) -> ?JOSE_CURVE448:ed448_verify(Signature, Message, PublicKey). -spec ed448_verify(maybe_invalid_signature(), message(), eddsa_public_key(), context()) -> boolean(). ed448_verify(Signature, Message, PublicKey, Context) -> ?JOSE_CURVE448:ed448_verify(Signature, Message, PublicKey, Context). % Ed448ph -spec ed448ph_sign(message(), eddsa_secret_key()) -> signature(). ed448ph_sign(Message, SecretKey) -> ?JOSE_CURVE448:ed448ph_sign(Message, SecretKey). -spec ed448ph_sign(message(), eddsa_secret_key(), context()) -> signature(). ed448ph_sign(Message, SecretKey, Context) -> ?JOSE_CURVE448:ed448ph_sign(Message, SecretKey, Context). -spec ed448ph_verify(maybe_invalid_signature(), message(), eddsa_public_key()) -> boolean(). ed448ph_verify(Signature, Message, PublicKey) -> ?JOSE_CURVE448:ed448ph_verify(Signature, Message, PublicKey). -spec ed448ph_verify(maybe_invalid_signature(), message(), eddsa_public_key(), context()) -> boolean(). ed448ph_verify(Signature, Message, PublicKey, Context) -> ?JOSE_CURVE448:ed448ph_verify(Signature, Message, PublicKey, Context). % X448 -spec x448_keypair() -> {x448_public_key(), x448_secret_key()}. x448_keypair() -> ?JOSE_CURVE448:x448_keypair(). -spec x448_keypair(x448_seed()) -> {x448_public_key(), x448_secret_key()}. x448_keypair(Seed) -> ?JOSE_CURVE448:x448_keypair(Seed). -spec x448_secret_to_public(x448_secret_key()) -> x448_public_key(). x448_secret_to_public(SecretKey) -> ?JOSE_CURVE448:x448_secret_to_public(SecretKey). -spec x448_shared_secret(MySecretKey :: x448_secret_key(), YourPublicKey :: x448_public_key()) -> x448_shared_secret(). x448_shared_secret(MySecretKey, YourPublicKey) -> ?JOSE_CURVE448:x448_shared_secret(MySecretKey, YourPublicKey). potatosalad-erlang-jose-30ac019/src/jwa/curve448/jose_curve448_crypto.erl0000664000232200023220000000553015267717064026611 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Brett Beatty %%% @copyright 2014-2022, Andrew Bennett, 2021, Brett Beatty %%% @doc %%% %%% @end %%% Created : 22 Oct 2021 by Brett Beatty %%%------------------------------------------------------------------- -module(jose_curve448_crypto). -behaviour(jose_curve448). %% jose_curve448 callbacks -export([eddsa_keypair/0]). -export([eddsa_keypair/1]). -export([eddsa_secret_to_public/1]). -export([ed448_sign/2]). -export([ed448_sign/3]). -export([ed448_verify/3]). -export([ed448_verify/4]). -export([ed448ph_sign/2]). -export([ed448ph_sign/3]). -export([ed448ph_verify/3]). -export([ed448ph_verify/4]). -export([x448_keypair/0]). -export([x448_keypair/1]). -export([x448_secret_to_public/1]). -export([x448_shared_secret/2]). %% Macros -define(FALLBACK_MOD, jose_curve448_fallback). %%==================================================================== %% jose_curve448 callbacks %%==================================================================== % EdDSA eddsa_keypair() -> {PublicKey, Secret} = crypto:generate_key(eddsa, ed448), {PublicKey, <>}. eddsa_keypair(<>) -> {PublicKey, Secret} = crypto:generate_key(eddsa, ed448, Secret), {PublicKey, <>}. eddsa_secret_to_public(<>) -> {PublicKey, _} = crypto:generate_key(eddsa, ed448, Secret), PublicKey. % Ed448 ed448_sign(Message, <>) -> crypto:sign(eddsa, none, Message, [Secret, ed448]). ed448_sign(Message, SecretKey, Context) -> ?FALLBACK_MOD:ed448_sign(Message, SecretKey, Context). ed448_verify(Signature, Message, <>) -> crypto:verify(eddsa, none, Message, Signature, [PublicKey, ed448]). ed448_verify(Signature, Message, PublicKey, Context) -> ?FALLBACK_MOD:ed448_verify(Signature, Message, PublicKey, Context). % Ed448ph ed448ph_sign(Message, SecretKey) -> ?FALLBACK_MOD:ed448ph_sign(Message, SecretKey). ed448ph_sign(Message, SecretKey, Context) -> ?FALLBACK_MOD:ed448ph_sign(Message, SecretKey, Context). ed448ph_verify(Signature, Message, PublicKey) -> ?FALLBACK_MOD:ed448ph_verify(Signature, Message, PublicKey). ed448ph_verify(Signature, Message, PublicKey, Context) -> ?FALLBACK_MOD:ed448ph_verify(Signature, Message, PublicKey, Context). % X448 x448_keypair() -> crypto:generate_key(ecdh, x448). x448_keypair(<>) -> crypto:generate_key(ecdh, x448, Secret). x448_secret_to_public(<>) -> {PublicKey, _} = crypto:generate_key(ecdh, x448, Secret), PublicKey. x448_shared_secret(MySecretKey, YourPublicKey) -> crypto:compute_key(ecdh, YourPublicKey, MySecretKey, x448). potatosalad-erlang-jose-30ac019/src/jwa/curve448/jose_curve448_libdecaf.erl0000664000232200023220000000475515267717064027032 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 01 Mar 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_curve448_libdecaf). -behaviour(jose_curve448). %% jose_curve448 callbacks -export([eddsa_keypair/0]). -export([eddsa_keypair/1]). -export([eddsa_secret_to_public/1]). -export([ed448_sign/2]). -export([ed448_sign/3]). -export([ed448_verify/3]). -export([ed448_verify/4]). -export([ed448ph_sign/2]). -export([ed448ph_sign/3]). -export([ed448ph_verify/3]). -export([ed448ph_verify/4]). -export([x448_keypair/0]). -export([x448_keypair/1]). -export([x448_secret_to_public/1]). -export([x448_shared_secret/2]). %%==================================================================== %% jose_curve448 callbacks %%==================================================================== % EdDSA eddsa_keypair() -> libdecaf_curve448:eddsa_keypair(). eddsa_keypair(Seed) -> libdecaf_curve448:eddsa_keypair(Seed). eddsa_secret_to_public(SecretKey) -> libdecaf_curve448:eddsa_secret_to_pk(SecretKey). % Ed448 ed448_sign(Message, SecretKey) -> libdecaf_curve448:ed448_sign(Message, SecretKey). ed448_sign(Message, SecretKey, Context) -> libdecaf_curve448:ed448_sign(Message, SecretKey, Context). ed448_verify(Signature, Message, PublicKey) -> libdecaf_curve448:ed448_verify(Signature, Message, PublicKey). ed448_verify(Signature, Message, PublicKey, Context) -> libdecaf_curve448:ed448_verify(Signature, Message, PublicKey, Context). % Ed448ph ed448ph_sign(Message, SecretKey) -> libdecaf_curve448:ed448ph_sign(Message, SecretKey). ed448ph_sign(Message, SecretKey, Context) -> libdecaf_curve448:ed448ph_sign(Message, SecretKey, Context). ed448ph_verify(Signature, Message, PublicKey) -> libdecaf_curve448:ed448ph_verify(Signature, Message, PublicKey). ed448ph_verify(Signature, Message, PublicKey, Context) -> libdecaf_curve448:ed448ph_verify(Signature, Message, PublicKey, Context). % X448 x448_keypair() -> libdecaf_curve448:x448_keypair(). x448_keypair(Seed) -> libdecaf_curve448:x448_keypair(Seed). x448_secret_to_public(SecretKey) -> libdecaf_curve448:x448(SecretKey). x448_shared_secret(MySecretKey, YourPublicKey) -> libdecaf_curve448:x448(MySecretKey, YourPublicKey). potatosalad-erlang-jose-30ac019/src/jwa/jose_jwa_aes_kw.erl0000664000232200023220000000640315267717064024353 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc Advanced Encryption Standard (AES) Key Wrap Algorithm %%% See RFC 3394 [https://tools.ietf.org/html/rfc3394] %%% @end %%% Created : 22 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwa_aes_kw). %% API -export([wrap/2]). -export([wrap/3]). -export([unwrap/2]). -export([unwrap/3]). -define(MSB64, 1/unsigned-big-integer-unit:64). -define(DEFAULT_IV, << 16#A6A6A6A6A6A6A6A6:?MSB64 >>). %%==================================================================== %% API functions %%==================================================================== wrap(PlainText, KEK) -> wrap(PlainText, KEK, ?DEFAULT_IV). wrap(PlainText, KEK, IV) when (byte_size(PlainText) rem 8) =:= 0 andalso (bit_size(KEK) =:= 128 orelse bit_size(KEK) =:= 192 orelse bit_size(KEK) =:= 256) -> Buffer = << IV/binary, PlainText/binary >>, BlockCount = (byte_size(Buffer) div 8) - 1, do_wrap(Buffer, 0, BlockCount, KEK). unwrap(CipherText, KEK) -> unwrap(CipherText, KEK, ?DEFAULT_IV). unwrap(CipherText, KEK, IV) when (byte_size(CipherText) rem 8) =:= 0 andalso (bit_size(KEK) =:= 128 orelse bit_size(KEK) =:= 192 orelse bit_size(KEK) =:= 256) -> BlockCount = (byte_size(CipherText) div 8) - 1, IVSize = byte_size(IV), case do_unwrap(CipherText, 5, BlockCount, KEK) of << IV:IVSize/binary, PlainText/binary >> -> PlainText; _ -> erlang:error({badarg, [CipherText, KEK, IV]}) end. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private do_wrap(Buffer, 6, _BlockCount, _KEK) -> Buffer; do_wrap(Buffer, J, BlockCount, KEK) -> do_wrap(do_wrap(Buffer, J, 1, BlockCount, KEK), J + 1, BlockCount, KEK). %% @private do_wrap(Buffer, _J, I, BlockCount, _KEK) when I > BlockCount -> Buffer; do_wrap(<< A0:8/binary, Rest/binary >>, J, I, BlockCount, KEK) -> HeadSize = (I - 1) * 8, << Head:HeadSize/binary, B0:8/binary, Tail/binary >> = Rest, Round = (BlockCount * J) + I, Data = << A0/binary, B0/binary >>, << A1:?MSB64, B1/binary >> = jose_jwa:block_encrypt({aes_ecb, bit_size(KEK)}, KEK, Data), A2 = A1 bxor Round, do_wrap(<< A2:?MSB64, Head/binary, B1/binary, Tail/binary >>, J, I + 1, BlockCount, KEK). %% @private do_unwrap(Buffer, J, _BlockCount, _KEK) when J < 0 -> Buffer; do_unwrap(Buffer, J, BlockCount, KEK) -> do_unwrap(do_unwrap(Buffer, J, BlockCount, BlockCount, KEK), J - 1, BlockCount, KEK). %% @private do_unwrap(Buffer, _J, I, _BlockCount, _KEK) when I < 1 -> Buffer; do_unwrap(<< A0:?MSB64, Rest/binary >>, J, I, BlockCount, KEK) -> HeadSize = (I - 1) * 8, << Head:HeadSize/binary, B0:8/binary, Tail/binary >> = Rest, Round = (BlockCount * J) + I, A1 = A0 bxor Round, Data = << A1:?MSB64, B0/binary >>, << A2:8/binary, B1/binary >> = jose_jwa:block_decrypt({aes_ecb, bit_size(KEK)}, KEK, Data), do_unwrap(<< A2/binary, Head/binary, B1/binary, Tail/binary >>, J, I - 1, BlockCount, KEK). potatosalad-erlang-jose-30ac019/src/jwa/jose_jwa_chacha20_poly1305.erl0000664000232200023220000000507315267717064026031 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc ChaCha20 and Poly1305 for IETF Protocols %%% See https://tools.ietf.org/html/rfc7539 %%% @end %%% Created : 08 Aug 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwa_chacha20_poly1305). -behaviour(jose_chacha20_poly1305). %% jose_chacha20_poly1305 callbacks -export([decrypt/5]). -export([encrypt/4]). -export([authenticate/3]). -export([verify/4]). %% Internal API -export([pad16/1]). -export([poly1305_key_gen/2]). %%==================================================================== %% jose_chacha20_poly1305 callbacks %%==================================================================== decrypt(CipherText, CipherTag, AAD, IV, CEK) -> OTK = poly1305_key_gen(CEK, IV), MacData = << AAD/binary, (pad16(AAD))/binary, CipherText/binary, (pad16(CipherText))/binary, (byte_size(AAD)):64/unsigned-little-integer-unit:1, (byte_size(CipherText)):64/unsigned-little-integer-unit:1 >>, Challenge = jose_jwa_poly1305:mac(MacData, OTK), case jose_jwa:constant_time_compare(CipherTag, Challenge) of true -> PlainText = jose_jwa_chacha20:encrypt(CEK, 1, IV, CipherText), PlainText; false -> error end. encrypt(PlainText, AAD, IV, CEK) -> OTK = poly1305_key_gen(CEK, IV), CipherText = jose_jwa_chacha20:encrypt(CEK, 1, IV, PlainText), MacData = << AAD/binary, (pad16(AAD))/binary, CipherText/binary, (pad16(CipherText))/binary, (byte_size(AAD)):64/unsigned-little-integer-unit:1, (byte_size(CipherText)):64/unsigned-little-integer-unit:1 >>, CipherTag = jose_jwa_poly1305:mac(MacData, OTK), {CipherText, CipherTag}. authenticate(Message, Key, Nonce) -> OTK = poly1305_key_gen(Key, Nonce), jose_jwa_poly1305:mac(Message, OTK). verify(MAC, Message, Key, Nonce) -> Challenge = authenticate(Message, Key, Nonce), jose_jwa:constant_time_compare(MAC, Challenge). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private pad16(X) when (byte_size(X) rem 16) == 0 -> <<>>; pad16(X) -> binary:copy(<< 0 >>, 16 - (byte_size(X) rem 16)). %% @private poly1305_key_gen(Key, Nonce) -> Counter = 0, << Block:32/binary, _/binary >> = jose_jwa_chacha20:block(Key, Counter, Nonce), Block. potatosalad-erlang-jose-30ac019/src/jwa/jose_jwa_poly1305.erl0000664000232200023220000000513515267717064024377 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc ChaCha20 and Poly1305 for IETF Protocols %%% See https://tools.ietf.org/html/rfc7539 %%% @end %%% Created : 31 May 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwa_poly1305). %% API -export([mac/2]). -export([mac_init/1]). -export([mac_update/2]). -export([mac_final/1]). %% Macros -define(math, jose_jwa_math). -define(clamp(R), R band 16#0ffffffc0ffffffc0ffffffc0fffffff). -define(p, 16#3fffffffffffffffffffffffffffffffb). %%==================================================================== %% API functions %%==================================================================== mac(M, K) when is_binary(M) andalso is_binary(K) andalso bit_size(K) =:= 256 -> mac_final(mac_update(mac_init(K), M)). mac_init(<< R:128/unsigned-little-integer-unit:1, S:128/unsigned-little-integer-unit:1 >>) -> A = 0, << (?clamp(R)):128/unsigned-little-integer-unit:1, A:136/unsigned-little-integer-unit:1, S:128/unsigned-little-integer-unit:1, 0:1656 >>. mac_update(C = << _:392, 0:1656 >>, <<>>) -> C; mac_update(<< R:128/unsigned-little-integer-unit:1, A:136/unsigned-little-integer-unit:1, S:128/unsigned-little-integer-unit:1, 0:1656 >>, << Block:128/bitstring, Rest/binary >>) -> << B:136/unsigned-little-integer-unit:1 >> = << Block:128/bitstring, 1:8/unsigned-little-integer-unit:1 >>, mac_update(<< R:128/unsigned-little-integer-unit:1, (?math:mod((A + B) * R, ?p)):136/unsigned-little-integer-unit:1, S:128/unsigned-little-integer-unit:1, 0:1656 >>, Rest); mac_update(<< R:128/unsigned-little-integer-unit:1, A:136/unsigned-little-integer-unit:1, S:128/unsigned-little-integer-unit:1, 0:1656 >>, << Block/binary >>) -> BlockBits = bit_size(Block), PadBits = 136 - BlockBits - 8, << B:136/unsigned-little-integer-unit:1 >> = << Block/binary, 1:8/unsigned-little-integer-unit:1, 0:PadBits/unsigned-little-integer-unit:1 >>, << R:128/unsigned-little-integer-unit:1, (?math:mod((A + B) * R, ?p)):136/unsigned-little-integer-unit:1, S:128/unsigned-little-integer-unit:1, 0:1656 >>. mac_final(<< _R:128/unsigned-little-integer-unit:1, A:136/unsigned-little-integer-unit:1, S:128/unsigned-little-integer-unit:1, 0:1656 >>) -> << (A + S):128/unsigned-little-integer-unit:1 >>. potatosalad-erlang-jose-30ac019/src/jwa/jose_jwa_concat_kdf.erl0000664000232200023220000001130715267717064025174 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc Concat KDF, as defined in Section 5.8.1 of NIST.800-56A %%% See NIST.800-56A: https://dx.doi.org/10.6028/NIST.SP.800-56Ar2 %%% @end %%% Created : 24 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwa_concat_kdf). %% API -export([kdf/3]). -export([kdf/4]). %%==================================================================== %% API functions %%==================================================================== kdf(Hash, Z, OtherInfo) -> HashFun = resolve_hash(Hash), KeyDataLen = bit_size(HashFun(<<>>)), kdf(HashFun, Z, OtherInfo, KeyDataLen). kdf(Hash, Z, OtherInfo, KeyDataLen) when is_function(Hash) andalso is_binary(Z) andalso is_binary(OtherInfo) andalso is_integer(KeyDataLen) -> HashLen = bit_size(Hash(<<>>)), Reps = ceiling(KeyDataLen / HashLen), case Reps of %% derive_key/6 counts up from 1, so it must never see Reps < 1. _ when KeyDataLen < 0 -> erlang:error({badarg, [Hash, Z, OtherInfo, KeyDataLen]}); 0 -> <<>>; 1 -> Concatenation = << 0, 0, 0, 1, Z/binary, OtherInfo/binary >>, << DerivedKey:KeyDataLen/bitstring, _/bitstring >> = Hash(Concatenation), DerivedKey; _ when Reps > 16#FFFFFFFF -> erlang:error({badarg, [Hash, Z, OtherInfo, KeyDataLen]}); _ -> derive_key(Hash, 1, Reps, KeyDataLen, << Z/binary, OtherInfo/binary >>, <<>>) end; kdf(Hash, Z, OtherInfo, KeyDataLen) when is_tuple(Hash) orelse is_atom(Hash) -> kdf(resolve_hash(Hash), Z, OtherInfo, KeyDataLen); kdf(Hash, Z, {AlgorithmID, PartyUInfo, PartyVInfo}, KeyDataLen) -> kdf(Hash, Z, {AlgorithmID, PartyUInfo, PartyVInfo, <<>>}, KeyDataLen); kdf(Hash, Z, {AlgorithmID, PartyUInfo, PartyVInfo, SuppPubInfo}, KeyDataLen) -> kdf(Hash, Z, {AlgorithmID, PartyUInfo, PartyVInfo, SuppPubInfo, <<>>}, KeyDataLen); kdf(Hash, Z, {AlgorithmID, PartyUInfo, PartyVInfo, SuppPubInfo, SuppPrivInfo}, KeyDataLen) when is_binary(AlgorithmID) andalso is_binary(PartyUInfo) andalso is_binary(PartyVInfo) andalso is_binary(SuppPubInfo) andalso is_binary(SuppPrivInfo) -> kdf(Hash, Z, << (byte_size(AlgorithmID)):1/unsigned-big-integer-unit:32, AlgorithmID/binary, (byte_size(PartyUInfo)):1/unsigned-big-integer-unit:32, PartyUInfo/binary, (byte_size(PartyVInfo)):1/unsigned-big-integer-unit:32, PartyVInfo/binary, SuppPubInfo/binary, SuppPrivInfo/binary >>, KeyDataLen); kdf(Hash, Z, {undefined, PartyUInfo, PartyVInfo, SuppPubInfo, SuppPrivInfo}, KeyDataLen) -> kdf(Hash, Z, {<<>>, PartyUInfo, PartyVInfo, SuppPubInfo, SuppPrivInfo}, KeyDataLen); kdf(Hash, Z, {AlgorithmID, undefined, PartyVInfo, SuppPubInfo, SuppPrivInfo}, KeyDataLen) -> kdf(Hash, Z, {AlgorithmID, <<>>, PartyVInfo, SuppPubInfo, SuppPrivInfo}, KeyDataLen); kdf(Hash, Z, {AlgorithmID, PartyUInfo, undefined, SuppPubInfo, SuppPrivInfo}, KeyDataLen) -> kdf(Hash, Z, {AlgorithmID, PartyUInfo, <<>>, SuppPubInfo, SuppPrivInfo}, KeyDataLen); kdf(Hash, Z, {AlgorithmID, PartyUInfo, PartyVInfo, undefined, SuppPrivInfo}, KeyDataLen) -> kdf(Hash, Z, {AlgorithmID, PartyUInfo, PartyVInfo, <<>>, SuppPrivInfo}, KeyDataLen); kdf(Hash, Z, {AlgorithmID, PartyUInfo, PartyVInfo, SuppPubInfo, undefined}, KeyDataLen) -> kdf(Hash, Z, {AlgorithmID, PartyUInfo, PartyVInfo, SuppPubInfo, <<>>}, KeyDataLen). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private ceiling(X) when X < 0 -> trunc(X); ceiling(X) -> T = trunc(X), case X - T == 0 of false -> T + 1; true -> T end. %% @private derive_key(Hash, Reps, Reps, KeyDataLen, ZOtherInfo, DerivedKeyingMaterial) -> Concatenation = << Reps:1/unsigned-big-integer-unit:32, ZOtherInfo/binary >>, << DerivedKey:KeyDataLen/bitstring, _/bitstring >> = << DerivedKeyingMaterial/binary, (Hash(Concatenation))/binary >>, DerivedKey; derive_key(Hash, Counter, Reps, KeyDataLen, ZOtherInfo, DerivedKeyingMaterial) -> Concatenation = << Counter:1/unsigned-big-integer-unit:32, ZOtherInfo/binary >>, derive_key(Hash, Counter + 1, Reps, KeyDataLen, ZOtherInfo, << DerivedKeyingMaterial/binary, (Hash(Concatenation))/binary >>). %% @private resolve_hash(HashFun) when is_function(HashFun) -> HashFun; resolve_hash(DigestType) when is_atom(DigestType) -> fun(Data) -> crypto:hash(DigestType, Data) end; resolve_hash({hmac, DigestType, Key}) when is_atom(DigestType) -> fun(Data) -> jose_crypto_compat:mac(hmac, DigestType, Key, Data) end. potatosalad-erlang-jose-30ac019/src/jwa/jose_jwa_pkcs1.erl0000664000232200023220000007441515267717064024133 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc PKCS #1: RSA Cryptography Specifications Version 2.1 %%% See RFC 3447: [https://tools.ietf.org/html/rfc3447] %%% @end %%% Created : 28 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwa_pkcs1). -include_lib("public_key/include/public_key.hrl"). %% Public Key API -export([decrypt_private/3]). -export([encrypt_public/3]). -export([sign/4]). -export([verify/5]). %% API -export([eme_oaep_decode/4]). -export([eme_oaep_encode/5]). -export([eme_pkcs1_decode/2]). -export([eme_pkcs1_encode/2]). -export([emsa_pkcs1_encode/4]). -export([emsa_pss_encode/3]). -export([emsa_pss_encode/4]). -export([emsa_pss_verify/4]). -export([emsa_pss_verify/5]). -export([mgf1/3]). -export([rsaes_oaep_decrypt/3]). -export([rsaes_oaep_decrypt/4]). -export([rsaes_oaep_encrypt/3]). -export([rsaes_oaep_encrypt/4]). -export([rsaes_oaep_encrypt/5]). -export([rsaes_pkcs1_decrypt/2]). -export([rsaes_pkcs1_encrypt/2]). -export([rsassa_pkcs1_sign/3]). -export([rsassa_pkcs1_sign/4]). -export([rsassa_pkcs1_verify/4]). -export([rsassa_pkcs1_verify/5]). -export([rsassa_pss_sign/3]). -export([rsassa_pss_sign/4]). -export([rsassa_pss_verify/4]). -export([rsassa_pss_verify/5]). %% Types -type rsa_digest_type() :: 'md5' | 'sha' | 'sha224' | 'sha256' | 'sha384' | 'sha512'. -type rsa_hash_fun() :: rsa_digest_type() | {hmac, rsa_digest_type(), iodata()} | fun((iodata()) -> binary()). -type rsa_public_key() :: #'RSAPublicKey'{}. -type rsa_private_key() :: #'RSAPrivateKey'{}. -define(PSS_TRAILER_FIELD, 16#BC). %%==================================================================== %% Public Key API functions %%==================================================================== decrypt_private(CipherText, RSAPrivateKey=#'RSAPrivateKey'{}, Options) when is_list(Options) -> case proplists:get_value(rsa_padding, Options) of rsa_pkcs1_oaep_padding -> Hash = proplists:get_value(rsa_oaep_md, Options, sha), Label = proplists:get_value(rsa_oaep_label, Options, <<>>), rsaes_oaep_decrypt(Hash, CipherText, Label, RSAPrivateKey); rsa_pkcs1_padding -> rsaes_pkcs1_decrypt(CipherText, RSAPrivateKey); _ -> erlang:error(notsup) end; decrypt_private(CipherText, PrivateKey, Options) -> erlang:error(badarg, [CipherText, PrivateKey, Options]). encrypt_public(PlainText, RSAPublicKey=#'RSAPublicKey'{}, Options) when is_list(Options) -> Res = case proplists:get_value(rsa_padding, Options) of rsa_pkcs1_oaep_padding -> Hash = proplists:get_value(rsa_oaep_md, Options, sha), Label = proplists:get_value(rsa_oaep_label, Options, <<>>), rsaes_oaep_encrypt(Hash, PlainText, Label, RSAPublicKey); rsa_pkcs1_padding -> rsaes_pkcs1_encrypt(PlainText, RSAPublicKey); _ -> erlang:error(notsup) end, case Res of {ok, Signature} -> Signature; {error, Reason} -> erlang:error(Reason) end; encrypt_public(PlainText, PublicKey, Options) -> erlang:error(badarg, [PlainText, PublicKey, Options]). sign(Message, DigestType, RSAPrivateKey=#'RSAPrivateKey'{}, Options) when is_list(Options) -> Res = case proplists:get_value(rsa_padding, Options) of rsa_pkcs1_pss_padding -> SaltLen = proplists:get_value(rsa_pss_saltlen, Options, -1), rsassa_pss_sign(DigestType, Message, SaltLen, RSAPrivateKey); rsa_pkcs1_padding -> rsassa_pkcs1_sign(DigestType, Message, RSAPrivateKey); _ -> erlang:error(notsup) end, case Res of {ok, Signature} -> Signature; {error, Reason} -> erlang:error(Reason) end; sign(Message, DigestType, PrivateKey, Options) -> erlang:error(badarg, [Message, DigestType, PrivateKey, Options]). verify(Message, DigestType, Signature, RSAPublicKey=#'RSAPublicKey'{}, Options) when is_list(Options) -> case proplists:get_value(rsa_padding, Options) of rsa_pkcs1_pss_padding -> SaltLen = proplists:get_value(rsa_pss_saltlen, Options, -1), rsassa_pss_verify(DigestType, Message, Signature, SaltLen, RSAPublicKey); rsa_pkcs1_padding -> rsassa_pkcs1_verify(DigestType, Message, Signature, RSAPublicKey); _ -> erlang:error(notsup) end; verify(Message, DigestType, Signature, PublicKey, Options) -> erlang:error(badarg, [Message, DigestType, Signature, PublicKey, Options]). %%==================================================================== %% API functions %%==================================================================== %% See [https://tools.ietf.org/html/rfc3447#section-7.1.2] -spec eme_oaep_decode(Hash, EM, Label, K) -> M | error when Hash :: rsa_hash_fun(), EM :: binary(), Label :: binary(), K :: integer(), M :: binary(). eme_oaep_decode(Hash, EM, Label, K) when is_function(Hash, 1) andalso is_binary(EM) andalso is_binary(Label) andalso is_integer(K) -> HLen = byte_size(Hash(<<>>)), LHash = Hash(Label), MaskedDBLen = K - HLen - 1, case EM of << Y, MaskedSeed:HLen/binary, MaskedDB:MaskedDBLen/binary >> -> case mgf1(Hash, MaskedDB, HLen) of {ok, SeedMask} -> Seed = crypto:exor(MaskedSeed, SeedMask), case mgf1(Hash, Seed, K - HLen - 1) of {ok, DBMask} -> DB = crypto:exor(MaskedDB, DBMask), case DB of << LHashPrime:HLen/binary, DBRight/binary >> -> case {Y, unpad_zero(DBRight), LHashPrime} of {16#00, << 16#01, M/binary >>, LHash} -> M; _BadPS -> error end; _BadDB -> error end; _DBMaskMGF1Error -> error end; _SeedMGF1Error -> error end; _BadEM -> error end; eme_oaep_decode(Hash, EM, Label, K) when is_tuple(Hash) orelse is_atom(Hash) -> HashFun = resolve_hash(Hash), eme_oaep_decode(HashFun, EM, Label, K). %% See [https://tools.ietf.org/html/rfc3447#section-7.1.1] -spec eme_oaep_encode(Hash, DM, Label, Seed, K) -> {ok, EM} | {error, Reason} when Hash :: rsa_hash_fun(), DM :: binary(), Label :: binary(), Seed :: binary(), K :: integer(), EM :: binary(), Reason :: term(). eme_oaep_encode(Hash, DM, Label, Seed, K) when is_function(Hash, 1) andalso is_binary(DM) andalso is_binary(Label) andalso is_binary(Seed) andalso is_integer(K) -> HLen = byte_size(Hash(<<>>)), MLen = byte_size(DM), LHash = Hash(Label), PSLen = ((K - MLen - (2 * HLen) - 2) * 8), PS = case PSLen > 0 of true -> << 0:PSLen >>; false -> <<>> end, DB = << LHash/binary, PS/binary, 16#01, DM/binary >>, case mgf1(Hash, Seed, K - HLen - 1) of {ok, DBMask} -> MaskedDB = crypto:exor(DB, DBMask), case mgf1(Hash, MaskedDB, HLen) of {ok, SeedMask} -> MaskedSeed = crypto:exor(Seed, SeedMask), EM = << 16#00, MaskedSeed/binary, MaskedDB/binary >>, {ok, EM}; MGF1SeedError -> MGF1SeedError end; MGF1Error -> MGF1Error end; eme_oaep_encode(Hash, DM, Label, Seed, K) when is_tuple(Hash) orelse is_atom(Hash) -> HashFun = resolve_hash(Hash), eme_oaep_encode(HashFun, DM, Label, Seed, K). %% See [https://tools.ietf.org/html/rfc3447#section-7.2.2] -spec eme_pkcs1_decode(EM, K) -> M | error when EM :: binary(), K :: integer(), M :: binary(). eme_pkcs1_decode(<< 16#00, 16#02, Rest/binary >>, K) when is_integer(K) -> case binary:split(Rest, << 16#00 >>) of [PS, M] when byte_size(PS) >= 8 -> M; _ -> error end; eme_pkcs1_decode(EM, K) when is_binary(EM) andalso is_integer(K) -> error. %% See [https://tools.ietf.org/html/rfc3447#section-7.2.1] -spec eme_pkcs1_encode(DM, K) -> {ok, EM} | {error, Reason} when DM :: binary(), K :: integer(), EM :: binary(), Reason :: term(). eme_pkcs1_encode(DM, K) when is_binary(DM) andalso is_integer(K) -> MLen = byte_size(DM), PSLen = K - MLen - 3, PS = non_zero_strong_random_bytes(PSLen), EM = << 16#00, 16#02, PS/binary, 16#00, DM/binary >>, {ok, EM}. %% See [https://tools.ietf.org/html/rfc3447#section-9.2] -spec emsa_pkcs1_encode(Hash, Algorithm, Message, EMBits) -> {ok, EM} | {error, Reason} when Hash :: rsa_hash_fun(), Algorithm :: md5 | sha | sha1 | sha256 | sha384 | sha512 | binary(), Message :: binary(), EMBits :: integer(), EM :: binary(), Reason :: term(). emsa_pkcs1_encode(Hash, Algorithm, Message, EMBits) when is_function(Hash, 1) andalso is_binary(Algorithm) andalso is_binary(Message) andalso is_integer(EMBits) -> H = Hash(Message), T = << Algorithm/binary, H/binary >>, TLen = byte_size(T), EMLen = ceiling(EMBits / 8), case EMLen < (TLen + 11) of false -> PSLen = EMLen - TLen - 3, PS = binary:copy(<< 16#FF >>, PSLen), EM = << 16#00, 16#01, PS/binary, 16#00, T/binary >>, {ok, EM}; true -> {error, modulus_too_short} end; emsa_pkcs1_encode(Hash, md5, Message, EMBits) -> Algorithm = << 16#30, 16#20, 16#30, 16#0c, 16#06, 16#08, 16#2a, 16#86, 16#48, 16#86, 16#f7, 16#0d, 16#02, 16#05, 16#05, 16#00, 16#04 >>, emsa_pkcs1_encode(Hash, Algorithm, Message, EMBits); emsa_pkcs1_encode(Hash, sha, Message, EMBits) -> emsa_pkcs1_encode(Hash, sha1, Message, EMBits); emsa_pkcs1_encode(Hash, sha1, Message, EMBits) -> Algorithm = << 16#30, 16#21, 16#30, 16#09, 16#06, 16#05, 16#2b, 16#0e, 16#03, 16#02, 16#1a, 16#05, 16#00, 16#04, 16#14 >>, emsa_pkcs1_encode(Hash, Algorithm, Message, EMBits); emsa_pkcs1_encode(Hash, sha256, Message, EMBits) -> Algorithm = << 16#30, 16#31, 16#30, 16#0d, 16#06, 16#09, 16#60, 16#86, 16#48, 16#01, 16#65, 16#03, 16#04, 16#02, 16#01, 16#05, 16#00, 16#04, 16#20 >>, emsa_pkcs1_encode(Hash, Algorithm, Message, EMBits); emsa_pkcs1_encode(Hash, sha384, Message, EMBits) -> Algorithm = << 16#30, 16#41, 16#30, 16#0d, 16#06, 16#09, 16#60, 16#86, 16#48, 16#01, 16#65, 16#03, 16#04, 16#02, 16#02, 16#05, 16#00, 16#04, 16#30 >>, emsa_pkcs1_encode(Hash, Algorithm, Message, EMBits); emsa_pkcs1_encode(Hash, sha512, Message, EMBits) -> Algorithm = << 16#30, 16#51, 16#30, 16#0d, 16#06, 16#09, 16#60, 16#86, 16#48, 16#01, 16#65, 16#03, 16#04, 16#02, 16#03, 16#05, 16#00, 16#04, 16#40 >>, emsa_pkcs1_encode(Hash, Algorithm, Message, EMBits); emsa_pkcs1_encode(Hash, Algorithm, Message, EMBits) when is_atom(Hash) -> HashFun = resolve_hash(Hash), emsa_pkcs1_encode(HashFun, Algorithm, Message, EMBits). %% See [https://tools.ietf.org/html/rfc3447#section-9.1.1] -spec emsa_pss_encode(Hash, Message, EMBits) -> {ok, EM} | {error, Reason} when Hash :: rsa_hash_fun(), Message :: binary(), EMBits :: integer(), EM :: binary(), Reason :: term(). emsa_pss_encode(Hash, Message, EMBits) when is_function(Hash, 1) andalso is_binary(Message) andalso is_integer(EMBits) -> emsa_pss_encode(Hash, Message, -2, EMBits); emsa_pss_encode(Hash, Message, EMBits) when is_tuple(Hash) orelse is_atom(Hash) -> HashFun = resolve_hash(Hash), emsa_pss_encode(HashFun, Message, EMBits). %% See [https://tools.ietf.org/html/rfc3447#section-9.1.1] -spec emsa_pss_encode(Hash, Message, Salt, EMBits) -> {ok, EM} | {error, Reason} when Hash :: rsa_hash_fun(), Message :: binary(), Salt :: binary() | integer(), EMBits :: integer(), EM :: binary(), Reason :: term(). emsa_pss_encode(Hash, Message, Salt, EMBits) when is_function(Hash, 1) andalso is_binary(Message) andalso is_binary(Salt) andalso is_integer(EMBits) -> MHash = Hash(Message), HashLen = byte_size(MHash), SaltLen = byte_size(Salt), EMLen = ceiling(EMBits / 8), case EMLen < (HashLen + SaltLen + 2) of false -> MPrime = << 0:64, MHash/binary, Salt/binary >>, H = Hash(MPrime), PS = << 0:((EMLen - SaltLen - HashLen - 2) * 8) >>, DB = << PS/binary, 16#01, Salt/binary >>, case mgf1(Hash, H, EMLen - HashLen - 1) of {ok, DBMask} -> LeftBits = (EMLen * 8) - EMBits, << _:LeftBits/bitstring, MaskedDBRight/bitstring >> = crypto:exor(DB, DBMask), MaskedDB = << 0:LeftBits, MaskedDBRight/bitstring >>, EM = << MaskedDB/binary, H/binary, ?PSS_TRAILER_FIELD >>, {ok, EM}; MGF1Error -> MGF1Error end; true -> {error, encoding_error} end; emsa_pss_encode(Hash, Message, -2, EMBits) when is_function(Hash, 1) andalso is_integer(EMBits) -> HashLen = byte_size(Hash(<<>>)), EMLen = ceiling(EMBits / 8), SaltLen = EMLen - HashLen - 2, case SaltLen < 0 of false -> emsa_pss_encode(Hash, Message, SaltLen, EMBits); true -> {error, encoding_error} end; emsa_pss_encode(Hash, Message, -1, EMBits) when is_function(Hash, 1) -> HashLen = byte_size(Hash(<<>>)), SaltLen = HashLen, emsa_pss_encode(Hash, Message, SaltLen, EMBits); emsa_pss_encode(Hash, Message, SaltLen, EMBits) when is_integer(SaltLen) andalso SaltLen >= 0 -> Salt = crypto:strong_rand_bytes(SaltLen), emsa_pss_encode(Hash, Message, Salt, EMBits); emsa_pss_encode(Hash, Message, Salt, EMBits) when is_tuple(Hash) orelse is_atom(Hash) -> HashFun = resolve_hash(Hash), emsa_pss_encode(HashFun, Message, Salt, EMBits). %% See [https://tools.ietf.org/html/rfc3447#section-9.1.2] -spec emsa_pss_verify(Hash, Message, EM, EMBits) -> boolean() when Hash :: rsa_hash_fun(), Message :: binary(), EM :: binary(), EMBits :: integer(). emsa_pss_verify(Hash, Message, EM, EMBits) when is_function(Hash, 1) andalso is_binary(Message) andalso is_binary(EM) andalso is_integer(EMBits) -> emsa_pss_verify(Hash, Message, EM, -2, EMBits); emsa_pss_verify(Hash, Message, EM, EMBits) when is_tuple(Hash) orelse is_atom(Hash) -> HashFun = resolve_hash(Hash), emsa_pss_verify(HashFun, Message, EM, EMBits). %% See [https://tools.ietf.org/html/rfc3447#section-9.1.2] -spec emsa_pss_verify(Hash, Message, EM, SaltLen, EMBits) -> boolean() when Hash :: rsa_hash_fun(), Message :: binary(), EM :: binary(), SaltLen :: integer(), EMBits :: integer(). emsa_pss_verify(Hash, Message, EM, SaltLen, EMBits) when is_function(Hash, 1) andalso is_binary(Message) andalso is_integer(SaltLen) andalso SaltLen >= 0 andalso is_integer(EMBits) -> MHash = Hash(Message), HashLen = byte_size(MHash), EMLen = ceiling(EMBits / 8), MaskedDBLen = (EMLen - HashLen - 1), case {EMLen < (HashLen + SaltLen + 2), byte_size(EM), EM} of {false, EMLen, << MaskedDB:MaskedDBLen/binary, H:HashLen/binary, ?PSS_TRAILER_FIELD >>} -> LeftBits = ((EMLen * 8) - EMBits), case MaskedDB of << 0:LeftBits, _/bitstring >> -> case mgf1(Hash, H, EMLen - HashLen - 1) of {ok, DBMask} -> << _:LeftBits/bitstring, DBRight/bitstring >> = crypto:exor(MaskedDB, DBMask), DB = << 0:LeftBits, DBRight/bitstring >>, PSLen = ((EMLen - HashLen - SaltLen - 2) * 8), case DB of << 0:PSLen, 16#01, Salt:SaltLen/binary >> -> MPrime = << 0:64, MHash/binary, Salt/binary >>, HPrime = Hash(MPrime), H =:= HPrime; _BadDB -> false end; _MGF1Error -> false end; _BadMaskedDB -> false end; _BadEMLen -> false end; emsa_pss_verify(Hash, Message, EM, -2, EMBits) when is_function(Hash, 1) andalso is_integer(EMBits) -> HashLen = byte_size(Hash(<<>>)), EMLen = ceiling(EMBits / 8), SaltLen = EMLen - HashLen - 2, case SaltLen < 0 of false -> emsa_pss_verify(Hash, Message, EM, SaltLen, EMBits); true -> false end; emsa_pss_verify(Hash, Message, EM, -1, EMBits) when is_function(Hash, 1) -> HashLen = byte_size(Hash(<<>>)), SaltLen = HashLen, emsa_pss_verify(Hash, Message, EM, SaltLen, EMBits). %% See [https://tools.ietf.org/html/rfc3447#appendix-B.2] -spec mgf1(Hash, Seed, MaskLen) -> {ok, binary()} | {error, mask_too_long} when Hash :: rsa_hash_fun(), Seed :: binary(), MaskLen :: pos_integer(). mgf1(Hash, Seed, MaskLen) when is_function(Hash, 1) andalso is_binary(Seed) andalso is_integer(MaskLen) andalso MaskLen >= 0 -> HashLen = byte_size(Hash(<<>>)), case MaskLen > (16#FFFFFFFF * HashLen) of false -> Reps = ceiling(MaskLen / HashLen), {ok, derive_mgf1(Hash, 0, Reps, Seed, MaskLen, <<>>)}; true -> {error, mask_too_long} end; mgf1(Hash, Seed, MaskLen) when is_tuple(Hash) orelse is_atom(Hash) -> HashFun = resolve_hash(Hash), mgf1(HashFun, Seed, MaskLen). %% See [https://tools.ietf.org/html/rfc3447#section-7.1.2] -spec rsaes_oaep_decrypt(Hash, CipherText, RSAPrivateKey) -> PlainText when Hash :: rsa_hash_fun(), CipherText :: binary(), RSAPrivateKey :: rsa_private_key(), PlainText :: binary(). rsaes_oaep_decrypt(Hash, CipherText, RSAPrivateKey=#'RSAPrivateKey'{}) when is_function(Hash, 1) andalso is_binary(CipherText) -> rsaes_oaep_decrypt(Hash, CipherText, <<>>, RSAPrivateKey); rsaes_oaep_decrypt(Hash, CipherText, RSAPrivateKey) when is_tuple(Hash) orelse is_atom(Hash) -> HashFun = resolve_hash(Hash), rsaes_oaep_decrypt(HashFun, CipherText, RSAPrivateKey). %% See [https://tools.ietf.org/html/rfc3447#section-7.1.2] -spec rsaes_oaep_decrypt(Hash, CipherText, Label, RSAPrivateKey) -> PlainText when Hash :: rsa_hash_fun(), CipherText :: binary(), Label :: binary(), RSAPrivateKey :: rsa_private_key(), PlainText :: binary(). rsaes_oaep_decrypt(Hash, CipherText, Label, RSAPrivateKey=#'RSAPrivateKey'{modulus=N}) when is_function(Hash, 1) andalso is_binary(CipherText) andalso is_binary(Label) -> HLen = byte_size(Hash(<<>>)), K = int_to_byte_size(N), case {byte_size(CipherText), K < ((2 * HLen) + 2)} of {K, false} -> EM = pad_to_key_size(K, dp(CipherText, RSAPrivateKey)), eme_oaep_decode(Hash, EM, Label, K); _BadSize -> {error, {badsize, _BadSize}} end; rsaes_oaep_decrypt(Hash, CipherText, Label, RSAPrivateKey) when is_tuple(Hash) orelse is_atom(Hash) -> HashFun = resolve_hash(Hash), rsaes_oaep_decrypt(HashFun, CipherText, Label, RSAPrivateKey). %% See [https://tools.ietf.org/html/rfc3447#section-7.1.1] -spec rsaes_oaep_encrypt(Hash, PlainText, RSAPublicKey) -> CipherText when Hash :: rsa_hash_fun(), PlainText :: binary(), RSAPublicKey :: rsa_public_key(), CipherText :: binary(). rsaes_oaep_encrypt(Hash, PlainText, RSAPublicKey=#'RSAPublicKey'{}) when is_function(Hash, 1) andalso is_binary(PlainText) -> rsaes_oaep_encrypt(Hash, PlainText, <<>>, RSAPublicKey); rsaes_oaep_encrypt(Hash, PlainText, RSAPublicKey) when is_tuple(Hash) orelse is_atom(Hash) -> HashFun = resolve_hash(Hash), rsaes_oaep_encrypt(HashFun, PlainText, RSAPublicKey). %% See [https://tools.ietf.org/html/rfc3447#section-7.1.1] -spec rsaes_oaep_encrypt(Hash, PlainText, Label, RSAPublicKey) -> CipherText when Hash :: rsa_hash_fun(), PlainText :: binary(), Label :: binary(), RSAPublicKey :: rsa_public_key(), CipherText :: binary(). rsaes_oaep_encrypt(Hash, PlainText, Label, RSAPublicKey=#'RSAPublicKey'{}) when is_function(Hash, 1) andalso is_binary(PlainText) andalso is_binary(Label) -> HLen = byte_size(Hash(<<>>)), Seed = crypto:strong_rand_bytes(HLen), rsaes_oaep_encrypt(Hash, PlainText, Label, Seed, RSAPublicKey); rsaes_oaep_encrypt(Hash, PlainText, Label, RSAPublicKey) when is_tuple(Hash) orelse is_atom(Hash) -> HashFun = resolve_hash(Hash), rsaes_oaep_encrypt(HashFun, PlainText, Label, RSAPublicKey). %% See [https://tools.ietf.org/html/rfc3447#section-7.1.1] -spec rsaes_oaep_encrypt(Hash, PlainText, Label, Seed, RSAPublicKey) -> CipherText when Hash :: rsa_hash_fun(), PlainText :: binary(), Label :: binary(), Seed :: binary(), RSAPublicKey :: rsa_public_key(), CipherText :: binary(). rsaes_oaep_encrypt(Hash, PlainText, Label, Seed, RSAPublicKey=#'RSAPublicKey'{modulus=N}) when is_function(Hash, 1) andalso is_binary(PlainText) andalso is_binary(Label) andalso is_binary(Seed) -> HLen = byte_size(Hash(<<>>)), MLen = byte_size(PlainText), K = int_to_byte_size(N), case MLen > (K - (2 * HLen) - 2) of false -> case eme_oaep_encode(Hash, PlainText, Label, Seed, K) of {ok, EM} -> C = pad_to_key_size(K, ep(EM, RSAPublicKey)), {ok, C}; EncodingError -> EncodingError end; true -> {error, message_too_long} end; rsaes_oaep_encrypt(Hash, PlainText, Label, Seed, RSAPublicKey) when is_tuple(Hash) orelse is_atom(Hash) -> HashFun = resolve_hash(Hash), rsaes_oaep_encrypt(HashFun, PlainText, Label, Seed, RSAPublicKey). %% See [https://tools.ietf.org/html/rfc3447#section-7.2.2] -spec rsaes_pkcs1_decrypt(CipherText, RSAPrivateKey) -> PlainText when CipherText :: binary(), RSAPrivateKey :: rsa_private_key(), PlainText :: binary(). rsaes_pkcs1_decrypt(CipherText, RSAPrivateKey=#'RSAPrivateKey'{modulus=N}) when is_binary(CipherText) -> K = int_to_byte_size(N), case {byte_size(CipherText), K < 11} of {K, false} -> EM = pad_to_key_size(K, dp(CipherText, RSAPrivateKey)), eme_pkcs1_decode(EM, K); _BadSize -> {error, {badsize, _BadSize}} end. %% See [https://tools.ietf.org/html/rfc3447#section-7.2.1] -spec rsaes_pkcs1_encrypt(PlainText, RSAPublicKey) -> CipherText when PlainText :: binary(), RSAPublicKey :: rsa_public_key(), CipherText :: binary(). rsaes_pkcs1_encrypt(PlainText, RSAPublicKey=#'RSAPublicKey'{modulus=N}) when is_binary(PlainText) -> MLen = byte_size(PlainText), K = int_to_byte_size(N), case MLen > (K - 11) of false -> case eme_pkcs1_encode(PlainText, K) of {ok, EM} -> C = pad_to_key_size(K, ep(EM, RSAPublicKey)), {ok, C}; EncodingError -> EncodingError end; true -> {error, message_too_long} end. %% See [https://tools.ietf.org/html/rfc3447#section-8.1.1] -spec rsassa_pkcs1_sign(Hash, Message, RSAPrivateKey) -> {ok, Signature} | {error, Reason} when Hash :: rsa_hash_fun(), Message :: binary(), RSAPrivateKey :: rsa_private_key(), Signature :: binary(), Reason :: term(). rsassa_pkcs1_sign(Hash, Message, RSAPrivateKey) when is_atom(Hash) -> rsassa_pkcs1_sign(Hash, Hash, Message, RSAPrivateKey). %% See [https://tools.ietf.org/html/rfc3447#section-8.1.1] -spec rsassa_pkcs1_sign(Hash, Algorithm, Message, RSAPrivateKey) -> {ok, Signature} | {error, Reason} when Hash :: rsa_hash_fun(), Algorithm :: md5 | sha | sha1 | sha256 | sha384 | sha512 | binary(), Message :: binary(), RSAPrivateKey :: rsa_private_key(), Signature :: binary(), Reason :: term(). rsassa_pkcs1_sign(Hash, Algorithm, Message, RSAPrivateKey=#'RSAPrivateKey'{modulus=Modulus}) when is_function(Hash, 1) andalso (is_atom(Algorithm) orelse is_binary(Algorithm)) andalso is_binary(Message) -> ModBits = int_to_bit_size(Modulus), case emsa_pkcs1_encode(Hash, Algorithm, Message, ModBits - 1) of {ok, EM} -> ModBytes = int_to_byte_size(Modulus), S = pad_to_key_size(ModBytes, dp(EM, RSAPrivateKey)), {ok, S}; EncodingError -> EncodingError end; rsassa_pkcs1_sign(Hash, Algorithm, Message, RSAPrivateKey=#'RSAPrivateKey'{}) when is_atom(Hash) -> HashFun = resolve_hash(Hash), rsassa_pkcs1_sign(HashFun, Algorithm, Message, RSAPrivateKey). %% See [https://tools.ietf.org/html/rfc3447#section-8.2.2] -spec rsassa_pkcs1_verify(Hash, Message, Signature, RSAPublicKey) -> boolean() when Hash :: rsa_hash_fun(), Message :: binary(), Signature :: binary(), RSAPublicKey :: rsa_public_key(). rsassa_pkcs1_verify(Hash, Message, Signature, RSAPublicKey) when is_atom(Hash) -> rsassa_pkcs1_verify(Hash, Hash, Message, Signature, RSAPublicKey). %% See [https://tools.ietf.org/html/rfc3447#section-8.2.2] -spec rsassa_pkcs1_verify(Hash, Algorithm, Message, Signature, RSAPublicKey) -> boolean() when Hash :: rsa_hash_fun(), Algorithm :: md5 | sha | sha1 | sha256 | sha384 | sha512 | binary(), Message :: binary(), Signature :: binary(), RSAPublicKey :: rsa_public_key(). rsassa_pkcs1_verify(Hash, Algorithm, Message, Signature, RSAPublicKey=#'RSAPublicKey'{modulus=Modulus}) when is_function(Hash, 1) andalso is_binary(Message) andalso is_binary(Signature) -> ModBytes = int_to_byte_size(Modulus), case byte_size(Signature) =:= ModBytes of true -> ModBits = int_to_bit_size(Modulus), EM = pad_to_key_size(ceiling((ModBits - 1) / 8), ep(Signature, RSAPublicKey)), case emsa_pkcs1_encode(Hash, Algorithm, Message, ModBits - 1) of {ok, EMPrime} -> jose_jwa:constant_time_compare(EM, EMPrime); _ -> false end; false -> false end; rsassa_pkcs1_verify(Hash, Algorithm, Message, Signature, RSAPublicKey=#'RSAPublicKey'{}) when is_atom(Hash) -> HashFun = resolve_hash(Hash), rsassa_pkcs1_verify(HashFun, Algorithm, Message, Signature, RSAPublicKey). %% See [https://tools.ietf.org/html/rfc3447#section-8.1.1] -spec rsassa_pss_sign(Hash, Message, RSAPrivateKey) -> {ok, Signature} | {error, Reason} when Hash :: rsa_hash_fun(), Message :: binary(), RSAPrivateKey :: rsa_private_key(), Signature :: binary(), Reason :: term(). rsassa_pss_sign(Hash, Message, RSAPrivateKey=#'RSAPrivateKey'{modulus=Modulus}) when is_function(Hash, 1) andalso is_binary(Message) -> ModBits = int_to_bit_size(Modulus), case emsa_pss_encode(Hash, Message, ModBits - 1) of {ok, EM} -> ModBytes = int_to_byte_size(Modulus), S = pad_to_key_size(ModBytes, dp(EM, RSAPrivateKey)), {ok, S}; EncodingError -> EncodingError end; rsassa_pss_sign(Hash, Message, RSAPrivateKey=#'RSAPrivateKey'{}) when is_tuple(Hash) orelse is_atom(Hash) -> HashFun = resolve_hash(Hash), rsassa_pss_sign(HashFun, Message, RSAPrivateKey). %% See [https://tools.ietf.org/html/rfc3447#section-8.1.1] -spec rsassa_pss_sign(Hash, Message, Salt, RSAPrivateKey) -> {ok, Signature} | {error, Reason} when Hash :: rsa_hash_fun(), Message :: binary(), Salt :: binary() | integer(), RSAPrivateKey :: rsa_private_key(), Signature :: binary(), Reason :: term(). rsassa_pss_sign(Hash, Message, Salt, RSAPrivateKey=#'RSAPrivateKey'{modulus=Modulus}) when is_function(Hash, 1) andalso is_binary(Message) andalso (is_binary(Salt) orelse is_integer(Salt)) -> ModBits = int_to_bit_size(Modulus), case emsa_pss_encode(Hash, Message, Salt, ModBits - 1) of {ok, EM} -> ModBytes = int_to_byte_size(Modulus), S = pad_to_key_size(ModBytes, dp(EM, RSAPrivateKey)), {ok, S}; EncodingError -> EncodingError end; rsassa_pss_sign(Hash, Message, Salt, RSAPrivateKey=#'RSAPrivateKey'{}) when is_tuple(Hash) orelse is_atom(Hash) -> HashFun = resolve_hash(Hash), rsassa_pss_sign(HashFun, Message, Salt, RSAPrivateKey). %% See [https://tools.ietf.org/html/rfc3447#section-8.1.2] -spec rsassa_pss_verify(Hash, Message, Signature, RSAPublicKey) -> boolean() when Hash :: rsa_hash_fun(), Message :: binary(), Signature :: binary(), RSAPublicKey :: rsa_public_key(). rsassa_pss_verify(Hash, Message, Signature, RSAPublicKey=#'RSAPublicKey'{modulus=Modulus}) when is_function(Hash, 1) andalso is_binary(Message) andalso is_binary(Signature) -> ModBytes = int_to_byte_size(Modulus), case byte_size(Signature) =:= ModBytes of true -> ModBits = int_to_bit_size(Modulus), EM = pad_to_key_size(ceiling((ModBits - 1) / 8), ep(Signature, RSAPublicKey)), emsa_pss_verify(Hash, Message, EM, ModBits - 1); false -> false end; rsassa_pss_verify(Hash, Message, Signature, RSAPublicKey=#'RSAPublicKey'{}) when is_tuple(Hash) orelse is_atom(Hash) -> HashFun = resolve_hash(Hash), rsassa_pss_verify(HashFun, Message, Signature, RSAPublicKey). %% See [https://tools.ietf.org/html/rfc3447#section-8.1.2] -spec rsassa_pss_verify(Hash, Message, Signature, SaltLen, RSAPublicKey) -> boolean() when Hash :: rsa_hash_fun(), Message :: binary(), Signature :: binary(), SaltLen :: integer(), RSAPublicKey :: rsa_public_key(). rsassa_pss_verify(Hash, Message, Signature, SaltLen, RSAPublicKey=#'RSAPublicKey'{modulus=Modulus}) when is_function(Hash, 1) andalso is_binary(Message) andalso is_binary(Signature) andalso is_integer(SaltLen) -> ModBytes = int_to_byte_size(Modulus), case byte_size(Signature) =:= ModBytes of true -> ModBits = int_to_bit_size(Modulus), EM = pad_to_key_size(ceiling((ModBits - 1) / 8), ep(Signature, RSAPublicKey)), emsa_pss_verify(Hash, Message, EM, SaltLen, ModBits - 1); false -> false end; rsassa_pss_verify(Hash, Message, Signature, SaltLen, RSAPublicKey=#'RSAPublicKey'{}) when is_tuple(Hash) orelse is_atom(Hash) -> HashFun = resolve_hash(Hash), rsassa_pss_verify(HashFun, Message, Signature, SaltLen, RSAPublicKey). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private ceiling(X) when X < 0 -> trunc(X); ceiling(X) -> T = trunc(X), case X - T == 0 of false -> T + 1; true -> T end. %% @private derive_mgf1(_Hash, Reps, Reps, _Seed, MaskLen, T) -> binary:part(T, 0, MaskLen); derive_mgf1(Hash, Counter, Reps, Seed, MaskLen, T) -> CounterBin = << Counter:8/unsigned-big-integer-unit:4 >>, NewT = << T/binary, (Hash(<< Seed/binary, CounterBin/binary >>))/binary >>, derive_mgf1(Hash, Counter + 1, Reps, Seed, MaskLen, NewT). %% @private dp(B, #'RSAPrivateKey'{modulus=N, privateExponent=E}) -> crypto:mod_pow(B, E, N). %% @private ep(B, #'RSAPublicKey'{modulus=N, publicExponent=E}) -> crypto:mod_pow(B, E, N). %% @private int_to_bit_size(I) -> int_to_bit_size(I, 0). %% @private int_to_bit_size(0, B) -> B; int_to_bit_size(I, B) -> int_to_bit_size(I bsr 1, B + 1). %% @private int_to_byte_size(I) -> int_to_byte_size(I, 0). %% @private int_to_byte_size(0, B) -> B; int_to_byte_size(I, B) -> int_to_byte_size(I bsr 8, B + 1). %% @private non_zero_strong_random_byte() -> case crypto:strong_rand_bytes(1) of << 0 >> -> non_zero_strong_random_byte(); Byte -> Byte end. %% @private non_zero_strong_random_bytes(N) -> << << (case C of 0 -> << (non_zero_strong_random_byte())/binary >>; _ -> << C >> end)/binary >> || << C >> <= crypto:strong_rand_bytes(N) >>. %% @private pad_to_key_size(Bytes, Data) when byte_size(Data) < Bytes -> pad_to_key_size(Bytes, << 0, Data/binary >>); pad_to_key_size(_Bytes, Data) -> Data. %% @private resolve_hash(HashFun) when is_function(HashFun, 1) -> HashFun; resolve_hash(DigestType) when is_atom(DigestType) -> fun(Data) -> crypto:hash(DigestType, Data) end; resolve_hash({hmac, DigestType, Key}) when is_atom(DigestType) -> fun(Data) -> jose_crypto_compat:mac(hmac, DigestType, Key, Data) end. %% @private unpad_zero(<< 0, Rest/binary >>) -> unpad_zero(Rest); unpad_zero(Rest) -> Rest. potatosalad-erlang-jose-30ac019/src/jwa/jose_jwa_curve25519.erl0000664000232200023220000001067315267717064024640 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc Pure Erlang `jose_curve25519' fallback. %%% %%% SECURITY WARNING: this implementation is NOT constant-time. The X25519 %%% callbacks below delegate to `jose_jwa_x25519', whose scalar %%% multiplication runs in time correlated with the private scalar (see the %%% module documentation for `jose_jwa_x25519'). The Ed25519 signing %%% callbacks delegate to `jose_jwa_ed25519' and carry the same caveat. %%% %%% This module exists as a last-resort fallback for platforms with no %%% native implementation, and `jose' selects a native module %%% in preference to it whenever one is available. Selecting it explicitly %%% via `jose:curve25519_module/1' opts out of that protection; do not do so %%% for long-lived private keys in any context where an attacker can observe %%% operation timing. %%% @end %%% Created : 07 Jan 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwa_curve25519). -behaviour(jose_curve25519). %% jose_curve25519 callbacks -export([eddsa_keypair/0]). -export([eddsa_keypair/1]). -export([eddsa_secret_to_public/1]). -export([ed25519_sign/2]). -export([ed25519_verify/3]). -export([ed25519ctx_sign/3]). -export([ed25519ctx_verify/4]). -export([ed25519ph_sign/2]). -export([ed25519ph_sign/3]). -export([ed25519ph_verify/3]). -export([ed25519ph_verify/4]). -export([x25519_keypair/0]). -export([x25519_keypair/1]). -export([x25519_secret_to_public/1]). -export([x25519_shared_secret/2]). %%==================================================================== %% jose_curve25519 callbacks %%==================================================================== % EdDSA eddsa_keypair() -> jose_jwa_ed25519:keypair(). eddsa_keypair(Seed) when is_binary(Seed) -> jose_jwa_ed25519:keypair(Seed). eddsa_secret_to_public(SecretKey) when is_binary(SecretKey) -> jose_jwa_ed25519:secret_to_pk(SecretKey). % Ed25519 ed25519_sign(Message, SecretKey) when is_binary(Message) andalso is_binary(SecretKey) -> jose_jwa_ed25519:ed25519_sign(Message, SecretKey). ed25519_verify(Signature, Message, PublicKey) when is_binary(Signature) andalso is_binary(Message) andalso is_binary(PublicKey) -> try jose_jwa_ed25519:ed25519_verify(Signature, Message, PublicKey) catch _:_ -> false end. % Ed25519ctx ed25519ctx_sign(Message, SecretKey, Context) when is_binary(Message) andalso is_binary(SecretKey) andalso is_binary(Context) andalso byte_size(Context) =< 255 -> jose_jwa_ed25519:ed25519ctx_sign(Message, SecretKey, Context). ed25519ctx_verify(Signature, Message, PublicKey, Context) when is_binary(Signature) andalso is_binary(Message) andalso is_binary(PublicKey) andalso is_binary(Context) andalso byte_size(Context) =< 255 -> try jose_jwa_ed25519:ed25519ctx_verify(Signature, Message, PublicKey, Context) catch _:_ -> false end. % Ed25519ph ed25519ph_sign(Message, SecretKey) when is_binary(Message) andalso is_binary(SecretKey) -> jose_jwa_ed25519:ed25519ph_sign(Message, SecretKey). ed25519ph_sign(Message, SecretKey, Context) when is_binary(Message) andalso is_binary(SecretKey) andalso is_binary(Context) andalso byte_size(Context) =< 255 -> jose_jwa_ed25519:ed25519ph_sign(Message, SecretKey, Context). ed25519ph_verify(Signature, Message, PublicKey) when is_binary(Signature) andalso is_binary(Message) andalso is_binary(PublicKey) -> try jose_jwa_ed25519:ed25519ph_verify(Signature, Message, PublicKey) catch _:_ -> false end. ed25519ph_verify(Signature, Message, PublicKey, Context) when is_binary(Signature) andalso is_binary(Message) andalso is_binary(PublicKey) andalso is_binary(Context) andalso byte_size(Context) =< 255 -> try jose_jwa_ed25519:ed25519ph_verify(Signature, Message, PublicKey, Context) catch _:_ -> false end. % X25519 x25519_keypair() -> jose_jwa_x25519:keypair(). x25519_keypair(Seed) when is_binary(Seed) -> jose_jwa_x25519:keypair(Seed). x25519_secret_to_public(SecretKey) when is_binary(SecretKey) -> jose_jwa_x25519:sk_to_pk(SecretKey). x25519_shared_secret(MySecretKey, YourPublicKey) when is_binary(MySecretKey) andalso is_binary(YourPublicKey) -> jose_jwa_x25519:x25519(MySecretKey, YourPublicKey). potatosalad-erlang-jose-30ac019/src/jwa/jose_jwa_xchacha20.erl0000664000232200023220000000243415267717064024643 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc XChaCha: eXtended-nonce ChaCha and AEAD_XChaCha20_Poly1305 %%% See https://tools.ietf.org/html/draft-irtf-cfrg-xchacha %%% @end %%% Created : 14 Sep 2019 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwa_xchacha20). %% API -export([encrypt/4]). -export([subkey_and_nonce/2]). %%==================================================================== %% API functions %%==================================================================== encrypt(Key, Counter, Nonce0, Plaintext) -> {Subkey, Nonce} = subkey_and_nonce(Key, Nonce0), jose_jwa_chacha20:encrypt(Subkey, Counter, Nonce, Plaintext). subkey_and_nonce(Key, << Nonce0:128/bitstring, Nonce1:64/bitstring >>) -> Subkey = jose_jwa_hchacha20:hash(Key, Nonce0), Nonce = << 0:32, Nonce1:64/bitstring >>, {Subkey, Nonce}. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jwa/jose_jwa_ed448.erl0000664000232200023220000003170415267717064023734 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc Edwards-curve Digital Signature Algorithm (EdDSA) - Ed448, Ed448ph %%% See https://datatracker.ietf.org/doc/html/rfc8032 %%% @end %%% Created : 20 Jan 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwa_ed448). %% API -export([xrecover/2]). -export([encode_point/1]). -export([decode_point/1]). -export([edwards_add/2]). -export([edwards_double/1]). -export([edwards_equal/2]). -export([scalarmult/2]). -export([scalarmult_base/1]). -export([normalize_point/1]). -export([secret/0]). -export([secret_to_curve448/1]). -export([secret_to_pk/1]). -export([keypair/0]). -export([keypair/1]). -export([sk_to_secret/1]). -export([sk_to_pk/1]). -export([sk_to_curve448/1]). -export([pk_to_curve448/1]). -export([dom4/2]). -export([sign_internal/4]). -export([ed448_sign/2]). -export([ed448_sign/3]). -export([ed448ph_sign/2]). -export([ed448ph_sign/3]). -export([verify_internal/5]). -export([ed448_verify/3]). -export([ed448_verify/4]). -export([ed448ph_verify/3]). -export([ed448ph_verify/4]). %% Macros -define(math, jose_jwa_math). -define(inv(Z), ?math:expmod(Z, ?p - 2, ?p)). % $= z^{-1} \mod p$, for z != 0 % 3. EdDSA Algorithm - https://datatracker.ietf.org/doc/html/rfc8032#section-3 % 5.2. Ed448ph and Ed448 - https://datatracker.ietf.org/doc/html/rfc8032#section-5.2 -define(d, -39081). % -39081 % -define(d, 611975850744529176160423220965553317543219696871016626328968936415087860042636474891785599283666020414768678979989378147065462815545017). %% 1. An odd prime power p. EdDSA uses an elliptic curve over the %% finite field GF(p). -define(p, 726838724295606890549323807888004534353641360687318060281490199180612328166730772686396383698676545930088884461843637361053498018365439). % ?math:intpow(2, 448) - ?math:intpow(2, 224) - 1 %% 2. An integer b with 2^(b-1) > p. EdDSA public keys have exactly b %% bits, and EdDSA signatures have exactly 2*b bits. b is %% recommended to be multiple of 8, so public key and signature %% lengths are integral number of octets. -define(b, 456). % ?math:intpow(2, ?b - 1) > ?p -define(b_curve448, 448). %% 3. A (b-1)-bit encoding of elements of the finite field GF(p). -define(GFp, << 16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF, 16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF, 16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF, 16#FF,16#FF,16#FF,16#FF,16#FE,16#FF,16#FF,16#FF, 16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF, 16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF, 16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF, 16#00 >>). % << ?p:(?b - 1)/unsigned-little-integer-unit:1, 0:1 >> %% 4. A cryptographic hash function H producing 2*b-bit output. %% Conservative hash functions are recommended and do not have much %% impact on the total cost of EdDSA. -define(HBits, 912). % ?b * 2 -define(HBytes, 114). % (?Hbits + 7) div 8 % -define(H(M), jose_sha3:shake256(<< "SigEd448", 16#00, 16#00, M/binary >>, ?HBytes)). -define(H(M), jose_sha3:shake256(M, ?HBytes)). %% 5. An integer c that is 2 or 3. Secret EdDSA scalars are multiples %% of 2^c. The integer c is the base-2 logarithm of the so called %% cofactor. -define(c, 2). %% 6. An integer n with c <= n < b. Secret EdDSA scalars have exactly %% n + 1 bits, with the top bit (the 2^n position) always set and %% the bottom c bits always cleared. -define(n, 448). % ?c =< ?n andalso ?n < ?b %% 7. A nonzero square element a of GF(p). The usual recommendation %% for best performance is a = -1 if p mod 4 = 1, and a = 1 if p %% mod 4 = 3. -define(a, 1). %% 8. An element B != (0,1) of the set E = { (x,y) is a member of %% GF(p) x GF(p) such that a * x^2 + y^2 = 1 + d * x^2 * y^2 }. -define(By, 298819210078481492676017930443930673437544040154080242095928241372331506189835876003536878655418784733982303233503462500531545062832660). -define(Bx, 224580040295924300187604334099896036246789641632564134246125461686950415467406032909029192869357953282578032075146446173674602635247710). % xrecover(?By) -define(B, {?Bx, ?By, 1}). % {?Bx, ?By, 1} % (?a * ?math:intpow(?Bx, 2) + ?math:intpow(?By, 2)) rem ?p == (1 + ?d * ?math:intpow(?Bx, 2) * ?math:intpow(?By, 2)) rem ?p %% 9. An odd prime l such that [l]B = 0 and 2^c * l = #E. The number %% #E (the number of points on the curve) is part of the standard %% data provided for an elliptic curve E. -define(l, 181709681073901722637330951972001133588410340171829515070372549795146003961539585716195755291692375963310293709091662304773755859649779). % ?math:intpow(2, 446) + 13818066809895115352007386748515426880336692474882178609894547503885 -define(E, ?math:intpow(2, ?c) * ?l). %% 10. A "prehash" function PH. PureEdDSA means EdDSA where PH is the %% identity function, i.e., PH(M) = M. HashEdDSA means EdDSA where %% PH generates a short output, no matter how long the message is; %% for example, PH(M) = SHA-512(M). -define(PHBits, 512). -define(PHBytes, 64). % (?PHBits + 7) div 8 -define(PH(C, M), jose_sha3:shake256(<< "SigEd448", 16#02, (byte_size(C)):8/integer, C/binary, M/binary >>, ?PHBytes)). % -define(PH(M), jose_sha3:shake256(M, ?PHBytes)). -define(secretbytes, 57). % (?b + 7) div 8 -define(publickeybytes, 57). % (?b + 7) div 8 -define(secretkeybytes, 114). % ?secretbytes + ?publickeybytes %%==================================================================== %% API %%==================================================================== % 5.2.1. Modular arithmetic - https://datatracker.ietf.org/doc/html/rfc8032#section-5.2.1 xrecover(Y, Xb) -> YY = Y * Y, U = (YY - 1), V = (?d * YY - 1), A = U * ?inv(V), X = ?math:expmod(A, (?p + 1) div 4, ?p), case ?math:mod((V * X * X), ?p) =:= ?math:mod(U, ?p) of true -> case X =:= 0 andalso Xb =:= 1 of true -> erlang:error(badarg); false -> case X rem 2 of Xb -> X; _ -> ?p - X end end; false -> erlang:error(badarg) end. % 5.2.2. Encoding - https://datatracker.ietf.org/doc/html/rfc8032#section-5.2.2 encode_point({X, Y, Z}) -> Zi = ?inv(Z), Xp = ?math:mod((X * Zi), ?p), Yp = ?math:mod((Y * Zi), ?p), << YpHead:(?b - 8)/bitstring, _:1/bitstring, YpTail:7/bitstring >> = << Yp:?b/unsigned-little-integer-unit:1 >>, << YpHead/bitstring, (Xp band 1):1/integer, YpTail:7/bitstring >>. % 5.2.3. Decoding - https://datatracker.ietf.org/doc/html/rfc8032#section-5.2.3 decode_point(<< YpHead:(?b - 8)/bitstring, Xb:1, YpTail:7/bitstring >>) -> << Y:?b/unsigned-little-integer-unit:1 >> = << YpHead/bitstring, 0:1, YpTail/bitstring >>, case Y >= ?p of true -> erlang:error(badarg); false -> X = xrecover(Y, Xb), {X, Y, 1} end. % 5.2.4. Point addition - https://datatracker.ietf.org/doc/html/rfc8032#section-5.2.4 edwards_add({X1, Y1, Z1}, {X2, Y2, Z2}) -> Xcp = (X1 * X2) rem ?p, Ycp = (Y1 * Y2) rem ?p, Zcp = (Z1 * Z2) rem ?p, B = (Zcp * Zcp) rem ?p, E = (?d * Xcp * Ycp) rem ?p, F = (B - E) rem ?p, G = (B + E) rem ?p, ZcpF = (Zcp * F) rem ?p, ZcpG = (Zcp * G) rem ?p, X3r = ((X1 + Y1) * (X2 + Y2) - Xcp - Ycp) rem ?p, Y3r = (Ycp - Xcp) rem ?p, X3 = ?math:mod((ZcpF * X3r), ?p), Y3 = ?math:mod((ZcpG * Y3r), ?p), Z3 = ?math:mod((F * G), ?p), {X3, Y3, Z3}. edwards_double({X, Y, Z}) -> XX = (X * X) rem ?p, YY = (Y * Y) rem ?p, ZZ = (Z * Z) rem ?p, XY = (X + Y) rem ?p, F = (XX + YY) rem ?p, J = (F - (ZZ + ZZ)) rem ?p, XYXY = (XY * XY) rem ?p, X3 = ?math:mod(((XYXY - XX - YY) * J), ?p), Y3 = ?math:mod((F * (XX - YY)), ?p), Z3 = ?math:mod((F * J), ?p), {X3, Y3, Z3}. edwards_equal({X1, Y1, Z1}, {X2, Y2, Z2}) -> Xn1 = ?math:mod((X1 * Z2), ?p), Xn2 = ?math:mod((X2 * Z1), ?p), Yn1 = ?math:mod((Y1 * Z2), ?p), Yn2 = ?math:mod((Y2 * Z1), ?p), Xn1 =:= Xn2 andalso Yn1 =:= Yn2. scalarmult(_P, 0) -> {0, 1, 1}; scalarmult(P, E) -> Q = scalarmult(P, E div 2), QQ = edwards_double(Q), case E band 1 of 0 -> QQ; 1 -> edwards_add(QQ, P) end. scalarmult_base(E) -> scalarmult(?B, E). normalize_point({X, Y, Z}) -> Zi = ?inv(Z), Xp = ?math:mod((X * Zi), ?p), Yp = ?math:mod((Y * Zi), ?p), Zp = ?math:mod((Z * Zi), ?p), {Xp, Yp, Zp}. % 5.2.5. Key Generation - https://datatracker.ietf.org/doc/html/rfc8032#section-5.2.5 secret() -> crypto:strong_rand_bytes(?secretbytes). secret_to_curve448(Secret = << _:?secretbytes/binary >>) -> << HHead0:6/bitstring, _:2/bitstring, HBody:54/binary, _:1/bitstring, HKnee0:7/bitstring, _HFoot0:8/integer, _/binary >> = ?H(Secret), << HHead:8/integer >> = << HHead0:6/bitstring, 0:2/integer >>, << HKnee:8/integer >> = << 0:1/integer, HKnee0:7/bitstring >>, HFoot = 0, << Scalar:?b/unsigned-little-integer-unit:1 >> = << HHead:8/integer, HBody/binary, HKnee:8/integer, HFoot:8/integer >>, Clamped = jose_jwa_x448:clamp_scalar(Scalar), << Clamped:?b_curve448/unsigned-little-integer-unit:1 >>. secret_to_pk(Secret = << _:?secretbytes/binary >>) -> << As:?b_curve448/unsigned-little-integer-unit:1 >> = secret_to_curve448(Secret), A = scalarmult(?B, As), encode_point(A). keypair() -> Secret = secret(), keypair(Secret). keypair(Secret = << _:?secretbytes/binary >>) -> PK = secret_to_pk(Secret), SK = << Secret/binary, PK/binary >>, {PK, SK}. sk_to_secret(<< Secret:?secretbytes/binary, _:?publickeybytes/binary >>) -> Secret. sk_to_pk(<< _:?secretbytes/binary, PK:?publickeybytes/binary >>) -> PK. sk_to_curve448(<< Secret:?secretbytes/binary, _:?publickeybytes/binary >>) -> secret_to_curve448(Secret). pk_to_curve448(<< PK:?publickeybytes/binary >>) -> % u = y^2/x^2 _A = {X, Y, _Z} = decode_point(PK), U = ?math:mod((Y * Y) * ?inv(X * X), ?p), % v = (2 - x^2 - y^2)*y/x^3 << U:?b_curve448/unsigned-little-integer-unit:1 >>. % 5.2.6. Sign - https://datatracker.ietf.org/doc/html/rfc8032#section-5.2.6 dom4(PHFlag, C) when (PHFlag =:= 0 orelse PHFlag =:= 1) andalso is_binary(C) -> <<"SigEd448", PHFlag:8, (byte_size(C)):8/integer, C/binary>>. sign_internal(M, << Secret:?secretbytes/binary, PK:?publickeybytes/binary >>, PHFlag, C) when is_binary(M) andalso (PHFlag =:= 0 orelse PHFlag =:= 1) andalso is_binary(C) -> % Recalculate PK to prevent misuse as described in https://github.com/MystenLabs/ed25519-unsafe-libs PK = secret_to_pk(Secret), Dom4 = dom4(PHFlag, C), << HHead0:6/bitstring, _:2/bitstring, HBody:54/binary, _:1/bitstring, HKnee0:7/bitstring, _HFoot0:8/integer, HTail:57/binary >> = ?H(Secret), << HHead:8/integer >> = << HHead0:6/bitstring, 0:2/integer >>, << HKnee:8/integer >> = << 0:1/integer, HKnee0:7/bitstring >>, HFoot = 0, << Scalar:?b/unsigned-little-integer-unit:1 >> = << HHead:8/integer, HBody/binary, HKnee:8/integer, HFoot:8/integer >>, As = jose_jwa_x448:clamp_scalar(Scalar), << Ri:?HBits/unsigned-little-integer-unit:1 >> = ?H(<< Dom4/binary, HTail/binary, M/binary >>), Rs = ?math:mod(Ri, ?l), R = encode_point(scalarmult(?B, Rs)), << Ki:?HBits/unsigned-little-integer-unit:1 >> = ?H(<< Dom4/binary, R/binary, PK/binary, M/binary >>), K = ?math:mod(Ki, ?l), S = ?math:mod(Rs + (K * As), ?l), << R/binary, S:?b/unsigned-little-integer-unit:1 >>. ed448_sign(M, SK = << _:?secretkeybytes/binary >>) when is_binary(M) -> sign_internal(M, SK, 0, <<>>). ed448_sign(M, SK = << _:?secretkeybytes/binary >>, C) when is_binary(M) andalso is_binary(C) -> sign_internal(M, SK, 0, C). ed448ph_sign(M, SK = << _:?secretkeybytes/binary >>) when is_binary(M) -> sign_internal(?PH(<<>>, M), SK, 1, <<>>). ed448ph_sign(M, SK = << _:?secretkeybytes/binary >>, C) when is_binary(M) andalso is_binary(C) -> sign_internal(?PH(C, M), SK, 1, C). % 5.2.7. Verify - https://datatracker.ietf.org/doc/html/rfc8032#section-5.2.7 verify_internal(<< R:?b/bitstring, S:?b/unsigned-little-integer-unit:1 >>, M, PK = << _:?publickeybytes/binary >>, PHFlag, C) when is_binary(M) andalso (PHFlag =:= 0 orelse PHFlag =:= 1) andalso is_binary(C) -> Dom4 = dom4(PHFlag, C), A = decode_point(PK), << Ki:?HBits/unsigned-little-integer-unit:1 >> = ?H(<< Dom4/binary, R/binary, PK/binary, M/binary >>), K = ?math:mod(Ki, ?l), edwards_equal(scalarmult(?B, S), edwards_add(decode_point(R), scalarmult(A, K))); verify_internal(Sig, M, _PK = << _:?publickeybytes/binary >>, PHFlag, C) when is_binary(Sig) andalso is_binary(M) andalso (PHFlag =:= 0 orelse PHFlag =:= 1) andalso is_binary(C) -> false. ed448_verify(Sig, M, PK = << _:?publickeybytes/binary >>) when is_binary(Sig) andalso is_binary(M) -> verify_internal(Sig, M, PK, 0, <<>>). ed448_verify(Sig, M, PK = << _:?publickeybytes/binary >>, C) when is_binary(Sig) andalso is_binary(M) andalso is_binary(C) -> verify_internal(Sig, M, PK, 0, C). ed448ph_verify(Sig, M, PK = << _:?publickeybytes/binary >>) when is_binary(Sig) andalso is_binary(M) -> verify_internal(Sig, ?PH(<<>>, M), PK, 1, <<>>). ed448ph_verify(Sig, M, PK = << _:?publickeybytes/binary >>, C) when is_binary(Sig) andalso is_binary(M) andalso is_binary(C) -> verify_internal(Sig, ?PH(C, M), PK, 1, C). potatosalad-erlang-jose-30ac019/src/jwa/jose_jwa_unsupported.erl0000664000232200023220000000402515267717064025470 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 10 Aug 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwa_unsupported). -behaviour(jose_block_encryptor). %% jose_block_encryptor callbacks -export([block_decrypt/3]). -export([block_decrypt/4]). -export([block_encrypt/3]). -export([block_encrypt/4]). %% Public Key API -export([decrypt_private/3]). -export([encrypt_public/3]). -export([sign/4]). -export([verify/5]). %%==================================================================== %% jose_block_encryptor callbacks %%==================================================================== block_decrypt(Cipher, _Key, _CipherText) -> erlang:error({cipher_unsupported, [Cipher]}). block_decrypt(Cipher, _Key, _IV, _CipherText) -> erlang:error({cipher_unsupported, [Cipher]}). block_encrypt(Cipher, _Key, _PlainText) -> erlang:error({cipher_unsupported, [Cipher]}). block_encrypt(Cipher, _Key, _IV, _PlainText) -> erlang:error({cipher_unsupported, [Cipher]}). %%==================================================================== %% Public Key API functions %%==================================================================== decrypt_private(_CipherText, _PrivateKey, Options) -> erlang:error({crypt_unsupported, [Options]}). encrypt_public(_PlainText, _PublicKey, Options) -> erlang:error({crypt_unsupported, [Options]}). sign(_Message, _DigestType, _PrivateKey, Options) -> erlang:error({sign_unsupported, [Options]}). verify(_Message, _DigestType, _Signature, _PublicKey, Options) -> erlang:error({sign_unsupported, [Options]}). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jwa/jose_jwa_pkcs7.erl0000664000232200023220000000577415267717064024143 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc PKCS-7 %%% See RFC 2315: https://tools.ietf.org/html/rfc2315 %%% @end %%% Created : 22 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwa_pkcs7). %% API -export([pad/1]). -export([unpad/1]). %%==================================================================== %% API functions %%==================================================================== -spec pad(binary()) -> binary(). pad(Bin) -> Size = 16 - (byte_size(Bin) rem 16), pad(Size, Bin). -spec unpad(binary()) -> binary(). unpad(Data) -> P = binary:last(Data), Size = byte_size(Data) - P, case Data of << Bin:Size/binary, P >> -> Bin; << Bin:Size/binary, P, P >> -> Bin; << Bin:Size/binary, P, P, P >> -> Bin; << Bin:Size/binary, P, P, P, P >> -> Bin; << Bin:Size/binary, P, P, P, P, P >> -> Bin; << Bin:Size/binary, P, P, P, P, P, P >> -> Bin; << Bin:Size/binary, P, P, P, P, P, P, P >> -> Bin; << Bin:Size/binary, P, P, P, P, P, P, P, P >> -> Bin; << Bin:Size/binary, P, P, P, P, P, P, P, P, P >> -> Bin; << Bin:Size/binary, P, P, P, P, P, P, P, P, P, P >> -> Bin; << Bin:Size/binary, P, P, P, P, P, P, P, P, P, P, P >> -> Bin; << Bin:Size/binary, P, P, P, P, P, P, P, P, P, P, P, P >> -> Bin; << Bin:Size/binary, P, P, P, P, P, P, P, P, P, P, P, P, P >> -> Bin; << Bin:Size/binary, P, P, P, P, P, P, P, P, P, P, P, P, P, P >> -> Bin; << Bin:Size/binary, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P >> -> Bin; << Bin:Size/binary, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P >> -> Bin; _ -> erlang:error({badarg, Data}) end. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private pad(P= 1, Bin) -> << Bin/binary, P >>; pad(P= 2, Bin) -> << Bin/binary, P, P >>; pad(P= 3, Bin) -> << Bin/binary, P, P, P >>; pad(P= 4, Bin) -> << Bin/binary, P, P, P, P >>; pad(P= 5, Bin) -> << Bin/binary, P, P, P, P, P >>; pad(P= 6, Bin) -> << Bin/binary, P, P, P, P, P, P >>; pad(P= 7, Bin) -> << Bin/binary, P, P, P, P, P, P, P >>; pad(P= 8, Bin) -> << Bin/binary, P, P, P, P, P, P, P, P >>; pad(P= 9, Bin) -> << Bin/binary, P, P, P, P, P, P, P, P, P >>; pad(P=10, Bin) -> << Bin/binary, P, P, P, P, P, P, P, P, P, P >>; pad(P=11, Bin) -> << Bin/binary, P, P, P, P, P, P, P, P, P, P, P >>; pad(P=12, Bin) -> << Bin/binary, P, P, P, P, P, P, P, P, P, P, P, P >>; pad(P=13, Bin) -> << Bin/binary, P, P, P, P, P, P, P, P, P, P, P, P, P >>; pad(P=14, Bin) -> << Bin/binary, P, P, P, P, P, P, P, P, P, P, P, P, P, P >>; pad(P=15, Bin) -> << Bin/binary, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P >>; pad(P=16, Bin) -> << Bin/binary, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P >>. potatosalad-erlang-jose-30ac019/src/jwa/jose_jwa_curve448.erl0000664000232200023220000000661615267717064024474 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 07 Jan 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwa_curve448). -behaviour(jose_curve448). %% jose_curve448 callbacks -export([eddsa_keypair/0]). -export([eddsa_keypair/1]). -export([eddsa_secret_to_public/1]). -export([ed448_sign/2]). -export([ed448_sign/3]). -export([ed448_verify/3]). -export([ed448_verify/4]). -export([ed448ph_sign/2]). -export([ed448ph_sign/3]). -export([ed448ph_verify/3]). -export([ed448ph_verify/4]). -export([x448_keypair/0]). -export([x448_keypair/1]). -export([x448_secret_to_public/1]). -export([x448_shared_secret/2]). %%==================================================================== %% jose_curve448 callbacks %%==================================================================== % EdDSA eddsa_keypair() -> jose_jwa_ed448:keypair(). eddsa_keypair(Seed) when is_binary(Seed) -> jose_jwa_ed448:keypair(Seed). eddsa_secret_to_public(SecretKey) when is_binary(SecretKey) -> jose_jwa_ed448:secret_to_pk(SecretKey). % Ed448 ed448_sign(Message, SecretKey) when is_binary(Message) andalso is_binary(SecretKey) -> jose_jwa_ed448:ed448_sign(Message, SecretKey). ed448_sign(Message, SecretKey, Context) when is_binary(Message) andalso is_binary(SecretKey) andalso is_binary(Context) -> jose_jwa_ed448:ed448_sign(Message, SecretKey, Context). ed448_verify(Signature, Message, PublicKey) when is_binary(Signature) andalso is_binary(Message) andalso is_binary(PublicKey) -> try jose_jwa_ed448:ed448_verify(Signature, Message, PublicKey) catch _:_ -> false end. ed448_verify(Signature, Message, PublicKey, Context) when is_binary(Signature) andalso is_binary(Message) andalso is_binary(PublicKey) andalso is_binary(Context) -> try jose_jwa_ed448:ed448_verify(Signature, Message, PublicKey, Context) catch _:_ -> false end. % Ed448ph ed448ph_sign(Message, SecretKey) when is_binary(Message) andalso is_binary(SecretKey) -> jose_jwa_ed448:ed448ph_sign(Message, SecretKey). ed448ph_sign(Message, SecretKey, Context) when is_binary(Message) andalso is_binary(SecretKey) andalso is_binary(Context) -> jose_jwa_ed448:ed448ph_sign(Message, SecretKey, Context). ed448ph_verify(Signature, Message, PublicKey) when is_binary(Signature) andalso is_binary(Message) andalso is_binary(PublicKey) -> try jose_jwa_ed448:ed448ph_verify(Signature, Message, PublicKey) catch _:_ -> false end. ed448ph_verify(Signature, Message, PublicKey, Context) when is_binary(Signature) andalso is_binary(Message) andalso is_binary(PublicKey) andalso is_binary(Context) -> try jose_jwa_ed448:ed448ph_verify(Signature, Message, PublicKey, Context) catch _:_ -> false end. % X448 x448_keypair() -> jose_jwa_x448:keypair(). x448_keypair(Seed) when is_binary(Seed) -> jose_jwa_x448:keypair(Seed). x448_secret_to_public(SecretKey) when is_binary(SecretKey) -> jose_jwa_x448:sk_to_pk(SecretKey). x448_shared_secret(MySecretKey, YourPublicKey) when is_binary(MySecretKey) andalso is_binary(YourPublicKey) -> jose_jwa_x448:x448(MySecretKey, YourPublicKey). potatosalad-erlang-jose-30ac019/src/jwa/jose_jwa_sha3.erl0000664000232200023220000001752015267717064023742 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 08 Jan 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwa_sha3). %% API -export([rol64/2]). -export([load64/1]). -export([store64/1]). -export([keccak_f_1600/1]). -export([load_lanes/1]). -export([store_lanes/1]). -export([keccak/5]). -export([keccak_absorb/4]). -export([keccak_pad/4]). -export([shake128/2]). -export([shake256/2]). -export([sha3_224/1]). -export([sha3_256/1]). -export([sha3_384/1]). -export([sha3_512/1]). %%==================================================================== %% API functions %%==================================================================== rol64(A, N) -> ((A bsr (64 - (N rem 64))) + (A bsl (N rem 64))) rem (1 bsl 64). keccak_f_1600_on_lanes(Lanes) -> keccak_f_1600_on_lanes(Lanes, 1, 0). keccak_f_1600_on_lanes(Lanes, _R, 24) -> Lanes; keccak_f_1600_on_lanes(Lanes, R, Round) -> % θ Lanes0 = theta(Lanes), % ρ and π Lanes1 = rho_and_pi(mget(Lanes0, 1, 0), Lanes0, 1, 0, 0), % χ Lanes2 = chi(Lanes1, 0), % ι {Lanes3, Rn} = iota(Lanes2, R, 0), keccak_f_1600_on_lanes(Lanes3, Rn, Round + 1). %% @private theta(Lanes) -> C = list_to_tuple([begin E = mget(Lanes, X), mget(E, 0) bxor mget(E, 1) bxor mget(E, 2) bxor mget(E, 3) bxor mget(E, 4) end || X <- lists:seq(0, 4)]), D = list_to_tuple([begin mget(C, (X + 4) rem 5) bxor rol64(mget(C, (X + 1) rem 5), 1) end || X <- lists:seq(0, 4)]), list_to_tuple([begin list_to_tuple([begin mget(Lanes, X, Y) bxor mget(D, X) end || Y <- lists:seq(0, 4)]) end || X <- lists:seq(0, 4)]). %% @private rho_and_pi(_Current, Lanes, _X, _Y, 24) -> Lanes; rho_and_pi(Current, Lanes, X, Y, T) -> Xn = Y, Yn = ((2 * X) + (3 * Y)) rem 5, Zn = rol64(Current, ((T + 1) * (T + 2)) div 2), rho_and_pi(mget(Lanes, Xn, Yn), mput(Lanes, Xn, Yn, Zn), Xn, Yn, T + 1). %% @private chi(Lanes, 5) -> Lanes; chi(Lanes, Y) -> T = list_to_tuple([mget(Lanes, X, Y) || X <- lists:seq(0, 4)]), chi(Lanes, T, Y, 0). chi(Lanes, _T, Y, 5) -> chi(Lanes, Y + 1); chi(Lanes, T, Y, X) -> V = mget(T, X) bxor ((bnot mget(T, (X + 1) rem 5)) band mget(T, (X + 2) rem 5)), chi(mput(Lanes, X, Y, V), T, Y, X + 1). %% @private iota(Lanes, R, 7) -> {Lanes, R}; iota(Lanes, R, J) -> Rn = ((R bsl 1) bxor ((R bsr 7) * 16#71)) rem 256, case Rn band 2 of 0 -> iota(Lanes, Rn, J + 1); _ -> Right = (1 bsl ((1 bsl J) - 1)), Left = mget(Lanes, 0, 0), Down = Left bxor Right, V = Down, iota(mput(Lanes, 0, 0, V), Rn, J + 1) end. load64(<< B:64/unsigned-little-integer-unit:1 >>) -> B. store64(B) when is_integer(B) -> << B:64/unsigned-little-integer-unit:1 >>. keccak_f_1600(State) -> Lanes0 = load_lanes(State), Lanes1 = keccak_f_1600_on_lanes(Lanes0), store_lanes(Lanes1). load_lanes(State) -> load_lanes(State, 0, 0, [], []). %% @private load_lanes(_State, 5, _Y, [], Lanes) -> list_to_tuple(lists:reverse(Lanes)); load_lanes(State, X, 5, Lane, Lanes) -> load_lanes(State, X + 1, 0, [], [list_to_tuple(lists:reverse(Lane)) | Lanes]); load_lanes(State, X, Y, Lane, Lanes) -> Pos = 8 * (X + 5 * Y), Len = 8, load_lanes(State, X, Y + 1, [load64(binary:part(State, Pos, Len)) | Lane], Lanes). store_lanes(Lanes) -> store_lanes(Lanes, 0, 0, << 0:1600 >>). store_lanes(_Lanes, 5, _Y, StateBytes) -> StateBytes; store_lanes(Lanes, X, 5, StateBytes) -> store_lanes(Lanes, X + 1, 0, StateBytes); store_lanes(Lanes, X, Y, StateBytes) -> V = mget(Lanes, X, Y), Pos = 8 * (X + 5 * Y), Len = 8, << StateHead:Pos/binary, _:Len/binary, StateTail/binary >> = StateBytes, store_lanes(Lanes, X, Y + 1, << StateHead/binary, (store64(V))/binary, StateTail/binary >>). keccak(Rate, Capacity, InputBytes, DelimitedSuffix, OutputByteLen) -> case (Rate + Capacity) =/= 1600 orelse (Rate rem 8) =/= 0 of true -> erlang:error(badarg); false -> {RateInBytes, StateBytes} = keccak_absorb(Rate div 8, InputBytes, << 0:1600 >>, DelimitedSuffix), keccak_squeeze(RateInBytes, OutputByteLen, StateBytes, <<>>) end. % Absorb all the input blocks keccak_absorb(RateInBytes, InputBytes, StateBytes, DelimitedSuffix) when is_integer(RateInBytes) andalso byte_size(InputBytes) >= RateInBytes -> << InputHead:RateInBytes/binary, InputTail/binary >> = InputBytes, << StateHead:RateInBytes/binary, StateTail/binary >> = StateBytes, State = << (crypto:exor(StateHead, InputHead))/binary, StateTail/binary >>, keccak_absorb(RateInBytes, InputTail, keccak_f_1600(State), DelimitedSuffix); keccak_absorb(RateInBytes, InputBytes, StateBytes, DelimitedSuffix) -> BlockSize = byte_size(InputBytes), << StateHead:BlockSize/binary, StateTail/binary >> = StateBytes, State = << (crypto:exor(StateHead, InputBytes))/binary, StateTail/binary >>, keccak_pad(RateInBytes, BlockSize, State, DelimitedSuffix). % Do the padding and switch to the squeezing phase keccak_pad(RateInBytes, BlockSize, StateBytes, DelimitedSuffix) -> << StateHead:BlockSize/binary, S:8/integer, StateTail/binary >> = StateBytes, State0 = << StateHead/binary, (S bxor DelimitedSuffix):8/integer, StateTail/binary >>, State1 = case (DelimitedSuffix band 16#80) =/= 0 andalso BlockSize =:= (RateInBytes - 1) of false -> State0; true -> keccak_f_1600(State0) end, RateInBytesSubOne = RateInBytes - 1, << XHead:RateInBytesSubOne/binary, X:8/integer, XTail/binary >> = State1, State2 = << XHead/binary, (X bxor 16#80):8/integer, XTail/binary >>, State3 = keccak_f_1600(State2), {RateInBytes, State3}. % Squeeze out all the output blocks keccak_squeeze(RateInBytes, OutputByteLen, StateBytes, OutputBytes) when OutputByteLen > 0 -> BlockSize = min(OutputByteLen, RateInBytes), << StateBlock:BlockSize/binary, _/binary >> = StateBytes, NewOutputByteLen = OutputByteLen - BlockSize, State = case NewOutputByteLen > 0 of true -> keccak_f_1600(StateBytes); false -> StateBytes end, keccak_squeeze(RateInBytes, NewOutputByteLen, State, << OutputBytes/binary, StateBlock/binary >>); keccak_squeeze(_RateInBytes, _OutputByteLen, _StateBytes, OutputBytes) -> OutputBytes. shake128(InputBytes, OutputByteLen) when is_binary(InputBytes) andalso is_integer(OutputByteLen) andalso OutputByteLen >= 0 -> keccak(1344, 256, InputBytes, 16#1F, OutputByteLen). shake256(InputBytes, OutputByteLen) when is_binary(InputBytes) andalso is_integer(OutputByteLen) andalso OutputByteLen >= 0 -> keccak(1088, 512, InputBytes, 16#1F, OutputByteLen). sha3_224(InputBytes) -> keccak(1152, 448, InputBytes, 16#06, 224 div 8). sha3_256(InputBytes) -> keccak(1088, 512, InputBytes, 16#06, 256 div 8). sha3_384(InputBytes) -> keccak(832, 768, InputBytes, 16#06, 384 div 8). sha3_512(InputBytes) -> keccak(576, 1024, InputBytes, 16#06, 512 div 8). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private mget(M, X) -> element(X + 1, M). %% @private mget(M, X, Y) -> mget(mget(M, X), Y). %% @private mput({E0, E1, E2, E3, E4}, 0, Y, V) -> {mput(E0, Y, V), E1, E2, E3, E4}; mput({E0, E1, E2, E3, E4}, 1, Y, V) -> {E0, mput(E1, Y, V), E2, E3, E4}; mput({E0, E1, E2, E3, E4}, 2, Y, V) -> {E0, E1, mput(E2, Y, V), E3, E4}; mput({E0, E1, E2, E3, E4}, 3, Y, V) -> {E0, E1, E2, mput(E3, Y, V), E4}; mput({E0, E1, E2, E3, E4}, 4, Y, V) -> {E0, E1, E2, E3, mput(E4, Y, V)}. %% @private mput({_, V1, V2, V3, V4}, 0, V0) -> {V0, V1, V2, V3, V4}; mput({V0, _, V2, V3, V4}, 1, V1) -> {V0, V1, V2, V3, V4}; mput({V0, V1, _, V3, V4}, 2, V2) -> {V0, V1, V2, V3, V4}; mput({V0, V1, V2, _, V4}, 3, V3) -> {V0, V1, V2, V3, V4}; mput({V0, V1, V2, V3, _}, 4, V4) -> {V0, V1, V2, V3, V4}. potatosalad-erlang-jose-30ac019/src/jwa/chacha20_poly1305/0000775000232200023220000000000015267717064023437 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/src/jwa/chacha20_poly1305/jose_chacha20_poly1305.erl0000664000232200023220000000607215267717064030115 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 31 May 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_chacha20_poly1305). -behaviour(jose_block_encryptor). %% Types -type additional_authenticated_data() :: binary(). -type cipher_text() :: binary(). -type message() :: binary(). -type plain_text() :: binary(). -type poly1305_mac() :: <<_:128>>. -type chacha20_key() :: <<_:256>>. -type chacha20_nonce() :: <<_:96>>. -export_type([ additional_authenticated_data/0, cipher_text/0, message/0, plain_text/0, poly1305_mac/0, chacha20_key/0, chacha20_nonce/0 ]). -callback decrypt(CipherText, CipherTag, AAD, IV, Key) -> PlainText | error when CipherText :: cipher_text(), CipherTag :: poly1305_mac(), AAD :: additional_authenticated_data(), IV :: chacha20_nonce(), Key :: chacha20_key(), PlainText :: binary(). -callback encrypt(PlainText, AAD, IV, Key) -> {CipherText, CipherTag} when PlainText :: plain_text(), AAD :: additional_authenticated_data(), IV :: chacha20_nonce(), Key :: chacha20_key(), CipherText :: cipher_text(), CipherTag :: poly1305_mac(). -callback authenticate(Message, Key, Nonce) -> MAC when Message :: message(), Key :: chacha20_key(), Nonce :: chacha20_nonce(), MAC :: poly1305_mac(). -callback verify(MAC, Message, Key, Nonce) -> boolean() when MAC :: poly1305_mac(), Message :: message(), Key :: chacha20_key(), Nonce :: chacha20_nonce(). %% jose_block_encryptor callbacks -export([block_decrypt/4]). -export([block_encrypt/4]). %% jose_chacha20_poly1305 callbacks -export([decrypt/5]). -export([encrypt/4]). -export([authenticate/3]). -export([verify/4]). %% Macros -define(JOSE_CHACHA20_POLY1305, (jose:chacha20_poly1305_module())). %%==================================================================== %% jose_block_encryptor callbacks %%==================================================================== block_decrypt({chacha20_poly1305, 256}, Key, IV, {AAD, CipherText, CipherTag}) -> decrypt(CipherText, CipherTag, AAD, IV, Key). block_encrypt({chacha20_poly1305, 256}, Key, IV, {AAD, PlainText}) -> encrypt(PlainText, AAD, IV, Key). %%==================================================================== %% jose_chacha20_poly1305 callbacks %%==================================================================== decrypt(CipherText, CipherTag, AAD, IV, CEK) -> ?JOSE_CHACHA20_POLY1305:decrypt(CipherText, CipherTag, AAD, IV, CEK). encrypt(PlainText, AAD, IV, CEK) -> ?JOSE_CHACHA20_POLY1305:encrypt(PlainText, AAD, IV, CEK). authenticate(Message, Key, Nonce) -> ?JOSE_CHACHA20_POLY1305:authenticate(Message, Key, Nonce). verify(MAC, Message, Key, Nonce) -> ?JOSE_CHACHA20_POLY1305:verify(MAC, Message, Key, Nonce). potatosalad-erlang-jose-30ac019/src/jwa/chacha20_poly1305/jose_chacha20_poly1305_unsupported.erl0000664000232200023220000000215715267717064032565 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 31 May 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_chacha20_poly1305_unsupported). -behaviour(jose_chacha20_poly1305). %% jose_chacha20_poly1305 callbacks -export([decrypt/5]). -export([encrypt/4]). -export([authenticate/3]). -export([verify/4]). %% Macros -define(unsupported, erlang:error(operation_not_supported)). %%==================================================================== %% jose_chacha20_poly1305 callbacks %%==================================================================== decrypt(_CipherText, _CipherTag, _AAD, _IV, _Key) -> ?unsupported. encrypt(_PlainText, _AAD, _IV, _Key) -> ?unsupported. authenticate(_Message, _Key, _Nonce) -> ?unsupported. verify(_MAC, _Message, _Key, _Nonce) -> ?unsupported. potatosalad-erlang-jose-30ac019/src/jwa/chacha20_poly1305/jose_chacha20_poly1305_libsodium.erl0000664000232200023220000000340715267717064032163 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 31 May 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_chacha20_poly1305_libsodium). -behaviour(jose_chacha20_poly1305). %% jose_chacha20_poly1305 callbacks -export([decrypt/5]). -export([encrypt/4]). -export([authenticate/3]). -export([verify/4]). %%==================================================================== %% jose_chacha20_poly1305 callbacks %%==================================================================== decrypt(CipherText, CipherTag, AAD, IV, CEK) -> case libsodium_crypto_aead_chacha20poly1305:ietf_decrypt_detached(CipherText, CipherTag, AAD, IV, CEK) of -1 -> error; PlainText when is_binary(PlainText) -> PlainText end. encrypt(PlainText, AAD, IV, CEK) -> libsodium_crypto_aead_chacha20poly1305:ietf_encrypt_detached(PlainText, AAD, IV, CEK). authenticate(Message, Key, Nonce) -> OTK = one_time_key(Key, Nonce), libsodium_crypto_onetimeauth_poly1305:crypto_onetimeauth_poly1305(Message, OTK). verify(MAC, Message, Key, Nonce) -> OTK = one_time_key(Key, Nonce), case libsodium_crypto_onetimeauth_poly1305:verify(MAC, Message, OTK) of 0 -> true; _ -> false end. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private one_time_key(Key, Nonce) -> libsodium_crypto_stream_chacha20:ietf_xor_ic(<< 0:256 >>, Nonce, 0, Key). potatosalad-erlang-jose-30ac019/src/jwa/chacha20_poly1305/jose_chacha20_poly1305_crypto.erl0000664000232200023220000000404615267717064031514 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 08 Aug 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_chacha20_poly1305_crypto). -behaviour(jose_chacha20_poly1305). %% jose_chacha20_poly1305 callbacks -export([decrypt/5]). -export([encrypt/4]). -export([authenticate/3]). -export([verify/4]). %% Internal API -export([poly1305_key_gen/2]). %% Types -type chacha20_key() :: <<_:256>>. -type chacha20_nonce() :: <<_:96>>. -type poly1305_otk() :: <<_:256>>. %%==================================================================== %% jose_chacha20_poly1305 callbacks %%==================================================================== decrypt(CipherText, CipherTag, AAD, IV, CEK) -> crypto:crypto_one_time_aead(chacha20_poly1305, CEK, IV, CipherText, AAD, CipherTag, false). encrypt(PlainText, AAD, IV, CEK) -> crypto:crypto_one_time_aead(chacha20_poly1305, CEK, IV, PlainText, AAD, true). authenticate(Message, Key, Nonce) -> OTK = poly1305_key_gen(Key, Nonce), jose_crypto_compat:mac(poly1305, OTK, Message). verify(MAC, Message, Key, Nonce) -> Challenge = authenticate(Message, Key, Nonce), jose_jwa:constant_time_compare(MAC, Challenge). %%==================================================================== %% Internal API Functions %%==================================================================== -spec poly1305_key_gen( Key :: chacha20_key(), Nonce :: chacha20_nonce() ) -> poly1305_otk(). poly1305_key_gen( <>, <> ) -> crypto:crypto_one_time(chacha20, Key, <<0:32, Nonce:96/bits>>, <<0:256>>, true). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jwa/jose_jwa_base64url.erl0000664000232200023220000000154315267717064024711 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc RFC 4648, Section 5: https://tools.ietf.org/html/rfc4648#section-5 %%% %%% @end %%% Created : 29 Jul 2019 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwa_base64url). %% API -export([decode/1]). -export([encode/1]). %%%=================================================================== %%% API functions %%%=================================================================== decode(Input) -> jose_base64url:'decode!'(Input). encode(Input) -> jose_base64url:encode(Input, #{ padding => false }). potatosalad-erlang-jose-30ac019/src/jwa/jose_jwa_hchacha20.erl0000664000232200023220000000401015267717064024613 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc XChaCha: eXtended-nonce ChaCha and AEAD_XChaCha20_Poly1305 %%% See https://tools.ietf.org/html/draft-irtf-cfrg-xchacha %%% @end %%% Created : 14 Sep 2019 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwa_hchacha20). %% API -export([hash/2]). %%==================================================================== %% API functions %%==================================================================== hash(Key, Nonce) when is_binary(Key) andalso bit_size(Key) =:= 256 andalso is_binary(Nonce) andalso bit_size(Nonce) =:= 128 -> State = << "expand 32-byte k", Key:256/bitstring, Nonce:128/bitstring >>, WS0 = list_to_tuple([Word || << Word:32/unsigned-little-integer-unit:1 >> <= State]), WS1 = rounds(WS0, 10), serialize(WS1). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private inner_block(State0) when is_tuple(State0) andalso tuple_size(State0) =:= 16 -> State1 = jose_jwa_chacha20:column_round(State0), State2 = jose_jwa_chacha20:diagonal_round(State1), State2. %% @private rounds(S, 0) -> S; rounds(S, N) when is_integer(N) andalso N > 0 -> rounds(inner_block(S), N - 1). %% @private serialize({Z00, Z01, Z02, Z03, _Z04, _Z05, _Z06, _Z07, _Z08, _Z09, _Z10, _Z11, Z12, Z13, Z14, Z15}) -> << Z00:32/unsigned-little-integer-unit:1, Z01:32/unsigned-little-integer-unit:1, Z02:32/unsigned-little-integer-unit:1, Z03:32/unsigned-little-integer-unit:1, Z12:32/unsigned-little-integer-unit:1, Z13:32/unsigned-little-integer-unit:1, Z14:32/unsigned-little-integer-unit:1, Z15:32/unsigned-little-integer-unit:1 >>. potatosalad-erlang-jose-30ac019/src/jwa/jose_jwa_x448.erl0000664000232200023220000001277715267717064023624 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc Elliptic Curves for Security - X448 %%% See https://tools.ietf.org/html/rfc7748 %%% @end %%% Created : 07 Jan 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwa_x448). %% API -export([coordinate_to_edwards448_4isogeny/1]). -export([vrecover/1]). -export([xrecover/1]). -export([curve448/2]). -export([clamp_scalar/1]). -export([decode_scalar/1]). -export([montgomery_add/3]). -export([montgomery_double/1]). -export([scalarmult/2]). -export([scalarmult_base/1]). -export([x448/2]). -export([x448_base/1]). -export([keypair/0]). -export([keypair/1]). -export([sk_to_pk/1]). %% Macros -define(math, jose_jwa_math). -define(inv(Z), ?math:expmod(Z, ?p - 2, ?p)). % $= z^{-1} \mod p$, for z != 0 -define(d, 611975850744529176160423220965553317543219696871016626328968936415087860042636474891785599283666020414768678979989378147065462815545017). % 4.2. Curve448 - https://tools.ietf.org/html/rfc7748#section-4.2 -define(p, 726838724295606890549323807888004534353641360687318060281490199180612328166730772686396383698676545930088884461843637361053498018365439). % ?math:intpow(2, 448) - ?math:intpow(2, 224) - 1 -define(A, 156326). -define(A2sqrt, 528950257000142451010969798134146496096806208096258258133290419984524923934728256972792120570883515182233459997657945594599653183173011). % ?math:expmod((?A - 2), (?p + 1) div 4, ?p) -define(order, 181709681073901722637330951972001133588410340171829515070372549795146003961539585716195755291692375963310293709091662304773755859649779). % ?math:intpow(2, 446) - 16#8335dc163bb124b65129c96fde933d8d723a70aadc873d6d54a7bb0d -define(cofactor, 4). -define(u, 5). -define(v, 355293926785568175264127502063783334808976399387714271831880898435169088786967410002932673765864550910142774147268105838985595290606362). -define(b, 448). -define(a24, 39081). % (?A - 2) div 4 -define(scalarbytes, 56). % (?b + 7) div 8 -define(coordinatebytes, 56). % (?b + 7) div 8 -define(publickeybytes, 56). % ?coordinatebytes -define(secretkeybytes, 56). % ?scalarbytes %%==================================================================== %% API %%==================================================================== coordinate_to_edwards448_4isogeny(<< U:?b/unsigned-little-integer-unit:1 >>) -> V = vrecover(U), % -(u^5 - 2*u^3 - 4*u*v^2 + u)/(u^5 - 2*u^2*v^2 - 2*u^3 - 2*v^2 + u) U2 = ?math:expmod(U, 2, ?p), U3 = ?math:expmod(U, 3, ?p), U5 = ?math:mod(U2*U3, ?p), V2 = ?math:expmod(V, 2, ?p), YN = ?math:mod(-(U5 - (2*U3) - (4*U*V2) + U), ?p), YD = ?inv((U5 - (2*U2*V2) - (2*U3) - (2*V2) + U)), Y = ?math:mod(YN*YD, ?p), % 4*v*(u^2 - 1)/(u^4 - 2*u^2 + 4*v^2 + 1) U4 = ?math:mod(U2*U2, ?p), XN = ?math:mod((4*V*(U2 - 1)), ?p), XD = ?inv((U4 - (2*U2) + (4*V2) + 1)), X = ?math:mod(XN*XD, ?p), jose_jwa_ed448:encode_point({X, Y, 1}). vrecover(U) -> Y = ?math:mod(((1 + U) * ?inv(1 - U)), ?p), X = xrecover(Y), UX = (U * ?inv(X)) rem ?p, V = (?A2sqrt * UX) rem ?p, case V rem 2 of 0 -> V; _ -> ?p - V end. xrecover(Y) -> YY = Y * Y, U = (YY - 1) rem ?p, V = ((?d * YY) - 1) rem ?p, A = (U * ?inv(V)) rem ?p, X = ?math:expmod(A, (?p + 1) div 4, ?p), case ((X * X) - A) rem ?p =:= 0 of true -> % x^2 = a (mod p). Then x is a square root. case X rem 2 of 0 -> X; _ -> ?p - X end; false -> % a is not a square modulo p. erlang:error(badarg) end. curve448(N, Base) -> One = {Base, 1}, Two = montgomery_double(One), % f(m) evaluates to a tuple containing the mth multiple and the % (m+1)th multiple of base. F = fun F(1) -> {One, Two}; F(M) when (M band 1) =:= 1 -> {Pm, Pm1} = F(M div 2), {montgomery_add(Pm, Pm1, One), montgomery_double(Pm1)}; F(M) -> {Pm, Pm1} = F(M div 2), {montgomery_double(Pm), montgomery_add(Pm, Pm1, One)} end, {{X, Z}, _} = F(N), (X * ?inv(Z)) rem ?p. % 5. The X25519 and X448 functions - https://tools.ietf.org/html/rfc7748#section-5 clamp_scalar(K0) when is_integer(K0) -> K1 = K0 band (bnot 3), K2 = K1 bor (128 bsl (?b - 8)), K2. decode_scalar(<< K0:8/integer, KBody:(?b - 16)/bitstring, K55:8/integer >>) -> << K:?b/unsigned-little-integer-unit:1 >> = << (K0 band 252):8/integer, KBody/bitstring, (K55 bor 128):8/integer >>, K. montgomery_add({Xn, Zn}, {Xm, Zm}, {Xd, Zd}) -> Z0 = (Xm * Xn) - (Zm * Zn), Z1 = (Xm * Zn) - (Zm * Xn), X = 4 * Z0 * Z0 * Zd, Z = 4 * Z1 * Z1 * Xd, {X rem ?p, Z rem ?p}. montgomery_double({Xn, Zn}) -> Xn2 = Xn * Xn, Zn2 = Zn * Zn, X = (Xn2 - Zn2), X2 = X * X, Z = 4 * Xn * Zn * (Xn2 + (?A * Xn * Zn) + Zn2), {X2 rem ?p, Z rem ?p}. scalarmult(<< Kb:?b/unsigned-little-integer-unit:1 >>, << U:?b/unsigned-little-integer-unit:1 >>) -> K = clamp_scalar(Kb), R = curve448(K, U), << R:?b/unsigned-little-integer-unit:1 >>. scalarmult_base(<< Kb:?b/unsigned-little-integer-unit:1 >>) -> K = clamp_scalar(Kb), R = curve448(K, ?u), << R:?b/unsigned-little-integer-unit:1 >>. x448(SK = << _:?secretkeybytes/binary >>, PK = << _:?publickeybytes/binary >>) -> scalarmult(SK, PK). x448_base(SK = << _:?secretkeybytes/binary >>) -> scalarmult_base(SK). keypair() -> keypair(crypto:strong_rand_bytes(?secretkeybytes)). keypair(SK = << _:?secretkeybytes/binary >>) -> PK = sk_to_pk(SK), {PK, SK}. sk_to_pk(SK = << _:?secretkeybytes/binary >>) -> x448_base(SK). potatosalad-erlang-jose-30ac019/src/jose_server.erl0000664000232200023220000010407715267717064022774 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 13 Aug 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_server). -behaviour(gen_server). -include_lib("public_key/include/public_key.hrl"). -define(SERVER, ?MODULE). %% API -export([start_link/0]). -export([config_change/0]). -export([chacha20_poly1305_module/1]). -export([curve25519_module/1]). -export([curve448_module/1]). -export([json_module/1]). -export([pbes2_count_maximum/1]). -export([sha3_module/1]). -export([unsecured_signing/1]). -export([xchacha20_poly1305_module/1]). -export([zip_size_maximum/1]). %% gen_server callbacks -export([init/1]). -export([handle_call/3]). -export([handle_cast/2]). -export([handle_info/2]). -export([terminate/2]). -export([code_change/3]). -define(CRYPTO_FALLBACK, application:get_env(jose, crypto_fallback, false)). -define(TAB, jose_jwa). -define(POISON_MAP, #{ <<"a">> => 1, <<"b">> => 2, <<"c">> => #{ <<"d">> => 3, <<"e">> => 4 } }). -define(POISON_BIN, <<"{\"a\":1,\"b\":2,\"c\":{\"d\":3,\"e\":4}}">>). %% Types -record(state, {}). %%%=================================================================== %%% API functions %%%=================================================================== start_link() -> gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). config_change() -> gen_server:call(?SERVER, config_change). chacha20_poly1305_module(ChaCha20Poly1305Module) when is_atom(ChaCha20Poly1305Module) -> gen_server:call(?SERVER, {chacha20_poly1305_module, ChaCha20Poly1305Module}). curve25519_module(Curve25519Module) when is_atom(Curve25519Module) -> gen_server:call(?SERVER, {curve25519_module, Curve25519Module}). curve448_module(Curve448Module) when is_atom(Curve448Module) -> gen_server:call(?SERVER, {curve448_module, Curve448Module}). json_module(JSONModule) when is_atom(JSONModule) -> gen_server:call(?SERVER, {json_module, JSONModule}). -spec pbes2_count_maximum(PBES2CountMaximum) -> ok when PBES2CountMaximum :: non_neg_integer(). pbes2_count_maximum(PBES2CountMaximum) when is_integer(PBES2CountMaximum) andalso PBES2CountMaximum >= 0 -> gen_server:call(?SERVER, {pbes2_count_maximum, PBES2CountMaximum}). sha3_module(SHA3Module) when is_atom(SHA3Module) -> gen_server:call(?SERVER, {sha3_module, SHA3Module}). -spec unsecured_signing(UnsecuredSigning) -> ok when UnsecuredSigning :: boolean(). unsecured_signing(UnsecuredSigning) when is_boolean(UnsecuredSigning) -> gen_server:call(?SERVER, {unsecured_signing, UnsecuredSigning}). xchacha20_poly1305_module(XChaCha20Poly1305Module) when is_atom(XChaCha20Poly1305Module) -> gen_server:call(?SERVER, {xchacha20_poly1305_module, XChaCha20Poly1305Module}). -spec zip_size_maximum(ZipSizeMaximum) -> ok when ZipSizeMaximum :: non_neg_integer(). zip_size_maximum(ZipSizeMaximum) when is_integer(ZipSizeMaximum) andalso ZipSizeMaximum >= 0 -> gen_server:call(?SERVER, {zip_size_maximum, ZipSizeMaximum}). %%%=================================================================== %%% gen_server callbacks %%%=================================================================== %% @private init([]) -> ?TAB = ets:new(?TAB, [ named_table, public, ordered_set, {read_concurrency, true} ]), ok = support_check(), {ok, #state{}}. %% @private handle_call(config_change, _From, State) -> {reply, support_check(), State}; handle_call({chacha20_poly1305_module, M}, _From, State) -> ChaCha20Poly1305Module = check_chacha20_poly1305_module(M), Entries = lists:flatten(check_crypto(?CRYPTO_FALLBACK, [{chacha20_poly1305_module, ChaCha20Poly1305Module}])), _ = ets:select_delete(?TAB, [{{{cipher, '_'}, '_'}, [], [true]}]), true = ets:insert(?TAB, Entries), {reply, ok, State}; handle_call({curve25519_module, M}, _From, State) -> Curve25519Module = check_curve25519_module(M), true = ets:insert(?TAB, {curve25519_module, Curve25519Module}), {reply, ok, State}; handle_call({curve448_module, M}, _From, State) -> Curve448Module = check_curve448_module(M), true = ets:insert(?TAB, {curve448_module, Curve448Module}), {reply, ok, State}; handle_call({json_module, M}, _From, State) -> JSONModule = check_json_module(M), true = ets:insert(?TAB, {json_module, JSONModule}), {reply, ok, State}; handle_call({pbes2_count_maximum, PBES2CountMaximum}, _From, State) when is_integer(PBES2CountMaximum) andalso PBES2CountMaximum >= 0 -> true = ets:insert(?TAB, {pbes2_count_maximum, PBES2CountMaximum}), {reply, ok, State}; handle_call({sha3_module, M}, _From, State) -> SHA3Module = check_sha3_module(M), true = ets:insert(?TAB, {sha3_module, SHA3Module}), {reply, ok, State}; handle_call({unsecured_signing, UnsecuredSigning}, _From, State) when is_boolean(UnsecuredSigning) -> true = ets:insert(?TAB, {unsecured_signing, UnsecuredSigning}), _ = spawn(fun() -> _ = try jose_jwa:unsecured_signing(UnsecuredSigning) catch _:_ -> ok end, exit(normal) end), {reply, ok, State}; handle_call({xchacha20_poly1305_module, M}, _From, State) -> XChaCha20Poly1305Module = check_xchacha20_poly1305_module(M), Entries = lists:flatten(check_crypto(?CRYPTO_FALLBACK, [{xchacha20_poly1305_module, XChaCha20Poly1305Module}])), _ = ets:select_delete(?TAB, [{{{cipher, '_'}, '_'}, [], [true]}]), true = ets:insert(?TAB, Entries), {reply, ok, State}; handle_call({zip_size_maximum, ZipSizeMaximum}, _From, State) when is_integer(ZipSizeMaximum) andalso ZipSizeMaximum >= 0 -> true = ets:insert(?TAB, {zip_size_maximum, ZipSizeMaximum}), {reply, ok, State}; handle_call(_Request, _From, State) -> {reply, ignore, State}. %% @private handle_cast(_Request, State) -> {noreply, State}. %% @private handle_info(_Info, State) -> {noreply, State}. %% @private terminate(_Reason, _State) -> ok. %% @private code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private support_check() -> PBES2CountMaximum = case application:get_env(jose, pbes2_count_maximum, 10000) of V1 when is_integer(V1) andalso V1 >= 0 -> V1 end, UnsecuredSigning = case application:get_env(jose, unsecured_signing, false) of V2 when is_boolean(V2) -> V2 end, ZipSizeMaximum = case application:get_env(jose, zip_size_maximum, 1048576) of V3 when is_integer(V3) andalso V3 >= 0 -> V3 end, Fallback = ?CRYPTO_FALLBACK, Entries1 = lists:flatten(lists:foldl(fun(Check, Acc) -> Check(Fallback, Acc) end, [], [ fun check_sha3/2, fun check_ec_key_mode/2, fun check_chacha20_poly1305/2, fun check_xchacha20_poly1305/2, fun check_curve25519/2, fun check_curve448/2, fun check_json/2, fun check_crypto/2, fun check_public_key/2 ])), Entries2 = [ {pbes2_count_maximum, PBES2CountMaximum}, {unsecured_signing, UnsecuredSigning}, {zip_size_maximum, ZipSizeMaximum} | Entries1 ], true = ets:delete_all_objects(?TAB), true = ets:insert(?TAB, Entries2), ok. %%%------------------------------------------------------------------- %%% Internal check functions %%%------------------------------------------------------------------- %% @private check_ec_key_mode(_Fallback, Entries) -> PEMBin = << 48,119,2,1,1,4,32,104,152,88,12,19,82,251,156,171,31,222,207, 0,76,115,88,210,229,36,106,137,192,81,153,154,254,226,38,247, 70,226,157,160,10,6,8,42,134,72,206,61,3,1,7,161,68,3,66,0,4, 46,75,29,46,150,77,222,40,220,159,244,193,125,18,190,254,216, 38,191,11,52,115,159,213,230,77,27,131,94,17,46,21,186,71,62, 36,225,0,90,21,186,235,132,152,229,13,189,196,121,64,84,64, 229,173,12,24,23,127,175,67,247,29,139,91 >>, PEMEntry = {'ECPrivateKey', PEMBin, not_encrypted}, %% Erlang 24 changes 'ECPrivateKey' record in a way that makes record matching fail %% when this module is compiled on Erlang 23 (or earlier) but runs on 24. %% So we destructure tuples, as ugly as it may be. %% %% See erlang-jose#113 for details. PrivateKey = case list_to_integer(erlang:system_info(otp_release)) >= 24 of true -> {'ECPrivateKey', _Version, PrivKey0, _Params, _PubKey0, _Attributes} = public_key:pem_entry_decode(PEMEntry), PrivKey0; false -> {'ECPrivateKey', _Version, PrivKey0, _Params, _PubKey0} = public_key:pem_entry_decode(PEMEntry), PrivKey0 end, case is_binary(PrivateKey) of true -> [{ec_key_mode, binary} | Entries]; _ -> [{ec_key_mode, list} | Entries] end. %% @private check_chacha20_poly1305(false, Entries) -> check_chacha20_poly1305(jose_chacha20_poly1305_unsupported, Entries); check_chacha20_poly1305(true, Entries) -> check_chacha20_poly1305(jose_jwa_chacha20_poly1305, Entries); check_chacha20_poly1305(Fallback, Entries) -> true = ets:delete_object(?TAB, {chacha20_poly1305_module, jose_jwa_chacha20_poly1305}), true = ets:delete_object(?TAB, {chacha20_poly1305_module, jose_chacha20_poly1305_unsupported}), ChaCha20Poly1305Module = case ets:lookup(?TAB, chacha20_poly1305_module) of [{chacha20_poly1305_module, M}] when is_atom(M) -> M; [] -> case application:get_env(jose, chacha20_poly1305_module, undefined) of undefined -> check_chacha20_poly1305_modules(Fallback, [crypto, libsodium]); M when is_atom(M) -> check_chacha20_poly1305_module(M) end end, %% Potentially used by XChaCha20-Poly1305 related functions below, needs to be inserted early. true = ets:insert(?TAB, {chacha20_poly1305_module, ChaCha20Poly1305Module}), [{chacha20_poly1305_module, ChaCha20Poly1305Module} | Entries]. %% @private check_chacha20_poly1305_module(crypto) -> jose_chacha20_poly1305_crypto; check_chacha20_poly1305_module(libsodium) -> jose_chacha20_poly1305_libsodium; check_chacha20_poly1305_module(Module) when is_atom(Module) -> Module. %% @private check_chacha20_poly1305_modules(Fallback, [Module | Modules]) -> case code:ensure_loaded(Module) of {module, Module} -> _ = application:ensure_all_started(Module), M = check_chacha20_poly1305_module(Module), PT = crypto:strong_rand_bytes(8), CEK = crypto:strong_rand_bytes(32), IV = crypto:strong_rand_bytes(12), AAD = <<>>, try M:encrypt(PT, AAD, IV, CEK) of {CT, TAG} when is_binary(CT) andalso is_binary(TAG) -> try M:decrypt(CT, TAG, AAD, IV, CEK) of PT -> M; _ -> check_chacha20_poly1305_modules(Fallback, Modules) catch _:_ -> check_chacha20_poly1305_modules(Fallback, Modules) end; _ -> check_chacha20_poly1305_modules(Fallback, Modules) catch _:_ -> check_chacha20_poly1305_modules(Fallback, Modules) end; _ -> check_chacha20_poly1305_modules(Fallback, Modules) end; check_chacha20_poly1305_modules(Fallback, []) -> Fallback. %% @private check_curve25519(false, Entries) -> check_curve25519(jose_curve25519_unsupported, Entries); check_curve25519(true, Entries) -> check_curve25519(jose_jwa_curve25519, Entries); check_curve25519(Fallback, Entries) -> true = ets:delete_object(?TAB, {curve25519_module, jose_jwa_curve25519}), true = ets:delete_object(?TAB, {curve25519_module, jose_curve25519_unsupported}), Curve25519Module = case ets:lookup(?TAB, curve25519_module) of [{curve25519_module, M}] when is_atom(M) -> M; [] -> case application:get_env(jose, curve25519_module, undefined) of undefined -> check_curve25519_modules(Fallback, [libdecaf, libsodium, crypto]); M when is_atom(M) -> check_curve25519_module(M) end end, [{curve25519_module, Curve25519Module} | Entries]. %% @private check_curve25519_module(crypto) -> jose_curve25519_crypto; check_curve25519_module(libdecaf) -> jose_curve25519_libdecaf; check_curve25519_module(libsodium) -> jose_curve25519_libsodium; check_curve25519_module(Module) when is_atom(Module) -> Module. %% @private check_curve25519_modules(Fallback, [Module | Modules]) -> case code:ensure_loaded(Module) of {module, Module} -> _ = application:ensure_all_started(Module), RealFallback = jose_jwa_curve25519, RealModule = check_curve25519_module(Module), try check_curve25519_module_does_it_work(RealFallback, RealModule) of true -> RealModule; false -> check_curve25519_modules(Fallback, Modules) catch _Class:_Reason:_Stacktrace -> % io:format("Class = ~p~nReason = ~p~nStacktrace = ~p~n", [Class, Reason, Stacktrace]), check_curve25519_modules(Fallback, Modules) end; _ -> check_curve25519_modules(Fallback, Modules) end; check_curve25519_modules(Fallback, []) -> Fallback. %% @private check_curve25519_module_does_it_work(Fallback, Module) -> {PK, SK = <>} = Module:eddsa_keypair(), {PK, SK} = Module:eddsa_keypair(Secret), PK = Module:eddsa_secret_to_public(Secret), Message = crypto:strong_rand_bytes(16), Signature = Module:ed25519_sign(Message, SK), true = Module:ed25519_verify(Signature, Message, PK), true = Fallback:ed25519_verify(Signature, Message, PK), {XPK, XSK} = Module:x25519_keypair(), XPK = Module:x25519_secret_to_public(XSK), {PeerPK, PeerSK} = Module:x25519_keypair(), Shared = Module:x25519_shared_secret(XSK, PeerPK), Shared = Module:x25519_shared_secret(PeerSK, XPK), %% NOTE: Ed25519ctx and Ed25519ph are lower priority, no need to check for now. % Ctx = <<"ctx">>, % CtxSignature = Module:ed25519ctx_sign(Message, SK, Ctx), % true = Module:ed25519ctx_verify(CtxSignature, Message, PK, Ctx), % true = Fallback:ed25519ctx_verify(CtxSignature, Message, PK, Ctx), % PHSignature = Module:ed25519ph_sign(Message, SK), % true = Module:ed25519ph_verify(PHSignature, Message, PK), % true = Fallback:ed25519ph_verify(PHSignature, Message, PK), % CtxPHSignature = Module:ed25519ph_sign(Message, SK, Ctx), % true = Module:ed25519ph_verify(CtxPHSignature, Message, PK, Ctx), % true = Fallback:ed25519ph_verify(CtxPHSignature, Message, PK, Ctx), true. %% @private check_curve448(false, Entries) -> check_curve448(jose_curve448_unsupported, Entries); check_curve448(true, Entries) -> check_curve448(jose_jwa_curve448, Entries); check_curve448(Fallback, Entries) -> true = ets:delete_object(?TAB, {curve448_module, jose_jwa_curve448}), true = ets:delete_object(?TAB, {curve448_module, jose_curve448_unsupported}), Curve448Module = case ets:lookup(?TAB, curve448_module) of [{curve448_module, M}] when is_atom(M) -> M; [] -> case application:get_env(jose, curve448_module, undefined) of undefined -> check_curve448_modules(Fallback, [libdecaf, crypto]); M when is_atom(M) -> check_curve448_module(M) end end, [{curve448_module, Curve448Module} | Entries]. %% @private check_curve448_module(crypto) -> jose_curve448_crypto; check_curve448_module(libdecaf) -> jose_curve448_libdecaf; check_curve448_module(Module) when is_atom(Module) -> Module. %% @private check_curve448_modules(Fallback, [Module | Modules]) -> case code:ensure_loaded(Module) of {module, Module} -> _ = application:ensure_all_started(Module), RealFallback = jose_jwa_curve448, RealModule = check_curve448_module(Module), try check_curve448_module_does_it_work(RealFallback, RealModule) of true -> RealModule; false -> check_curve448_modules(Fallback, Modules) catch _Class:_Reason:_Stacktrace -> % io:format("Class = ~p~nReason = ~p~nStacktrace = ~p~n", [_Class, _Reason, _Stacktrace]), check_curve448_modules(Fallback, Modules) end; _ -> check_curve448_modules(Fallback, Modules) end; check_curve448_modules(Fallback, []) -> Fallback. %% @private check_curve448_module_does_it_work(Fallback, Module) -> {PK, SK = <>} = Module:eddsa_keypair(), {PK, SK} = Module:eddsa_keypair(Secret), PK = Module:eddsa_secret_to_public(Secret), Message = crypto:strong_rand_bytes(16), Signature = Module:ed448_sign(Message, SK), true = Module:ed448_verify(Signature, Message, PK), %% The pure verifier needs SHAKE256, which may be unavailable when %% fallback is disabled. Do not reject a working native provider for that. case ?CRYPTO_FALLBACK of true -> true = Fallback:ed448_verify(Signature, Message, PK); false -> ok end, {XPK, XSK} = Module:x448_keypair(), XPK = Module:x448_secret_to_public(XSK), {PeerPK, PeerSK} = Module:x448_keypair(), Shared = Module:x448_shared_secret(XSK, PeerPK), Shared = Module:x448_shared_secret(PeerSK, XPK), %% NOTE: Ed448ph is lower priority, no need to check for now. % Ctx = <<"ctx">>, % CtxSignature = Module:ed448_sign(Message, SK, Ctx), % true = Module:ed448_verify(CtxSignature, Message, PK, Ctx), % true = Fallback:ed448_verify(CtxSignature, Message, PK, Ctx), % PHSignature = Module:ed448ph_sign(Message, SK), % true = Module:ed448ph_verify(PHSignature, Message, PK), % true = Fallback:ed448ph_verify(PHSignature, Message, PK), % CtxPHSignature = Module:ed448ph_sign(Message, SK, Ctx), % true = Module:ed448ph_verify(CtxPHSignature, Message, PK, Ctx), % true = Fallback:ed448ph_verify(CtxPHSignature, Message, PK, Ctx), true. %% @private check_json(_Fallback, Entries) -> JSONModule = case ets:lookup(?TAB, json_module) of [{json_module, M}] when is_atom(M) -> M; [] -> case application:get_env(jose, json_module, undefined) of undefined -> case code:ensure_loaded(elixir) of {module, elixir} -> check_json_modules([json, ojson, 'Elixir.Jason', 'Elixir.Poison', jiffy, jsone, jsx, thoas]); _ -> check_json_modules([json, ojson, jiffy, jsone, jsx, thoas]) end; M when is_atom(M) -> check_json_module(M) end end, [{json_module, JSONModule} | Entries]. %% @private check_json_module(json) -> jose_json_otp; check_json_module(jiffy) -> jose_json_jiffy; check_json_module(jsx) -> jose_json_jsx; check_json_module(jsone) -> jose_json_jsone; check_json_module(ojson) -> jose_json_ojson; check_json_module(thoas) -> jose_json_thoas; check_json_module('Elixir.Jason') -> jose_json_jason; check_json_module('Elixir.Poison') -> Map = ?POISON_MAP, Bin = ?POISON_BIN, case jose_json_poison:encode(Map) of Bin -> jose_json_poison; _ -> check_json_module('Elixir.JOSE.Poison') end; check_json_module('Elixir.JOSE.Poison') -> Map = ?POISON_MAP, Bin = ?POISON_BIN, case code:ensure_loaded('Elixir.JOSE.Poison') of {module, 'Elixir.JOSE.Poison'} -> try jose_json_poison_lexical_encoder:encode(Map) of Bin -> jose_json_poison_lexical_encoder; _ -> check_json_module(jose_json_poison_compat_encoder) catch _:_ -> check_json_module(jose_json_poison_compat_encoder) end; _ -> check_json_module(jose_json_poison_compat_encoder) end; check_json_module(jose_json_poison_compat_encoder) -> Map = ?POISON_MAP, Bin = ?POISON_BIN, try jose_json_poison_compat_encoder:encode(Map) of Bin -> jose_json_poison_compat_encoder; _ -> jose_json_poison catch _:_ -> jose_json_poison end; check_json_module(Module) when is_atom(Module) -> Module. %% @private check_json_modules([Module | Modules]) -> case code:ensure_loaded(Module) of {module, Module} -> _ = application:ensure_all_started(Module), check_json_module(Module); _ -> check_json_modules(Modules) end; check_json_modules([]) -> jose_json_unsupported. %% @private check_sha3(false, Entries) -> check_sha3(jose_sha3_unsupported, Entries); check_sha3(true, Entries) -> check_sha3(jose_jwa_sha3, Entries); check_sha3(Fallback, Entries) -> true = ets:delete_object(?TAB, {sha3_module, jose_jwa_sha3}), true = ets:delete_object(?TAB, {sha3_module, jose_sha3_unsupported}), SHA3Module = case ets:lookup(?TAB, sha3_module) of [{sha3_module, M}] when is_atom(M) -> M; [] -> case application:get_env(jose, sha3_module, undefined) of undefined -> check_sha3_modules(Fallback, [libdecaf, keccakf1600]); M when is_atom(M) -> check_sha3_module(M) end end, %% Potentially used by Ed448 related functions below, needs to be inserted early. true = ets:insert(?TAB, {sha3_module, SHA3Module}), [{sha3_module, SHA3Module} | Entries]. %% @private check_sha3_module(keccakf1600) -> check_sha3_module(jose_sha3_keccakf1600); check_sha3_module(libdecaf) -> check_sha3_module(jose_sha3_libdecaf); check_sha3_module(jose_sha3_keccakf1600) -> _ = code:ensure_loaded(keccakf1600), case erlang:function_exported(keccakf1600, hash, 3) of false -> % version < 2 check_sha3_module(jose_sha3_keccakf1600_driver); true -> % version >= 2 check_sha3_module(jose_sha3_keccakf1600_nif) end; check_sha3_module(Module) when is_atom(Module) -> Module. %% @private check_sha3_modules(Fallback, [Module | Modules]) -> case code:ensure_loaded(Module) of {module, Module} -> _ = application:ensure_all_started(Module), check_sha3_module(Module); _ -> check_sha3_modules(Fallback, Modules) end; check_sha3_modules(Fallback, []) -> Fallback. %% @private check_crypto(false, Entries) -> check_crypto(jose_jwa_unsupported, Entries); check_crypto(true, Entries) -> check_crypto(jose_jwa_aes, Entries); check_crypto(Fallback, Entries) -> Ciphers = [ aes_cbc, aes_ecb, aes_gcm ], KeySizes = [ 128, 192, 256 ], CipherEntries0 = [begin case has_cipher(Cipher, KeySize) of false -> {{cipher, {Cipher, KeySize}}, {Fallback, {Cipher, KeySize}}}; {true, CryptoCipher} -> {{cipher, {Cipher, KeySize}}, {crypto, CryptoCipher}} end end || Cipher <- Ciphers, KeySize <- KeySizes], CipherEntries1 = case lists:keyfind(chacha20_poly1305_module, 1, Entries) of {chacha20_poly1305_module, jose_chacha20_poly1305_unsupported} -> CipherEntries0 ++ [{{cipher, {chacha20_poly1305, 256}}, {Fallback, {chacha20_poly1305, 256}}}]; _ -> CipherEntries0 ++ [{{cipher, {chacha20_poly1305, 256}}, {jose_chacha20_poly1305, {chacha20_poly1305, 256}}}] end, CipherEntries2 = case lists:keyfind(xchacha20_poly1305_module, 1, Entries) of {xchacha20_poly1305_module, jose_xchacha20_poly1305_unsupported} -> CipherEntries1 ++ [{{cipher, {xchacha20_poly1305, 256}}, {Fallback, {xchacha20_poly1305, 256}}}]; _ -> CipherEntries1 ++ [{{cipher, {xchacha20_poly1305, 256}}, {jose_xchacha20_poly1305, {xchacha20_poly1305, 256}}}] end, [CipherEntries2 | Entries]. %% @private check_public_key(Fallback, Entries) -> RSACrypt = check_rsa_crypt(Fallback), RSASign = check_rsa_sign(Fallback), [RSACrypt, RSASign | Entries]. %% @private check_rsa_crypt(false) -> check_rsa_crypt(jose_jwa_unsupported); check_rsa_crypt(true) -> check_rsa_crypt(jose_jwa_pkcs1); check_rsa_crypt(Fallback) -> Algorithms = [ %% Algorithm, LegacyOptions, FutureOptions {rsa1_5, [{rsa_pad, rsa_pkcs1_padding}], [{rsa_padding, rsa_pkcs1_padding}]}, {rsa_oaep, [{rsa_pad, rsa_pkcs1_oaep_padding}], [{rsa_padding, rsa_pkcs1_oaep_padding}]}, {rsa_oaep_256, notsup, [{rsa_padding, rsa_pkcs1_oaep_padding}, {rsa_oaep_md, sha256}]} ], _ = code:ensure_loaded(public_key), _ = application:ensure_all_started(public_key), Legacy = case erlang:function_exported(public_key, sign, 4) of false -> legacy; true -> future end, CryptEntries = [begin case has_rsa_crypt(Algorithm, Legacy, LegacyOptions, FutureOptions) of false -> {{rsa_crypt, Algorithm}, {Fallback, FutureOptions}}; {true, Module, Options} -> {{rsa_crypt, Algorithm}, {Module, Options}} end end || {Algorithm, LegacyOptions, FutureOptions} <- Algorithms], CryptEntries. %% @private check_rsa_sign(false) -> check_rsa_sign(jose_jwa_unsupported); check_rsa_sign(true) -> check_rsa_sign(jose_jwa_pkcs1); check_rsa_sign(Fallback) -> Paddings = [ rsa_pkcs1_padding, rsa_pkcs1_pss_padding ], _ = code:ensure_loaded(public_key), _ = application:ensure_all_started(public_key), Legacy = case erlang:function_exported(public_key, sign, 4) of false -> legacy; true -> future end, SignEntries = [begin case has_rsa_sign(Padding, Legacy, sha256) of false -> {{rsa_sign, Padding}, {Fallback, [{rsa_padding, Padding}]}}; {true, Module} -> {{rsa_sign, Padding}, {Module, undefined}}; {true, Module, Options} -> {{rsa_sign, Padding}, {Module, Options}} end end || Padding <- Paddings], SignEntries. %% @private check_xchacha20_poly1305(false, Entries) -> check_xchacha20_poly1305(jose_xchacha20_poly1305_unsupported, Entries); check_xchacha20_poly1305(true, Entries) -> check_xchacha20_poly1305(jose_jwa_xchacha20_poly1305, Entries); check_xchacha20_poly1305(Fallback, Entries) -> true = ets:delete_object(?TAB, {xchacha20_poly1305_module, jose_jwa_xchacha20_poly1305}), true = ets:delete_object(?TAB, {xchacha20_poly1305_module, jose_xchacha20_poly1305_unsupported}), ChaCha20Poly1305Module = case ets:lookup(?TAB, xchacha20_poly1305_module) of [{xchacha20_poly1305_module, M}] when is_atom(M) -> M; [] -> case application:get_env(jose, xchacha20_poly1305_module, undefined) of undefined -> check_xchacha20_poly1305_modules(Fallback, [libsodium, crypto]); M when is_atom(M) -> check_xchacha20_poly1305_module(M) end end, [{xchacha20_poly1305_module, ChaCha20Poly1305Module} | Entries]. %% @private check_xchacha20_poly1305_module(crypto) -> jose_xchacha20_poly1305_crypto; check_xchacha20_poly1305_module(libsodium) -> jose_xchacha20_poly1305_libsodium; check_xchacha20_poly1305_module(Module) when is_atom(Module) -> Module. %% @private check_xchacha20_poly1305_modules(Fallback, [Module | Modules]) -> case code:ensure_loaded(Module) of {module, Module} -> _ = application:ensure_all_started(Module), M = check_xchacha20_poly1305_module(Module), PT = crypto:strong_rand_bytes(8), CEK = crypto:strong_rand_bytes(32), IV = crypto:strong_rand_bytes(24), AAD = <<>>, try M:encrypt(PT, AAD, IV, CEK) of {CT, TAG} when is_binary(CT) andalso is_binary(TAG) -> try M:decrypt(CT, TAG, AAD, IV, CEK) of PT -> M; _ -> check_xchacha20_poly1305_modules(Fallback, Modules) catch _:_ -> check_xchacha20_poly1305_modules(Fallback, Modules) end; _ -> check_xchacha20_poly1305_modules(Fallback, Modules) catch _:_ -> check_xchacha20_poly1305_modules(Fallback, Modules) end; _ -> check_xchacha20_poly1305_modules(Fallback, Modules) end; check_xchacha20_poly1305_modules(Fallback, []) -> Fallback. %% @private has_cipher(aes_cbc, KeySize) -> Key = << 0:KeySize >>, IV = << 0:128 >>, PlainText = jose_jwa_pkcs7:pad(<<>>), case has_block_cipher(aes_cbc, {Key, IV, PlainText}) of false -> Cipher = list_to_atom("aes_" ++ integer_to_list(KeySize) ++ "_cbc"), has_block_cipher(Cipher, {Key, IV, PlainText}); Other -> Other end; has_cipher(aes_ecb, KeySize) -> Key = << 0:KeySize >>, PlainText = jose_jwa_pkcs7:pad(<<>>), case has_block_cipher(aes_ecb, {Key, PlainText}) of false -> Cipher = list_to_atom("aes_" ++ integer_to_list(KeySize) ++ "_ecb"), has_block_cipher(Cipher, {Key, PlainText}); Other -> Other end; has_cipher(aes_gcm, KeySize) -> Key = << 0:KeySize >>, IV = << 0:96 >>, AAD = <<>>, PlainText = jose_jwa_pkcs7:pad(<<>>), case has_block_cipher(aes_gcm, {Key, IV, AAD, PlainText}) of false -> Cipher = list_to_atom("aes_" ++ integer_to_list(KeySize) ++ "_gcm"), has_block_cipher(Cipher, {Key, IV, AAD, PlainText}); Other -> Other end. %% @private has_block_cipher(Cipher, {Key, PlainText}) -> try jose_crypto_compat:crypto_one_time(Cipher, Key, PlainText, true) of CipherText when is_binary(CipherText) -> try jose_crypto_compat:crypto_one_time(Cipher, Key, CipherText, false) of PlainText -> {true, Cipher}; _ -> false catch _:_ -> false end; _ -> false catch _:_ -> false end; has_block_cipher(Cipher, {Key, IV, PlainText}) -> try jose_crypto_compat:crypto_one_time(Cipher, Key, IV, PlainText, true) of CipherText when is_binary(CipherText) -> try jose_crypto_compat:crypto_one_time(Cipher, Key, IV, CipherText, false) of PlainText -> {true, Cipher}; _ -> false catch _:_ -> false end; _ -> false catch _:_ -> false end; has_block_cipher(Cipher, {Key, IV, AAD, PlainText}) -> try jose_crypto_compat:crypto_one_time(Cipher, Key, IV, {AAD, PlainText}, true) of {CipherText, CipherTag} when is_binary(CipherText) andalso is_binary(CipherTag) -> try jose_crypto_compat:crypto_one_time(Cipher, Key, IV, {AAD, CipherText, CipherTag}, false) of PlainText -> {true, Cipher}; _ -> false catch _:_ -> false end; _ -> false catch _:_ -> false end. %% @private has_rsa_crypt(Algorithm, future, _LegacyOptions, FutureOptions) -> PlainText = << 0:8 >>, PublicKey = rsa_public_key(), try public_key:encrypt_public(PlainText, PublicKey, FutureOptions) of CipherText when is_binary(CipherText) -> PrivateKey = rsa_private_key(), try public_key:decrypt_private(CipherText, PrivateKey, FutureOptions) of PlainText -> try public_key:decrypt_private(rsa_ciphertext(Algorithm), PrivateKey, FutureOptions) of <<"ciphertext">> -> {true, public_key, FutureOptions}; _ -> false catch _:_ -> false end; _ -> false catch _:_ -> false end; _ -> false catch _:_ -> false end; has_rsa_crypt(_Algorithm, legacy, notsup, _FutureOptions) -> false; has_rsa_crypt(Algorithm, legacy, LegacyOptions, _FutureOptions) -> PlainText = << 0:8 >>, PublicKey = rsa_public_key(), try public_key:encrypt_public(PlainText, PublicKey, LegacyOptions) of CipherText when is_binary(CipherText) -> PrivateKey = rsa_private_key(), try public_key:decrypt_private(CipherText, PrivateKey, LegacyOptions) of PlainText -> try public_key:decrypt_private(rsa_ciphertext(Algorithm), PrivateKey, LegacyOptions) of <<"ciphertext">> -> {true, public_key, LegacyOptions}; _ -> false catch _:_ -> false end; _ -> false catch _:_ -> false end; _ -> false catch _:_ -> false end. %% @private has_rsa_sign(Padding, future, DigestType) -> Message = << 0:8 >>, PrivateKey = rsa_private_key(), Options = [{rsa_padding, Padding}], try public_key:sign(Message, DigestType, PrivateKey, Options) of Signature when is_binary(Signature) -> PublicKey = rsa_public_key(), try public_key:verify(Message, DigestType, Signature, PublicKey, Options) of true -> {true, public_key, Options}; _ -> false catch _:_ -> false end; _ -> false catch _:_ -> false end; has_rsa_sign(rsa_pkcs1_padding, legacy, DigestType) -> Message = << 0:8 >>, PrivateKey = rsa_private_key(), try public_key:sign(Message, DigestType, PrivateKey) of Signature when is_binary(Signature) -> PublicKey = rsa_public_key(), try public_key:verify(Message, DigestType, Signature, PublicKey) of true -> {true, public_key}; _ -> false catch _:_ -> false end; _ -> false catch _:_ -> false end; has_rsa_sign(_Padding, legacy, _DigestType) -> false. %% @private read_pem_key(PEM) -> public_key:pem_entry_decode(hd(public_key:pem_decode(PEM))). %% @private rsa_ciphertext(rsa1_5) -> << 16#67, 16#3F, 16#BF, 16#D4, 16#93, 16#1E, 16#6C, 16#54, 16#67, 16#DE, 16#29, 16#3C, 16#71, 16#5F, 16#95, 16#BE, 16#69, 16#99, 16#D3, 16#6C, 16#E4, 16#81, 16#1E, 16#49, 16#BE, 16#5D, 16#91, 16#85, 16#E7, 16#1D, 16#04, 16#C5, 16#38, 16#0A, 16#6F, 16#3F, 16#32, 16#2C, 16#3D, 16#67, 16#53, 16#B1, 16#EA, 16#D7, 16#2E, 16#ED, 16#6A, 16#7A, 16#EB, 16#49, 16#79, 16#71, 16#CA, 16#F5, 16#71, 16#67, 16#FA, 16#8B, 16#B8, 16#A8, 16#30, 16#59, 16#2E, 16#88, 16#98, 16#19, 16#AE, 16#B2, 16#94, 16#BA, 16#6E, 16#D2, 16#EF, 16#28, 16#BE, 16#04, 16#4F, 16#90, 16#77, 16#CA, 16#3D, 16#11, 16#2B, 16#E7, 16#17, 16#D8, 16#89, 16#7F, 16#EC, 16#7A, 16#2C, 16#70, 16#A5, 16#08, 16#FB, 16#5B >>; rsa_ciphertext(rsa_oaep) -> << 16#B8, 16#F7, 16#0C, 16#A8, 16#F8, 16#30, 16#2A, 16#E9, 16#68, 16#8A, 16#DB, 16#3E, 16#5D, 16#AE, 16#84, 16#A7, 16#16, 16#FA, 16#9D, 16#E2, 16#FC, 16#81, 16#F7, 16#DF, 16#A8, 16#DB, 16#8F, 16#4F, 16#92, 16#A1, 16#51, 16#9E, 16#6B, 16#C5, 16#36, 16#CE, 16#93, 16#10, 16#11, 16#D9, 16#D5, 16#C2, 16#C9, 16#85, 16#14, 16#EF, 16#D5, 16#C3, 16#AC, 16#63, 16#BE, 16#49, 16#FA, 16#02, 16#1A, 16#FC, 16#3D, 16#D0, 16#2C, 16#83, 16#C5, 16#76, 16#1D, 16#F5, 16#FA, 16#A0, 16#D7, 16#42, 16#ED, 16#3F, 16#A4, 16#12, 16#32, 16#14, 16#93, 16#51, 16#79, 16#2E, 16#40, 16#FB, 16#14, 16#18, 16#DF, 16#30, 16#62, 16#9F, 16#F3, 16#59, 16#5D, 16#83, 16#0F, 16#4A, 16#8F, 16#9B, 16#3F, 16#39 >>; rsa_ciphertext(rsa_oaep_256) -> << 16#09, 16#24, 16#EA, 16#EB, 16#D4, 16#EF, 16#00, 16#BE, 16#8E, 16#02, 16#BE, 16#25, 16#24, 16#24, 16#18, 16#81, 16#8D, 16#7A, 16#A2, 16#EB, 16#F1, 16#BE, 16#5C, 16#DC, 16#D0, 16#71, 16#43, 16#09, 16#53, 16#12, 16#44, 16#AD, 16#8A, 16#CD, 16#F8, 16#45, 16#7F, 16#1F, 16#30, 16#B6, 16#54, 16#8E, 16#AB, 16#D2, 16#10, 16#14, 16#BC, 16#CE, 16#7A, 16#99, 16#DC, 16#A6, 16#8D, 16#16, 16#5A, 16#A0, 16#50, 16#3A, 16#93, 16#0E, 16#53, 16#4A, 16#B5, 16#6B, 16#51, 16#E8, 16#43, 16#8F, 16#BD, 16#2D, 16#E0, 16#63, 16#36, 16#24, 16#5B, 16#8D, 16#DD, 16#98, 16#AC, 16#37, 16#7C, 16#16, 16#DB, 16#03, 16#C8, 16#BD, 16#22, 16#D2, 16#15, 16#98, 16#91, 16#B7, 16#3C, 16#01, 16#CF, 16#0E >>. %% @private rsa_public_key() -> read_pem_key(<< "-----BEGIN PUBLIC KEY-----\n" "MHwwDQYJKoZIhvcNAQEBBQADawAwaAJhAL/f1xISwDSm4m6sYHm6WD4WK2egfyfZ\n" "hd0w4iVeZvHjUurZVRVQojs7hZC7DKBfjShl6M7BT9j7gkaYOXlJHLhK6/J+Zr0C\n" "g6PMkkbejQltgr4fUzbG8zUBo7BMs4Xm0wIDAQAB\n" "-----END PUBLIC KEY-----\n" >>). %% @private rsa_private_key() -> read_pem_key(<< "-----BEGIN RSA PRIVATE KEY-----\n" "MIIBzAIBAAJhAL/f1xISwDSm4m6sYHm6WD4WK2egfyfZhd0w4iVeZvHjUurZVRVQ\n" "ojs7hZC7DKBfjShl6M7BT9j7gkaYOXlJHLhK6/J+Zr0Cg6PMkkbejQltgr4fUzbG\n" "8zUBo7BMs4Xm0wIDAQABAmEAiisNO7WG9SNLoPi+TEn061iZjvjTOAX60Io3/0LY\n" "jMzu07EHBN9Yw6CcENmxQPcsdIRlSKLlt+UeUdBES6Zoccek5fJl+gnqExeX2Av1\n" "v0Y8vIP2yejV7Pw+SrNxpY5ZAjEA+WMEZEgFrK8cPJmZLR9Kj3jvN5P+AmIKzg00\n" "VMW93rS+sdHmYQUStqBuu2XRw5SlAjEAxPZlLCZ83GrqdStcmChCFpflzCRyU/wC\n" "qVVP8QYfct49Cca3TyC8lCywwXI5s5wXAjA1JQK0lByRdiegSmM4GGj9NhpUT7db\n" "rqT60BmMzy7tHLtejYp4tmoMfRfb25DeCvkCMQCO+usQ9NOZUsfmzNaH4lmvew8n\n" "daHFE+F+uV6x8ibsRSZ8LVQuze33hsW9eEUo/HsCMQDKkImE3DSqHgwfKPjtecFH\n" "oftdsGQ4u+MUGkST94Hh8479oNYaNveCRDOTJ4GJjUE=\n" "-----END RSA PRIVATE KEY-----\n" >>). potatosalad-erlang-jose-30ac019/src/jwt/0000775000232200023220000000000015267717064020535 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/src/jwt/jose_jwt.erl0000664000232200023220000002060315267717064023066 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 21 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jwt). -include("jose_jwe.hrl"). -include("jose_jwk.hrl"). -include("jose_jws.hrl"). -include("jose_jwt.hrl"). %% Decode API -export([from/1]). -export([from_binary/1]). -export([from_file/1]). -export([from_map/1]). %% Encode API -export([to_binary/1]). -export([to_file/2]). -export([to_map/1]). %% API -export([decrypt/2]). -export([encrypt/2]). -export([encrypt/3]). -export([merge/2]). -export([peek/1]). -export([peek_payload/1]). -export([peek_protected/1]). -export([sign/2]). -export([sign/3]). -export([verify/2]). -export([verify_strict/3]). %%==================================================================== %% Decode API functions %%==================================================================== from(List) when is_list(List) -> [from(Element) || Element <- List]; from({Modules, Map}) when is_map(Modules) andalso is_map(Map) -> from_map({Modules, Map}); from({Modules, Binary}) when is_map(Modules) andalso is_binary(Binary) -> from_binary({Modules, Binary}); from(JWT=#jose_jwt{}) -> JWT; from(Other) when is_map(Other) orelse is_binary(Other) -> from({#{}, Other}). from_binary(List) when is_list(List) -> [from_binary(Element) || Element <- List]; from_binary({Modules, Binary}) when is_map(Modules) andalso is_binary(Binary) -> from_map({Modules, jose:decode(Binary)}); from_binary(Binary) when is_binary(Binary) -> from_binary({#{}, Binary}). from_file({Modules, File}) when is_map(Modules) andalso (is_binary(File) orelse is_list(File)) -> case file:read_file(File) of {ok, Binary} -> from_binary({Modules, Binary}); ReadError -> ReadError end; from_file(File) when is_binary(File) orelse is_list(File) -> from_file({#{}, File}). from_map(List) when is_list(List) -> [from_map(Element) || Element <- List]; from_map(Map) when is_map(Map) -> from_map({#{}, Map}); from_map({Modules, Map}) when is_map(Modules) andalso is_map(Map) -> from_map({#jose_jwt{}, Modules, Map}); from_map({JWT, _Modules, Fields}) -> JWT#jose_jwt{ fields = Fields }. %%==================================================================== %% Encode API functions %%==================================================================== to_binary(List) when is_list(List) -> [to_binary(Element) || Element <- List]; to_binary(JWT=#jose_jwt{}) -> {Modules, Map} = to_map(JWT), {Modules, jose:encode(Map)}; to_binary(Other) -> to_binary(from(Other)). to_file(File, JWT=#jose_jwt{}) when is_binary(File) orelse is_list(File) -> {Modules, Binary} = to_binary(JWT), case file:write_file(File, Binary) of ok -> {Modules, File}; WriteError -> WriteError end; to_file(File, Other) when is_binary(File) orelse is_list(File) -> to_file(File, from(Other)). to_map(List) when is_list(List) -> [to_map(Element) || Element <- List]; to_map(JWT=#jose_jwt{fields=Fields}) -> record_to_map(JWT, #{}, Fields); to_map(Other) -> to_map(from(Other)). %%==================================================================== %% API functions %%==================================================================== decrypt(Key, {Modules, Encrypted}) when is_map(Modules) andalso is_map(Encrypted) -> {JWTBinary, JWE=#jose_jwe{}} = jose_jwe:block_decrypt(Key, {Modules, Encrypted}), {JWE, from_binary({Modules, JWTBinary})}; decrypt(Key, {Modules, Encrypted = << ${, _/binary >>}) when is_map(Modules) -> EncryptedMap = jose:decode(Encrypted), decrypt(Key, {Modules, EncryptedMap}); decrypt(Key, {Modules, Encrypted}) when is_map(Modules) andalso is_binary(Encrypted) -> {JWTBinary, JWE=#jose_jwe{}} = jose_jwe:block_decrypt(Key, {Modules, Encrypted}), {JWE, from_binary({Modules, JWTBinary})}; decrypt(Key, Encrypted) when is_map(Encrypted) orelse is_binary(Encrypted) -> decrypt(Key, {#{}, Encrypted}). encrypt(JWK=#jose_jwk{}, JWT=#jose_jwt{}) -> encrypt(JWK, jose_jwk:block_encryptor(JWK), JWT); encrypt(JWKOther, JWTOther) -> encrypt(jose_jwk:from(JWKOther), from(JWTOther)). encrypt(JWK=#jose_jwk{}, JWE=#jose_jwe{}, JWT=#jose_jwt{}) -> {Modules0, JWTBinary} = to_binary(JWT), {Modules1, SignedMap} = jose_jwk:block_encrypt(JWTBinary, JWE, JWK), {maps:merge(Modules0, Modules1), SignedMap}; encrypt(JWKOther, {JWEModules, JWEMap=#{ <<"typ">> := _ }}, JWTOther) -> encrypt(JWKOther, jose_jwe:from({JWEModules, JWEMap}), JWTOther); encrypt(JWKOther, {JWEModules, JWEMap0}, JWTOther) when is_map(JWEMap0) -> Keys = [<<"typ">>] -- maps:keys(JWEMap0), JWEMap1 = normalize_block_encryptor(Keys, JWEMap0), encrypt(JWKOther, {JWEModules, JWEMap1}, JWTOther); encrypt(JWKOther, JWEMap, JWTOther) when is_map(JWEMap) -> encrypt(JWKOther, {#{}, JWEMap}, JWTOther); encrypt(JWKOther, JWEOther, JWTOther) -> encrypt(jose_jwk:from(JWKOther), jose_jwe:from(JWEOther), from(JWTOther)). merge(LeftJWT=#jose_jwt{}, RightMap) when is_map(RightMap) -> {Modules, LeftMap} = to_map(LeftJWT), from_map({Modules, maps:merge(LeftMap, RightMap)}); merge(LeftOther, RightJWT=#jose_jwt{}) -> merge(LeftOther, element(2, to_map(RightJWT))); merge(LeftOther, RightMap) when is_map(RightMap) -> merge(from(LeftOther), RightMap). peek(Signed) -> peek_payload(Signed). peek_payload(Signed) -> from(jose_jws:peek_payload(Signed)). peek_protected(Signed) -> jose_jws:from(jose_jws:peek_protected(Signed)). sign(JWK=#jose_jwk{}, JWT=#jose_jwt{}) -> sign(JWK, jose_jwk:signer(JWK), JWT); sign([], JWTOther) -> erlang:error({badarg, [[], JWTOther]}); sign(JWKOther, JWTOther) -> sign(jose_jwk:from(JWKOther), from(JWTOther)). sign(JWK=#jose_jwk{}, JWS=#jose_jws{}, JWT=#jose_jwt{}) -> {Modules0, JWTBinary} = to_binary(JWT), {Modules1, SignedMap} = jose_jwk:sign(JWTBinary, JWS, JWK), {maps:merge(Modules0, Modules1), SignedMap}; sign(JWKOther, {JWSModules, JWSMap=#{ <<"typ">> := _ }}, JWTOther) -> sign(JWKOther, jose_jws:from({JWSModules, JWSMap}), JWTOther); sign(JWKOther, {JWSModules, JWSMap0}, JWTOther) when is_map(JWSMap0) -> Keys = [<<"typ">>] -- maps:keys(JWSMap0), JWSMap1 = normalize_signer(Keys, JWSMap0), sign(JWKOther, {JWSModules, JWSMap1}, JWTOther); sign(JWKOther, JWSMap, JWTOther) when is_map(JWSMap) -> sign(JWKOther, {#{}, JWSMap}, JWTOther); sign([], JWSOther, JWTOther) -> erlang:error({badarg, [[], JWSOther, JWTOther]}); sign(JWKOther, JWSOther, JWTOther) -> sign(jose_jwk:from(JWKOther), jose_jws:from(JWSOther), from(JWTOther)). verify(JWK=#jose_jwk{}, {Modules, Signed}) -> {Verified, JWTBinary, JWS} = jose_jwk:verify({Modules, Signed}, JWK), {Verified, from_binary({Modules, JWTBinary}), JWS}; verify(Other, SignedBinary) when is_binary(SignedBinary) -> verify(Other, {#{}, SignedBinary}); verify(Other, SignedMap) when is_map(SignedMap) -> verify(Other, {#{}, SignedMap}); verify(JWK=#jose_jwk{}, Signed) -> erlang:error({badarg, [JWK, Signed]}); verify([], Signed) -> erlang:error({badarg, [[], Signed]}); verify(Other, Signed) -> verify(jose_jwk:from(Other), Signed). verify_strict(JWK=#jose_jwk{}, Allow, {Modules, Signed}) -> {Verified, JWTBinary, JWS} = jose_jwk:verify_strict({Modules, Signed}, Allow, JWK), {Verified, from_binary({Modules, JWTBinary}), JWS}; verify_strict(Other, Allow, SignedBinary) when is_binary(SignedBinary) -> verify_strict(Other, Allow, {#{}, SignedBinary}); verify_strict(Other, Allow, SignedMap) when is_map(SignedMap) -> verify_strict(Other, Allow, {#{}, SignedMap}); verify_strict(JWK=#jose_jwk{}, Allow, Signed) -> erlang:error({badarg, [JWK, Allow, Signed]}); verify_strict([], Allow, Signed) -> erlang:error({badarg, [[], Allow, Signed]}); verify_strict(Other, Allow, Signed) -> verify_strict(jose_jwk:from(Other), Allow, Signed). %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private normalize_block_encryptor([<<"typ">> | Keys], Map) -> normalize_block_encryptor(Keys, Map#{ <<"typ">> => <<"JWT">> }); normalize_block_encryptor([], Map) -> Map. %% @private normalize_signer([<<"typ">> | Keys], Map) -> normalize_signer(Keys, Map#{ <<"typ">> => <<"JWT">> }); normalize_signer([], Map) -> Map. %% @private record_to_map(_JWT, Modules, Fields) -> {Modules, Fields}. potatosalad-erlang-jose-30ac019/src/jose_crypto_compat.erl0000664000232200023220000000656115267717064024350 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%%------------------------------------------------------------------- -module(jose_crypto_compat). -include("jose_compat.hrl"). %% API -export([crypto_init/4]). -export([crypto_one_time/4]). -export([crypto_one_time/5]). -export([crypto_update_encrypt/2]). -export([mac/3]). -export([mac/4]). -export([mac/5]). %%==================================================================== %% API functions %%==================================================================== -ifdef(JOSE_CRYPTO_OTP_23). %% "New API" for OTP 23 and higher crypto_init(Cipher, Key, IV, FlagOrOptions) -> crypto:crypto_init(Cipher, Key, IV, FlagOrOptions). crypto_one_time(Cipher, Key, Data, FlagOrOptions) -> crypto:crypto_one_time(Cipher, Key, Data, FlagOrOptions). crypto_one_time(Cipher, Key, IV, {AAD, PlainText}, FlagOrOptions) -> crypto:crypto_one_time_aead(Cipher, Key, IV, PlainText, AAD, FlagOrOptions); crypto_one_time(Cipher, Key, IV, {AAD, PlainText, TagOrTagLength}, FlagOrOptions) -> crypto:crypto_one_time_aead(Cipher, Key, IV, PlainText, AAD, TagOrTagLength, FlagOrOptions); crypto_one_time(Cipher, Key, IV, Data, FlagOrOptions) -> crypto:crypto_one_time(Cipher, Key, IV, Data, FlagOrOptions). crypto_update_encrypt(State, Data) -> Result = crypto:crypto_update(State, Data), {State, Result}. mac(Type, Key, Data) -> crypto:mac(Type, Key, Data). mac(Type, SubType, Key, Data) -> crypto:mac(Type, SubType, Key, Data). mac(Type, SubType, Key, Data, MacLength) -> crypto:macN(Type, SubType, Key, Data, MacLength). -else. %% "Old API" for OTP 22 and earlier crypto_init(Cipher, Key, IV, _FlagOrOptions) -> crypto:stream_init(legacy_cipher_iv(Cipher), Key, IV). crypto_one_time(Cipher, Key, Data, true) -> crypto:block_encrypt(legacy_cipher_no_iv(Cipher), Key, Data); crypto_one_time(Cipher, Key, Data, false) -> crypto:block_decrypt(legacy_cipher_no_iv(Cipher), Key, Data). crypto_one_time(Cipher, Key, IV, Data, true) -> crypto:block_encrypt(legacy_cipher_iv(Cipher), Key, IV, Data); crypto_one_time(Cipher, Key, IV, Data, false) -> crypto:block_decrypt(legacy_cipher_iv(Cipher), Key, IV, Data). crypto_update_encrypt(State, Data) -> crypto:stream_encrypt(State, Data). mac(poly1305, Key, Data) -> crypto:poly1305(Key, Data). mac(hmac, SubType, Key, Data) -> crypto:hmac(SubType, Key, Data). mac(hmac, SubType, Key, Data, MacLength) -> crypto:hmac(SubType, Key, Data, MacLength). -endif. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- -ifndef(JOSE_CRYPTO_OTP_23). legacy_cipher_no_iv(aes_128_ecb) -> aes_ecb; legacy_cipher_no_iv(aes_192_ecb) -> aes_ecb; legacy_cipher_no_iv(aes_256_ecb) -> aes_ecb; legacy_cipher_no_iv(Cipher) -> Cipher. legacy_cipher_iv(aes_128_cbc) -> aes_cbc128; legacy_cipher_iv(aes_192_cbc) -> aes_cbc192; legacy_cipher_iv(aes_256_cbc) -> aes_cbc256; legacy_cipher_iv(aes_128_ctr) -> aes_ctr; legacy_cipher_iv(aes_192_ctr) -> aes_ctr; legacy_cipher_iv(aes_256_ctr) -> aes_ctr; legacy_cipher_iv(Cipher) -> Cipher. -endif. potatosalad-erlang-jose-30ac019/src/jws/0000775000232200023220000000000015267717064020534 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/src/jws/jose_jws_alg_none.erl0000664000232200023220000000354615267717064024735 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 23 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jws_alg_none). -behaviour(jose_jws). -behaviour(jose_jws_alg). %% jose_jws callbacks -export([from_map/1]). -export([to_map/2]). %% jose_jws_alg callbacks -export([generate_key/2]). -export([sign/3]). -export([verify/4]). %% API %% Types -type alg() :: none. -export_type([alg/0]). %%==================================================================== %% jose_jws callbacks %%==================================================================== from_map(F = #{ <<"alg">> := <<"none">> }) -> {none, maps:remove(<<"alg">>, F)}. to_map(none, F) -> F#{ <<"alg">> => <<"none">> }. %%==================================================================== %% jose_jws_alg callbacks %%==================================================================== generate_key(none, _Fields) -> erlang:error(not_supported). sign(_Key, _Message, none) -> case jose_jwa:unsecured_signing() of true -> <<>>; _ -> erlang:error(not_supported) end. verify(_Key, _Message, <<>>, none) -> case jose_jwa:unsecured_signing() of true -> true; _ -> false end; verify(_Key, _Message, _Signature, none) -> false. %%==================================================================== %% API functions %%==================================================================== %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jws/jose_jws_alg_poly1305.erl0000664000232200023220000000523115267717064025263 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 08 Aug 2016 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jws_alg_poly1305). -behaviour(jose_jws). -behaviour(jose_jws_alg). -include("jose_jwk.hrl"). %% jose_jws callbacks -export([from_map/1]). -export([to_map/2]). %% jose_jws_alg callbacks -export([generate_key/2]). -export([presign/2]). -export([sign/3]). -export([verify/4]). %% API %% Types -record('Poly1305', { nonce = undefined :: undefined | << _:12 >> }). -type alg() :: #'Poly1305'{}. -export_type([alg/0]). %%==================================================================== %% jose_jws callbacks %%==================================================================== from_map(F = #{ <<"alg">> := <<"Poly1305">> }) -> from_map(maps:remove(<<"alg">>, F), #'Poly1305'{}). to_map(#'Poly1305'{nonce=undefined}, F) -> F#{ <<"alg">> => <<"Poly1305">> }; to_map(#'Poly1305'{nonce=Nonce}, F) -> F#{ <<"alg">> => <<"Poly1305">>, <<"nonce">> => jose_jwa_base64url:encode(Nonce) }. %%==================================================================== %% jose_jws_alg callbacks %%==================================================================== generate_key(#'Poly1305'{}, _Fields) -> jose_jws_alg:generate_key({oct, 32}, <<"Poly1305">>). presign(_Key, ALG=#'Poly1305'{nonce=undefined}) -> Nonce = crypto:strong_rand_bytes(12), ALG#'Poly1305'{nonce=Nonce}; presign(_Key, ALG) -> ALG. sign(#jose_jwk{kty={KTYModule, KTY}}, Message, ALG=#'Poly1305'{nonce=Nonce}) when is_binary(Nonce) andalso bit_size(Nonce) == 96 -> KTYModule:sign(Message, ALG, KTY). verify(_Key, _Message, _Signature, #'Poly1305'{nonce=undefined}) -> false; verify(#jose_jwk{kty={KTYModule, KTY}}, Message, Signature, ALG=#'Poly1305'{nonce=Nonce}) when is_binary(Nonce) andalso bit_size(Nonce) == 96 -> KTYModule:verify(Message, ALG, Signature, KTY). %%==================================================================== %% API functions %%==================================================================== %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private from_map(F = #{ <<"nonce">> := Nonce }, ALG) -> from_map(maps:remove(<<"nonce">>, F), ALG#'Poly1305'{nonce=jose_jwa_base64url:decode(Nonce)}); from_map(F, ALG) -> {ALG, F}. potatosalad-erlang-jose-30ac019/src/jws/jose_jws_alg_rsa_pss.erl0000664000232200023220000000445415267717064025447 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 23 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jws_alg_rsa_pss). -behaviour(jose_jws). -behaviour(jose_jws_alg). -include("jose_jwk.hrl"). %% jose_jws callbacks -export([from_map/1]). -export([to_map/2]). %% jose_jws_alg callbacks -export([generate_key/2]). -export([sign/3]). -export([verify/4]). %% API %% Types -type alg() :: 'PS256' | 'PS384' | 'PS512'. -export_type([alg/0]). %%==================================================================== %% jose_jws callbacks %%==================================================================== from_map(F = #{ <<"alg">> := <<"PS256">> }) -> {'PS256', maps:remove(<<"alg">>, F)}; from_map(F = #{ <<"alg">> := <<"PS384">> }) -> {'PS384', maps:remove(<<"alg">>, F)}; from_map(F = #{ <<"alg">> := <<"PS512">> }) -> {'PS512', maps:remove(<<"alg">>, F)}. to_map('PS256', F) -> F#{ <<"alg">> => <<"PS256">> }; to_map('PS384', F) -> F#{ <<"alg">> => <<"PS384">> }; to_map('PS512', F) -> F#{ <<"alg">> => <<"PS512">> }. %%==================================================================== %% jose_jws_alg callbacks %%==================================================================== generate_key('PS256', _Fields) -> jose_jws_alg:generate_key({rsa, 2048}, <<"PS256">>); generate_key('PS384', _Fields) -> jose_jws_alg:generate_key({rsa, 3072}, <<"PS384">>); generate_key('PS512', _Fields) -> jose_jws_alg:generate_key({rsa, 4096}, <<"PS512">>). sign(#jose_jwk{kty={KTYModule, KTY}}, Message, ALG) -> KTYModule:sign(Message, ALG, KTY). verify(#jose_jwk{kty={KTYModule, KTY}}, Message, Signature, ALG) -> KTYModule:verify(Message, ALG, Signature, KTY). %%==================================================================== %% API functions %%==================================================================== %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jws/jose_jws_alg_rsa_pkcs1_v1_5.erl0000664000232200023220000000502415267717064026507 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 23 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jws_alg_rsa_pkcs1_v1_5). -behaviour(jose_jws). -behaviour(jose_jws_alg). -include("jose_jwk.hrl"). %% jose_jws callbacks -export([from_map/1]). -export([to_map/2]). %% jose_jws_alg callbacks -export([generate_key/2]). -export([sign/3]). -export([verify/4]). %% API %% Types -type alg() :: 'RS1' | 'RS256' | 'RS384' | 'RS512'. -export_type([alg/0]). %%==================================================================== %% 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">> }) -> {'RS384', maps:remove(<<"alg">>, F)}; 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) -> F#{ <<"alg">> => <<"RS384">> }; to_map('RS512', F) -> F#{ <<"alg">> => <<"RS512">> }. %%==================================================================== %% 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) -> jose_jws_alg:generate_key({rsa, 3072}, <<"RS384">>); generate_key('RS512', _Fields) -> jose_jws_alg:generate_key({rsa, 4096}, <<"RS512">>). sign(#jose_jwk{kty={KTYModule, KTY}}, Message, ALG) -> KTYModule:sign(Message, ALG, KTY). verify(#jose_jwk{kty={KTYModule, KTY}}, Message, Signature, ALG) -> KTYModule:verify(Message, ALG, Signature, KTY). %%==================================================================== %% API functions %%==================================================================== %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jws/jose_jws_alg_ecdsa.erl0000664000232200023220000000507415267717064025053 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 23 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jws_alg_ecdsa). -behaviour(jose_jws). -behaviour(jose_jws_alg). -include("jose_jwk.hrl"). %% jose_jws callbacks -export([from_map/1]). -export([to_map/2]). %% jose_jws_alg callbacks -export([generate_key/2]). -export([sign/3]). -export([verify/4]). %% API %% Types -type alg() :: 'ES256K' | 'ES256' | 'ES384' | 'ES512'. -export_type([alg/0]). %%==================================================================== %% 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">> }) -> {'ES384', maps:remove(<<"alg">>, F)}; 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) -> F#{ <<"alg">> => <<"ES384">> }; to_map('ES512', F) -> F#{ <<"alg">> => <<"ES512">> }. %%==================================================================== %% 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) -> jose_jws_alg:generate_key({ec, <<"P-384">>}, <<"ES384">>); generate_key('ES512', _Fields) -> jose_jws_alg:generate_key({ec, <<"P-521">>}, <<"ES512">>). sign(#jose_jwk{kty={KTYModule, KTY}}, Message, ALG) -> KTYModule:sign(Message, ALG, KTY). verify(#jose_jwk{kty={KTYModule, KTY}}, Message, Signature, ALG) -> KTYModule:verify(Message, ALG, Signature, KTY). %%==================================================================== %% API functions %%==================================================================== %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jws/jose_jws.erl0000664000232200023220000004530215267717064023067 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc JSON Web Signature (JWS) %%% See RFC 7515: https://tools.ietf.org/html/rfc7515 %%% See RFC 7797: https://tools.ietf.org/html/rfc7797 %%% @end %%% Created : 21 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jws). -include("jose_jwk.hrl"). -include("jose_jws.hrl"). -callback from_map(Fields) -> State when Fields :: map(), State :: any(). -callback to_map(State, Fields) -> Map when State :: any(), Fields :: map(), Map :: map(). %% Decode API -export([from/1]). -export([from_binary/1]). -export([from_file/1]). -export([from_map/1]). %% Encode API -export([to_binary/1]). -export([to_file/2]). -export([to_map/1]). %% API -export([compact/1]). -export([expand/1]). -export([generate_key/1]). -export([merge/2]). -export([peek/1]). -export([peek_payload/1]). -export([peek_protected/1]). -export([peek_signature/1]). -export([sign/3]). -export([sign/4]). -export([signing_input/2]). -export([signing_input/3]). -export([verify/2]). -export([verify_strict/3]). -define(ALG_ECDSA_MODULE, jose_jws_alg_ecdsa). -define(ALG_EDDSA_MODULE, jose_jws_alg_eddsa). -define(ALG_HMAC_MODULE, jose_jws_alg_hmac). -define(ALG_NONE_MODULE, jose_jws_alg_none). -define(ALG_POLY1305_MODULE, jose_jws_alg_poly1305). -define(ALG_RSA_PKCS1_V1_5_MODULE, jose_jws_alg_rsa_pkcs1_v1_5). -define(ALG_RSA_PSS_MODULE, jose_jws_alg_rsa_pss). %%==================================================================== %% Decode API functions %%==================================================================== from(List) when is_list(List) -> [from(Element) || Element <- List]; from({Modules, Map}) when is_map(Modules) andalso is_map(Map) -> from_map({Modules, Map}); from({Modules, Binary}) when is_map(Modules) andalso is_binary(Binary) -> from_binary({Modules, Binary}); from(JWS=#jose_jws{}) -> JWS; from(Other) when is_map(Other) orelse is_binary(Other) -> from({#{}, Other}). from_binary(List) when is_list(List) -> [from_binary(Element) || Element <- List]; from_binary({Modules, Binary}) when is_map(Modules) andalso is_binary(Binary) -> from_map({Modules, jose:decode(Binary)}); from_binary(Binary) when is_binary(Binary) -> from_binary({#{}, Binary}). from_file({Modules, File}) when is_map(Modules) andalso (is_binary(File) orelse is_list(File)) -> case file:read_file(File) of {ok, Binary} -> from_binary({Modules, Binary}); ReadError -> ReadError end; from_file(File) when is_binary(File) orelse is_list(File) -> from_file({#{}, File}). from_map(List) when is_list(List) -> [from_map(Element) || Element <- List]; from_map(Map) when is_map(Map) -> from_map({#{}, Map}); from_map({Modules, Map}) when is_map(Modules) andalso is_map(Map) -> from_map({#jose_jws{}, Modules, Map}); from_map({JWS, Modules = #{ alg := Module }, Map=#{ <<"alg">> := _ }}) -> {ALG, Fields} = Module:from_map(Map), from_map({JWS#jose_jws{ alg = {Module, ALG} }, maps:remove(alg, Modules), Fields}); from_map({JWS, Modules, Map=#{ <<"b64">> := B64 }}) -> from_map({JWS#jose_jws{ b64 = B64 }, Modules, maps:remove(<<"b64">>, Map)}); from_map({JWS, Modules, Map=#{ <<"alg">> := << "Ed25519", _/binary >> }}) -> from_map({JWS, Modules#{ alg => ?ALG_EDDSA_MODULE }, Map}); from_map({JWS, Modules, Map=#{ <<"alg">> := << "Ed448", _/binary >> }}) -> from_map({JWS, Modules#{ alg => ?ALG_EDDSA_MODULE }, Map}); from_map({JWS, Modules, Map=#{ <<"alg">> := << "EdDSA", _/binary >> }}) -> from_map({JWS, Modules#{ alg => ?ALG_EDDSA_MODULE }, Map}); from_map({JWS, Modules, Map=#{ <<"alg">> := << "ES", _/binary >> }}) -> from_map({JWS, Modules#{ alg => ?ALG_ECDSA_MODULE }, Map}); from_map({JWS, Modules, Map=#{ <<"alg">> := << "HS", _/binary >> }}) -> from_map({JWS, Modules#{ alg => ?ALG_HMAC_MODULE }, Map}); from_map({JWS, Modules, Map=#{ <<"alg">> := << "Poly1305" >> }}) -> from_map({JWS, Modules#{ alg => ?ALG_POLY1305_MODULE }, Map}); from_map({JWS, Modules, Map=#{ <<"alg">> := << "PS", _/binary >> }}) -> from_map({JWS, Modules#{ alg => ?ALG_RSA_PSS_MODULE }, Map}); from_map({JWS, Modules, Map=#{ <<"alg">> := << "RS", _/binary >> }}) -> from_map({JWS, Modules#{ alg => ?ALG_RSA_PKCS1_V1_5_MODULE }, Map}); from_map({JWS, Modules, Map=#{ <<"alg">> := << "none" >> }}) -> from_map({JWS, Modules#{ alg => ?ALG_NONE_MODULE }, Map}); from_map({#jose_jws{ alg = undefined }, _Modules, _Map}) -> {error, {missing_required_keys, [<<"alg">>]}}; from_map({JWS, _Modules, Fields}) -> JWS#jose_jws{ fields = Fields }. %%==================================================================== %% Encode API functions %%==================================================================== to_binary(List) when is_list(List) -> [to_binary(Element) || Element <- List]; to_binary(JWS=#jose_jws{}) -> {Modules, Map} = to_map(JWS), {Modules, jose:encode(Map)}; to_binary(Other) -> to_binary(from(Other)). to_file(File, JWS=#jose_jws{}) when is_binary(File) orelse is_list(File) -> {Modules, Binary} = to_binary(JWS), case file:write_file(File, Binary) of ok -> {Modules, File}; WriteError -> WriteError end; to_file(File, Other) when is_binary(File) orelse is_list(File) -> to_file(File, from(Other)). to_map(List) when is_list(List) -> [to_map(Element) || Element <- List]; to_map(JWS=#jose_jws{fields=Fields}) -> record_to_map(JWS, #{}, Fields); to_map(Other) -> to_map(from(Other)). %%==================================================================== %% API functions %%==================================================================== compact({Modules, #{ <<"payload">> := Payload, <<"signatures">> := Signatures }}) when is_list(Signatures) -> {Modules, [do_compact(Map#{ <<"payload">> => Payload }) || Map <- Signatures]}; compact({Modules, Map}) when is_map(Map) -> {Modules, do_compact(Map)}; compact({Modules, List}) when is_list(List) -> {Modules, [do_compact(Map) || Map <- List]}; compact(Map) when is_map(Map) -> compact({#{}, Map}); compact(List) when is_list(List) -> compact({#{}, List}); compact(BadArg) -> erlang:error({badarg, [BadArg]}). expand({Modules, Binary}) when is_binary(Binary) -> {Modules, do_expand(Binary)}; expand({Modules, List}) when is_list(List) -> Expanded = [do_expand(Binary) || Binary <- List], Eligible = lists:foldl(fun (_, false) -> false; (#{ <<"payload">> := Payload }, undefined) when is_binary(Payload) -> Payload; (#{ <<"payload">> := Payload }, Payload) when is_binary(Payload) -> Payload; (_, _) -> false end, undefined, Expanded), case Eligible of _ when Eligible =:= false orelse Eligible =:= undefined -> {Modules, Expanded}; Payload -> Signatures = [maps:remove(<<"payload">>, Map) || Map <- Expanded], {Modules, #{ <<"payload">> => Payload, <<"signatures">> => Signatures }} end; expand(Binary) when is_binary(Binary) -> expand({#{}, Binary}); expand(List) when is_list(List) -> expand({#{}, List}). generate_key(List) when is_list(List) -> [generate_key(Element) || Element <- List]; generate_key(#jose_jws{alg={Module, ALG}, fields=Fields}) -> Module:generate_key(ALG, Fields); generate_key(Other) -> generate_key(from(Other)). merge(LeftJWS=#jose_jws{}, RightMap) when is_map(RightMap) -> {Modules, LeftMap} = to_map(LeftJWS), from_map({Modules, maps:merge(LeftMap, RightMap)}); merge(LeftOther, RightJWS=#jose_jws{}) -> merge(LeftOther, element(2, to_map(RightJWS))); merge(LeftOther, RightMap) when is_map(RightMap) -> merge(from(LeftOther), RightMap). peek(Signed) -> peek_payload(Signed). peek_payload({_Modules, Signed}) when is_binary(Signed) or is_map(Signed) -> peek_payload(Signed); peek_payload(SignedBinary) when is_binary(SignedBinary) -> peek_payload(expand(SignedBinary)); peek_payload(#{ <<"payload">> := Payload }) -> jose_jwa_base64url:decode(Payload). peek_protected({_Modules, Signed}) when is_binary(Signed) or is_map(Signed) -> peek_protected(Signed); peek_protected(SignedBinary) when is_binary(SignedBinary) -> peek_protected(expand(SignedBinary)); peek_protected(#{ <<"protected">> := Protected }) -> jose_jwa_base64url:decode(Protected). peek_signature({_Modules, Signed}) when is_binary(Signed) or is_map(Signed) -> peek_signature(Signed); peek_signature(SignedBinary) when is_binary(SignedBinary) -> peek_signature(expand(SignedBinary)); peek_signature(#{ <<"signature">> := Signature }) -> jose_jwa_base64url:decode(Signature). sign(KeyList, PlainText, SignerList) when is_list(KeyList) andalso is_list(SignerList) andalso length(KeyList) =:= length(SignerList) -> HeaderList = [#{} || _ <- SignerList], sign(KeyList, PlainText, HeaderList, SignerList); sign(KeyList, PlainText, SignerList) when is_list(KeyList) andalso is_list(SignerList) andalso length(KeyList) =/= length(SignerList) -> erlang:error({badarg, [KeyList, PlainText, SignerList]}); sign(KeyOrKeyList, PlainText, JWS=#jose_jws{}) -> sign(KeyOrKeyList, PlainText, #{}, JWS); sign(KeyOrKeyList, PlainText, Other) -> sign(KeyOrKeyList, PlainText, from(Other)). sign(KeyList, PlainText, Header, Signer=#jose_jws{}) when is_list(KeyList) andalso is_binary(PlainText) andalso is_map(Header) -> HeaderList = [Header || _ <- KeyList], SignerList = [Signer || _ <- KeyList], sign(KeyList, PlainText, HeaderList, SignerList); sign(KeyList, PlainText, Header, SignerList) when is_list(KeyList) andalso is_binary(PlainText) andalso is_map(Header) andalso is_list(SignerList) andalso length(KeyList) =:= length(SignerList) -> HeaderList = [Header || _ <- KeyList], sign(KeyList, PlainText, HeaderList, SignerList); sign(KeyList, PlainText, HeaderList, Signer=#jose_jws{}) when is_list(KeyList) andalso is_binary(PlainText) andalso is_list(HeaderList) andalso length(KeyList) =:= length(HeaderList) -> SignerList = [Signer || _ <- KeyList], sign(KeyList, PlainText, HeaderList, SignerList); sign(KeyList, PlainText, HeaderList, SignerList) when is_list(KeyList) andalso is_binary(PlainText) andalso is_list(HeaderList) andalso is_list(SignerList) andalso length(KeyList) =:= length(SignerList) andalso length(KeyList) =:= length(HeaderList) -> Keys = jose_jwk:from(KeyList), Signers = from(SignerList), Payload = jose_jwa_base64url:encode(PlainText), Signatures = map_signatures(Keys, PlainText, HeaderList, Signers, []), {#{}, #{ <<"payload">> => Payload, <<"signatures">> => Signatures }}; sign(Key=#jose_jwk{}, PlainText, Header, JWS=#jose_jws{alg={ALGModule, ALG}}) when is_binary(PlainText) andalso is_map(Header) -> _ = code:ensure_loaded(ALGModule), NewALG = case erlang:function_exported(ALGModule, presign, 2) of false -> ALG; true -> ALGModule:presign(Key, ALG) end, NewJWS = JWS#jose_jws{alg={ALGModule, NewALG}}, {Modules, ProtectedBinary} = to_binary(NewJWS), Protected = jose_jwa_base64url:encode(ProtectedBinary), Payload = jose_jwa_base64url:encode(PlainText), SigningInput = signing_input(PlainText, Protected, NewJWS), Signature = jose_jwa_base64url:encode(ALGModule:sign(Key, SigningInput, NewALG)), {Modules, maps:put(<<"payload">>, Payload, signature_to_map(Protected, Header, Key, Signature))}; sign(Key=none, PlainText, Header, JWS=#jose_jws{alg={ALGModule, ALG}}) when is_binary(PlainText) andalso is_map(Header) -> _ = code:ensure_loaded(ALGModule), NewALG = case erlang:function_exported(ALGModule, presign, 2) of false -> ALG; true -> ALGModule:presign(Key, ALG) end, NewJWS = JWS#jose_jws{alg={ALGModule, NewALG}}, {Modules, ProtectedBinary} = to_binary(NewJWS), Protected = jose_jwa_base64url:encode(ProtectedBinary), Payload = jose_jwa_base64url:encode(PlainText), SigningInput = signing_input(PlainText, Protected, NewJWS), Signature = jose_jwa_base64url:encode(ALGModule:sign(Key, SigningInput, NewALG)), {Modules, maps:put(<<"payload">>, Payload, signature_to_map(Protected, Header, Key, Signature))}; sign(KeyList, PlainText, HeaderList, SignerList) when (is_list(KeyList) andalso is_list(HeaderList) andalso length(KeyList) =/= length(HeaderList)) orelse (is_list(KeyList) andalso is_list(SignerList) andalso length(KeyList) =/= length(SignerList)) orelse (is_list(HeaderList) andalso is_list(SignerList) andalso length(HeaderList) =/= length(SignerList)) orelse (is_list(HeaderList) andalso not is_list(KeyList) andalso not is_list(SignerList)) -> erlang:error({badarg, [KeyList, PlainText, HeaderList, SignerList]}); sign(KeyOrKeyList, PlainText, Header, Other) when is_binary(PlainText) andalso is_map(Header) -> sign(jose_jwk:from(KeyOrKeyList), PlainText, Header, from(Other)). %% See https://tools.ietf.org/html/rfc7797 signing_input(Payload, JWS=#jose_jws{}) -> {_, ProtectedBinary} = to_binary(JWS), Protected = jose_jwa_base64url:encode(ProtectedBinary), signing_input(Payload, Protected, JWS); signing_input(Payload, Other) -> signing_input(Payload, from(Other)). signing_input(PlainText, Protected, #jose_jws{b64=B64}) when (B64 =:= true orelse B64 =:= undefined) -> Payload = jose_jwa_base64url:encode(PlainText), << Protected/binary, $., Payload/binary >>; signing_input(Payload, Protected, #jose_jws{b64=false}) -> << Protected/binary, $., Payload/binary >>. verify(Key, SignedMap) when is_map(SignedMap) -> verify(Key, {#{}, SignedMap}); verify(Key, SignedBinary) when is_binary(SignedBinary) -> verify(Key, expand(SignedBinary)); verify(Key, {Modules, SignedBinary}) when is_binary(SignedBinary) -> verify(Key, expand({Modules, SignedBinary})); verify(Key, {Modules, #{ <<"payload">> := Payload, <<"protected">> := Protected, <<"signature">> := EncodedSignature}}) -> JWS = #jose_jws{alg={ALGModule, ALG}} = from_binary({Modules, jose_jwa_base64url:decode(Protected)}), Signature = jose_jwa_base64url:decode(EncodedSignature), PlainText = jose_jwa_base64url:decode(Payload), SigningInput = signing_input(PlainText, Protected, JWS), {ALGModule:verify(Key, SigningInput, Signature, ALG), PlainText, JWS}; verify(Keys = [_ | _], {Modules, Signed=#{ <<"payload">> := _Payload, <<"signatures">> := EncodedSignatures}}) when is_list(EncodedSignatures) -> [begin {Key, verify(Key, {Modules, Signed})} end || Key <- Keys]; verify(Key, {Modules, #{ <<"payload">> := Payload, <<"signatures">> := EncodedSignatures}}) when is_list(EncodedSignatures) -> [begin verify(Key, {Modules, maps:put(<<"payload">>, Payload, EncodedSignature)}) end || EncodedSignature <- EncodedSignatures]. verify_strict(Key, Allow, SignedMap) when is_map(SignedMap) -> verify_strict(Key, Allow, {#{}, SignedMap}); verify_strict(Key, Allow, SignedBinary) when is_binary(SignedBinary) -> verify_strict(Key, Allow, expand(SignedBinary)); verify_strict(Key, Allow, {Modules, SignedBinary}) when is_binary(SignedBinary) -> verify_strict(Key, Allow, expand({Modules, SignedBinary})); verify_strict(Key, Allow, {Modules, #{ <<"payload">> := Payload, <<"protected">> := Protected, <<"signature">> := EncodedSignature}}) -> ProtectedMap = jose:decode(jose_jwa_base64url:decode(Protected)), Signature = jose_jwa_base64url:decode(EncodedSignature), PlainText = jose_jwa_base64url:decode(Payload), case ProtectedMap of #{ <<"alg">> := Algorithm } -> case lists:member(Algorithm, Allow) of false -> {false, PlainText, ProtectedMap}; true -> JWS = #jose_jws{alg={ALGModule, ALG}} = from_map({Modules, ProtectedMap}), SigningInput = signing_input(PlainText, Protected, JWS), {ALGModule:verify(Key, SigningInput, Signature, ALG), PlainText, JWS} end; _ -> {false, PlainText, ProtectedMap} end; verify_strict(Keys = [_ | _], Allow, {Modules, Signed=#{ <<"payload">> := _Payload, <<"signatures">> := EncodedSignatures}}) when is_list(EncodedSignatures) -> [begin {Key, verify_strict(Key, Allow, {Modules, Signed})} end || Key <- Keys]; verify_strict(Key, Allow, {Modules, #{ <<"payload">> := Payload, <<"signatures">> := EncodedSignatures}}) when is_list(EncodedSignatures) -> [begin verify_strict(Key, Allow, {Modules, maps:put(<<"payload">>, Payload, EncodedSignature)}) end || EncodedSignature <- EncodedSignatures]. %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- %% @private do_compact(#{ <<"payload">> := Payload, <<"protected">> := Protected, <<"signature">> := Signature}) -> << Protected/binary, $., Payload/binary, $., Signature/binary >>; do_compact(BadArg) -> erlang:error({badarg, [BadArg]}). %% @private do_expand(Binary) when is_binary(Binary) -> case binary:split(Binary, <<".">>, [global]) of [Protected, Payload, Signature] -> #{ <<"payload">> => Payload, <<"protected">> => Protected, <<"signature">> => Signature }; _ -> erlang:error({badarg, [Binary]}) end; do_expand(BadArg) -> erlang:error({badarg, [BadArg]}). %% @private map_signatures([Key | Keys], PlainText, [Header | Headers], [Signer=#jose_jws{alg={ALGModule, ALG}} | Signers], Acc) -> _ = code:ensure_loaded(ALGModule), NewALG = case erlang:function_exported(ALGModule, presign, 2) of false -> ALG; true -> ALGModule:presign(Key, ALG) end, NewSigner = Signer#jose_jws{alg={ALGModule, NewALG}}, {_Modules, ProtectedBinary} = to_binary(NewSigner), Protected = jose_jwa_base64url:encode(ProtectedBinary), SigningInput = signing_input(PlainText, Protected, NewSigner), Signature = jose_jwa_base64url:encode(ALGModule:sign(Key, SigningInput, NewALG)), map_signatures(Keys, PlainText, Headers, Signers, [signature_to_map(Protected, Header, Key, Signature) | Acc]); map_signatures([], _PlainText, [], [], Acc) -> lists:reverse(Acc). %% @private record_to_map(JWS=#jose_jws{alg={Module, ALG}}, Modules, Fields0) -> Fields1 = Module:to_map(ALG, Fields0), record_to_map(JWS#jose_jws{alg=undefined}, Modules#{ alg => Module }, Fields1); record_to_map(JWS=#jose_jws{b64=B64}, Modules, Fields0) when is_boolean(B64) -> Fields1 = Fields0#{ <<"b64">> => B64 }, record_to_map(JWS#jose_jws{b64=undefined}, Modules, Fields1); record_to_map(_JWS, Modules, Fields) -> {Modules, Fields}. %% @private signature_to_map(Protected, Header, #jose_jwk{fields=Fields}, Signature) -> signature_to_map(Protected, Header, Fields, Signature); signature_to_map(Protected, Header, #{ <<"kid">> := KID }, Signature) -> #{ <<"protected">> => Protected, <<"header">> => maps:put(<<"kid">>, KID, Header), <<"signature">> => Signature }; signature_to_map(Protected, Header, _Fields, Signature) -> case maps:size(Header) of 0 -> #{ <<"protected">> => Protected, <<"signature">> => Signature }; _ -> #{ <<"protected">> => Protected, <<"header">> => Header, <<"signature">> => Signature } end. potatosalad-erlang-jose-30ac019/src/jws/jose_jws_alg_eddsa.erl0000664000232200023220000000523315267717064025051 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 31 Dec 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jws_alg_eddsa). -behaviour(jose_jws). -behaviour(jose_jws_alg). -include("jose_jwk.hrl"). %% jose_jws callbacks -export([from_map/1]). -export([to_map/2]). %% jose_jws_alg callbacks -export([generate_key/2]). -export([sign/3]). -export([verify/4]). %% Types -type alg() :: 'Ed25519' | 'Ed25519ph' | 'Ed448' | 'Ed448ph' | 'EdDSA'. -export_type([alg/0]). %%==================================================================== %% jose_jws callbacks %%==================================================================== from_map(F = #{ <<"alg">> := <<"Ed25519">> }) -> {'Ed25519', maps:remove(<<"alg">>, F)}; from_map(F = #{ <<"alg">> := <<"Ed25519ph">> }) -> {'Ed25519ph', maps:remove(<<"alg">>, F)}; from_map(F = #{ <<"alg">> := <<"Ed448">> }) -> {'Ed448', maps:remove(<<"alg">>, F)}; from_map(F = #{ <<"alg">> := <<"Ed448ph">> }) -> {'Ed448ph', maps:remove(<<"alg">>, F)}; from_map(F = #{ <<"alg">> := <<"EdDSA">> }) -> {'EdDSA', maps:remove(<<"alg">>, F)}. to_map('Ed25519', F) -> F#{ <<"alg">> => <<"Ed25519">> }; to_map('Ed25519ph', F) -> F#{ <<"alg">> => <<"Ed25519ph">> }; to_map('Ed448', F) -> F#{ <<"alg">> => <<"Ed448">> }; to_map('Ed448ph', F) -> F#{ <<"alg">> => <<"Ed448ph">> }; to_map('EdDSA', F) -> F#{ <<"alg">> => <<"EdDSA">> }. %%==================================================================== %% jose_jws_alg callbacks %%==================================================================== generate_key(ALG, _Fields) when ALG =:= 'Ed25519' orelse ALG =:= 'Ed25519ph' orelse ALG =:= 'Ed448' orelse ALG =:= 'Ed448ph' -> jose_jws_alg:generate_key({okp, ALG}, atom_to_binary(ALG, unicode)); generate_key('EdDSA', _Fields) -> jose_jws_alg:generate_key({okp, 'Ed25519'}, <<"EdDSA">>). sign(#jose_jwk{kty={KTYModule, KTY}}, Message, ALG) -> KTYModule:sign(Message, ALG, KTY). verify(#jose_jwk{kty={KTYModule, KTY}}, Message, Signature, ALG) -> KTYModule:verify(Message, ALG, Signature, KTY). %%==================================================================== %% API functions %%==================================================================== %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jws/jose_jws_alg_hmac.erl0000664000232200023220000000444215267717064024702 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 23 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jws_alg_hmac). -behaviour(jose_jws). -behaviour(jose_jws_alg). -include("jose_jwk.hrl"). %% jose_jws callbacks -export([from_map/1]). -export([to_map/2]). %% jose_jws_alg callbacks -export([generate_key/2]). -export([sign/3]). -export([verify/4]). %% API %% Types -type alg() :: 'HS256' | 'HS384' | 'HS512'. -export_type([alg/0]). %%==================================================================== %% jose_jws callbacks %%==================================================================== from_map(F = #{ <<"alg">> := <<"HS256">> }) -> {'HS256', maps:remove(<<"alg">>, F)}; from_map(F = #{ <<"alg">> := <<"HS384">> }) -> {'HS384', maps:remove(<<"alg">>, F)}; from_map(F = #{ <<"alg">> := <<"HS512">> }) -> {'HS512', maps:remove(<<"alg">>, F)}. to_map('HS256', F) -> F#{ <<"alg">> => <<"HS256">> }; to_map('HS384', F) -> F#{ <<"alg">> => <<"HS384">> }; to_map('HS512', F) -> F#{ <<"alg">> => <<"HS512">> }. %%==================================================================== %% jose_jws_alg callbacks %%==================================================================== generate_key('HS256', _Fields) -> jose_jws_alg:generate_key({oct, 32}, <<"HS256">>); generate_key('HS384', _Fields) -> jose_jws_alg:generate_key({oct, 48}, <<"HS384">>); generate_key('HS512', _Fields) -> jose_jws_alg:generate_key({oct, 64}, <<"HS512">>). sign(#jose_jwk{kty={KTYModule, KTY}}, Message, ALG) -> KTYModule:sign(Message, ALG, KTY). verify(#jose_jwk{kty={KTYModule, KTY}}, Message, Signature, ALG) -> KTYModule:verify(Message, ALG, Signature, KTY). %%==================================================================== %% API functions %%==================================================================== %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- potatosalad-erlang-jose-30ac019/src/jws/jose_jws_alg.erl0000664000232200023220000000252415267717064023711 0ustar debalancedebalance%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- %% vim: ts=4 sw=4 ft=erlang noet %%%------------------------------------------------------------------- %%% @author Andrew Bennett %%% @copyright 2014-2022, Andrew Bennett %%% @doc %%% %%% @end %%% Created : 29 Jul 2015 by Andrew Bennett %%%------------------------------------------------------------------- -module(jose_jws_alg). -callback generate_key(ALG, Fields) -> JWK when ALG :: any(), Fields :: map(), JWK :: jose_jwk:key(). -callback sign(Key, Message, ALG) -> Signature when Key :: any(), Message :: iodata(), ALG :: any(), Signature :: iodata(). -callback verify(Key, Message, Signature, ALG) -> boolean() when Key :: any(), Message :: iodata(), Signature :: iodata(), ALG :: any(). -callback presign(Key, ALG) -> NewALG when Key :: any(), ALG :: any(), NewALG :: any(). -optional_callbacks([presign/2]). %% API -export([generate_key/2]). %%==================================================================== %% API functions %%==================================================================== generate_key(Parameters, Algorithm) -> jose_jwk:merge(jose_jwk:generate_key(Parameters), #{ <<"alg">> => Algorithm, <<"use">> => <<"sig">> }). potatosalad-erlang-jose-30ac019/priv/0000775000232200023220000000000015267717064020122 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/priv/.keep0000664000232200023220000000000015267717064021035 0ustar debalancedebalancepotatosalad-erlang-jose-30ac019/rebar.lock0000664000232200023220000000000415267717064021101 0ustar debalancedebalance[]. potatosalad-erlang-jose-30ac019/lib/0000775000232200023220000000000015267717064017710 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/lib/jose/0000775000232200023220000000000015267717064020650 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/lib/jose/poison/0000775000232200023220000000000015267717064022157 5ustar debalancedebalancepotatosalad-erlang-jose-30ac019/lib/jose/poison/lexical_encoder.ex0000664000232200023220000002631615267717064025645 0ustar debalancedebalanceif Code.ensure_loaded?(Poison) do defmodule JOSE.Poison.LexicalEncodeError do @type t :: %__MODULE__{message: String.t(), value: any} defexception message: nil, value: nil def exception(args) when is_list(args) do if Code.ensure_loaded?(Poison) and Code.ensure_loaded?(Poison.EncodeError) do Poison.EncodeError.exception(args) else struct = __struct__() {valid, invalid} = Enum.split_with(args, fn {k, _} -> Map.has_key?(struct, k) end) case invalid do [] -> :ok _ -> IO.warn( "the following fields are unknown when raising " <> "#{inspect(__MODULE__)}: #{inspect(invalid)}. " <> "Please make sure to only give known fields when raising " <> "or redefine #{inspect(__MODULE__)}.exception/1 to " <> "discard unknown fields. Future Elixir versions will raise on " <> "unknown fields given to raise/2" ) end Kernel.struct!(struct, valid) end end def message(%{message: nil, value: value}) do "unable to encode value: #{inspect(value)}" end def message(%{message: message}) do message end end defmodule JOSE.Poison.LexicalEncode do @moduledoc false alias JOSE.Poison.{LexicalEncodeError, LexicalEncoder} defmacro __using__(_) do quote do alias JOSE.Poison.LexicalEncodeError alias String.Chars @compile {:inline, encode_name: 1} # Fast path encoding string keys defp encode_name(value) when is_binary(value) do value end defp encode_name(value) do case Chars.impl_for(value) do nil -> raise LexicalEncodeError, value: value, message: "expected a String.Chars encodable value, got: #{inspect(value)}" impl -> impl.to_string(value) end end end end end defmodule JOSE.Poison.LexicalPretty do @moduledoc false defmacro __using__(_) do quote do @default_indent 2 @default_offset 0 @compile {:inline, pretty: 1, indent: 1, offset: 1, offset: 2, spaces: 1} defp pretty(options) do Map.get(options, :pretty) == true end defp indent(options) do Map.get(options, :indent, @default_indent) end defp offset(options) do Map.get(options, :offset, @default_offset) end defp offset(options, value) do Map.put(options, :offset, value) end defp spaces(count) do :binary.copy(" ", count) end end end end defprotocol JOSE.Poison.LexicalEncoder do @fallback_to_any true @typep escape :: :unicode | :javascript | :html_safe @typep pretty :: boolean @typep indent :: non_neg_integer @typep offset :: non_neg_integer @typep strict_keys :: boolean @type options :: %{ optional(:escape) => escape, optional(:pretty) => pretty, optional(:indent) => indent, optional(:offset) => offset, optional(:strict_keys) => strict_keys } @spec encode(t, options) :: iodata def encode(value, options) end defimpl JOSE.Poison.LexicalEncoder, for: Atom do alias JOSE.Poison.LexicalEncoder def encode(nil, _), do: "null" def encode(true, _), do: "true" def encode(false, _), do: "false" def encode(atom, options) do LexicalEncoder.BitString.encode(Atom.to_string(atom), options) end end defimpl JOSE.Poison.LexicalEncoder, for: BitString do alias JOSE.Poison.LexicalEncodeError import Bitwise def encode("", _), do: "\"\"" def encode(string, options) do [?", escape(string, Map.get(options, :escape)), ?"] end defp escape("", _), do: [] for {char, seq} <- Enum.zip(~c"\"\\\n\t\r\f\b", ~c"\"\\ntrfb") do defp escape(<> <> rest, mode) do [unquote("\\" <> <>) | escape(rest, mode)] end end # http://en.wikipedia.org/wiki/Unicode_control_characters defp escape(<> <> rest, mode) when char <= 0x1F or char == 0x7F do [seq(char) | escape(rest, mode)] end defp escape(<> <> rest, mode) when char in 0x80..0x9F do [seq(char) | escape(rest, mode)] end defp escape(<> <> rest, :unicode) when char in 0xA0..0xFFFF do [seq(char) | escape(rest, :unicode)] end # http://en.wikipedia.org/wiki/UTF-16#Example_UTF-16_encoding_procedure # http://unicodebook.readthedocs.org/unicode_encodings.html defp escape(<> <> rest, :unicode) when char > 0xFFFF do code = char - 0x10000 [ seq(0xD800 ||| code >>> 10), seq(0xDC00 ||| (code &&& 0x3FF)) | escape(rest, :unicode) ] end defp escape(<> <> rest, mode) when mode in [:html_safe, :javascript] and char in [0x2028, 0x2029] do [seq(char) | escape(rest, mode)] end defp escape(<> <> rest, :html_safe) do ["\\/" | escape(rest, :html_safe)] end defp escape(string, mode) do size = chunk_size(string, mode, 0) <> = string [chunk | escape(rest, mode)] end defp chunk_size(<> <> _, _mode, acc) when char <= 0x1F or char in ~c"\"\\" do acc end defp chunk_size(<> <> _, :html_safe, acc) do acc end defp chunk_size(<> <> rest, mode, acc) when char < 0x80 do chunk_size(rest, mode, acc + 1) end defp chunk_size(<<_::utf8>> <> _, :unicode, acc) do acc end defp chunk_size(<> <> _, mode, acc) when mode in [:html_safe, :javascript] and char in [0x2028, 0x2029] do acc end defp chunk_size(<> <> rest, mode, acc) do size = cond do codepoint < 0x800 -> 2 codepoint < 0x10000 -> 3 true -> 4 end chunk_size(rest, mode, acc + size) end defp chunk_size("", _, acc), do: acc defp chunk_size(other, _, _) do raise LexicalEncodeError, value: other end @compile {:inline, seq: 1} defp seq(char) do case Integer.to_charlist(char, 16) do s when length(s) < 2 -> ["\\u000" | s] s when length(s) < 3 -> ["\\u00" | s] s when length(s) < 4 -> ["\\u0" | s] s -> ["\\u" | s] end end end defimpl JOSE.Poison.LexicalEncoder, for: Integer do def encode(integer, _options) do Integer.to_string(integer) end end defimpl JOSE.Poison.LexicalEncoder, for: Float do def encode(float, _options) do :io_lib_format.fwrite_g(float) end end defimpl JOSE.Poison.LexicalEncoder, for: Map do @compile :inline_list_funcs alias JOSE.Poison.{LexicalEncoder, LexicalEncodeError} use JOSE.Poison.{LexicalEncode, LexicalPretty} def encode(map, _) when map_size(map) < 1, do: "{}" def encode(map, options) do map |> strict_keys(Map.get(options, :strict_keys, false)) |> encode(pretty(options), options) end def encode(map, true, options) do indent = indent(options) offset = offset(options) + indent options = offset(options, offset) fun = &[ ",\n", spaces(offset), LexicalEncoder.BitString.encode(encode_name(&1), options), ": ", LexicalEncoder.encode(:maps.get(&1, map), options) | &2 ] [ "{\n", tl(:lists.foldr(fun, [], :lists.sort(:maps.keys(map)))), ?\n, spaces(offset - indent), ?} ] end def encode(map, _, options) do fun = &[ ?,, LexicalEncoder.BitString.encode(encode_name(&1), options), ?:, LexicalEncoder.encode(:maps.get(&1, map), options) | &2 ] [?{, tl(:lists.foldr(fun, [], :lists.sort(:maps.keys(map)))), ?}] end defp strict_keys(map, false), do: map defp strict_keys(map, true) do map |> Map.keys() |> Enum.each(fn key -> name = encode_name(key) if Map.has_key?(map, name) do raise LexicalEncodeError, value: name, message: "duplicate key found: #{inspect(key)}" end end) map end end defimpl JOSE.Poison.LexicalEncoder, for: List do alias JOSE.Poison.LexicalEncoder use JOSE.Poison.LexicalPretty @compile :inline_list_funcs def encode([], _), do: "[]" def encode(list, options) do encode(list, pretty(options), options) end def encode(list, false, options) do fun = &[?,, LexicalEncoder.encode(&1, options) | &2] [?[, tl(:lists.foldr(fun, [], list)), ?]] end def encode(list, true, options) do indent = indent(options) offset = offset(options) + indent options = offset(options, offset) fun = &[",\n", spaces(offset), LexicalEncoder.encode(&1, options) | &2] ["[\n", tl(:lists.foldr(fun, [], list)), ?\n, spaces(offset - indent), ?]] end end defimpl JOSE.Poison.LexicalEncoder, for: [Range, Stream, MapSet, HashSet] do alias JOSE.Poison.LexicalEncoder use JOSE.Poison.LexicalPretty def encode(collection, options) do encode(collection, pretty(options), options) end def encode(collection, false, options) do fun = &[?,, LexicalEncoder.encode(&1, options)] case Enum.flat_map(collection, fun) do [] -> "[]" [_ | tail] -> [?[, tail, ?]] end end def encode(collection, true, options) do indent = indent(options) offset = offset(options) + indent options = offset(options, offset) fun = &[",\n", spaces(offset), LexicalEncoder.encode(&1, options)] case Enum.flat_map(collection, fun) do [] -> "[]" [_ | tail] -> ["[\n", tail, ?\n, spaces(offset - indent), ?]] end end end defimpl JOSE.Poison.LexicalEncoder, for: [Date, Time, NaiveDateTime, DateTime] do alias JOSE.Poison.LexicalEncoder def encode(value, options) do LexicalEncoder.BitString.encode(@for.to_iso8601(value), options) end end defimpl JOSE.Poison.LexicalEncoder, for: Any do alias JOSE.Poison.{LexicalEncoder, LexicalEncodeError} defmacro __deriving__(module, struct, options) do deriving(module, struct, options) end def deriving(module, _struct, options) do only = options[:only] except = options[:except] extractor = cond do only -> quote(do: Map.take(struct, unquote(only))) except -> except = [:__struct__ | except] quote(do: Map.drop(struct, unquote(except))) true -> quote(do: :maps.remove(:__struct__, struct)) end quote do defimpl LexicalEncoder, for: unquote(module) do def encode(struct, options) do LexicalEncoder.Map.encode(unquote(extractor), options) end end end end def encode(%{__struct__: _} = struct, options) do LexicalEncoder.Map.encode(Map.from_struct(struct), options) end def encode(value, _options) do raise LexicalEncodeError, value: value end end end potatosalad-erlang-jose-30ac019/lib/jose/poison.ex0000664000232200023220000000064515267717064022522 0ustar debalancedebalanceif Code.ensure_loaded?(Poison) do defmodule JOSE.Poison do alias JOSE.Poison.LexicalEncoder @spec lexical_encode!(LexicalEncoder.t(), LexicalEncoder.options()) :: iodata | no_return def lexical_encode!(value, options \\ %{}) do iodata = LexicalEncoder.encode(value, options) if !options[:iodata] do iodata |> IO.iodata_to_binary() else iodata end end end end potatosalad-erlang-jose-30ac019/lib/jose/jws.ex0000664000232200023220000007703115267717064022021 0ustar debalancedebalancerequire Record defmodule JOSE.JWS do @moduledoc ~S""" JWS stands for JSON Web Signature which is defined in [RFC 7515](https://tools.ietf.org/html/rfc7515). ## Unsecured Signing Vulnerability The [`"none"`](https://tools.ietf.org/html/rfc7515#appendix-A.5) signing algorithm is disabled by default to prevent accidental verification of empty signatures (read about the vulnerability [here](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/)). You may also enable the `"none"` algorithm as an application environment variable for `:jose` or by using `JOSE.unsecured_signing/1`. ## Strict Verification Recommended `JOSE.JWS.verify_strict/3` is recommended over `JOSE.JWS.verify/2` so that signing algorithms may be whitelisted during verification of signed input. ## Algorithms The following algorithms are currently supported by `JOSE.JWS` (some may need the `JOSE.crypto_fallback/1` option to be enabled): * `"Ed25519"` * `"Ed25519ph"` * `"Ed448"` * `"Ed448ph"` * `"EdDSA"` * `"ES256"` * `"ES384"` * `"ES512"` * `"HS256"` * `"HS384"` * `"HS512"` * `"Poly1305"` * `"PS256"` * `"PS384"` * `"PS512"` * `"RS256"` * `"RS384"` * `"RS512"` * `"none"` (disabled by default, enable with `JOSE.unsecured_signing/1`) ## Examples All of the example keys generated below can be found here: [https://gist.github.com/potatosalad/925a8b74d85835e285b9](https://gist.github.com/potatosalad/925a8b74d85835e285b9) ### Ed25519 and Ed25519ph # let's generate the 2 keys we'll use below jwk_ed25519 = JOSE.JWK.generate_key({:okp, :Ed25519}) jwk_ed25519ph = JOSE.JWK.generate_key({:okp, :Ed25519ph}) # Ed25519 iex> signed_ed25519 = JOSE.JWS.sign(jwk_ed25519, "{}", %{ "alg" => "Ed25519" }) |> JOSE.JWS.compact |> elem(1) "eyJhbGciOiJFZDI1NTE5In0.e30.xyg2LTblm75KbLFJtROZRhEgAFJdlqH9bhx8a9LO1yvLxNLhO9fLqnFuU3ojOdbObr8bsubPkPqUfZlPkGHXCQ" iex> JOSE.JWS.verify(jwk_ed25519, signed_ed25519) |> elem(0) true # Ed25519ph iex> signed_ed25519ph = JOSE.JWS.sign(jwk_ed25519ph, "{}", %{ "alg" => "Ed25519ph" }) |> JOSE.JWS.compact |> elem(1) "eyJhbGciOiJFZDI1NTE5cGgifQ.e30.R3je4TTxQvoBOupIKkel_b8eW-G8KaWmXuC14NMGSCcHCTalURtMmVqX2KbcIpFBeI-OKP3BLHNIpt1keKveDg" iex> JOSE.JWS.verify(jwk_ed25519ph, signed_ed25519ph) |> elem(0) true ### Ed448 and Ed448ph # let's generate the 2 keys we'll use below jwk_ed448 = JOSE.JWK.generate_key({:okp, :Ed448}) jwk_ed448ph = JOSE.JWK.generate_key({:okp, :Ed448ph}) # Ed448 iex> signed_ed448 = JOSE.JWS.sign(jwk_ed448, "{}", %{ "alg" => "Ed448" }) |> JOSE.JWS.compact |> elem(1) "eyJhbGciOiJFZDQ0OCJ9.e30.UlqTx962FvZP1G5pZOrScRXlAB0DJI5dtZkknNTm1E70AapkONi8vzpvKd355czflQdc7uyOzTeAz0-eLvffCKgWm_zebLly7L3DLBliynQk14qgJgz0si-60mBFYOIxRghk95kk5hCsFpxpVE45jRIA" iex> JOSE.JWS.verify(jwk_ed448, signed_ed448) |> elem(0) true # Ed448ph iex> signed_ed448ph = JOSE.JWS.sign(jwk_ed448ph, "{}", %{ "alg" => "Ed448ph" }) |> JOSE.JWS.compact |> elem(1) "eyJhbGciOiJFZDQ0OHBoIn0.e30._7wxQF8Am-Fg3E-KgREXBv3Gr2vqLM6ja_7hs6kA5EakCrJVQ2QiAHrr4NriLABmiPbVd7F7IiaAApyR3Ud4ak3lGcHVxSyksjJjvBUbKnSB_xkT6v_QMmx27hV08JlxskUkfvjAG0-yKGC8BXoT9R0A" iex> JOSE.JWS.verify(jwk_ed448ph, signed_ed448ph) |> elem(0) true ### EdDSA # EdDSA works with Ed25519, Ed25519ph, Ed448, and Ed448ph keys. # However, it defaults to Ed25519 for key generation. jwk_eddsa = JOSE.JWS.generate_key(%{ "alg" => "EdDSA" }) # EdDSA iex> signed_eddsa = JOSE.JWS.sign(jwk_eddsa, "{}", %{ "alg" => "EdDSA" }) |> JOSE.JWS.compact |> elem(1) "eyJhbGciOiJFZERTQSJ9.e30.rhb5ZY7MllNbW9q-SCn_NglhYtaRGMXEUDj6BvJjltOt19tEI_1wFrVK__jL91i9hO7WtVqRH_OfHiilnO1CAQ" iex> JOSE.JWS.verify(jwk_eddsa, signed_eddsa) |> elem(0) true ### ES256, ES384, and ES512 # let's generate the 3 keys we'll use below jwk_es256 = JOSE.JWK.generate_key({:ec, :secp256r1}) jwk_es384 = JOSE.JWK.generate_key({:ec, :secp384r1}) jwk_es512 = JOSE.JWK.generate_key({:ec, :secp521r1}) # ES256 iex> signed_es256 = JOSE.JWS.sign(jwk_es256, "{}", %{ "alg" => "ES256" }) |> JOSE.JWS.compact |> elem(1) "eyJhbGciOiJFUzI1NiJ9.e30.nb7cEQQuIi2NgcP5A468FHGG8UZg8gWZjloISyVIwNh3X6FiTTFZsvc0mL3RnulWoNJzKF6xwhae3botI1LbRg" iex> JOSE.JWS.verify(jwk_es256, signed_es256) |> elem(0) true # ES384 iex> signed_es384 = JOSE.JWS.sign(jwk_es384, "{}", %{ "alg" => "ES384" }) |> JOSE.JWS.compact |> elem(1) "eyJhbGciOiJFUzM4NCJ9.e30.-2kZkNe66y2SprhgvvtMa0qBrSb2imPhMYkbi_a7vx-vpEHuVKsxCpUyNVLe5_CXaHWhHyc2rNi4uEfU73c8XQB3e03rg_JOj0H5XGIGS5G9f4RmNMSCiYGwqshLSDFI" iex> JOSE.JWS.verify(jwk_es384, signed_es384) |> elem(0) true # ES512 iex> signed_es512 = JOSE.JWS.sign(jwk_es512, "{}", %{ "alg" => "ES512" }) |> JOSE.JWS.compact |> elem(1) "eyJhbGciOiJFUzUxMiJ9.e30.AOIw4KTq5YDu6QNrAYKtFP8R5IljAbhqXuPK1dUARPqlfc5F3mM0kmSh5KOVNHDmdCdapBv0F3b6Hl6glFDPlxpiASuSWtvvs9K8_CRfSkEzvToj8wf3WLGOarQHDwYXtlZoki1zMPGeWABwafTZNQaItNSpqYd_P9GtN0XM3AALdua0" iex> JOSE.JWS.verify(jwk_es512, signed_es512) |> elem(0) true ### HS256, HS384, and HS512 # let's generate the 3 keys we'll use below jwk_hs256 = JOSE.JWK.generate_key({:oct, 16}) jwk_hs384 = JOSE.JWK.generate_key({:oct, 24}) jwk_hs512 = JOSE.JWK.generate_key({:oct, 32}) # HS256 iex> signed_hs256 = JOSE.JWS.sign(jwk_hs256, "{}", %{ "alg" => "HS256" }) |> JOSE.JWS.compact |> elem(1) "eyJhbGciOiJIUzI1NiJ9.e30.r2JwwMFHECoDZlrETLT-sgFT4qN3w0MLee9MrgkDwXs" iex> JOSE.JWS.verify(jwk_hs256, signed_hs256) |> elem(0) true # HS384 iex> signed_hs384 = JOSE.JWS.sign(jwk_hs384, "{}", %{ "alg" => "HS384" }) |> JOSE.JWS.compact |> elem(1) "eyJhbGciOiJIUzM4NCJ9.e30.brqQFXXM0XtMWDdKf0foEQcvK18swcoDkxBqCPeed_IO317_tisr60H2mz79SlNR" iex> JOSE.JWS.verify(jwk_hs384, signed_hs384) |> elem(0) true # HS512 iex> signed_hs512 = JOSE.JWS.sign(jwk_hs512, "{}", %{ "alg" => "HS512" }) |> JOSE.JWS.compact |> elem(1) "eyJhbGciOiJIUzUxMiJ9.e30.ge1JYomO8Fyl6sgxLbc4g3AMPbaMHLmeTl0jrUYAJZSloN9j4VyhjucX8d-RWIlMjzdG0xyklw53k1-kaTlRVQ" iex> JOSE.JWS.verify(jwk_hs512, signed_hs512) |> elem(0) true ### Poly1305 This is highly experimental and based on [RFC 7539](https://tools.ietf.org/html/rfc7539). Every signed message has a new 96-bit nonce generated which is used to generate a one-time key from the secret. # let's generate the key we'll use below jwk_poly1305 = JOSE.JWK.generate_key({:oct, 32}) # Poly1305 iex> signed_poly1305 = JOSE.JWS.sign(jwk_poly1305, "{}", %{ "alg" => "Poly1305" }) |> JOSE.JWS.compact |> elem(1) "eyJhbGciOiJQb2x5MTMwNSIsIm5vbmNlIjoiTjhiR3A1QXdob0Y3Yk1YUiJ9.e30.XWcCkV1WU72cTO-XuiNRAQ" iex> JOSE.JWS.verify(jwk_poly1305, signed_poly1305) |> elem(0) true # let's inspect the protected header to see the generated nonce iex> JOSE.JWS.peek_protected(signed_poly1305) "{\"alg\":\"Poly1305\",\"nonce\":\"N8bGp5AwhoF7bMXR\"}" ### PS256, PS384, and PS512 # let's generate the 3 keys we'll use below (cutkey must be installed as a dependency) jwk_ps256 = JOSE.JWK.generate_key({:rsa, 2048}) jwk_ps384 = JOSE.JWK.generate_key({:rsa, 4096}) jwk_ps512 = JOSE.JWK.generate_key({:rsa, 8192}) # this may take a few seconds # PS256 iex> signed_ps256 = JOSE.JWS.sign(jwk_ps256, "{}", %{ "alg" => "PS256" }) |> JOSE.JWS.compact |> elem(1) "eyJhbGciOiJQUzI1NiJ9.e30.RY5A3rG2TjmdlARE57eSSSFE6plkuQPKLKsyqz3WrqKRWZgSrvROACRTzoGyrx1sNvQEZJLZ-xVhrFvP-80Q14XzQbPfYLubvn-2wcMNCmih3OVQNVtFdFjA5U2NG-sF-SWAUmm9V_DvMShFGG0qHxLX7LqT83lAIgEulgsytb0xgOjtJObBru5jLjN_uEnc7fCfnxi3my1GAtnrs9NiKvMfuIVlttvOORDFBTO2aFiCv1F-S6Xgj16rc0FGImG0x3amQcmFAD9g41KY0_KsCXgUfoiVpC6CqO6saRC4UDykks91B7Nuoxjsm3nKWa_4vKh9QJy-V8Sf0gHxK58j8Q" iex> JOSE.JWS.verify(jwk_ps256, signed_ps256) |> elem(0) true # PS384 iex> signed_ps384 = JOSE.JWS.sign(jwk_ps384, "{}", %{ "alg" => "PS384" }) |> JOSE.JWS.compact |> elem(1) "eyJhbGciOiJQUzM4NCJ9.e30.xmYVenIhi75hDMy3bnL6WVpVlTzYmO1ejOZeq9AkSjkp_STrdIp6uUEs9H_y7CLD9LrGYYHDNDl9WmoH6cn95WZT9KJgAVNFFYd8owY6JUHGKU1jUbLkptAgvdphVpWZ1C5fVCRt4vmp8K9f6jy3er9jCBNjl9gSBdmToFwYdXI26ZKSBjfoVm2tFFQIOThye4YQWCWHbzSho6J7d5ATje72L30zDvWXavJ-XNvof5Tkju4WQQB-ukFoqTw4yV8RVwCa-DX61I1hNrq-Zr75_iWmHak3GqNkg5ACBEjDtvtyxJizqy9KINKSlbB9jGztiWoEiXZ6wJ5sSJ6ZrSFJuQVEmns_dLqzpSHEFkWfczEV_gj9Eu_EXwMp9YQlQ3GktfXaz-mzH_jUaLmudEUskQGCiR92gK9KR6_ROQPJfD54Tkqdh6snwg6y17k8GdlTc5qMM3V84q3R6zllmhrRhV1Dlduc0MEqKcsQSX_IX21-sfiVMIcUsW73dIPXVZI2jsNlEHKqwMjWdSfjYUf3YApxSGERU3u4lRS3F0yRrZur8KWS3ToilApjg0cNg9jKas8g8C8ZPgGFYM6StVxUnXRmsJILDnsZMIPjbUDAPHhB0DwLwOB7OqGUBcItX-zwur1OVnHR7aIh1DbfWfyTIml8VIhYfGfazgXfgQVcGEM" iex> JOSE.JWS.verify(jwk_ps384, signed_ps384) |> elem(0) true # PS512 iex> signed_ps512 = JOSE.JWS.sign(jwk_ps512, "{}", %{ "alg" => "PS512" }) |> JOSE.JWS.compact |> elem(1) "eyJhbGciOiJQUzUxMiJ9.e30.fJe52-PF3I7UrpQamLCnmVAGkBhP0HVeJi48qZqaFc1-_tQEiYTfxuwQBDlt01GQWpjTZRb097bZF6RcrKWwRHyAo3otOZdR32emWfOHddWLL3qotj_fTaDR2-OhLixwce6mFjnHqppHH1zjCmgbKPG8S2cAadNd5w10VR-IS6LdnFRhNZOahuuB7dzCEJaSjkGfm3_9xdj3I0ZRl4fauR_LO9NQIyvMMeCFevowz1sVGG1G-I2njPrEXvxhAMp7y2mao5Yik8UUORXRjcn2Wai3umy8Yh4nHYU5qqruHjLjDwudCPNDjxjg294z1uAUpt7S0v7VbrkgUvgutTFAT-bcHywFODiycajQuqIpFp1TCUAq3Xe2yk4DTRduvPIKcPkJQnFrVkClJAU9A4D4602xpdK-z2uCgWsBVHVokf5-9ba5EqVb8BJx2xYZUIA5CdrIiTBfoe_cI5Jh92uprcWC_llio2ZJvGdQpPgwCgca7-RQ94LAmIA4u3mAndrZj_z48T2GjHbaKzl18FOPQH0XEvK_W5oypUe5NOGlz9mMGZigbFqBY2lM-7oVVYc4ZA3VFy8Dv1nWhU6DGb2NnDnQUyChllyBREuZbwrkOTQEvqqdV-6lM6VwXNu1gqc3YHly9W6u5CmsnxtvlIxsUVg679HiqdtdWxLSaIJObd9Xji56-eEkWMEA08SNy9p-F9AgHOxzoZqgrAQDEwqyEwqoAW681xLc5Vck580AQDxO9Ha4IqLIPirpO5EODQjOd8-S_SlAP5o_wz1Oh38MC5T5V13PqPuZ70dbggB4bUgVaHYC4FE4XHCqP7W3xethaPc68cY9-g9f1RUvthmnEYXSRpvyaMY3iX0txZazWIS_Jg7pNTCEaWr9JCLTZd1MiLbFowPvKYGM-z-39K31OUbq5PIScy0I9OOz9joecm8KsCesA2ysPph1E7cL7Etiw5tGhCFzcdQwm8Gm6SDwj8vCEcZUkXeZJfhlS1cJtZk1sNu3KZNndevtZjRWaXi2m4WNKVxVE-nuaF7V3GWfDemh9RXxyFK8OC8aYLIqcc2pAKJM47ANVty2ll1xaCIB3q3CKdnk5fmsnzKkQI9SjKy70p9TWT-NNoYU682KG_mZo-ByEs5CvJ8w7qysmX8Xpb2I6oSJf7S3qjbqkqtXQcV5MuQ232vk7-g42CcQGL82xvRc09TuvwnmykpKHmjUaJ4U9k9zTN3g2iTdpkvl6vbnND9uG1SBaieVeFYWCT-6VdhovEiD9bvIdA7D_R7NZO8YHBt_lfBQRle_jDyLzHSlkP6kt9dYRhrc2SNMzF_4i3iEUAihbaQYvbNsGwWrHqyGofnva20pRXwc4GxOlw" iex> JOSE.JWS.verify(jwk_ps512, signed_ps512) |> elem(0) true ### RS256, RS384, and RS512 # let's generate the 3 keys we'll use below jwk_rs256 = JOSE.JWK.generate_key({:rsa, 1024}) jwk_rs384 = JOSE.JWK.generate_key({:rsa, 2048}) jwk_rs512 = JOSE.JWK.generate_key({:rsa, 4096}) # RS256 iex> signed_rs256 = JOSE.JWS.sign(jwk_rs256, "{}", %{ "alg" => "RS256" }) |> JOSE.JWS.compact |> elem(1) "eyJhbGciOiJSUzI1NiJ9.e30.C0J8v5R-sEe9-g_s0SMgPorCh8VDdaZ9gLpWNm1Tn1Cv2xRph1Xn9Rzm10ZCEs84sj7kxA4v28fVShQ_P1AHN83yQ2mvstkKwsuwXxr-cludx_NLQL5CKKQtTR0ITD_pxUowjfAkBYuJv0677jUj-8lGKs1P5e2dbwW9IqFe4uE" iex> JOSE.JWS.verify(jwk_rs256, signed_rs256) |> elem(0) true # RS384 iex> signed_rs384 = JOSE.JWS.sign(jwk_rs384, "{}", %{ "alg" => "RS384" }) |> JOSE.JWS.compact |> elem(1) "eyJhbGciOiJSUzM4NCJ9.e30.fvPxeNhO0oitOsdqFmrBgpGE7Gn_NdJ1J8F5ArKon54pdHB2v30hua9wbG4V2Hr-hNAyflaBJtoGAwIpKVkfHn-IW7d06hKw_Hv0ecG-VvZr60cK2IJnHS149Htz_652egThZh1GIKRZN1IrRVlraLMozFcWP0Ojc-L-g5XjcTFafesmV0GFGfFubAiQWEiWIgNV3822L-wPe7ZGeFe5yYsZ70WMHQQ1tSuNsm5QUOUVInOThAhJ30FRTCNFgv46l4TEF9aaI9443cKAbwzd_EavD0FpvgpwEhGyNTVx0sxiCZIYUE_jN53aSaHXB82d0xwIr2-GXlr3Y-dLwERIMw" iex> JOSE.JWS.verify(jwk_rs384, signed_rs384) |> elem(0) true # RS512 iex> signed_rs512 = JOSE.JWS.sign(jwk_rs512, "{}", %{ "alg" => "RS512" }) |> JOSE.JWS.compact |> elem(1) "eyJhbGciOiJSUzUxMiJ9.e30.le2_kCnmj6Y02bl16Hh5EPqmLsFkB3YZpiEfvmA6xfdg9I3QJ5uSgOejs_HpuIbItuMFUdcqtkfW45_6YKlI7plB49iWiNnWY0PLxsvbiZaSmT4R4dOUWx9KlO_Ui5SE94XkigUoFanDTHTr9bh4NpvoIaNdi_xLdC7FYA-AqZspegRcgY-QZQv4kbD3NQJtxsEiAXk8-C8CX3lF6haRlh7s4pyAmgj7SJeElsPjhPNVZ7EduhTLZfVwiLrRmzLKQ6dJ_PrZDig1lgl9jf2NjzcsFpt6lvfrMsDdIQEGyJoh53-zXiD_ltyAZGS3pX-_tHRxoAZ1SyAPkkC4cCra6wc-03sBQPoUa26xyyhrgf4h7E2l-JqhKPXT7pJv6AbRPgKUH4prEH636gpoWQrRc-JxbDIJHR0ShdL8ssf5e-rKpcVVAZKnRI64NbSKXTg-JtDxhU9QG8JVEkHqOxSeo-VSXOoExdmm8lCfqylrw7qmDxjEwOq7TGjhINyjVaK1Op_64BWVuCzgooea6G2ZvCTIEl0-k8wY8s9VC7hxSrsgCAnpWeKpIcbLQoDIoyasG-6Qb5OuSLR367eg9NAQ8WMTbrrQkm-KLNCYvMFaxmlWzBFST2JDmIr0VH9BzXRAdfG81SymuyFA7_FdpiVYwAwEGR4Q5HYEpequ38tHu3Y" iex> JOSE.JWS.verify(jwk_rs512, signed_rs512) |> elem(0) true """ record = Record.extract(:jose_jws, from_lib: "jose/include/jose_jws.hrl") keys = :lists.map(&elem(&1, 0), record) vals = :lists.map(&{&1, [], nil}, keys) pairs = :lists.zip(keys, vals) defstruct keys @type t :: %__MODULE__{} @doc """ Converts a `JOSE.JWS` struct to a `:jose_jws` record. """ def to_record(%JOSE.JWS{unquote_splicing(pairs)}) do {:jose_jws, unquote_splicing(vals)} end def to_record(list) when is_list(list), do: for(element <- list, into: [], do: to_record(element)) @doc """ Converts a `:jose_jws` record into a `JOSE.JWS`. """ def from_record(jose_jws) def from_record({:jose_jws, unquote_splicing(vals)}) do %JOSE.JWS{unquote_splicing(pairs)} end def from_record(list) when is_list(list), do: for(element <- list, into: [], do: from_record(element)) ## Decode API @doc """ Converts a binary or map into a `JOSE.JWS`. iex> JOSE.JWS.from(%{ "alg" => "HS256" }) %JOSE.JWS{alg: {:jose_jws_alg_hmac, :HS256}, b64: :undefined, fields: %{}} iex> JOSE.JWS.from("{\"alg\":\"HS256\"}") %JOSE.JWS{alg: {:jose_jws_alg_hmac, :HS256}, b64: :undefined, fields: %{}} Support for custom algorithms may be added by specifying a map tuple: iex> JOSE.JWS.from({%{ alg: MyCustomAlgorithm }, %{ "alg" => "custom" }}) %JOSE.JWS{alg: {MyCustomAlgorithm, :state}, b64: :undefined, fields: %{}} *Note:* `MyCustomAlgorithm` must implement the `:jose_jws` and `:jose_jws_alg` behaviours. """ def from(list) when is_list(list), do: for(element <- list, into: [], do: from(element)) def from(jws = %JOSE.JWS{}), do: from(to_record(jws)) def from(any), do: :jose_jws.from(any) |> from_record() @doc """ Converts a binary into a `JOSE.JWS`. """ def from_binary(list) when is_list(list), do: for(element <- list, into: [], do: from_binary(element)) def from_binary(binary), do: :jose_jws.from_binary(binary) |> from_record() @doc """ Reads file and calls `from_binary/1` to convert into a `JOSE.JWS`. """ def from_file(file), do: :jose_jws.from_file(file) |> from_record() @doc """ Converts a map into a `JOSE.JWS`. """ def from_map(list) when is_list(list), do: for(element <- list, into: [], do: from_map(element)) def from_map(map), do: :jose_jws.from_map(map) |> from_record() ## Encode API @doc """ Converts a `JOSE.JWS` into a binary. """ def to_binary(list) when is_list(list), do: for(element <- list, into: [], do: to_binary(element)) def to_binary(jws = %JOSE.JWS{}), do: to_binary(to_record(jws)) def to_binary(any), do: :jose_jws.to_binary(any) @doc """ Calls `to_binary/1` on a `JOSE.JWS` and then writes the binary to file. """ def to_file(file, jws = %JOSE.JWS{}), do: to_file(file, to_record(jws)) def to_file(file, any), do: :jose_jws.to_file(file, any) @doc """ Converts a `JOSE.JWS` into a map. """ def to_map(list) when is_list(list), do: for(element <- list, into: [], do: to_map(element)) def to_map(jws = %JOSE.JWS{}), do: to_map(to_record(jws)) def to_map(any), do: :jose_jws.to_map(any) ## API @doc """ Compacts an expanded signed map or signed list into a binary. iex> JOSE.JWS.compact(%{"payload" => "e30", "protected" => "eyJhbGciOiJIUzI1NiJ9", "signature" => "5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU"}) {%{}, "eyJhbGciOiJIUzI1NiJ9.e30.5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU"} iex> JOSE.JWS.compact(%{"payload" => "e30", "signatures" => [ %{"protected" => "eyJhbGciOiJIUzI1NiJ9", "signature" => "5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU"}, %{"protected" => "eyJhbGciOiJIUzI1NiJ9", "signature" => "himAUXqVJnW2ZWOD8zaOZr0YzsA61lo48wu6-WP-Ks0"}]}) {%{}, ["eyJhbGciOiJIUzI1NiJ9.e30.5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU", "eyJhbGciOiJIUzI1NiJ9.e30.himAUXqVJnW2ZWOD8zaOZr0YzsA61lo48wu6-WP-Ks0"]}} See `expand/1`. """ defdelegate compact(signed), to: :jose_jws @doc """ Expands a compacted signed binary or list of signed binaries into a map. iex> JOSE.JWS.expand("eyJhbGciOiJIUzI1NiJ9.e30.5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU") {%{}, %{"payload" => "e30", "protected" => "eyJhbGciOiJIUzI1NiJ9", "signature" => "5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU"}} iex> JOSE.JWS.expand([ "eyJhbGciOiJIUzI1NiJ9.e30.5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU", "eyJhbGciOiJIUzI1NiJ9.e30.himAUXqVJnW2ZWOD8zaOZr0YzsA61lo48wu6-WP-Ks0"]) {%{}, %{"payload" => "e30", "signatures" => [ %{"protected" => "eyJhbGciOiJIUzI1NiJ9", "signature" => "5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU"}, %{"protected" => "eyJhbGciOiJIUzI1NiJ9", "signature" => "himAUXqVJnW2ZWOD8zaOZr0YzsA61lo48wu6-WP-Ks0"}]}} See `compact/1`. """ defdelegate expand(signed), to: :jose_jws @doc """ Generates a new `JOSE.JWK` based on the algorithms of the specified `JOSE.JWS`. iex> JOSE.JWS.generate_key(%{"alg" => "HS256"}) %JOSE.JWK{fields: %{"alg" => "HS256", "use" => "sig"}, keys: :undefined, kty: {:jose_jwk_kty_oct, <<150, 71, 29, 79, 228, 32, 218, 4, 111, 250, 212, 129, 226, 173, 86, 205, 72, 48, 98, 100, 66, 68, 113, 13, 43, 60, 122, 248, 179, 44, 140, 24>>}} """ def generate_key(list) when is_list(list), do: for(element <- list, into: [], do: generate_key(element)) def generate_key(jws = %JOSE.JWS{}), do: generate_key(to_record(jws)) def generate_key(any), do: JOSE.JWK.from_record(:jose_jws.generate_key(any)) @doc """ Merges map on right into map on left. """ def merge(left = %JOSE.JWS{}, right), do: merge(left |> to_record(), right) def merge(left, right = %JOSE.JWS{}), do: merge(left, right |> to_record()) def merge(left, right), do: :jose_jws.merge(left, right) |> from_record() @doc """ See `peek_payload/1`. """ defdelegate peek(signed), to: :jose_jws @doc """ Returns the decoded payload portion of a signed binary or map without verifying the signature. iex> JOSE.JWS.peek_payload("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.dMAojPMVbFvvkouYUSI9AxIRBxgqretQMCvNF7KmTHU") "{}" """ defdelegate peek_payload(signed), to: :jose_jws @doc """ Returns the decoded protected portion of a signed binary or map without verifying the signature. iex> JOSE.JWS.peek_protected("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.dMAojPMVbFvvkouYUSI9AxIRBxgqretQMCvNF7KmTHU") "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" """ defdelegate peek_protected(signed), to: :jose_jws @doc """ Returns the decoded signature portion of a signed binary or map without verifying the signature. iex> JOSE.JWS.peek_signature("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.dMAojPMVbFvvkouYUSI9AxIRBxgqretQMCvNF7KmTHU") <<116, 192, 40, 140, 243, 21, 108, 91, 239, 146, 139, 152, 81, 34, 61, 3, 18, 17, 7, 24, 42, 173, 235, 80, 48, 43, 205, 23, 178, 166, 76, 117>> """ defdelegate peek_signature(signed), to: :jose_jws @doc """ Signs the `plain_text` using the `jwk` and algorithm specified by the `jws`. iex> jwk = JOSE.JWK.from(%{"k" => "qUg4Yw", "kty" => "oct"}) %JOSE.JWK{fields: %{}, keys: :undefined, kty: {:jose_jwk_kty_oct, <<169, 72, 56, 99>>}} iex> JOSE.JWS.sign(jwk, "{}", %{ "alg" => "HS256" }) {%{alg: :jose_jws_alg_hmac}, %{"payload" => "e30", "protected" => "eyJhbGciOiJIUzI1NiJ9", "signature" => "5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU"}} If the `jwk` has a `"kid"` assigned, it will be added to the `"header"` on the signed map: iex> jwk = JOSE.JWK.from(%{"k" => "qUg4Yw", "kid" => "eyHC48MN26DvoBpkaudvOVXuI5Sy8fKMxQMYiRWmjFw", "kty" => "oct"}) %JOSE.JWK{fields: %{"kid" => "eyHC48MN26DvoBpkaudvOVXuI5Sy8fKMxQMYiRWmjFw"}, keys: :undefined, kty: {:jose_jwk_kty_oct, <<169, 72, 56, 99>>}} iex> JOSE.JWS.sign(jwk, "test", %{ "alg" => "HS256" }) {%{alg: :jose_jws_alg_hmac}, %{"header" => %{"kid" => "eyHC48MN26DvoBpkaudvOVXuI5Sy8fKMxQMYiRWmjFw"}, "payload" => "e30", "protected" => "eyJhbGciOiJIUzI1NiJ9", "signature" => "5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU"}} A list of `jwk` keys can also be specified to produce a signed list: iex> jwk1 = JOSE.JWK.from(%{"k" => "qUg4Yw", "kty" => "oct"}) %JOSE.JWK{fields: %{}, keys: :undefined, kty: {:jose_jwk_kty_oct, <<169, 72, 56, 99>>}} iex> jwk2 = JOSE.JWK.from_map(%{"k" => "H-v_Nw", "kty" => "oct"}) %JOSE.JWK{fields: %{}, keys: :undefined, kty: {:jose_jwk_kty_oct, <<31, 235, 255, 55>>}} iex> JOSE.JWS.sign([jwk1, jwk2], "{}", %{ "alg" => "HS256" }) {%{alg: :jose_jws_alg_hmac}, %{"payload" => "e30", "signatures" => [ %{"protected" => "eyJhbGciOiJIUzI1NiJ9", "signature" => "5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU"}, %{"protected" => "eyJhbGciOiJIUzI1NiJ9", "signature" => "himAUXqVJnW2ZWOD8zaOZr0YzsA61lo48wu6-WP-Ks0"}]}} *Note:* Signed maps with a `"header"` or other fields will have data loss when used with `compact/1`. """ def sign(jwk = %JOSE.JWK{}, plain_text, jws), do: sign(JOSE.JWK.to_record(jwk), plain_text, jws) def sign(jwk, plain_text, jws = %JOSE.JWS{}), do: sign(jwk, plain_text, to_record(jws)) def sign(key_list, plain_text, signer_list) when is_list(key_list) and is_list(signer_list) and length(key_list) === length(signer_list) do keys = for key <- key_list, into: [] do case key do %JOSE.JWK{} -> JOSE.JWK.to_record(key) _ -> key end end signers = for signer <- signer_list, into: [] do case signer do %JOSE.JWS{} -> JOSE.JWS.to_record(signer) _ -> signer end end :jose_jws.sign(keys, plain_text, signers) end def sign(key_list, plain_text, jws) when is_list(key_list) and not is_list(jws) do keys = for key <- key_list, into: [] do case key do %JOSE.JWK{} -> JOSE.JWK.to_record(key) _ -> key end end :jose_jws.sign(keys, plain_text, jws) end def sign(jwk, plain_text, signer_list) when is_list(signer_list) and not is_list(jwk) do signers = for signer <- signer_list, into: [] do case signer do %JOSE.JWS{} -> JOSE.JWS.to_record(signer) _ -> signer end end :jose_jws.sign(jwk, plain_text, signers) end def sign(jwk, plain_text, jws), do: :jose_jws.sign(jwk, plain_text, jws) @doc """ Signs the `plain_text` using the `jwk` and algorithm specified by the `jws` and adds the `header` to the signed map. iex> jwk = JOSE.JWK.from(%{"k" => "qUg4Yw", "kty" => "oct"}) %JOSE.JWK{fields: %{}, keys: :undefined, kty: {:jose_jwk_kty_oct, <<169, 72, 56, 99>>}} iex> JOSE.JWS.sign(jwk, "{}", %{ "test" => true }, %{ "alg" => "HS256" }) {%{alg: :jose_jws_alg_hmac}, %{"header" => %{"test" => true}, "payload" => "e30", "protected" => "eyJhbGciOiJIUzI1NiJ9", "signature" => "5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU"}} If the `jwk` has a `"kid"` assigned, it will be added to the `"header"` on the signed map. See `sign/3`. """ def sign(jwk = %JOSE.JWK{}, plain_text, header, jws), do: sign(JOSE.JWK.to_record(jwk), plain_text, header, jws) def sign(jwk, plain_text, header, jws = %JOSE.JWS{}), do: sign(jwk, plain_text, header, to_record(jws)) def sign(key_list, plain_text, header, signer) when is_list(key_list) and is_map(header) and not is_list(signer) do headers = for _ <- key_list, into: [], do: header signers = for _ <- key_list, into: [], do: signer sign(key_list, plain_text, headers, signers) end def sign(key_list, plain_text, header, signer_list) when is_list(key_list) and is_map(header) and is_list(signer_list) and length(key_list) === length(signer_list) do headers = for _ <- key_list, into: [], do: header sign(key_list, plain_text, headers, signer_list) end def sign(key_list, plain_text, header_list, signer) when is_list(key_list) and is_list(header_list) and not is_list(signer) and length(key_list) === length(header_list) do signers = for _ <- key_list, into: [], do: signer sign(key_list, plain_text, header_list, signers) end def sign(key_list, plain_text, header_list, signer_list) when is_list(key_list) and is_list(header_list) and is_list(signer_list) and length(key_list) === length(signer_list) and length(key_list) === length(header_list) do keys = for key <- key_list, into: [] do case key do %JOSE.JWK{} -> JOSE.JWK.to_record(key) _ -> key end end signers = for signer <- signer_list, into: [] do case signer do %JOSE.JWS{} -> JOSE.JWS.to_record(signer) _ -> signer end end :jose_jws.sign(keys, plain_text, header_list, signers) end def sign(jwk = [%JOSE.JWK{} | _], plain_text, header, jws) do sign( for k <- jwk do case k do %JOSE.JWK{} -> JOSE.JWK.to_record(k) _ -> k end end, plain_text, header, jws ) end def sign(jwk, plain_text, header, jws), do: :jose_jws.sign(jwk, plain_text, header, jws) @doc """ Converts the `jws` to the `protected` argument used by `signing_input/3`. """ def signing_input(payload, jws = %JOSE.JWS{}), do: signing_input(payload, to_record(jws)) def signing_input(payload, jws), do: :jose_jws.signing_input(payload, jws) @doc """ Combines `payload` and `protected` based on the `"b64"` setting on the `jws` for the signing input used by `sign/3` and `sign/4`. If `"b64"` is set to `false` on the `jws`, the raw `payload` will be used: iex> JOSE.JWS.signing_input("{}", %{ "alg" => "HS256" }) "eyJhbGciOiJIUzI1NiJ9.e30" iex> JOSE.JWS.signing_input("{}", %{ "alg" => "HS256", "b64" => false }) "eyJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2V9.{}" See [JWS Unencoded Payload Option](https://tools.ietf.org/html/draft-ietf-jose-jws-signing-input-options-04) for more information. """ def signing_input(payload, protected, jws = %JOSE.JWS{}), do: signing_input(payload, protected, to_record(jws)) def signing_input(payload, protected, jws), do: :jose_jws.signing_input(payload, protected, jws) @doc """ Verifies the `signed` using the `jwk`. iex> jwk = JOSE.JWK.from(%{"k" => "qUg4Yw", "kty" => "oct"}) %JOSE.JWK{fields: %{}, keys: :undefined, kty: {:jose_jwk_kty_oct, <<169, 72, 56, 99>>}} iex> JOSE.JWS.verify(jwk, "eyJhbGciOiJIUzI1NiJ9.e30.5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU") {true, "{}", %JOSE.JWS{alg: {:jose_jws_alg_hmac, :HS256}, b64: :undefined, fields: %{}}} A list of `jwk` keys can also be specified where each key will be used to verify every entry in a signed list: iex> jwk1 = JOSE.JWK.from(%{"k" => "qUg4Yw", "kty" => "oct"}) %JOSE.JWK{fields: %{}, keys: :undefined, kty: {:jose_jwk_kty_oct, <<169, 72, 56, 99>>}} iex> jwk2 = JOSE.JWK.from_map(%{"k" => "H-v_Nw", "kty" => "oct"}) %JOSE.JWK{fields: %{}, keys: :undefined, kty: {:jose_jwk_kty_oct, <<31, 235, 255, 55>>}} iex> JOSE.JWS.verify([jwk1, jwk2], %{"payload" => "e30", "signatures" => [ %{"protected" => "eyJhbGciOiJIUzI1NiJ9", "signature" => "5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU"}, %{"protected" => "eyJhbGciOiJIUzI1NiJ9", "signature" => "himAUXqVJnW2ZWOD8zaOZr0YzsA61lo48wu6-WP-Ks0"}]}) [{%JOSE.JWK{fields: %{}, keys: :undefined, kty: {:jose_jwk_kty_oct, <<169, 72, 56, 99>>}}, [{true, "{}", %JOSE.JWS{alg: {:jose_jws_alg_hmac, :HS256}, b64: :undefined, fields: %{}}}, {false, "{}", %JOSE.JWS{alg: {:jose_jws_alg_hmac, :HS256}, b64: :undefined, fields: %{}}}]}, {%JOSE.JWK{fields: %{}, keys: :undefined, kty: {:jose_jwk_kty_oct, <<31, 235, 255, 55>>}}, [{false, "{}", %JOSE.JWS{alg: {:jose_jws_alg_hmac, :HS256}, b64: :undefined, fields: %{}}}, {true, "{}", %JOSE.JWS{alg: {:jose_jws_alg_hmac, :HS256}, b64: :undefined, fields: %{}}}]}] """ def verify(jwk = %JOSE.JWK{}, signed), do: verify(JOSE.JWK.to_record(jwk), signed) def verify(jwk = [%JOSE.JWK{} | _], signed) do verify( for k <- jwk do case k do %JOSE.JWK{} -> JOSE.JWK.to_record(k) _ -> k end end, signed ) end def verify(key, signed) do try do case :jose_jws.verify(key, signed) do {verified, payload, jws} when is_tuple(jws) -> {verified, payload, from_record(jws)} list when is_list(list) -> for {jwk, verifications} <- list do {JOSE.JWK.from_record(jwk), for {verified, payload, jws} <- verifications do {verified, payload, from_record(jws)} end} end error -> error end catch class, reason -> {class, reason} end end @doc """ Same as `verify/2`, but uses `allow` as a whitelist for `"alg"` which are allowed to verify against. If the detected algorithm is not present in `allow`, then `false` is returned. iex> jwk = JOSE.JWK.from(%{"k" => "qUg4Yw", "kty" => "oct"}) %JOSE.JWK{fields: %{}, keys: :undefined, kty: {:jose_jwk_kty_oct, <<169, 72, 56, 99>>}} iex> signed_hs256 = JOSE.JWS.sign(jwk, "{}", %{ "alg" => "HS256" }) |> JOSE.JWS.compact |> elem(1) "eyJhbGciOiJIUzI1NiJ9.e30.5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU" iex> signed_hs512 = JOSE.JWS.sign(jwk, "{}", %{ "alg" => "HS512" }) |> JOSE.JWS.compact |> elem(1) "eyJhbGciOiJIUzUxMiJ9.e30.DN_JCks5rzQiDJJ15E6uJFskAMw-KcasGINKK_4S8xKo7W6tZ-a00ZL8UWOWgE7oHpcFrYnvSpNRldAMp19iyw" iex> JOSE.JWS.verify_strict(jwk, ["HS256"], signed_hs256) |> elem(0) true iex> JOSE.JWS.verify_strict(jwk, ["HS256"], signed_hs512) |> elem(0) false iex> JOSE.JWS.verify_strict(jwk, ["HS256", "HS512"], signed_hs512) |> elem(0) true """ def verify_strict(jwk = %JOSE.JWK{}, allow, signed), do: verify_strict(JOSE.JWK.to_record(jwk), allow, signed) def verify_strict(jwk = [%JOSE.JWK{} | _], allow, signed) do verify_strict( for k <- jwk do case k do %JOSE.JWK{} -> JOSE.JWK.to_record(k) _ -> k end end, allow, signed ) end def verify_strict(key, allow, signed) do try do case :jose_jws.verify_strict(key, allow, signed) do {verified, payload, jws} when is_tuple(jws) -> {verified, payload, from_record(jws)} list when is_list(list) -> for {jwk, verifications} <- list do {JOSE.JWK.from_record(jwk), for {verified, payload, jws} <- verifications do {verified, payload, from_record(jws)} end} end error -> error end catch class, reason -> {class, reason} end end end potatosalad-erlang-jose-30ac019/lib/jose/jwa.ex0000664000232200023220000002434315267717064021775 0ustar debalancedebalancedefmodule JOSE.JWA do @moduledoc ~S""" JWA stands for JSON Web Algorithms which is defined in [RFC 7518](https://tools.ietf.org/html/rfc7518). ## Cryptographic Algorithm Fallback Native implementations of all cryptographic and public key algorithms required by the JWA specifications are not present in current versions of Elixir and OTP. JOSE will detect whether a specific algorithm is natively supported or not and, by default, it will mark the algorithm as unsupported if a native implementation is not found. However, JOSE also has pure Erlang versions of many of the missing algorithms which can be used as a fallback by calling `JOSE.crypto_fallback/1` and passing `true`. """ ## Crypto API @doc """ Decrypts `cipher_text` according to `cipher` block cipher. Currently supported block ciphers: * `{:aes_ecb, 128}` - AES ECB with 128-bit `key` size * `{:aes_ecb, 192}` - AES ECB with 192-bit `key` size * `{:aes_ecb, 256}` - AES ECB with 256-bit `key` size """ @spec block_decrypt(term(), binary(), binary()) :: binary() defdelegate block_decrypt(cipher, key, cipher_text), to: :jose_jwa @doc """ Decrypts `cipher_text` according to `cipher` block cipher. Currently supported block ciphers: * `{:aes_cbc, 128}` - AES CBC with 128-bit `key` size and 128-bit `iv` size * `{:aes_cbc, 192}` - AES CBC with 192-bit `key` size and 128-bit `iv` size * `{:aes_cbc, 256}` - AES CBC with 256-bit `key` size and 128-bit `iv` size * `{:aes_gcm, 128}` - AES GCM with 128-bit `key` size and variable `iv` size * `{:aes_gcm, 192}` - AES GCM with 192-bit `key` size and variable `iv` size * `{:aes_gcm, 256}` - AES GCM with 256-bit `key` size and variable `iv` size * `{:chacha20_poly1305, 256}` - ChaCha20/Poly1305 with 256-bit `key` size and 96-bit `iv` size """ @spec block_decrypt(term(), binary(), binary(), binary()) :: binary() defdelegate block_decrypt(cipher, key, iv, cipher_text), to: :jose_jwa @doc """ Encrypts `plain_text` according to `cipher` block cipher. Currently supported block ciphers: * `{:aes_ecb, 128}` - AES ECB with 128-bit `key` size * `{:aes_ecb, 192}` - AES ECB with 192-bit `key` size * `{:aes_ecb, 256}` - AES ECB with 256-bit `key` size """ @spec block_encrypt(term(), binary(), binary()) :: binary() defdelegate block_encrypt(cipher, key, plain_text), to: :jose_jwa @doc """ Encrypts `plain_text` according to `cipher` block cipher. Currently supported block ciphers: * `{:aes_cbc, 128}` - AES CBC with 128-bit `key` size and 128-bit `iv` size * `{:aes_cbc, 192}` - AES CBC with 192-bit `key` size and 128-bit `iv` size * `{:aes_cbc, 256}` - AES CBC with 256-bit `key` size and 128-bit `iv` size * `{:aes_gcm, 128}` - AES GCM with 128-bit `key` size and variable `iv` size * `{:aes_gcm, 192}` - AES GCM with 192-bit `key` size and variable `iv` size * `{:aes_gcm, 256}` - AES GCM with 256-bit `key` size and variable `iv` size * `{:chacha20_poly1305, 256}` - ChaCha20/Poly1305 with 256-bit `key` size and 96-bit `iv` size """ @spec block_encrypt(term(), binary(), binary(), binary()) :: binary() defdelegate block_encrypt(cipher, key, iv, plain_text), to: :jose_jwa ## Public Key API @doc """ Decrypts `cipher_text` using the `private_key`. ## Options * `:rsa_padding` - one of `:rsa_pkcs1_oaep_padding` or `:rsa_pkcs1_padding` * `:rsa_oaep_md` - sets the hashing algorithm for `:rsa_pkcs1_oaep_padding`, defaults to `:sha` * `:rsa_oaep_label` - sets the label for `:rsa_pkcs1_oaep_padding`, defaults to `<<>>` """ @spec decrypt_private(binary(), :public_key.rsa_private_key(), atom() | keyword()) :: binary() defdelegate decrypt_private(cipher_text, private_key, options), to: :jose_jwa @doc """ Encrypts `plain_text` using the `public_key`. ## Options * `:rsa_padding` - one of `:rsa_pkcs1_oaep_padding` or `:rsa_pkcs1_padding` * `:rsa_oaep_md` - sets the hashing algorithm for `:rsa_pkcs1_oaep_padding`, defaults to `:sha` * `:rsa_oaep_label` - sets the label for `:rsa_pkcs1_oaep_padding`, defaults to `<<>>` """ @spec encrypt_public(binary(), :public_key.rsa_public_key(), atom() | keyword()) :: binary() defdelegate encrypt_public(plain_text, public_key, options), to: :jose_jwa @doc """ Signs the digested `message` using the `digest_type` and `private_key`. ## Options * `:rsa_padding` - one of `:rsa_pkcs1_pss_padding` or `:rsa_pkcs1_padding` * `:rsa_pss_saltlen` - sets the salt length for `:rsa_pkcs1_pss_padding`, defaults to `-1` * `-2` - use maximum for salt length * `-1` - use hash length for salt length * any number higher than `-1` is used as the actual salt length """ @spec sign(binary() | {:digest, binary()}, :public_key.digest_type(), :public_key.private_key(), atom() | keyword()) :: binary() defdelegate sign(message, digest_type, private_key, options), to: :jose_jwa @doc """ Verifies the `signature` with the digested `message` using the `digest_type` and `public_key`. ## Options * `:rsa_padding` - one of `:rsa_pkcs1_pss_padding` or `:rsa_pkcs1_padding` * `:rsa_pss_saltlen` - sets the salt length for `:rsa_pkcs1_pss_padding`, defaults to `-1` * `-2` - automatically determine based on the PSS block structure * `-1` - use hash length for salt length * any number higher than `-1` is used as the actual salt length """ @spec verify(binary() | {:digest, binary()}, :public_key.digest_type(), binary(), :public_key.public_key(), atom() | keyword()) :: boolean() defdelegate verify(message, digest_type, signature, public_key, options), to: :jose_jwa ## API @doc """ Returns the current module and first argument for the specified `cipher`. iex> JOSE.JWA.block_cipher({:aes_cbc, 128}) {:crypto, :aes_cbc128} iex> JOSE.JWA.block_cipher({:aes_cbc, 192}) {:jose_jwa_unsupported, {:aes_cbc, 192}} iex> JOSE.crypto_fallback(true) :ok iex> JOSE.JWA.block_cipher({:aes_cbc, 192}) {:jose_jwa_aes, {:aes_cbc, 192}} """ @spec block_cipher(term()) :: {module(), term()} defdelegate block_cipher(cipher), to: :jose_jwa @doc """ Returns the current block ciphers and their associated modules. iex> JOSE.JWA.crypto_ciphers() [{{:aes_cbc, 128}, :crypto}, {{:aes_cbc, 192}, :crypto}, {{:aes_cbc, 256}, :crypto}, {{:aes_ecb, 128}, :crypto}, {{:aes_ecb, 192}, :crypto}, {{:aes_ecb, 256}, :crypto}, {{:aes_gcm, 128}, :crypto}, {{:aes_gcm, 192}, :crypto}, {{:aes_gcm, 256}, :crypto}, {{:chacha20_poly1305, 256}, :jose_chacha20_poly1305}] """ @spec crypto_ciphers() :: [{term(), module()}] defdelegate crypto_ciphers(), to: :jose_jwa @doc """ See `JOSE.crypto_fallback/0` """ @spec crypto_fallback() :: boolean() defdelegate crypto_fallback(), to: :jose_jwa @doc """ See `JOSE.crypto_fallback/1` """ @spec crypto_fallback(boolean()) :: :ok defdelegate crypto_fallback(boolean), to: :jose_jwa @doc """ Returns the current listing of supported `:crypto` and `:public_key` algorithms. iex> JOSE.JWA.crypto_supports() [ciphers: [aes_cbc: 128, aes_cbc: 192, aes_cbc: 256, aes_ecb: 128, aes_ecb: 192, aes_ecb: 256, aes_gcm: 128, aes_gcm: 192, aes_gcm: 256, chacha20_poly1305: 256], hashs: [:md5, :poly1305, :sha, :sha256, :sha384, :sha512, :shake256], public_keys: [:ec_gf2m, :ecdh, :ecdsa, :ed25519, :ed25519ph, :ed448, :ed448ph, :rsa, :x25519, :x448], rsa_crypt: [:rsa1_5, :rsa_oaep, :rsa_oaep_256], rsa_sign: [:rsa_pkcs1_padding, :rsa_pkcs1_pss_padding]] """ @spec crypto_supports() :: keyword() defdelegate crypto_supports(), to: :jose_jwa @doc """ Performs a constant time comparison between two binaries to help avoid [timing attacks](https://en.wikipedia.org/wiki/Timing_attack). """ @spec constant_time_compare(binary(), binary()) :: boolean() defdelegate constant_time_compare(a, b), to: :jose_jwa @doc """ Returns either `:binary` or `:list` depending on the detected runtime behavior for EC keys. """ @spec ec_key_mode() :: :binary | :list defdelegate ec_key_mode(), to: :jose_jwa @doc """ Checks whether the `cipher` is natively supported by `:crypto` or not. """ @spec is_block_cipher_supported(term()) :: boolean() defdelegate is_block_cipher_supported(cipher), to: :jose_jwa @doc """ Checks whether ChaCha20/Poly1305 support is available or not. """ @spec is_chacha20_poly1305_supported() :: boolean() defdelegate is_chacha20_poly1305_supported(), to: :jose_jwa @doc """ Checks whether the `padding` is natively supported by `:public_key` or not. """ @spec is_rsa_crypt_supported(atom()) :: boolean() defdelegate is_rsa_crypt_supported(padding), to: :jose_jwa @doc """ Checks whether the `padding` is natively supported by `:public_key` or not. """ @spec is_rsa_sign_supported(atom()) :: boolean() defdelegate is_rsa_sign_supported(padding), to: :jose_jwa @doc """ Returns the current listing of supported JOSE algorithms. iex> JOSE.JWA.supports() [{:jwe, {:alg, ["A128GCMKW", "A128KW", "A192GCMKW", "A192KW", "A256GCMKW", "A256KW", "ECDH-ES", "ECDH-ES+A128KW", "ECDH-ES+A192KW", "ECDH-ES+A256KW", "PBES2-HS256+A128KW", "PBES2-HS384+A192KW", "PBES2-HS512+A256KW", "RSA-OAEP", "RSA-OAEP-256", "RSA1_5", "dir"]}, {:enc, ["A128CBC-HS256", "A128GCM", "A192CBC-HS384", "A192GCM", "A256CBC-HS512", "A256GCM", "ChaCha20/Poly1305"]}, {:zip, ["DEF"]}}, {:jwk, {:kty, ["EC", "OKP", "RSA", "oct"]}, {:kty_OKP_crv, ["Ed25519", "Ed25519ph", "Ed448", "Ed448ph", "X25519", "X448"]}}, {:jws, {:alg, ["ES256", "ES384", "ES512", "Ed25519", "Ed25519ph", "Ed448", "Ed448ph", "HS256", "HS384", "HS512", "PS256", "PS384", "PS512", "Poly1305", "RS256", "RS384", "RS512", "none"]}}] """ @spec supports() :: [{:jwe | :jwk | :jws, keyword()}, ...] defdelegate supports(), to: :jose_jwa @doc """ See `JOSE.unsecured_signing/0` """ @spec unsecured_signing() :: boolean() defdelegate unsecured_signing(), to: :jose_jwa @doc """ See `JOSE.unsecured_signing/1` """ @spec unsecured_signing(boolean()) :: :ok defdelegate unsecured_signing(boolean), to: :jose_jwa end potatosalad-erlang-jose-30ac019/lib/jose/jwt.ex0000664000232200023220000002667115267717064022026 0ustar debalancedebalancerequire Record defmodule JOSE.JWT do @moduledoc ~S""" JWT stands for JSON Web Token which is defined in [RFC 7519](https://tools.ietf.org/html/rfc7519). ## Encryption Examples ## Signature Examples All of the example keys generated below can be found here: [https://gist.github.com/potatosalad/925a8b74d85835e285b9](https://gist.github.com/potatosalad/925a8b74d85835e285b9) See `JOSE.JWS` for more Signature examples. For security purposes, `verify_strict/3` is recommended over `verify/2`. ### HS256 # let's generate the key we'll use below and define our jwt jwk_hs256 = JOSE.JWK.generate_key({:oct, 16}) jwt = %{ "test" => true } # HS256 iex> signed_hs256 = JOSE.JWT.sign(jwk_hs256, %{ "alg" => "HS256" }, jwt) |> JOSE.JWS.compact |> elem(1) "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0Ijp0cnVlfQ.XYsFJDhfBZCAKnEZjR0WWd1l1ZPDD4bYpZYMHizexfQ" # verify_strict/3 is recommended over verify/2 iex> JOSE.JWT.verify_strict(jwk_hs256, ["HS256"], signed_hs256) {true, %JOSE.JWT{fields: %{"test" => true}}, %JOSE.JWS{alg: {:jose_jws_alg_hmac, {:jose_jws_alg_hmac, :sha256}}, b64: :undefined, fields: %{"typ" => "JWT"}}} # verify/2 returns the same thing without "alg" whitelisting iex> JOSE.JWT.verify(jwk_hs256, signed_hs256) {true, %JOSE.JWT{fields: %{"test" => true}}, %JOSE.JWS{alg: {:jose_jws_alg_hmac, {:jose_jws_alg_hmac, :sha256}}, b64: :undefined, fields: %{"typ" => "JWT"}}} # the default signing algorithm is also "HS256" based on the type of jwk used iex> signed_hs256 == JOSE.JWT.sign(jwk_hs256, jwt) |> JOSE.JWS.compact |> elem(1) true """ record = Record.extract(:jose_jwt, from_lib: "jose/include/jose_jwt.hrl") keys = :lists.map(&elem(&1, 0), record) vals = :lists.map(&{&1, [], nil}, keys) pairs = :lists.zip(keys, vals) defstruct keys @type t :: %__MODULE__{} @doc """ Converts a `JOSE.JWT` struct to a `:jose_jwt` record. This also works for converting a list of `JOSE.JWT` structs to a list of `:jose_jwt` records. """ @spec to_record(t()) :: tuple() @spec to_record([t()]) :: [tuple()] def to_record(jose_jwt) def to_record(%JOSE.JWT{unquote_splicing(pairs)}) do {:jose_jwt, unquote_splicing(vals)} end def to_record(list) when is_list(list), do: for(element <- list, into: [], do: to_record(element)) @doc """ Converts a `:jose_jwt` record into a `JOSE.JWT`. This also works for converting a list of `:jose_jwt` records into a list of `JOSE.JWT` structs. """ @spec from_record(tuple()) :: t() @spec from_record([tuple()]) :: [t()] def from_record(jose_jwt) def from_record({:jose_jwt, unquote_splicing(vals)}) do %JOSE.JWT{unquote_splicing(pairs)} end def from_record(list) when is_list(list), do: for(element <- list, into: [], do: from_record(element)) ## Decode API @doc """ Converts a binary or map into a `JOSE.JWT`. iex> JOSE.JWT.from(%{ "test" => true }) %JOSE.JWT{fields: %{"test" => true}} iex> JOSE.JWT.from("{\"test\":true}") %JOSE.JWT{fields: %{"test" => true}} """ def from(list) when is_list(list), do: for(element <- list, into: [], do: from(element)) def from(jwt = %JOSE.JWT{}), do: from(to_record(jwt)) def from(any), do: :jose_jwt.from(any) |> from_record() @doc """ Converts a binary into a `JOSE.JWT`. """ def from_binary(list) when is_list(list), do: for(element <- list, into: [], do: from_binary(element)) def from_binary(binary), do: :jose_jwt.from_binary(binary) |> from_record() @doc """ Reads file and calls `from_binary/1` to convert into a `JOSE.JWT`. """ @spec from_file(String.t() | charlist()) :: t() def from_file(file), do: :jose_jwt.from_file(file) |> from_record() @doc """ Converts a map into a `JOSE.JWT`. """ def from_map(list) when is_list(list), do: for(element <- list, into: [], do: from_map(element)) def from_map(map), do: :jose_jwt.from_map(map) |> from_record() ## Encode API @doc """ Converts a `JOSE.JWT` into a binary. """ def to_binary(list) when is_list(list), do: for(element <- list, into: [], do: to_binary(element)) def to_binary(jwt = %JOSE.JWT{}), do: to_binary(to_record(jwt)) def to_binary(any), do: :jose_jwt.to_binary(any) @doc """ Calls `to_binary/1` on a `JOSE.JWT` and then writes the binary to file. """ def to_file(file, jwt = %JOSE.JWT{}), do: to_file(file, to_record(jwt)) def to_file(file, any), do: :jose_jwt.to_file(file, any) @doc """ Converts a `JOSE.JWT` into a map. """ def to_map(list) when is_list(list), do: for(element <- list, into: [], do: to_map(element)) def to_map(jwt = %JOSE.JWT{}), do: to_map(to_record(jwt)) def to_map(any), do: :jose_jwt.to_map(any) ## API @doc """ Decrypts an encrypted `JOSE.JWT` using the `jwk`. See `JOSE.JWE.block_decrypt/2`. """ def decrypt(jwk = %JOSE.JWK{}, encrypted), do: decrypt(JOSE.JWK.to_record(jwk), encrypted) def decrypt(key, encrypted) do case :jose_jwt.decrypt(key, encrypted) do {jwe, jwt} when is_tuple(jwe) and is_tuple(jwt) -> {JOSE.JWE.from_record(jwe), from_record(jwt)} error -> error end end @doc """ Encrypts a `JOSE.JWT` using the `jwk` and the default block encryptor algorithm `jwe` for the key type. See `encrypt/3`. """ def encrypt(jwk = %JOSE.JWK{}, jwt), do: encrypt(JOSE.JWK.to_record(jwk), jwt) def encrypt({your_public_jwk = %JOSE.JWK{}, my_private_jwk}, jwt), do: encrypt({JOSE.JWK.to_record(your_public_jwk), my_private_jwk}, jwt) def encrypt({your_public_jwk, my_private_jwk = %JOSE.JWK{}}, jwt), do: encrypt({your_public_jwk, JOSE.JWK.to_record(my_private_jwk)}, jwt) def encrypt(jwk, jwt = %JOSE.JWT{}), do: encrypt(jwk, to_record(jwt)) def encrypt(jwk, jwt), do: :jose_jwt.encrypt(jwk, jwt) @doc """ Encrypts a `JOSE.JWT` using the `jwk` and the `jwe` algorithm. See `JOSE.JWK.block_encrypt/3`. If `"typ"` is not specified in the `jwe`, `%{ "typ" => "JWT" }` will be added. """ def encrypt(jwk = %JOSE.JWK{}, jwe, jwt), do: encrypt(JOSE.JWK.to_record(jwk), jwe, jwt) def encrypt({your_public_jwk = %JOSE.JWK{}, my_private_jwk}, jwe, jwt), do: encrypt({JOSE.JWK.to_record(your_public_jwk), my_private_jwk}, jwe, jwt) def encrypt({your_public_jwk, my_private_jwk = %JOSE.JWK{}}, jwe, jwt), do: encrypt({your_public_jwk, JOSE.JWK.to_record(my_private_jwk)}, jwe, jwt) def encrypt(jwk, jwe = %JOSE.JWE{}, jwt), do: encrypt(jwk, JOSE.JWE.to_record(jwe), jwt) def encrypt(jwk, jwe, jwt = %JOSE.JWT{}), do: encrypt(jwk, jwe, to_record(jwt)) def encrypt(jwk, jwe, jwt), do: :jose_jwt.encrypt(jwk, jwe, jwt) @doc """ Merges map on right into map on left. """ def merge(left = %JOSE.JWT{}, right), do: merge(left |> to_record(), right) def merge(left, right = %JOSE.JWT{}), do: merge(left, right |> to_record()) def merge(left, right), do: :jose_jwt.merge(left, right) |> from_record() @doc """ Same as `peek_payload/1`. """ @spec peek(binary()) :: t() def peek(signed), do: from_record(:jose_jwt.peek(signed)) @doc """ Returns the decoded payload as a `JOSE.JWT` of a signed binary or map without verifying the signature. See `JOSE.JWS.peek_payload/1`. """ @spec peek_payload(binary()) :: t() def peek_payload(signed), do: from_record(:jose_jwt.peek_payload(signed)) @doc """ Returns the decoded protected as a `JOSE.JWS` of a signed binary or map without verifying the signature. See `JOSE.JWS.peek_protected/1`. """ @spec peek_protected(binary()) :: JOSE.JWS.t() def peek_protected(signed), do: JOSE.JWS.from_record(:jose_jwt.peek_protected(signed)) @doc """ Signs a `JOSE.JWT` using the `jwk` and the default signer algorithm `jws` for the key type. See `sign/3`. """ def sign(jwk = %JOSE.JWK{}, jwt), do: sign(JOSE.JWK.to_record(jwk), jwt) def sign(jwk, jwt = %JOSE.JWT{}), do: sign(jwk, to_record(jwt)) def sign(jwk = [%JOSE.JWK{} | _], jwt) do sign( for k <- jwk do case k do %JOSE.JWK{} -> JOSE.JWK.to_record(k) _ -> k end end, jwt ) end def sign(jwk, jwt), do: :jose_jwt.sign(jwk, jwt) @doc """ Signs a `JOSE.JWT` using the `jwk` and the `jws` algorithm. See `JOSE.JWK.sign/3`. If `"typ"` is not specified in the `jws`, `%{ "typ" => "JWT" }` will be added. """ def sign(jwk = %JOSE.JWK{}, jws, jwt), do: sign(JOSE.JWK.to_record(jwk), jws, jwt) def sign(jwk, jws = %JOSE.JWS{}, jwt), do: sign(jwk, JOSE.JWS.to_record(jws), jwt) def sign(jwk, jws, jwt = %JOSE.JWT{}), do: sign(jwk, jws, to_record(jwt)) def sign(jwk = [%JOSE.JWK{} | _], jws, jwt) do sign( for k <- jwk do case k do %JOSE.JWK{} -> JOSE.JWK.to_record(k) _ -> k end end, jws, jwt ) end def sign(jwk, jws, jwt), do: :jose_jwt.sign(jwk, jws, jwt) @doc """ Verifies the `signed` using the `jwk` and calls `from/1` on the payload. See `JOSE.JWS.verify/2`. """ @spec verify(JOSE.JWK.t(), binary()) :: {valid? :: boolean(), jwt :: t(), jws :: JOSE.JWS.t()} def verify(jwk = %JOSE.JWK{}, signed), do: verify(JOSE.JWK.to_record(jwk), signed) def verify(jwk = [%JOSE.JWK{} | _], signed) do verify( for k <- jwk do case k do %JOSE.JWK{} -> JOSE.JWK.to_record(k) _ -> k end end, signed ) end def verify(key, signed) do try do case :jose_jwt.verify(key, signed) do {verified, jwt, jws} when is_tuple(jwt) and is_tuple(jws) -> {verified, from_record(jwt), JOSE.JWS.from_record(jws)} list when is_list(list) -> for {jwk, verifications} <- list do {JOSE.JWK.from_record(jwk), Enum.map(verifications, fn {verified, jwt, jws} when is_tuple(jwt) and is_tuple(jws) -> {verified, from_record(jwt), JOSE.JWS.from_record(jws)} other -> other end)} end error -> error end catch class, reason -> {class, reason} end end @doc """ Verifies the `signed` using the `jwk`, whitelists the `"alg"` using `allow`, and calls `from/1` on the payload. See `JOSE.JWS.verify_strict/3`. """ @spec verify_strict(JOSE.JWK.t(), [String.t()], binary()) :: {valid? :: boolean(), jwt :: t(), jws :: JOSE.JWS.t()} def verify_strict(jwk = %JOSE.JWK{}, allow, signed), do: verify_strict(JOSE.JWK.to_record(jwk), allow, signed) def verify_strict(jwk = [%JOSE.JWK{} | _], allow, signed) do verify_strict( for k <- jwk do case k do %JOSE.JWK{} -> JOSE.JWK.to_record(k) _ -> k end end, allow, signed ) end def verify_strict(key, allow, signed) do try do case :jose_jwt.verify_strict(key, allow, signed) do {verified, jwt, jws} when is_tuple(jwt) and is_tuple(jws) -> {verified, from_record(jwt), JOSE.JWS.from_record(jws)} list when is_list(list) -> for {jwk, verifications} <- list do {JOSE.JWK.from_record(jwk), Enum.map(verifications, fn {verified, jwt, jws} when is_tuple(jwt) and is_tuple(jws) -> {verified, from_record(jwt), JOSE.JWS.from_record(jws)} other -> other end)} end error -> error end catch class, reason -> {class, reason} end end end potatosalad-erlang-jose-30ac019/lib/jose/jwk.ex0000664000232200023220000014543315267717064022013 0ustar debalancedebalancerequire Record defmodule JOSE.JWK do @moduledoc ~S""" JWK stands for JSON Web Key which is defined in [RFC 7517](https://tools.ietf.org/html/rfc7517). """ record = Record.extract(:jose_jwk, from_lib: "jose/include/jose_jwk.hrl") keys = :lists.map(&elem(&1, 0), record) vals = :lists.map(&{&1, [], nil}, keys) pairs = :lists.zip(keys, vals) @derive {Inspect, except: [:kty]} defstruct keys @type t :: %__MODULE__{} @doc """ Converts a `JOSE.JWK` struct to a `:jose_jwk` record. """ def to_record(%JOSE.JWK{unquote_splicing(pairs)}) do {:jose_jwk, unquote_splicing(vals)} end 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`. """ def from_record(jose_jwk) def from_record({:jose_jwk, unquote_splicing(vals)}) do %JOSE.JWK{unquote_splicing(pairs)} end def from_record(list) when is_list(list), do: for(element <- list, into: [], do: from_record(element)) ## Decode API @doc """ Converts a binary or map into a `JOSE.JWK`. iex> JOSE.JWK.from(%{"k" => "", "kty" => "oct"}) %JOSE.JWK{fields: %{}, keys: :undefined, kty: {:jose_jwk_kty_oct, ""}} iex> JOSE.JWK.from("{\"k\":\"\",\"kty\":\"oct\"}") %JOSE.JWK{fields: %{}, keys: :undefined, kty: {:jose_jwk_kty_oct, ""}} The `"kty"` field may be overridden with a custom module that implements the `:jose_jwk` and `:jose_jwk_kty` behaviours. For example: iex> JOSE.JWK.from({%{ kty: MyCustomKey }, %{ "kty" => "custom" }}) %JOSE.JWK{fields: %{}, keys: :undefined, kty: {MyCustomKey, :state}} """ def from(list) when is_list(list), do: for(element <- list, into: [], do: from(element)) def from(jwk = %JOSE.JWK{}), do: from(to_record(jwk)) def from(any), do: :jose_jwk.from(any) |> from_record() @doc """ Decrypts an encrypted binary or map into a `JOSE.JWK` using the specified `password`. iex> JOSE.JWK.from("password", "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkExMjhHQ00iLCJwMmMiOjQwOTYsInAycyI6Im5OQ1ZNQUktNTU5UVFtbWRFcnBsZFEifQ.Ucye69ii4dxd1ykNFlJyBVeA6xeNu4aV.2pZ4nBoxBjmdrneS.boqwdFZVNAFHk1M5P6kPYgBUgGwW32QuKzHuFA.wL9Hy6dcE_DPkUW9s5iwKA") {%JOSE.JWE{alg: {:jose_jwe_alg_pbes2, {:jose_jwe_alg_pbes2, :sha256, 128, <<80, 66, 69, 83, 50, 45, 72, 83, 50, 53, 54, 43, 65, 49, 50, 56, 75, 87, 0, 156, 208, 149, 48, 2, 62, 231, 159, 80, 66, 105, 157, 18, 186, 101, 117>>, 4096}}, enc: {:jose_jwe_enc_aes, {:jose_jwe_enc_aes, {:aes_gcm, 128}, 128, 16, 12, :undefined, :undefined, :undefined, :undefined}}, fields: %{"cty" => "jwk+json"}, zip: :undefined}, %JOSE.JWK{fields: %{}, keys: :undefined, kty: {:jose_jwk_kty_oct, "secret"}}} """ def from(password, list) when is_list(list), do: for(element <- list, into: [], do: from(password, element)) def from(password, jwk = %JOSE.JWK{}), do: from(password, to_record(jwk)) def from(password, any), do: :jose_jwk.from(password, any) |> from_encrypted_record() @doc """ Converts a binary into a `JOSE.JWK`. """ def from_binary(list) when is_list(list), do: for(element <- list, into: [], do: from_binary(element)) def from_binary(binary), do: :jose_jwk.from_binary(binary) |> from_record() @doc """ Decrypts an encrypted binary into a `JOSE.JWK` using `password`. See `from/2`. """ def from_binary(password, list) when is_list(list), do: for(element <- list, into: [], do: from_binary(password, element)) def from_binary(password, binary), do: :jose_jwk.from_binary(password, binary) |> from_encrypted_record() @doc """ Converts a DER (Distinguished Encoding Rules) binary into a `JOSE.JWK`. """ def from_der(list) when is_list(list), do: for(element <- list, into: [], do: from_der(element)) def from_der(pem), do: :jose_jwk.from_der(pem) |> from_record() @doc """ Decrypts an encrypted DER (Distinguished Encoding Rules) binary into a `JOSE.JWK` using `password`. """ def from_der(password, list) when is_list(list), do: for(element <- list, into: [], do: from_der(password, element)) def from_der(password, pem), do: :jose_jwk.from_der(password, pem) |> from_record() @doc """ Reads file and calls `from_der/1` to convert into a `JOSE.JWK`. """ def from_der_file(file), do: :jose_jwk.from_der_file(file) |> from_record() @doc """ Reads encrypted file and calls `from_der/2` to convert into a `JOSE.JWK` using `password`. """ def from_der_file(password, file), do: :jose_jwk.from_der_file(password, file) |> from_record() @doc """ Reads file and calls `from_binary/1` to convert into a `JOSE.JWK`. """ def from_file(file), do: :jose_jwk.from_file(file) |> from_record() @doc """ Reads encrypted file and calls `from_binary/2` to convert into a `JOSE.JWK` using `password`. See `from/2`. """ def from_file(password, file), do: :jose_jwk.from_file(password, file) |> from_encrypted_record() @doc """ Converts Firebase certificate public keys into a map of `JOSE.JWK`. """ def from_firebase(any), do: :maps.fold(fn k, v, a -> :maps.put(k, from_record(v), a) end, %{}, :jose_jwk.from_firebase(any)) @doc """ Converts Erlang records for `:ECPrivateKey`, `:ECPublicKey`, `:RSAPrivateKey`, and `:RSAPublicKey` into a `JOSE.JWK`. """ def from_key(list) when is_list(list), do: for(element <- list, into: [], do: from_key(element)) def from_key(key), do: :jose_jwk.from_key(key) |> from_record() @doc """ Converts a map into a `JOSE.JWK`. """ def from_map(list) when is_list(list), do: for(element <- list, into: [], do: from_map(element)) def from_map(map), do: :jose_jwk.from_map(map) |> from_record() @doc """ Decrypts an encrypted map into a `JOSE.JWK` using `password`. See `from/2`. """ def from_map(password, list) when is_list(list), do: for(element <- list, into: [], do: from_map(password, element)) def from_map(password, map), do: :jose_jwk.from_map(password, map) |> from_encrypted_record() @doc """ Converts an arbitrary binary into a `JOSE.JWK` with `"kty"` of `"oct"`. """ def from_oct(list) when is_list(list), do: for(element <- list, into: [], do: from_oct(element)) def from_oct(oct), do: :jose_jwk.from_oct(oct) |> from_record() @doc """ Decrypts an encrypted arbitrary binary into a `JOSE.JWK` with `"kty"` of `"oct"` using `password`. See `from/2`. """ def from_oct(password, list) when is_list(list), do: for(element <- list, into: [], do: from_oct(password, element)) def from_oct(password, oct), do: :jose_jwk.from_oct(password, oct) |> from_encrypted_record() @doc """ Reads file and calls `from_oct/1` to convert into a `JOSE.JWK`. """ def from_oct_file(file), do: :jose_jwk.from_oct_file(file) |> from_record() @doc """ Reads encrypted file and calls `from_oct/2` to convert into a `JOSE.JWK` using `password`. See `from/2`. """ def from_oct_file(password, file), do: :jose_jwk.from_oct_file(password, file) |> from_encrypted_record() @doc """ Converts an octet key pair into a `JOSE.JWK` with `"kty"` of `"OKP"`. """ def from_okp(list) when is_list(list), do: for(element <- list, into: [], do: from_okp(element)) def from_okp(okp), do: :jose_jwk.from_okp(okp) |> from_record() @doc """ Converts an openssh key into a `JOSE.JWK` with `"kty"` of `"OKP"`. """ def from_openssh_key(list) when is_list(list), do: for(element <- list, into: [], do: from_openssh_key(element)) def from_openssh_key(openssh_key), do: :jose_jwk.from_openssh_key(openssh_key) |> from_record() @doc """ Reads file and calls `from_openssh_key/1` to convert into a `JOSE.JWK`. """ def from_openssh_key_file(file), do: :jose_jwk.from_openssh_key_file(file) |> from_record() @doc """ Converts a PEM (Privacy Enhanced Email) binary into a `JOSE.JWK`. """ def from_pem(list) when is_list(list), do: for(element <- list, into: [], do: from_pem(element)) def from_pem(pem), do: :jose_jwk.from_pem(pem) |> from_record() @doc """ Decrypts an encrypted PEM (Privacy Enhanced Email) binary into a `JOSE.JWK` using `password`. """ def from_pem(password, list) when is_list(list), do: for(element <- list, into: [], do: from_pem(password, element)) def from_pem(password, pem), do: :jose_jwk.from_pem(password, pem) |> from_record() @doc """ Reads file and calls `from_pem/1` to convert into a `JOSE.JWK`. """ def from_pem_file(file), do: :jose_jwk.from_pem_file(file) |> from_record() @doc """ Reads encrypted file and calls `from_pem/2` to convert into a `JOSE.JWK` using `password`. """ def from_pem_file(password, file), do: :jose_jwk.from_pem_file(password, file) |> from_record() defp from_encrypted_record({jwe, jwk}) when is_tuple(jwe) and is_tuple(jwk), do: {JOSE.JWE.from_record(jwe), from_record(jwk)} defp from_encrypted_record(any), do: any ## Encode API @doc """ Converts a `JOSE.JWK` into a binary. """ def to_binary(list) when is_list(list), do: for(element <- list, into: [], do: to_binary(element)) def to_binary(jwk = %JOSE.JWK{}), do: to_binary(to_record(jwk)) def to_binary(jwk), do: :jose_jwk.to_binary(jwk) @doc """ Encrypts a `JOSE.JWK` into a binary using `password` and the default `jwe` for the key type. See `to_binary/3`. """ def to_binary(password, list) when is_list(list), do: for(element <- list, into: [], do: to_binary(password, element)) def to_binary(password, jwk = %JOSE.JWK{}), do: to_binary(password, to_record(jwk)) def to_binary(password, jwk), do: :jose_jwk.to_binary(password, jwk) @doc """ Encrypts a `JOSE.JWK` into a binary using `password` and `jwe`. """ def to_binary(password, jwe = %JOSE.JWE{}, jwk), do: to_binary(password, JOSE.JWE.to_record(jwe), jwk) def to_binary(password, jwe, jwk = %JOSE.JWK{}), do: to_binary(password, jwe, to_record(jwk)) def to_binary(password, jwe, jwk), do: :jose_jwk.to_binary(password, jwe, jwk) @doc """ Converts a `JOSE.JWK` into a DER (Distinguished Encoding Rules) binary. """ def to_der(list) when is_list(list), do: for(element <- list, into: [], do: to_der(element)) def to_der(jwk = %JOSE.JWK{}), do: to_der(to_record(jwk)) def to_der(jwk), do: :jose_jwk.to_der(jwk) @doc """ Encrypts a `JOSE.JWK` into a DER (Distinguished Encoding Rules) encrypted binary using `password`. """ def to_der(password, list) when is_list(list), do: for(element <- list, into: [], do: to_der(password, element)) def to_der(password, jwk = %JOSE.JWK{}), do: to_der(password, to_record(jwk)) def to_der(password, jwk), do: :jose_jwk.to_der(password, jwk) @doc """ Calls `to_der/1` on a `JOSE.JWK` and then writes the binary to file. """ def to_der_file(file, jwk = %JOSE.JWK{}), do: to_der_file(file, to_record(jwk)) def to_der_file(file, jwk), do: :jose_jwk.to_der_file(file, jwk) @doc """ Calls `to_der/2` on a `JOSE.JWK` and then writes the encrypted binary to file. """ def to_der_file(password, file, jwk = %JOSE.JWK{}), do: to_der_file(password, file, to_record(jwk)) def to_der_file(password, file, jwk), do: :jose_jwk.to_der_file(password, file, jwk) @doc """ Calls `to_binary/1` on a `JOSE.JWK` and then writes the binary to file. """ def to_file(file, jwk = %JOSE.JWK{}), do: to_file(file, to_record(jwk)) def to_file(file, jwk), do: :jose_jwk.to_file(file, jwk) @doc """ Calls `to_binary/2` on a `JOSE.JWK` and then writes the encrypted binary to file. """ def to_file(password, file, jwk = %JOSE.JWK{}), do: to_file(password, file, to_record(jwk)) def to_file(password, file, jwk), do: :jose_jwk.to_file(password, file, jwk) @doc """ Calls `to_binary/3` on a `JOSE.JWK` and then writes the encrypted binary to file. """ def to_file(password, file, jwe = %JOSE.JWE{}, jwk), do: to_file(password, file, JOSE.JWE.to_record(jwe), jwk) def to_file(password, file, jwe, jwk = %JOSE.JWK{}), do: to_file(password, file, jwe, to_record(jwk)) def to_file(password, file, jwe, jwk), do: :jose_jwk.to_file(password, file, jwe, jwk) @doc """ Converts a `JOSE.JWK` into the raw key format. """ def to_key(list) when is_list(list), do: for(element <- list, into: [], do: to_key(element)) def to_key(jwk = %JOSE.JWK{}), do: to_key(to_record(jwk)) def to_key(jwk), do: :jose_jwk.to_key(jwk) @doc """ Converts a `JOSE.JWK` into a map. """ def to_map(list) when is_list(list), do: for(element <- list, into: [], do: to_map(element)) def to_map(jwk = %JOSE.JWK{}), do: to_map(to_record(jwk)) def to_map(jwk), do: :jose_jwk.to_map(jwk) @doc """ Encrypts a `JOSE.JWK` into a map using `password` and the default `jwe` for the key type. See `to_map/3`. """ def to_map(password, list) when is_list(list), do: for(element <- list, into: [], do: to_map(password, element)) def to_map(password, jwk = %JOSE.JWK{}), do: to_map(password, to_record(jwk)) def to_map(password, jwk), do: :jose_jwk.to_map(password, jwk) @doc """ Encrypts a `JOSE.JWK` into a map using `password` and `jwe`. """ def to_map(password, jwe = %JOSE.JWE{}, jwk), do: to_map(password, JOSE.JWE.to_record(jwe), jwk) def to_map(password, jwe, jwk = %JOSE.JWK{}), do: to_map(password, jwe, to_record(jwk)) def to_map(password, jwe, jwk), do: :jose_jwk.to_map(password, jwe, jwk) @doc """ Converts a `JOSE.JWK` into a raw binary octet. """ def to_oct(list) when is_list(list), do: for(element <- list, into: [], do: to_oct(element)) def to_oct(jwk = %JOSE.JWK{}), do: to_oct(to_record(jwk)) def to_oct(jwk), do: :jose_jwk.to_oct(jwk) @doc """ Encrypts a `JOSE.JWK` into a raw binary octet using `password` and the default `jwe` for the key type. See `to_oct/3`. """ def to_oct(password, list) when is_list(list), do: for(element <- list, into: [], do: to_oct(password, element)) def to_oct(password, jwk = %JOSE.JWK{}), do: to_oct(password, to_record(jwk)) def to_oct(password, jwk), do: :jose_jwk.to_oct(password, jwk) @doc """ Encrypts a `JOSE.JWK` into a raw binary octet using `password` and `jwe`. """ def to_oct(password, jwe = %JOSE.JWE{}, jwk), do: to_oct(password, JOSE.JWE.to_record(jwe), jwk) def to_oct(password, jwe, jwk = %JOSE.JWK{}), do: to_oct(password, jwe, to_record(jwk)) def to_oct(password, jwe, jwk), do: :jose_jwk.to_oct(password, jwe, jwk) @doc """ Calls `to_oct/1` on a `JOSE.JWK` and then writes the binary to file. """ def to_oct_file(file, jwk = %JOSE.JWK{}), do: to_oct_file(file, to_record(jwk)) def to_oct_file(file, jwk), do: :jose_jwk.to_oct_file(file, jwk) @doc """ Calls `to_oct/2` on a `JOSE.JWK` and then writes the encrypted binary to file. """ def to_oct_file(password, file, jwk = %JOSE.JWK{}), do: to_oct_file(password, file, to_record(jwk)) def to_oct_file(password, file, jwk), do: :jose_jwk.to_oct_file(password, file, jwk) @doc """ Calls `to_oct/3` on a `JOSE.JWK` and then writes the encrypted binary to file. """ def to_oct_file(password, file, jwe = %JOSE.JWE{}, jwk), do: to_oct_file(password, file, JOSE.JWE.to_record(jwe), jwk) def to_oct_file(password, file, jwe, jwk = %JOSE.JWK{}), do: to_oct_file(password, file, jwe, to_record(jwk)) def to_oct_file(password, file, jwe, jwk), do: :jose_jwk.to_oct_file(password, file, jwe, jwk) @doc """ Converts a `JOSE.JWK` into an octet key pair. """ def to_okp(list) when is_list(list), do: for(element <- list, into: [], do: to_okp(element)) def to_okp(jwk = %JOSE.JWK{}), do: to_okp(to_record(jwk)) def to_okp(jwk), do: :jose_jwk.to_okp(jwk) @doc """ Converts a `JOSE.JWK` into an OpenSSH key binary. """ def to_openssh_key(list) when is_list(list), do: for(element <- list, into: [], do: to_openssh_key(element)) def to_openssh_key(jwk = %JOSE.JWK{}), do: to_openssh_key(to_record(jwk)) def to_openssh_key(jwk), do: :jose_jwk.to_openssh_key(jwk) @doc """ Calls `to_openssh_key/1` on a `JOSE.JWK` and then writes the binary to file. """ def to_openssh_key_file(file, jwk = %JOSE.JWK{}), do: to_openssh_key_file(file, to_record(jwk)) def to_openssh_key_file(file, jwk), do: :jose_jwk.to_openssh_key_file(file, jwk) @doc """ Converts a `JOSE.JWK` into a PEM (Privacy Enhanced Email) binary. """ def to_pem(list) when is_list(list), do: for(element <- list, into: [], do: to_pem(element)) def to_pem(jwk = %JOSE.JWK{}), do: to_pem(to_record(jwk)) def to_pem(jwk), do: :jose_jwk.to_pem(jwk) @doc """ Encrypts a `JOSE.JWK` into a PEM (Privacy Enhanced Email) encrypted binary using `password`. """ def to_pem(password, list) when is_list(list), do: for(element <- list, into: [], do: to_pem(password, element)) def to_pem(password, jwk = %JOSE.JWK{}), do: to_pem(password, to_record(jwk)) def to_pem(password, jwk), do: :jose_jwk.to_pem(password, jwk) @doc """ Calls `to_pem/1` on a `JOSE.JWK` and then writes the binary to file. """ def to_pem_file(file, jwk = %JOSE.JWK{}), do: to_pem_file(file, to_record(jwk)) def to_pem_file(file, jwk), do: :jose_jwk.to_pem_file(file, jwk) @doc """ Calls `to_pem/2` on a `JOSE.JWK` and then writes the encrypted binary to file. """ def to_pem_file(password, file, jwk = %JOSE.JWK{}), do: to_pem_file(password, file, to_record(jwk)) def to_pem_file(password, file, jwk), do: :jose_jwk.to_pem_file(password, file, jwk) @doc """ Converts a private `JOSE.JWK` into a public `JOSE.JWK`. iex> jwk_rsa = JOSE.JWK.generate_key({:rsa, 256}) %JOSE.JWK{fields: %{}, keys: :undefined, kty: {:jose_jwk_kty_rsa, {:RSAPrivateKey, :"two-prime", 89657271283923333213688956979801646886488725937927826421780028977595670900943, 65537, 49624301670095289515744590467755999498582844809776145284365095264133428741569, 336111124810514302695156165996294214367, 266748895426976520545002702829665062929, 329628611699439793965634256329704106687, 266443630200356088742496100410997365601, 145084675516165292189647528713269147163, :asn1_NOVALUE}}} iex> JOSE.JWK.to_public(jwk_rsa) %JOSE.JWK{fields: %{}, keys: :undefined, kty: {:jose_jwk_kty_rsa, {:RSAPublicKey, 89657271283923333213688956979801646886488725937927826421780028977595670900943, 65537}}} """ def to_public(list) when is_list(list), do: for(element <- list, into: [], do: to_public(element)) def to_public(jwk = %JOSE.JWK{}), do: to_public(to_record(jwk)) def to_public(jwk), do: :jose_jwk.to_public(jwk) |> from_record() @doc """ Calls `to_public/1` and then `to_file/2` on a `JOSE.JWK`. """ def to_public_file(file, jwk = %JOSE.JWK{}), do: to_public_file(file, to_record(jwk)) def to_public_file(file, jwk), do: :jose_jwk.to_public_file(file, jwk) @doc """ Calls `to_public/1` and then `to_key/1` on a `JOSE.JWK`. """ def to_public_key(list) when is_list(list), do: for(element <- list, into: [], do: to_public_key(element)) def to_public_key(jwk = %JOSE.JWK{}), do: to_public_key(to_record(jwk)) def to_public_key(jwk), do: :jose_jwk.to_public_key(jwk) @doc """ Calls `to_public/1` and then `to_map/1` on a `JOSE.JWK`. """ def to_public_map(list) when is_list(list), do: for(element <- list, into: [], do: to_public_map(element)) def to_public_map(jwk = %JOSE.JWK{}), do: to_public_map(to_record(jwk)) def to_public_map(jwk), do: :jose_jwk.to_public_map(jwk) @doc """ Converts a `JOSE.JWK` into a map that can be used by `thumbprint/1` and `thumbprint/2`. """ def to_thumbprint_map(list) when is_list(list), do: for(element <- list, into: [], do: to_thumbprint_map(element)) def to_thumbprint_map(jwk = %JOSE.JWK{}), do: to_thumbprint_map(to_record(jwk)) def to_thumbprint_map(jwk), do: :jose_jwk.to_thumbprint_map(jwk) ## API @doc """ Decrypts the `encrypted` binary or map using the `jwk`. See `JOSE.JWE.block_decrypt/2`. """ def block_decrypt(encrypted, jwk = %JOSE.JWK{}), do: block_decrypt(encrypted, to_record(jwk)) def block_decrypt(encrypted, {your_public_jwk = %JOSE.JWK{}, my_private_jwk}), do: block_decrypt(encrypted, {to_record(your_public_jwk), my_private_jwk}) def block_decrypt(encrypted, {your_public_jwk, my_private_jwk = %JOSE.JWK{}}), do: block_decrypt(encrypted, {your_public_jwk, to_record(my_private_jwk)}) def block_decrypt(encrypted, jwk) do case :jose_jwk.block_decrypt(encrypted, jwk) do {plain_text, jwe} when is_tuple(jwe) -> {plain_text, JOSE.JWE.from_record(jwe)} error -> error end end @doc """ Encrypts the `plain_text` using the `jwk` and the default `jwe` based on the key type. See `block_encrypt/3`. """ def block_encrypt(plain_text, jwk = %JOSE.JWK{}), do: block_encrypt(plain_text, to_record(jwk)) def block_encrypt(plain_text, {your_public_jwk = %JOSE.JWK{}, my_private_jwk}), do: block_encrypt(plain_text, {to_record(your_public_jwk), my_private_jwk}) def block_encrypt(plain_text, {your_public_jwk, my_private_jwk = %JOSE.JWK{}}), do: block_encrypt(plain_text, {your_public_jwk, to_record(my_private_jwk)}) def block_encrypt(plain_text, jwk), do: :jose_jwk.block_encrypt(plain_text, jwk) @doc """ Encrypts the `plain_text` using the `jwk` and algorithms specified by the `jwe`. See `JOSE.JWE.block_encrypt/3`. """ def block_encrypt(plain_text, jwe = %JOSE.JWE{}, jwk), do: block_encrypt(plain_text, JOSE.JWE.to_record(jwe), jwk) def block_encrypt(plain_text, jwe, jwk = %JOSE.JWK{}), do: block_encrypt(plain_text, jwe, to_record(jwk)) def block_encrypt(plain_text, jwe, {your_public_jwk = %JOSE.JWK{}, my_private_jwk}), do: block_encrypt(plain_text, jwe, {to_record(your_public_jwk), my_private_jwk}) def block_encrypt(plain_text, jwe, {your_public_jwk, my_private_jwk = %JOSE.JWK{}}), do: block_encrypt(plain_text, jwe, {your_public_jwk, to_record(my_private_jwk)}) def block_encrypt(plain_text, jwe, jwk), do: :jose_jwk.block_encrypt(plain_text, jwe, jwk) @doc """ Returns a block encryptor map for the key type. """ def block_encryptor(list) when is_list(list), do: for(element <- list, into: [], do: block_encryptor(element)) def block_encryptor(jwk = %JOSE.JWK{}), do: block_encryptor(to_record(jwk)) def block_encryptor(jwk), do: :jose_jwk.block_encryptor(jwk) @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}), do: box_decrypt(encrypted, {to_record(your_public_jwk), my_private_jwk}) def box_decrypt(encrypted, {your_public_jwk, my_private_jwk = %JOSE.JWK{}}), do: box_decrypt(encrypted, {your_public_jwk, to_record(my_private_jwk)}) def box_decrypt(encrypted, my_private_jwk) do case :jose_jwk.box_decrypt(encrypted, my_private_jwk) do {plain_text, jwe} when is_tuple(jwe) -> {plain_text, JOSE.JWE.from_record(jwe)} error -> error end end @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 case :jose_jwk.box_encrypt(plain_text, other_public_jwk) do {encrypted, my_private_jwk} when is_tuple(my_private_jwk) -> {encrypted, from_record(my_private_jwk)} error -> error end end @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) def box_encrypt(plain_text, other_public_jwk, my_private_jwk = %JOSE.JWK{}), do: box_encrypt(plain_text, other_public_jwk, to_record(my_private_jwk)) def box_encrypt(plain_text, other_public_jwk, my_private_jwk), do: :jose_jwk.box_encrypt(plain_text, other_public_jwk, my_private_jwk) @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) def box_encrypt(plain_text, jwe, other_public_jwk = %JOSE.JWK{}, my_private_jwk), do: box_encrypt(plain_text, jwe, to_record(other_public_jwk), my_private_jwk) def box_encrypt(plain_text, jwe, other_public_jwk, my_private_jwk = %JOSE.JWK{}), do: box_encrypt(plain_text, jwe, other_public_jwk, to_record(my_private_jwk)) 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 """ ECDH-SS Key Agreement decryption of the `encrypted` binary or map using `v_static_secret_key`. See `box_encrypt_ecdh_ss/2` and `JOSE.JWE.block_decrypt/2`. """ def box_decrypt_ecdh_ss(encrypted, v_static_secret_key) do v_static_secret_key = maybe_to_record(v_static_secret_key) case :jose_jwk.box_decrypt_ecdh_ss(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-SS Key Agreement encryption of `plain_text` by generating a static secret key based on `v_static_public_key` curve. See `box_encrypt_ecdh_ss/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 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 and Alice's (U) static secret key to encrypt the message: iex> enc_alice2bob_tuple = JOSE.JWK.box_encrypt_ecdh_ss("secret message", v_static_public_key, u_static_secret_key) {%{alg: :jose_jwe_alg_ecdh_ss, enc: :jose_jwe_enc_aes}, %{ "ciphertext" => "yug6TzZUgAEt0DAPNnw", "encrypted_key" => "", "iv" => "f3rEdLgkLLXqDjno", "protected" => "eyJhbGciOiJFQ0RILVNTIiwiYXB1IjoieG1RUUNGbzk5OF9TVzhkMy1vQnRMX0ZfbGtBd1E0Y1J0VHZSRWtlUjBaVTdNR2Q0SkZqSHczR2t1dVdXNFI5YzJDSWkzQzQtQW84NFRzR2x5c0JOMVEiLCJhcHYiOiJrNDR0QzE4U2tYTUVXektFQ0JSZ3VhMHpaX01MRHY1VTQ2TWNueVl6ajBBIiwiZW5jIjoiQTEyOEdDTSIsInNwayI6eyJjcnYiOiJYMjU1MTkiLCJrdHkiOiJPS1AiLCJ4Ijoic3oxSk1NYXNOUkxRZlhJa3ZMVFJhT3U5NzhRUXUxcm9GS3hCUEtaZHNDOCJ9fQ", "tag" => "gAszp6UPSNozXeoqk428Og" }} # Alice (U) compacts the encrypted message and sends it to Bob (V), which contains Alice's (U) static public key: iex> enc_alice2bob_binary = JOSE.JWE.compact(enc_alice2bob_tuple) |> elem(1) "eyJhbGciOiJFQ0RILVNTIiwiYXB1IjoieG1RUUNGbzk5OF9TVzhkMy1vQnRMX0ZfbGtBd1E0Y1J0VHZSRWtlUjBaVTdNR2Q0SkZqSHczR2t1dVdXNFI5YzJDSWkzQzQtQW84NFRzR2x5c0JOMVEiLCJhcHYiOiJrNDR0QzE4U2tYTUVXektFQ0JSZ3VhMHpaX01MRHY1VTQ2TWNueVl6ajBBIiwiZW5jIjoiQTEyOEdDTSIsInNwayI6eyJjcnYiOiJYMjU1MTkiLCJrdHkiOiJPS1AiLCJ4Ijoic3oxSk1NYXNOUkxRZlhJa3ZMVFJhT3U5NzhRUXUxcm9GS3hCUEtaZHNDOCJ9fQ..f3rEdLgkLLXqDjno.yug6TzZUgAEt0DAPNnw.gAszp6UPSNozXeoqk428Og" # Bob (V) can then decrypt the encrypted message with Bob's (V) static secret key: iex> JOSE.JWK.box_decrypt_ecdh_ss(enc_alice2bob_binary, v_static_secret_key) {"secret message", %JOSE.JWE{ alg: {:jose_jwe_alg_ecdh_ss, {:jose_jwe_alg_ecdh_ss, {:jose_jwk, :undefined, {:jose_jwk_kty_okp_x25519, <<179, 61, 73, 48, 198, 172, 53, 18, 208, 125, 114, 36, 188, 180, 209, 104, 235, 189, 239, 196, 16, 187, 90, 232, 20, 172, 65, 60, 166, 93, 176, 47>>}, %{}}, <<198, 100, 16, 8, 90, 61, 247, 207, 210, 91, 199, 119, 250, 128, 109, 47, 241, 127, 150, 64, 48, 67, 135, 17, 181, 59, 209, 18, 71, 145, 209, 149, 59, 48, 103, 120, 36, 88, 199, 195, 113, 164, 186, 229, 150, 225, 31, 92, 216, 34, 34, 220, 46, 62, 2, 143, 56, 78, 193, 165, 202, 192, 77, 213>>, <<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_ss(plain_text, v_static_public_key) do v_static_public_key = maybe_to_record(v_static_public_key) {encrypted, u_static_secret_key} = :jose_jwk.box_encrypt_ecdh_ss(plain_text, v_static_public_key) {encrypted, from_record(u_static_secret_key)} end @doc """ ECDH-SS Key Agreement encryption of `plain_text` using `v_static_public_key`, `u_static_secret_key`, and a derived `jwe` based on the input key types. See `box_encrypt_ecdh_ss/4`. """ def box_encrypt_ecdh_ss(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) :jose_jwk.box_encrypt_ecdh_ss(plain_text, v_static_public_key, u_static_secret_key) end @doc """ ECDH-SS Key Agreement encryption of `plain_text` using `v_static_public_key`, `u_static_secret_key`, and the algorithms specified by the `jwe`. """ def box_encrypt_ecdh_ss(plain_text, jwe, v_static_public_key, u_static_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) :jose_jwk.box_encrypt_ecdh_ss(plain_text, jwe, v_static_public_key, u_static_secret_key) end @doc """ Generates a new `JOSE.JWK` based on another `JOSE.JWK` or from initialization params provided. Passing another `JOSE.JWK` results in different behavior depending on the `"kty"`: * `"EC"` - uses the same named curve to generate a new key * `"oct"` - uses the byte size to generate a new key * `"OKP"` - uses the same named curve to generate a new key * `"RSA"` - uses the same modulus and exponent sizes to generate a new key The following initialization params may also be used: * `{: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` """ def generate_key(jwk = %JOSE.JWK{}), do: jwk |> to_record() |> generate_key() def generate_key(parameters), do: :jose_jwk.generate_key(parameters) |> from_record() @doc """ Merges map on right into map on left. """ def merge(left = %JOSE.JWK{}, right), do: merge(left |> to_record(), right) def merge(left, right = %JOSE.JWK{}), do: merge(left, right |> to_record()) def merge(left, right), do: :jose_jwk.merge(left, right) |> from_record() @doc """ Computes the shared secret between two keys. Currently only works for `"EC"` keys and `"OKP"` keys with `"crv"` set to `"X25519"` or `"X448"`. """ def shared_secret(your_jwk = %JOSE.JWK{}, my_jwk), do: shared_secret(to_record(your_jwk), my_jwk) def shared_secret(your_jwk, my_jwk = %JOSE.JWK{}), do: shared_secret(your_jwk, to_record(my_jwk)) def shared_secret(your_jwk, my_jwk), do: :jose_jwk.shared_secret(your_jwk, my_jwk) @doc """ Signs the `plain_text` using the `jwk` and the default signer algorithm `jws` for the key type. See `sign/3`. """ def sign(plain_text, jwk = %JOSE.JWK{}), do: sign(plain_text, to_record(jwk)) def sign(plain_text, key_list) when is_list(key_list) do keys = for key <- key_list, into: [] do case key do %JOSE.JWK{} -> JOSE.JWK.to_record(key) _ -> key end end :jose_jwk.sign(plain_text, keys) end def sign(plain_text, jwk), do: :jose_jwk.sign(plain_text, jwk) @doc """ Signs the `plain_text` using the `jwk` and the algorithm specified by the `jws`. See `JOSE.JWS.sign/3`. """ def sign(plain_text, jws = %JOSE.JWS{}, jwk), do: sign(plain_text, JOSE.JWS.to_record(jws), jwk) def sign(plain_text, jws, jwk = %JOSE.JWK{}), do: sign(plain_text, jws, to_record(jwk)) def sign(plain_text, signer_list, key_list) when is_list(signer_list) and is_list(key_list) and length(signer_list) === length(key_list) do signers = for signer <- signer_list, into: [] do case signer do %JOSE.JWS{} -> JOSE.JWS.to_record(signer) _ -> signer end end keys = for key <- key_list, into: [] do case key do %JOSE.JWK{} -> JOSE.JWK.to_record(key) _ -> key end end :jose_jwk.sign(plain_text, signers, keys) end def sign(plain_text, jws, key_list) when is_list(key_list) and not is_list(jws) do keys = for key <- key_list, into: [] do case key do %JOSE.JWK{} -> JOSE.JWK.to_record(key) _ -> key end end :jose_jwk.sign(plain_text, jws, keys) end def sign(plain_text, jws, jwk), do: :jose_jwk.sign(plain_text, jws, jwk) @doc """ Returns a signer map for the key type. """ def signer(list) when is_list(list), do: for(element <- list, into: [], do: signer(element)) def signer(jwk = %JOSE.JWK{}), do: signer(to_record(jwk)) def signer(jwk), do: :jose_jwk.signer(jwk) @doc """ Returns the unique thumbprint for a `JOSE.JWK` using the `:sha256` digest type. See `thumbprint/2`. """ def thumbprint(list) when is_list(list), do: for(element <- list, into: [], do: thumbprint(element)) def thumbprint(jwk = %JOSE.JWK{}), do: thumbprint(to_record(jwk)) def thumbprint(jwk), do: :jose_jwk.thumbprint(jwk) @doc """ Returns the unique thumbprint for a `JOSE.JWK` using the `digest_type`. # let's define two different keys that will have the same thumbprint jwk1 = JOSE.JWK.from_oct("secret") jwk2 = JOSE.JWK.from(%{ "use" => "sig", "k" => "c2VjcmV0", "kty" => "oct" }) iex> JOSE.JWK.thumbprint(jwk1) "DWBh0SEIAPYh1x5uvot4z3AhaikHkxNJa3Ada2fT-Cg" iex> JOSE.JWK.thumbprint(jwk2) "DWBh0SEIAPYh1x5uvot4z3AhaikHkxNJa3Ada2fT-Cg" iex> JOSE.JWK.thumbprint(:md5, jwk1) "Kldz8k5PQm7y1E3aNBlMiA" iex> JOSE.JWK.thumbprint(:md5, jwk2) "Kldz8k5PQm7y1E3aNBlMiA" See JSON Web Key (JWK) Thumbprint [RFC 7638](https://tools.ietf.org/html/rfc7638) for more information. """ def thumbprint(digest_type, list) when is_list(list), do: for(element <- list, into: [], do: thumbprint(digest_type, element)) def thumbprint(digest_type, jwk = %JOSE.JWK{}), do: thumbprint(digest_type, to_record(jwk)) def thumbprint(digest_type, jwk), do: :jose_jwk.thumbprint(digest_type, jwk) @doc """ Returns a verifier algorithm list for the key type. """ def verifier(list) when is_list(list), do: for(element <- list, into: [], do: verifier(element)) def verifier(jwk = %JOSE.JWK{}), do: verifier(to_record(jwk)) def verifier(jwk), do: :jose_jwk.verifier(jwk) @doc """ Verifies the `signed` using the `jwk`. See `JOSE.JWS.verify_strict/3`. """ def verify(signed, jwk = %JOSE.JWK{}), do: verify(signed, to_record(jwk)) def verify(signed, jwk = [%JOSE.JWK{} | _]) do verify( signed, for k <- jwk do case k do %JOSE.JWK{} -> JOSE.JWK.to_record(k) _ -> k end end ) end def verify(signed, jwk) do try do case :jose_jwk.verify(signed, jwk) do {verified, payload, jws} when is_tuple(jws) -> {verified, payload, JOSE.JWS.from_record(jws)} list when is_list(list) -> for {jwk, verifications} <- list do {JOSE.JWK.from_record(jwk), Enum.map(verifications, fn {verified, jwt, jws} when is_tuple(jwt) and is_tuple(jws) -> {verified, from_record(jwt), JOSE.JWS.from_record(jws)} other -> other end)} end error -> error end catch class, reason -> {class, reason} end end @doc """ Verifies the `signed` using the `jwk` and whitelists the `"alg"` using `allow`. See `JOSE.JWS.verify/2`. """ def verify_strict(signed, allow, jwk = %JOSE.JWK{}), do: verify_strict(signed, allow, to_record(jwk)) def verify_strict(signed, allow, jwk = [%JOSE.JWK{} | _]) do verify_strict( signed, allow, for k <- jwk do case k do %JOSE.JWK{} -> JOSE.JWK.to_record(k) _ -> k end end ) end def verify_strict(signed, allow, jwk) do try do case :jose_jwk.verify_strict(signed, allow, jwk) do {verified, payload, jws} when is_tuple(jws) -> {verified, payload, JOSE.JWS.from_record(jws)} list when is_list(list) -> for {jwk, verifications} <- list do {JOSE.JWK.from_record(jwk), Enum.map(verifications, fn {verified, jwt, jws} when is_tuple(jwt) and is_tuple(jws) -> {verified, from_record(jwt), JOSE.JWS.from_record(jws)} other -> other end)} end error -> error end catch class, reason -> {class, reason} end end end potatosalad-erlang-jose-30ac019/lib/jose/jwe.ex0000664000232200023220000011672015267717064022002 0ustar debalancedebalancerequire Record defmodule JOSE.JWE do @moduledoc ~S""" JWE stands for JSON Web Encryption which is defined in [RFC 7516](https://tools.ietf.org/html/rfc7516). ## Key Derivation Algorithms The following key derivation algorithms for the `"alg"` field are currently supported by `JOSE.JWE` (some may need the `JOSE.crypto_fallback/1` option to be enabled): * `"A128GCMKW"` * `"A192GCMKW"` * `"A256GCMKW"` * `"A128KW"` * `"A192KW"` * `"A256KW"` * `"dir"` * `"ECDH-1PU"` * `"ECDH-1PU+A128GCMKW"` * `"ECDH-1PU+A192GCMKW"` * `"ECDH-1PU+A256GCMKW"` * `"ECDH-1PU+A128KW"` * `"ECDH-1PU+A192KW"` * `"ECDH-1PU+A256KW"` * `"ECDH-1PU+C20PKW"` * `"ECDH-1PU+XC20PKW"` * `"ECDH-ES"` * `"ECDH-ES+A128GCMKW"` * `"ECDH-ES+A192GCMKW"` * `"ECDH-ES+A256GCMKW"` * `"ECDH-ES+A128KW"` * `"ECDH-ES+A192KW"` * `"ECDH-ES+A256KW"` * `"ECDH-ES+C20PKW"` * `"ECDH-ES+XC20PKW"` * `"ECDH-SS"` * `"ECDH-SS+A128GCMKW"` * `"ECDH-SS+A192GCMKW"` * `"ECDH-SS+A256GCMKW"` * `"ECDH-SS+A128KW"` * `"ECDH-SS+A192KW"` * `"ECDH-SS+A256KW"` * `"ECDH-SS+C20PKW"` * `"ECDH-SS+XC20PKW"` * `"PBES2-HS256+A128GCMKW"` * `"PBES2-HS384+A192GCMKW"` * `"PBES2-HS512+A256GCMKW"` * `"PBES2-HS256+A128KW"` * `"PBES2-HS384+A192KW"` * `"PBES2-HS512+A256KW"` * `"PBES2-HS512+C20PKW"` * `"PBES2-HS512+XC20PKW"` * `"RSA1_5"` * `"RSA-OAEP"` * `"RSA-OAEP-256"` ## Encryption Algorithms The following encryption algorithms for the `"enc"` field are currently supported by `JOSE.JWE` (some may need the `JOSE.crypto_fallback/1` option to be enabled): * `"A128CBC-HS256"` * `"A192CBC-HS384"` * `"A256CBC-HS512"` * `"A128GCM"` * `"A192GCM"` * `"A256GCM"` * `"C20P"` * `"XC20P"` ## Compression Algorithms The following compression algorithms for the `"zip"` field are currently supported by `JOSE.JWE`: * `"DEF"` ## Key Derivation Examples All of the examples below will use `"enc"` set to `"A128GCM"`, `"A192GCM"`, or `"A256GCM"` depending on the derived key size. The octet key used will typically be all zeroes of the required size in the form of `<<0::128>>` (for a 128-bit key). All of the example keys generated below can be found here: [https://gist.github.com/potatosalad/dd140560b2bdbdab886d](https://gist.github.com/potatosalad/dd140560b2bdbdab886d) # octet keys we'll use below jwk_oct128 = JOSE.JWK.from_oct(<<0::128>>) jwk_oct192 = JOSE.JWK.from_oct(<<0::192>>) jwk_oct256 = JOSE.JWK.from_oct(<<0::256>>) jwk_secret = JOSE.JWK.from_oct("secret") # EC keypairs we'll use below jwk_ec256_alice_sk = JOSE.JWK.generate_key({:ec, :secp256r1}) jwk_ec256_alice_pk = JOSE.JWK.to_public(jwk_ec256_alice_sk) jwk_ec256_bob_sk = JOSE.JWK.generate_key({:ec, :secp256r1}) jwk_ec256_bob_pk = JOSE.JWK.to_public(jwk_ec256_bob_sk) # X25519 keypairs we'll use below jwk_x25519_alice_sk = JOSE.JWK.generate_key({:okp, :X25519}) jwk_x25519_alice_pk = JOSE.JWK.to_public(jwk_x25519_alice_sk) jwk_x25519_bob_sk = JOSE.JWK.generate_key({:okp, :X25519}) jwk_x25519_bob_pk = JOSE.JWK.to_public(jwk_x25519_bob_sk) # X448 keypairs we'll use below jwk_x448_alice_sk = JOSE.JWK.generate_key({:okp, :X448}) jwk_x448_alice_pk = JOSE.JWK.to_public(jwk_x448_alice_sk) jwk_x448_bob_sk = JOSE.JWK.generate_key({:okp, :X448}) jwk_x448_bob_pk = JOSE.JWK.to_public(jwk_x448_bob_sk) # RSA keypairs we'll use below jwk_rsa_sk = JOSE.JWK.generate_key({:rsa, 4096}) jwk_rsa_pk = JOSE.JWK.to_public(jwk_rsa_sk) ### A128GCMKW, A192GCMKW, and A256GCMKW # A128GCMKW iex> encrypted_a128gcmkw = JOSE.JWE.block_encrypt(jwk_oct128, "{}", %{ "alg" => "A128GCMKW", "enc" => "A128GCM" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJBMTI4R0NNS1ciLCJlbmMiOiJBMTI4R0NNIiwiaXYiOiJzODNFNjhPNjhsWlM5ZVprIiwidGFnIjoieF9Ea2M5dm1LMk5RQV8tU2hvTkFRdyJ9.8B2qX8fVEa-s61RsZXqkCg.J7yJ8sKLbUlzyor6.FRs.BhBwImTv9B14NwVuxmfU6A" iex> JOSE.JWE.block_decrypt(jwk_oct128, encrypted_a128gcmkw) |> elem(0) "{}" # A192GCMKW iex> encrypted_a192gcmkw = JOSE.JWE.block_encrypt(jwk_oct192, "{}", %{ "alg" => "A192GCMKW", "enc" => "A192GCM" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJBMTkyR0NNS1ciLCJlbmMiOiJBMTkyR0NNIiwiaXYiOiIxMkduZWQyTDB6NE5LZG83IiwidGFnIjoiM0thbG9iaER1Wmx5dE1YSjhjcXhZZyJ9.jJC4E1c6augIhvGDp3fquRfO-mnnud4F.S2NkKNGxBKTsCnKo.gZA.MvfhqSTeEN75H8HDyvfzRQ" iex> JOSE.JWE.block_decrypt(jwk_oct192, encrypted_a192gcmkw) |> elem(0) "{}" # A256GCMKW iex> encrypted_a256gcmkw = JOSE.JWE.block_encrypt(jwk_oct256, "{}", %{ "alg" => "A256GCMKW", "enc" => "A256GCM" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJBMjU2R0NNS1ciLCJlbmMiOiJBMjU2R0NNIiwiaXYiOiJHU3lFMTBLQURxZTczNUMzIiwidGFnIjoiR3dVbDJCbXRNWlVseDlXNEMtY0tQZyJ9.sSsbFw9z8WTkzBLvPMywSedTXXygFxfP9g5U2qpzUX8.eiVFfe7iojfK0AXb._v8.YVfk9dNrtS7wxbGqCVge-g" iex> JOSE.JWE.block_decrypt(jwk_oct256, encrypted_a256gcmkw) |> elem(0) "{}" ### A128KW, A192KW, and A256KW # A128KW iex> encrypted_a128kw = JOSE.JWE.block_encrypt(jwk_oct128, "{}", %{ "alg" => "A128KW", "enc" => "A128GCM" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIn0.t4_Fb4kCl6BcS1cXnR4P4Xgm-jwVNsFl.RerKfWjzqqtLIUrz.JmE.ZDpVlWo-aQYM5la9eshwWw" iex> JOSE.JWE.block_decrypt(jwk_oct128, encrypted_a128kw) |> elem(0) "{}" # A192KW iex> encrypted_a192kw = JOSE.JWE.block_encrypt(jwk_oct192, "{}", %{ "alg" => "A192KW", "enc" => "A192GCM" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJBMTkyS1ciLCJlbmMiOiJBMTkyR0NNIn0.edpvNrztlNADbkwfq5YBJgqFBSH_Znv1Y1uXKNQ_13w.yCkEYTCPOKH6CoxZ.siw.zP_ZM9OEeX1FIdFjqNawtQ" iex> JOSE.JWE.block_decrypt(jwk_oct192, encrypted_a192kw) |> elem(0) "{}" # A256KW iex> encrypted_a256kw = JOSE.JWE.block_encrypt(jwk_oct256, "{}", %{ "alg" => "A256KW", "enc" => "A256GCM" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIn0.OvAhC1a2BoP_2SMIiZXwIHWPoIkD-Cosgp3nlpiTs8ySUBPfPzwG1g.4GeackYJbuBksAWA.HPE.vG0sGC2kuklH5xk8KXhyNA" iex> JOSE.JWE.block_decrypt(jwk_oct256, encrypted_a256kw) |> elem(0) "{}" ### dir The `"dir"` key derivation algorithm is essentially just a pass-through to the underlying `"enc"` algorithm. The `"encrypted_key"` is not included in the protected header, so the key must be fully known by both parties. # dir iex> encrypted_dir = JOSE.JWE.block_encrypt(jwk_oct128, "{}", %{ "alg" => "dir", "enc" => "A128GCM" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIn0..HdRR8O0kk_SvOjAS.rxo.JTMPGPKZZKVNlWV0RexsmQ" iex> JOSE.JWE.block_decrypt(jwk_oct128, encrypted_dir) |> elem(0) "{}" ### ECDH-ES, ECDH-ES+A128KW, ECDH-ES+A192KW, and ECDH-ES+A256KW The `"ECDH-ES"` key derivation algorithm does not include the `"encrypted_key"` field in the protected header, similar to how `"dir"` functions. The size of the generated key is dependent on the `"enc"` setting (for example, `"A128GCM"` will generate a 128-bit key, `"A256GCM"` a 256-bit key, etc). # ECDH-ES with EC keypairs iex> encrypted_ecdhes_ec256_alice2bob = JOSE.JWE.block_encrypt({jwk_ec256_bob_pk, jwk_ec256_alice_sk}, "{}", %{ "alg" => "ECDH-ES", "enc" => "A128GCM" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOEdDTSIsImVwayI6eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6IjQ4UVUzUTBDeVN4d0piRXdXckpyWVhscDg4X2RWcEhUeHE0YXZjNjZoNVEiLCJ5IjoiWnpxcklOdE1NeEh4US1RQjcyUk1jZGxtRHNPSXdsS2hNcVZtX2dZV0MxNCJ9fQ..UssNrY5qEeFdluZY.R6g.32nlr0wHF2TwfL1UnBtIow" iex> JOSE.JWE.block_decrypt({jwk_ec256_alice_pk, jwk_ec256_bob_sk}, encrypted_ecdhes_ec256_alice2bob) |> elem(0) "{}" # ECDH-ES with X25519 keypairs iex> encrypted_ecdhes_x25519_alice2bob = JOSE.JWE.block_encrypt({jwk_x25519_bob_pk, jwk_x25519_alice_sk}, "{}", %{ "alg" => "ECDH-ES", "enc" => "A128GCM" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOEdDTSIsImVwayI6eyJjcnYiOiJYMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiZ0g3TjJwT0duenZfd0tBLUhqREZKTlVSZVhfdG05XzdiMkZSUjI3cXFYcyJ9fQ..T-0q42FPCUy3hlla.MHU.9TNP2jG5bN1vSvaesijdww" iex> JOSE.JWE.block_decrypt({jwk_x25519_alice_pk, jwk_x25519_bob_sk}, encrypted_ecdhes_x25519_alice2bob) |> elem(0) "{}" # ECDH-ES with X448 keypairs iex> encrypted_ecdhes_x448_alice2bob = JOSE.JWE.block_encrypt({jwk_x448_bob_pk, jwk_x448_alice_sk}, "{}", %{ "alg" => "ECDH-ES", "enc" => "A128GCM" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOEdDTSIsImVwayI6eyJjcnYiOiJYNDQ4Iiwia3R5IjoiT0tQIiwieCI6ImFFaHZISGxFM2V1Y3lsY0RNNzBMd1paY2dDRk9acXExNWM3YXZNMjJkcWZIUEtja1FZNmo3LXFfM19kMGI1cGVWZEFoNVoyQWZIWSJ9fQ..T-UNE-wOApuRH71r.Uj8.l8bIfhC1UPAPVWBV3wkc6A" iex> JOSE.JWE.block_decrypt({jwk_x448_alice_pk, jwk_x448_bob_sk}, encrypted_ecdhes_x448_alice2bob) |> elem(0) "{}" When decrypting with any of the `"ECDH-ES"` related algorithms, the other party's public key is recommended, but not required for decryption (the embedded Ephemeral Public Key will be used instead): # decrypting the X448 example with and without the public key specified iex> JOSE.JWE.block_decrypt({jwk_x448_alice_pk, jwk_x448_bob_sk}, encrypted_ecdhes_x448_alice2bob) |> elem(0) "{}" iex> JOSE.JWE.block_decrypt(jwk_x448_bob_sk, encrypted_ecdhes_x448_alice2bob) |> elem(0) "{}" The `"ECDH-ES+A128KW"`, `"ECDH-ES+A192KW"`, and `"ECDH-ES+A256KW"` key derivation algorithms do include the `"encrypted_key"` and the suffix after `"ECDH-ES+"` determines the key size (so `"ECDH-ES+A128KW"` computes a 128-bit key). # ECDH-ES+A128KW with EC keypairs iex> encrypted_ecdhesa128kw_alice2bob = JOSE.JWE.block_encrypt({jwk_ec256_bob_pk, jwk_ec256_alice_sk}, "{}", %{ "alg" => "ECDH-ES+A128KW", "enc" => "A128GCM" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImVuYyI6IkExMjhHQ00iLCJlcGsiOnsiY3J2IjoiUC0yNTYiLCJrdHkiOiJFQyIsIngiOiI0OFFVM1EwQ3lTeHdKYkV3V3JKcllYbHA4OF9kVnBIVHhxNGF2YzY2aDVRIiwieSI6Ilp6cXJJTnRNTXhIeFEtUUI3MlJNY2RsbURzT0l3bEtoTXFWbV9nWVdDMTQifX0.ZwuqXf7svd3SH0M-XYLjWz5JsN6xX03C.l8tt83EJjy86IovL.i5A.nw05dPUA0a18xdtvmHbhHA" iex> JOSE.JWE.block_decrypt({jwk_ec256_alice_pk, jwk_ec256_bob_sk}, encrypted_ecdhesa128kw_alice2bob) |> elem(0) "{}" # ECDH-ES+A192KW with EC keypairs iex> encrypted_ecdhesa192kw_alice2bob = JOSE.JWE.block_encrypt({jwk_ec256_bob_pk, jwk_ec256_alice_sk}, "{}", %{ "alg" => "ECDH-ES+A192KW", "enc" => "A192GCM" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJFQ0RILUVTK0ExOTJLVyIsImVuYyI6IkExOTJHQ00iLCJlcGsiOnsiY3J2IjoiUC0yNTYiLCJrdHkiOiJFQyIsIngiOiI0OFFVM1EwQ3lTeHdKYkV3V3JKcllYbHA4OF9kVnBIVHhxNGF2YzY2aDVRIiwieSI6Ilp6cXJJTnRNTXhIeFEtUUI3MlJNY2RsbURzT0l3bEtoTXFWbV9nWVdDMTQifX0.S9LZ1i_Lua_if4I83WcaCQ9yT5qqPI_NhCFR7tMiZDQ.kG3taKEjGeKDRTzs.H1s.oVGBFP63z4gd3e-R2d1cmA" iex> JOSE.JWE.block_decrypt({jwk_ec256_alice_pk, jwk_ec256_bob_sk}, encrypted_ecdhesa192kw_alice2bob) |> elem(0) "{}" # ECDH-ES+A256KW with EC keypairs iex> encrypted_ecdhesa256kw_alice2bob = JOSE.JWE.block_encrypt({jwk_ec256_bob_pk, jwk_ec256_alice_sk}, "{}", %{ "alg" => "ECDH-ES+A256KW", "enc" => "A256GCM" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJFQ0RILUVTK0EyNTZLVyIsImVuYyI6IkEyNTZHQ00iLCJlcGsiOnsiY3J2IjoiUC0yNTYiLCJrdHkiOiJFQyIsIngiOiI0OFFVM1EwQ3lTeHdKYkV3V3JKcllYbHA4OF9kVnBIVHhxNGF2YzY2aDVRIiwieSI6Ilp6cXJJTnRNTXhIeFEtUUI3MlJNY2RsbURzT0l3bEtoTXFWbV9nWVdDMTQifX0.4KWy1-vRiJyNINF6mWYbUPPTVNG9ADfvvfpSDbddPTftz7GmUHUsuQ.IkRhtGH23R-9dFF3.9yk.RnALhnqWMHWCZFxqc-DU4A" iex> JOSE.JWE.block_decrypt({jwk_ec256_alice_pk, jwk_ec256_bob_sk}, encrypted_ecdhesa256kw_alice2bob) |> elem(0) "{}" See `JOSE.JWK.box_encrypt/2` for generating an Ephemeral Public Key based on the same curve as the supplied other party key in the same step. ### PBES2-HS256+A128KW, PBES2-HS384+A192KW, and PBES2-HS512+A256KW # PBES2-HS256+A128KW iex> encrypted_pbes2hs256a128kw = JOSE.JWE.block_encrypt(jwk_secret, "{}", %{ "alg" => "PBES2-HS256+A128KW", "enc" => "A128GCM" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIiwicDJjIjo0MDk2LCJwMnMiOiJRR0laNTlzbjRnQThySHBWYjFrSkd3In0.8WMQ0fysLiHU8AjpjkcqJGpYe53VRf2s.vVEb2ZtKmtPIw8M-.Cmg.GCjDtdKV6khqEuyZy2gUxw" iex> JOSE.JWE.block_decrypt(jwk_secret, encrypted_pbes2hs256a128kw) |> elem(0) "{}" # PBES2-HS384+A192KW iex> encrypted_pbes2hs384a192kw = JOSE.JWE.block_encrypt(jwk_secret, "{}", %{ "alg" => "PBES2-HS384+A192KW", "enc" => "A192GCM" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJQQkVTMi1IUzM4NCtBMTkyS1ciLCJlbmMiOiJBMTkyR0NNIiwicDJjIjo2MTQ0LCJwMnMiOiJKSDRjZ0hlNTZiU0prZ1d6VktpWWJCb0FzWEJBY1A1NiJ9.Ck5GvgXxmyac3jzs0lRavoRh6tI9nEs3lYkx8sdDzGw.IdxaPATMkQ8FYiYQ.uHk.rDU6ltWsTsw9vuvA73bgJQ" iex> JOSE.JWE.block_decrypt(jwk_secret, encrypted_pbes2hs384a192kw) |> elem(0) "{}" # PBES2-HS512+A256KW iex> encrypted_pbes2hs512a256kw = JOSE.JWE.block_encrypt(jwk_secret, "{}", %{ "alg" => "PBES2-HS512+A256KW", "enc" => "A256GCM" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJQQkVTMi1IUzUxMitBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIiwicDJjIjo4MTkyLCJwMnMiOiJ6YWRiMVNmT1F4V1gyTHJrSVgwWDFGM2QzNlBIdUdxRVFzUDVhbWVnTk00In0.6SUVO9sSevqZrZ5yPX-JvJNJrzfIQeTTzrkWBHEqHra1_AITtwEe0A.0AaF_3ZlJOkRlqgb.W8I.jFWob73QTn52IFSIPEWHFA" iex> JOSE.JWE.block_decrypt(jwk_secret, encrypted_pbes2hs512a256kw) |> elem(0) "{}" The `"p2s"` and `"p2i"` fields may also be specified to control the Salt and Iterations of the PBES2 Key Derivation Function, respectively. The default Salt is a randomly generated binary the same length of bytes as the key wrap (for example, `"PBES2-HS256+A128KW"` will generate a 16-byte Salt). The default Iterations is 32 times the number of bits specified by the key wrap (for example, `"PBES2-HS256+A128KW"` will have 4096 Iterations). # let's setup the JWE header iterations = 8192 salt = <<0::256>> # all zero salt, for example usage only jwe = %{ "alg" => "PBES2-HS256+A128KW", "enc" => "A128GCM", "p2i" => iterations, "p2s" => :jose_base64url.encode(salt) } # PBES2-HS256+A128KW iex> encrypted_pbes2 = JOSE.JWE.block_encrypt(jwk_secret, "{}", jwe) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIiwicDJjIjo0MDk2LCJwMmkiOjgxOTIsInAycyI6IkFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUEifQ.I7wcBmg7O_rOWpg1aak7wQWX84YtED6k.Rgh3f6Kzl5SZ1z7x.FNo.eyK1ySx4SGR-xC2EYNySQA" iex> JOSE.JWE.block_decrypt(jwk_secret, encrypted_pbes2) |> elem(0) "{}" ### RSA1_5, RSA-OAEP, and RSA-OAEP-256 # RSA1_5 iex> encrypted_rsa1_5 = JOSE.JWE.block_encrypt(jwk_rsa_pk, "{}", %{ "alg" => "RSA1_5", "enc" => "A128GCM" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4R0NNIn0.NlndPTqULN1vArshEzfEXY0nHCf4ubsTK9iHAeIxL85fReYrYG8EDB2_IirUneavvHSa-hsVLXNzBu0F9OY3TRFAIuJ8Jt1tqZZEhHZ97vzTEIjdlPNctGNI11-mhNCJ0doSvx9T4ByngaAFtJnRoR2cqbJkJFGja60fHtO0CfKLW5XzPf0NAhr8Tof-5IJfbNpMcC_LdCItJ6i8cuj4i5pG_CikOKDrNzbaBP72200_kl_-YaLDMA4tVb2YjWksY5Vau0Hz16QvI9QwDIcIDLYPAlTlDrU7s_FfmO_89S9Z69-lc_OBG7x2CYzIhB-0wzx753nZRl_WNJKi1Ya_AV552FEqVUhR-SuKcyrTA9OwkKC2JoL3lFqsCL9jkZkBrVREQlT0cxNI_AInyx5FHNLBbdtkz0JQbvzMJ854RP0V_eTlI5u8DZ42aOTRMBLHPi-4gP0J_CGWyKDQreXEEF6LSuLJb1cGk-NX1Vd85aARstQPuOoy7pWJjPvBEKEib70fjkUuMA0Atid-5BusQLKc1H-D6c5HIFH0DgYtXhN6AtQ_fmqw1F_X1JrGnYiYGzJCD2hh0Yt2UJZoCuHlPKk8aM5L3lNU3AISb1soSQl3hfX8Skb817ffC7jYezdhZc12cRNzOPAYqJYjN2eDlQhx-gpFjVzc-W1bFG8Yijo.grliT3M1iZ48aSY9.F4Y.pBRqIGZ4Q_fI1kmeAggvRg" iex> JOSE.JWE.block_decrypt(jwk_rsa_sk, encrypted_rsa1_5) |> elem(0) "{}" # RSA-OAEP iex> encrypted_rsaoaep = JOSE.JWE.block_encrypt(jwk_rsa_pk, "{}", %{ "alg" => "RSA-OAEP", "enc" => "A128GCM" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.YZfGKTTU2KuvwIMpSYadbNmGzWIbLrwRYD8JvZAWkvcnFeky09S04VadRNPXmCBSl4EF1K7oBm0fiYXuvNbLFNKYT_Jo_y6Lb-XsP--BZKaEcq6wIdZ4-xTJ7YYX5dfco_cMknZLG8W2sQRwtWopisn9NyzSpfGNlYqeJqjpoJy0qnO8yZeEYeadwoVF9-XZfYwvMjEt7HORqBIPF1JIaOYTQ-LQBvya6XYhOR7dkSnuCZ_ITGW5ZbPvzOILSMW_3Ixe78ncfO2gxF6AiLh02oTLsOSrF9xDlJvuU0k1TdkNWtGroeP_WVbXEO7O_GI5LVW-cDzoVm5ZCQs2Df0018-qDxFyY9xhKS9aNDi_btiarstXMSz3EkOfPhWR_IzlVyUkYnzs3GS993gKLQ0Tk-ipvOT9Bcw9VTLLK3-f5YSkf51IA---hPFlxVlboH9bmTXlT4JzSbErQEYp3JuXjOP7FQn0OPko5Utqbbm41XBEJhUpBNhjrBGDspsMxML_eJdyzBgA5UyNfdCEQ2vM1pCegxG_hSKAhCKVNn71wW4O_y_eqUcoyhjB7HtVxiF29jzNUKF-y14171L4-mxsIpixaM1ofnayWMiherVP0Wz2MXkzWB0AUv8c3kNEJIh3oeyrczWwzpmeCh1Bq7-J4D6aaFjyGFcm-03_QZmfwho.ymxveKBeRuaZ8HzD.3H4.6oKLh2NouhPGpO1dmA-tTg" iex> JOSE.JWE.block_decrypt(jwk_rsa_sk, encrypted_rsaoaep) |> elem(0) "{}" # RSA-OAEP-256 iex> encrypted_rsaoaep256 = JOSE.JWE.block_encrypt(jwk_rsa_pk, "{}", %{ "alg" => "RSA-OAEP-256", "enc" => "A128GCM" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMTI4R0NNIn0.OW9Hy9qpOIgVueODQXcWIUw_-Sm3UFGtxosyOAaI6JUQFt8q-iEtKkUp4NHrOlczO6tP5t8zRKdNXFfCm9QZk6F9PsSO-NzE2-DV1ANAMck-CDfGTK0mwG5U_KZwlObSgU0gxf87K49Wuno1rWlHWzJb__C_hCJXi_aQW17tLmbuTpJMkB0NTCKX3y6QaxvynP98jqwMJT6uGmE3AeuZYhPGzAOWbltbWyw-TqWqyLJirAUY_fvDNsKt1TDrTd9216TK5y7RQeUtdGfbuYK9lt2TIwfh9ycAHd7SANH_YJc2cKYa3e6CgqnQAjVpbhpogBz5sz5HaK95XYbXOdnYyHQ00gS44YquiQCvX331UgEWnthtmYwDZfnCxTkPydafGOBsjaagGvV2tQtxUKW3JmVChF97bNj5lQZ7rAkyooxx-k3IMT0005x6_74O5tXGN5fb7oyT3Mx_NZ5dKzlYAA_V8oOpNslaFhV5K5Q_-hRkUsEPWdaD5s2uS9Z7l7ot39CzzTKDj65f2eCTWFReFKOjhabCL4ZiFXbElB3dA3y5FdxXPAfe6N31G9ynalx1JIcrEaRb8sdqk6U6uC3s3DpkoRSnp3osBJOxxuk_Lgb-ZM9d8UuRVj4W78-qjfX_lcG1RlRmlYoDIU03ly0UfRWi-7HmpPECrGTsGZEfULg.J-txckmMXEi-bZVh.Rbw.D7UpSkticmDCGiNyLVggLg" iex> JOSE.JWE.block_decrypt(jwk_rsa_sk, encrypted_rsaoaep256) |> elem(0) "{}" ## Encryption Examples All of the examples below will use `"alg"` set to `"dir"` passing the key directly to the Encryption Algorithm. The octet key used will typically be all zeroes of the required size in the form of `<<0::128>>` (for a 128-bit key). All of the example keys generated below can be found here: [https://gist.github.com/potatosalad/dd140560b2bdbdab886d](https://gist.github.com/potatosalad/dd140560b2bdbdab886d) # octet keys we'll use below jwk_oct128 = JOSE.JWK.from_oct(<<0::128>>) jwk_oct192 = JOSE.JWK.from_oct(<<0::192>>) jwk_oct256 = JOSE.JWK.from_oct(<<0::256>>) jwk_oct384 = JOSE.JWK.from_oct(<<0::384>>) jwk_oct512 = JOSE.JWK.from_oct(<<0::512>>) ### A128CBC-HS256, A192CBC-HS384, and A256CBC-HS512 # A128CBC-HS256 iex> encrypted_a128cbchs256 = JOSE.JWE.block_encrypt(jwk_oct256, "{}", %{ "alg" => "dir", "enc" => "A128CBC-HS256" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0..bxps64-UIQoFvhkjr05e9A.HrtJ3AqrqJ4f5PHjGseHYw.kopJoTDxk34IVhheoToLSA" iex> JOSE.JWE.block_decrypt(jwk_oct256, encrypted_a128cbchs256) |> elem(0) "{}" # A192CBC-HS384 iex> encrypted_a192cbchs384 = JOSE.JWE.block_encrypt(jwk_oct384, "{}", %{ "alg" => "dir", "enc" => "A192CBC-HS384" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTkyQ0JDLUhTMzg0In0..3zSCHwvHrcxsNyssIgEBRA.XB70tUoQZlnOgY5ygMxfKA.Avl7Z8jCpShh3_iTcPcU3Woh6E9ykNyB" iex> JOSE.JWE.block_decrypt(jwk_oct384, encrypted_a192cbchs384) |> elem(0) "{}" # A256CBC-HS512 iex> encrypted_a256cbchs512 = JOSE.JWE.block_encrypt(jwk_oct512, "{}", %{ "alg" => "dir", "enc" => "A256CBC-HS512" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIn0..mqMhkWAMF7HmW_Nu1ERUzQ.bzd-tmykuru0Lu_rsNZ2ow.mlOFO8JcC_UJ35TsZgiUeEwAjRDs6cwfN7Umyzm7mmY" iex> JOSE.JWE.block_decrypt(jwk_oct512, encrypted_a256cbchs512) |> elem(0) "{}" ### A128GCM, A192GCM, and A256GCM # A128GCM iex> encrypted_a128gcm = JOSE.JWE.block_encrypt(jwk_oct128, "{}", %{ "alg" => "dir", "enc" => "A128GCM" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIn0..pPF4SbzGZwxS1J-M.Ic0.qkHuC-hOO44HPlykBJLSsA" iex> JOSE.JWE.block_decrypt(jwk_oct128, encrypted_a128gcm) |> elem(0) "{}" # A192GCM iex> encrypted_a192gcm = JOSE.JWE.block_encrypt(jwk_oct192, "{}", %{ "alg" => "dir", "enc" => "A192GCM" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTkyR0NNIn0..muNgk2GFW9ATwqqZ.bvE.gYvC0G6DAodJdyrUqLw7Iw" iex> JOSE.JWE.block_decrypt(jwk_oct192, encrypted_a192gcm) |> elem(0) "{}" # A256GCM iex> encrypted_a256gcm = JOSE.JWE.block_encrypt(jwk_oct256, "{}", %{ "alg" => "dir", "enc" => "A256GCM" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..rDTJhd5ja5pDAYtn.PrM.MQdLgiVXQsG_cLas93ZEHw" iex> JOSE.JWE.block_decrypt(jwk_oct256, encrypted_a256gcm) |> elem(0) "{}" ### ChaCha20/Poly1305 and XChaCha20/Poly1305 This is experimental and based on [RFC 7539](https://tools.ietf.org/html/rfc7539) and [draft-amringer-jose-chacha](https://tools.ietf.org/html/draft-amringer-jose-chacha-01). # C20P iex> encrypted_c20p = JOSE.JWE.block_encrypt(jwk_oct256, "{}", %{ "alg" => "dir", "enc" => "C20P" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJkaXIiLCJlbmMiOiJDMjBQIn0..W3qFkCKCEJz5H5jt.Hag.2TUFobBK_TYdtC2auoiiKA" iex> JOSE.JWE.block_decrypt(jwk_oct256, encrypted_c20p) |> elem(0) "{}" # XC20P iex> encrypted_xc20p = JOSE.JWE.block_encrypt(jwk_oct256, "{}", %{ "alg" => "dir", "enc" => "XC20P" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJkaXIiLCJlbmMiOiJYQzIwUCJ9..aMrioLxn-KO8Dyy8LcYD2mSNY7yPE_yf.Wxg.PJgIuI0ZADBE6Gi5-f7Tfg" iex> JOSE.JWE.block_decrypt(jwk_oct256, encrypted_xc20p) |> elem(0) "{}" ## Compression Examples All of the examples below will use `"alg"` set to `"dir"` passing the key directly to the Encryption Algorithm (`"enc"` is set to `"A128GCM"`). The octet key used will typically be all zeroes of the required size in the form of `<<0::128>>` (for a 128-bit key). All of the example keys generated below can be found here: [https://gist.github.com/potatosalad/dd140560b2bdbdab886d](https://gist.github.com/potatosalad/dd140560b2bdbdab886d) # octet keys we'll use below jwk_oct128 = JOSE.JWK.from_oct(<<0::128>>) ### DEF # DEF iex> encrypted_def = JOSE.JWE.block_encrypt(jwk_oct128, "{}", %{ "alg" => "dir", "enc" => "A128GCM", "zip" => "DEF" }) |> JOSE.JWE.compact |> elem(1) "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIiwiemlwIjoiREVGIn0..Vvr0vlKWE9rAJ8CR.UpOz7w10Uc9pMg.Pctxzz0ijPSOY8zyRcbjww" iex> JOSE.JWE.block_decrypt(jwk_oct128, encrypted_def) |> elem(0) "{}" """ record = Record.extract(:jose_jwe, from_lib: "jose/include/jose_jwe.hrl") keys = :lists.map(&elem(&1, 0), record) vals = :lists.map(&{&1, [], nil}, keys) pairs = :lists.zip(keys, vals) defstruct keys @type t :: %__MODULE__{} @doc """ Converts a `JOSE.JWE` struct to a `:jose_jwe` record. """ @spec to_record(t()) :: tuple() @spec to_record([t()]) :: [tuple()] def to_record(%JOSE.JWE{unquote_splicing(pairs)}) do {:jose_jwe, unquote_splicing(vals)} end def to_record(list) when is_list(list), do: for(element <- list, into: [], do: to_record(element)) @doc """ Converts a `:jose_jwe` record into a `JOSE.JWE`. """ @spec from_record(tuple()) :: t() @spec from_record([tuple()]) :: [t()] def from_record(jose_jwe) def from_record({:jose_jwe, unquote_splicing(vals)}) do %JOSE.JWE{unquote_splicing(pairs)} end def from_record(list) when is_list(list), do: for(element <- list, into: [], do: from_record(element)) ## Decode API @doc """ Converts a binary or map into a `JOSE.JWE`. iex> JOSE.JWE.from(%{ "alg" => "dir" }) %JOSE.JWE{alg: {:jose_jwe_alg_dir, :dir}, enc: :undefined, fields: %{}, zip: :undefined} iex> JOSE.JWE.from("{\"alg\":\"dir\"}") %JOSE.JWE{alg: {:jose_jwe_alg_dir, :dir}, enc: :undefined, fields: %{}, zip: :undefined} There are 3 keys which can have custom modules defined for them: * `"alg"` - must implement `:jose_jwe` and `:jose_jwe_alg` behaviours * `"enc"` - must implement `:jose_jwe` and `:jose_jwe_enc` behaviours * `"zip"` - must implement `:jose_jwe` and `:jose_jwe_zip` behaviours For example: iex> JOSE.JWE.from({%{ zip: MyCustomCompress }, %{ "alg" => "dir", "zip" => "custom" }}) %JOSE.JWE{alg: {:jose_jwe_alg_dir, :dir}, enc: :undefined, fields: %{}, zip: {MyCustomCompress, :state}} """ @spec from(t() | map() | binary()) :: t() @spec from([t() | map() | binary()]) :: [t()] def from(list) when is_list(list), do: for(element <- list, into: [], do: from(element)) def from(jwe = %JOSE.JWE{}), do: from(to_record(jwe)) def from(any), do: :jose_jwe.from(any) |> from_record() @doc """ Converts a binary into a `JOSE.JWE`. """ @spec from_binary(binary()) :: t() @spec from_binary([binary()]) :: [t()] def from_binary(list) when is_list(list), do: for(element <- list, into: [], do: from_binary(element)) def from_binary(binary), do: :jose_jwe.from_binary(binary) |> from_record() @doc """ Reads file and calls `from_binary/1` to convert into a `JOSE.JWE`. """ @spec from_file(String.t()) :: t() def from_file(file), do: :jose_jwe.from_file(file) |> from_record() @doc """ Converts a map into a `JOSE.JWE`. """ @spec from_map(map()) :: t() @spec from_map([map()]) :: [t()] def from_map(list) when is_list(list), do: for(element <- list, into: [], do: from_map(element)) def from_map(map), do: :jose_jwe.from_map(map) |> from_record() ## Encode API @doc """ Converts a `JOSE.JWE` into a binary. """ @spec to_binary(t() | map() | binary()) :: binary() @spec to_binary([t() | map() | binary()]) :: [binary()] def to_binary(list) when is_list(list), do: for(element <- list, into: [], do: to_binary(element)) def to_binary(jwe = %JOSE.JWE{}), do: to_binary(to_record(jwe)) def to_binary(any), do: :jose_jwe.to_binary(any) @doc """ Calls `to_binary/1` on a `JOSE.JWE` and then writes the binary to file. """ @spec to_file(String.t(), t() | map() | binary()) :: :ok | {:error, reason :: term()} def to_file(file, jwe = %JOSE.JWE{}), do: to_file(file, to_record(jwe)) def to_file(file, any), do: :jose_jwe.to_file(file, any) @doc """ Converts a `JOSE.JWE` into a map. """ @spec to_map(t() | map() | binary()) :: map() @spec to_map([t() | map() | binary()]) :: [map()] def to_map(list) when is_list(list), do: for(element <- list, into: [], do: to_map(element)) def to_map(jwe = %JOSE.JWE{}), do: to_map(to_record(jwe)) def to_map(any), do: :jose_jwe.to_map(any) ## API @doc """ Decrypts the `encrypted` binary or map using the `jwk`. iex> jwk = JOSE.JWK.from(%{"k" => "STlqtIOhWJjoVnYjUjxFLZ6oN1oB70QARGSTWQ_5XgM", "kty" => "oct"}) %JOSE.JWK{fields: %{}, keys: :undefined, kty: {:jose_jwk_kty_oct, <<73, 57, 106, 180, 131, 161, 88, 152, 232, 86, 118, 35, 82, 60, 69, 45, 158, 168, 55, 90, 1, 239, 68, 0, 68, 100, 147, 89, 15, 249, 94, 3>>}} iex> JOSE.JWE.block_decrypt(jwk, "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0..jBt5tTa1Q0N3uFPEkf30MQ.Ei49MvTLLje7bsZ5EZCZMA.gMWOAmhZSq9ksHCZm6VSoA") {"{}", %JOSE.JWE{alg: {:jose_jwe_alg_dir, :dir}, enc: {:jose_jwe_enc_aes, {:jose_jwe_enc_aes, {:aes_cbc, 128}, 256, 32, 16, 16, 16, 16, :sha256}}, fields: %{}, zip: :undefined}} See `block_encrypt/2`. """ @spec block_decrypt(JOSE.JWK.t(), binary()) :: {plaintext :: binary(), t()} | {:error, t()} def block_decrypt(jwk = %JOSE.JWK{}, encrypted), do: block_decrypt(JOSE.JWK.to_record(jwk), encrypted) def block_decrypt({your_public_jwk = %JOSE.JWK{}, my_private_jwk}, encrypted), do: block_decrypt({JOSE.JWK.to_record(your_public_jwk), my_private_jwk}, encrypted) def block_decrypt({your_public_jwk, my_private_jwk = %JOSE.JWK{}}, encrypted), do: block_decrypt({your_public_jwk, JOSE.JWK.to_record(my_private_jwk)}, encrypted) def block_decrypt(jwk, encrypted) do case :jose_jwe.block_decrypt(jwk, encrypted) do {plain_text, jwe} when is_tuple(jwe) -> {plain_text, from_record(jwe)} error -> error end end @doc """ Encrypts `plain_text` using the `jwk` and algorithm specified by the `jwe` by getting the `cek` for `block_encrypt/4`. """ @spec block_encrypt(JOSE.JWK.t(), binary(), t()) :: binary() def block_encrypt(jwk = %JOSE.JWK{}, plain_text, jwe), do: block_encrypt(JOSE.JWK.to_record(jwk), plain_text, jwe) def block_encrypt({your_public_jwk = %JOSE.JWK{}, my_private_jwk}, plain_text, jwe), do: block_encrypt({JOSE.JWK.to_record(your_public_jwk), my_private_jwk}, plain_text, jwe) def block_encrypt({your_public_jwk, my_private_jwk = %JOSE.JWK{}}, plain_text, jwe), do: block_encrypt({your_public_jwk, JOSE.JWK.to_record(my_private_jwk)}, plain_text, jwe) def block_encrypt(jwk, plain_text, jwe = %JOSE.JWE{}), do: block_encrypt(jwk, plain_text, to_record(jwe)) def block_encrypt(jwk, plain_text, jwe), do: :jose_jwe.block_encrypt(jwk, plain_text, jwe) @doc """ Encrypts `plain_text` using the `jwk`, `cek`, and algorithm specified by the `jwe` by getting the `iv` for `block_encrypt/5`. """ @spec block_encrypt(JOSE.JWK.t(), binary(), map(), t()) :: binary() def block_encrypt(jwk = %JOSE.JWK{}, plain_text, cek, jwe), do: block_encrypt(JOSE.JWK.to_record(jwk), plain_text, cek, jwe) def block_encrypt({your_public_jwk = %JOSE.JWK{}, my_private_jwk}, plain_text, cek, jwe), do: block_encrypt({JOSE.JWK.to_record(your_public_jwk), my_private_jwk}, plain_text, cek, jwe) def block_encrypt({your_public_jwk, my_private_jwk = %JOSE.JWK{}}, plain_text, cek, jwe), do: block_encrypt({your_public_jwk, JOSE.JWK.to_record(my_private_jwk)}, plain_text, cek, jwe) def block_encrypt(jwk, plain_text, cek, jwe = %JOSE.JWE{}), do: block_encrypt(jwk, plain_text, cek, to_record(jwe)) def block_encrypt(jwk, plain_text, cek, jwe), do: :jose_jwe.block_encrypt(jwk, plain_text, cek, jwe) @doc """ Encrypts the `plain_text` using the `jwk`, `cek`, `iv`, and algorithm specified by the `jwe`. iex> jwk = JOSE.JWK.from(%{"k" => "STlqtIOhWJjoVnYjUjxFLZ6oN1oB70QARGSTWQ_5XgM", "kty" => "oct"}) %JOSE.JWK{fields: %{}, keys: :undefined, kty: {:jose_jwk_kty_oct, <<73, 57, 106, 180, 131, 161, 88, 152, 232, 86, 118, 35, 82, 60, 69, 45, 158, 168, 55, 90, 1, 239, 68, 0, 68, 100, 147, 89, 15, 249, 94, 3>>}} iex> JOSE.JWE.block_encrypt(jwk, "{}", %{ "alg" => "dir", "enc" => "A128CBC-HS256" }) {%{alg: :jose_jwe_alg_dir, enc: :jose_jwe_enc_aes}, %{"ciphertext" => "Ei49MvTLLje7bsZ5EZCZMA", "encrypted_key" => "", "iv" => "jBt5tTa1Q0N3uFPEkf30MQ", "protected" => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0", "tag" => "gMWOAmhZSq9ksHCZm6VSoA"}} See `block_decrypt/2`. """ @spec block_encrypt(JOSE.JWK.t(), binary(), map(), binary(), t()) :: {binary(), t()} def block_encrypt(jwk = %JOSE.JWK{}, plain_text, cek, iv, jwe), do: block_encrypt(JOSE.JWK.to_record(jwk), plain_text, cek, iv, jwe) def block_encrypt({your_public_jwk = %JOSE.JWK{}, my_private_jwk}, plain_text, cek, iv, jwe), do: block_encrypt({JOSE.JWK.to_record(your_public_jwk), my_private_jwk}, plain_text, cek, iv, jwe) def block_encrypt({your_public_jwk, my_private_jwk = %JOSE.JWK{}}, plain_text, cek, iv, jwe), do: block_encrypt({your_public_jwk, JOSE.JWK.to_record(my_private_jwk)}, plain_text, cek, iv, jwe) def block_encrypt(jwk, plain_text, cek, iv, jwe = %JOSE.JWE{}), do: block_encrypt(jwk, plain_text, cek, iv, to_record(jwe)) def block_encrypt(jwk, plain_text, cek, iv, jwe), do: :jose_jwe.block_encrypt(jwk, plain_text, cek, iv, jwe) @doc """ Compacts an expanded encrypted map into a binary. iex> JOSE.JWE.compact(%{"ciphertext" => "Ei49MvTLLje7bsZ5EZCZMA", "encrypted_key" => "", "iv" => "jBt5tTa1Q0N3uFPEkf30MQ", "protected" => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0", "tag" => "gMWOAmhZSq9ksHCZm6VSoA"}) {%{}, "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0..jBt5tTa1Q0N3uFPEkf30MQ.Ei49MvTLLje7bsZ5EZCZMA.gMWOAmhZSq9ksHCZm6VSoA"} See `expand/1`. """ @spec compact(map() | {map(), map()}) :: {map(), binary()} defdelegate compact(encrypted), to: :jose_jwe @doc """ Compresses the `plain_text` using the `"zip"` algorithm specified by the `jwe`. iex> JOSE.JWE.compress("{}", %{ "alg" => "dir", "zip" => "DEF" }) <<120, 156, 171, 174, 5, 0, 1, 117, 0, 249>> See `uncompress/2`. """ @spec compress(binary(), t() | tuple()) :: binary() def compress(plain_text, jwe = %JOSE.JWE{}), do: compress(plain_text, to_record(jwe)) def compress(plain_text, jwe), do: :jose_jwe.compress(plain_text, jwe) @doc """ Expands a compacted encrypted binary into a map. iex> JOSE.JWE.expand("eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0..jBt5tTa1Q0N3uFPEkf30MQ.Ei49MvTLLje7bsZ5EZCZMA.gMWOAmhZSq9ksHCZm6VSoA") {%{}, %{"ciphertext" => "Ei49MvTLLje7bsZ5EZCZMA", "encrypted_key" => "", "iv" => "jBt5tTa1Q0N3uFPEkf30MQ", "protected" => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0", "tag" => "gMWOAmhZSq9ksHCZm6VSoA"}} See `compact/1`. """ @spec expand(binary()) :: {map(), map()} defdelegate expand(encrypted), to: :jose_jwe @doc """ Generates a new `JOSE.JWK` based on the algorithms of the specified `JOSE.JWE`. iex> JOSE.JWE.generate_key(%{"alg" => "dir", "enc" => "A128GCM"}) %JOSE.JWK{fields: %{"alg" => "dir", "enc" => "A128GCM", "use" => "enc"}, keys: :undefined, kty: {:jose_jwk_kty_oct, <<188, 156, 171, 224, 232, 231, 41, 250, 210, 117, 112, 219, 134, 218, 94, 50>>}} """ @spec generate_key(t() | binary() | map()) :: JOSE.JWK.t() @spec generate_key([t() | binary() | map()]) :: [JOSE.JWK.t()] def generate_key(list) when is_list(list), do: for(element <- list, into: [], do: generate_key(element)) def generate_key(jwe = %JOSE.JWE{}), do: generate_key(to_record(jwe)) def generate_key(any), do: JOSE.JWK.from_record(:jose_jwe.generate_key(any)) @doc """ Decrypts the `encrypted_key` using the `jwk` and the `"alg"` and `"enc"` specified by the `jwe`. # let's define our jwk and encrypted_key jwk = JOSE.JWK.from(%{"k" => "idN_YyeYZqEE7BkpexhA2Q", "kty" => "oct"}) enc = <<27, 123, 126, 121, 56, 105, 105, 81, 140, 76, 30, 2, 14, 92, 231, 174, 203, 196, 110, 204, 57, 238, 248, 73>> iex> JOSE.JWE.key_decrypt(jwk, enc, %{ "alg" => "A128KW", "enc" => "A128CBC-HS256" }) <<134, 82, 15, 176, 181, 115, 173, 19, 13, 44, 189, 185, 187, 125, 28, 240>> See `key_encrypt/3`. """ @spec key_decrypt(JOSE.JWK.t(), binary(), t()) :: {binary(), t()} | {:error, term()} def key_decrypt(jwk = %JOSE.JWK{}, encrypted_key, jwe), do: key_decrypt(JOSE.JWK.to_record(jwk), encrypted_key, jwe) def key_decrypt({your_public_jwk = %JOSE.JWK{}, my_private_jwk}, encrypted_key, jwe), do: key_decrypt({JOSE.JWK.to_record(your_public_jwk), my_private_jwk}, encrypted_key, jwe) def key_decrypt({your_public_jwk, my_private_jwk = %JOSE.JWK{}}, encrypted_key, jwe), do: key_decrypt({your_public_jwk, JOSE.JWK.to_record(my_private_jwk)}, encrypted_key, jwe) def key_decrypt(jwk, encrypted_key, jwe = %JOSE.JWE{}), do: key_decrypt(jwk, encrypted_key, to_record(jwe)) def key_decrypt(jwk, encrypted_key, jwe), do: :jose_jwe.key_decrypt(jwk, encrypted_key, jwe) @doc """ Encrypts the `decrypted_key` using the `jwk` and the `"alg"` and `"enc"` specified by the `jwe`. # let's define our jwk and cek (or decrypted_key) jwk = JOSE.JWK.from(%{"k" => "idN_YyeYZqEE7BkpexhA2Q", "kty" => "oct"}) # JOSE.JWK.generate_key({:oct, 16}) cek = <<134, 82, 15, 176, 181, 115, 173, 19, 13, 44, 189, 185, 187, 125, 28, 240>> # :crypto.rand_bytes(16) iex> JOSE.JWE.key_encrypt(jwk, cek, %{ "alg" => "A128KW", "enc" => "A128CBC-HS256" }) {<<27, 123, 126, 121, 56, 105, 105, 81, 140, 76, 30, 2, 14, 92, 231, 174, 203, 196, 110, 204, 57, 238, 248, 73>>, %JOSE.JWE{alg: {:jose_jwe_alg_aes_kw, {:jose_jwe_alg_aes_kw, 128, false, :undefined, :undefined}}, enc: {:jose_jwe_enc_aes, {:jose_jwe_enc_aes, {:aes_cbc, 128}, 256, 32, 16, 16, 16, 16, :sha256}}, fields: %{}, zip: :undefined}} See `key_decrypt/3`. """ @spec key_encrypt(JOSE.JWK.t(), binary(), t()) :: {binary(), t()} | {:error, term()} def key_encrypt(jwk = %JOSE.JWK{}, decrypted_key, jwe), do: key_encrypt(JOSE.JWK.to_record(jwk), decrypted_key, jwe) def key_encrypt({your_public_jwk = %JOSE.JWK{}, my_private_jwk}, decrypted_key, jwe), do: key_encrypt({JOSE.JWK.to_record(your_public_jwk), my_private_jwk}, decrypted_key, jwe) def key_encrypt({your_public_jwk, my_private_jwk = %JOSE.JWK{}}, decrypted_key, jwe), do: key_encrypt({your_public_jwk, JOSE.JWK.to_record(my_private_jwk)}, decrypted_key, jwe) def key_encrypt(jwk, decrypted_key, jwe = %JOSE.JWE{}), do: key_encrypt(jwk, decrypted_key, to_record(jwe)) def key_encrypt(jwk, decrypted_key, jwe) do case :jose_jwe.key_encrypt(jwk, decrypted_key, jwe) do {encrypted_key, jwe} when is_tuple(jwe) -> {encrypted_key, from_record(jwe)} error -> error end end @doc """ Merges map on right into map on left. """ @spec merge(t() | tuple(), t() | tuple()) :: t() def merge(left = %JOSE.JWE{}, right), do: merge(left |> to_record, right) def merge(left, right = %JOSE.JWE{}), do: merge(left, right |> to_record) def merge(left, right), do: :jose_jwe.merge(left, right) |> from_record @doc """ Returns the next `cek` using the `jwk` and the `"alg"` and `"enc"` specified by the `jwe`. # let's define our jwk jwk = JOSE.JWK.from(%{"k" => "idN_YyeYZqEE7BkpexhA2Q", "kty" => "oct"}) # JOSE.JWK.generate_key({:oct, 16}) iex> JOSE.JWE.next_cek(jwk, %{ "alg" => "A128KW", "enc" => "A128CBC-HS256" }) <<37, 83, 139, 165, 44, 23, 163, 186, 255, 155, 183, 17, 220, 211, 80, 247, 239, 149, 194, 53, 134, 41, 254, 176, 0, 247, 66, 38, 217, 252, 82, 233>> # when using the "dir" algorithm, the jwk itself will be used iex> JOSE.JWE.next_cek(jwk, %{ "alg" => "dir", "enc" => "A128GCM" }) <<137, 211, 127, 99, 39, 152, 102, 161, 4, 236, 25, 41, 123, 24, 64, 217>> """ @spec next_cek(JOSE.JWK.t(), t() | tuple() | map() | binary()) :: binary() def next_cek(jwk = %JOSE.JWK{}, jwe), do: next_cek(JOSE.JWK.to_record(jwk), jwe) def next_cek({your_public_jwk = %JOSE.JWK{}, my_private_jwk}, jwe), do: next_cek({JOSE.JWK.to_record(your_public_jwk), my_private_jwk}, jwe) def next_cek({your_public_jwk, my_private_jwk = %JOSE.JWK{}}, jwe), do: next_cek({your_public_jwk, JOSE.JWK.to_record(my_private_jwk)}, jwe) def next_cek(jwk, jwe = %JOSE.JWE{}), do: next_cek(jwk, to_record(jwe)) def next_cek(jwk, jwe), do: :jose_jwe.next_cek(jwk, jwe) @doc """ Returns the next `iv` the `"alg"` and `"enc"` specified by the `jwe`. # typically just returns random bytes for the specified "enc" algorithm iex> bit_size(JOSE.JWE.next_iv(%{ "alg" => "dir", "enc" => "A128CBC-HS256" })) 128 iex> bit_size(JOSE.JWE.next_iv(%{ "alg" => "dir", "enc" => "A128GCM" })) 96 """ @spec next_iv(t() | tuple()) :: binary() def next_iv(jwe = %JOSE.JWE{}), do: next_iv(to_record(jwe)) def next_iv(jwe), do: :jose_jwe.next_iv(jwe) @doc """ Uncompresses the `cipher_text` using the `"zip"` algorithm specified by the `jwe`. iex> JOSE.JWE.uncompress(<<120, 156, 171, 174, 5, 0, 1, 117, 0, 249>>, %{ "alg" => "dir", "zip" => "DEF" }) "{}" See `compress/2`. """ @spec uncompress(binary(), t() | tuple()) :: binary() def uncompress(cipher_text, jwe = %JOSE.JWE{}), do: uncompress(cipher_text, to_record(jwe)) def uncompress(cipher_text, jwe), do: :jose_jwe.uncompress(cipher_text, jwe) end potatosalad-erlang-jose-30ac019/lib/jose.ex0000664000232200023220000001375715267717064021223 0ustar debalancedebalancedefmodule JOSE do @moduledoc ~S""" JOSE stands for JSON Object Signing and Encryption which is a is a set of standards established by the [JOSE Working Group](https://datatracker.ietf.org/wg/jose). JOSE is split into 5 main components: * `JOSE.JWA` - JSON Web Algorithms (JWA) [RFC 7518](https://tools.ietf.org/html/rfc7518) * `JOSE.JWE` - JSON Web Encryption (JWE) [RFC 7516](https://tools.ietf.org/html/rfc7516) * `JOSE.JWK` - JSON Web Key (JWK) [RFC 7517](https://tools.ietf.org/html/rfc7517) * `JOSE.JWS` - JSON Web Signature (JWS) [RFC 7515](https://tools.ietf.org/html/rfc7515) * `JOSE.JWT` - JSON Web Token (JWT) [RFC 7519](https://tools.ietf.org/html/rfc7519) Additional specifications and drafts implemented: * JSON Web Key (JWK) Thumbprint [RFC 7638](https://tools.ietf.org/html/rfc7638) * JWS Unencoded Payload Option [RFC 7797](https://tools.ietf.org/html/rfc7797) """ ## Functions @doc """ Gets the current ChaCha20/Poly1305 module used by `jose_chacha20_poly1305`. See `chacha20_poly1305_module/1` for default. """ @spec chacha20_poly1305_module() :: module() defdelegate chacha20_poly1305_module(), to: :jose @doc """ Sets the current ChaCha20/Poly1305 module used by `jose_chacha20_poly1305`. Currently supported ChaCha20/Poly1305 modules (first found is used as default): * `crypto` - only when 96-bit nonce is supported * [`libsodium`](https://github.com/potatosalad/erlang-libsodium) * `jose_jwa_chacha20_poly1305` - only supported when `crypto_fallback/0` is `true` Additional modules that implement the `jose_chacha20_poly1305` behavior may also be used. """ @spec chacha20_poly1305_module(module()) :: :ok defdelegate chacha20_poly1305_module(module), to: :jose @doc """ Gets the current Cryptographic Algorithm Fallback state Defaults to `false`. """ @spec crypto_fallback() :: boolean() defdelegate crypto_fallback(), to: :jose @doc """ Sets the current Cryptographic Algorithm Fallback state. """ @spec crypto_fallback(boolean()) :: :ok defdelegate crypto_fallback(boolean), to: :jose @doc """ Gets the current Curve25519 module used by `jose_curve25519` See `curve25519_module/1` for default. """ @spec curve25519_module() :: module() defdelegate curve25519_module(), to: :jose @doc """ Sets the current Curve25519 module used by `jose_curve25519`. Currently supported Curve25519 modules (first found is used as default): * [`libdecaf`](https://github.com/potatosalad/erlang-libdecaf) * [`libsodium`](https://github.com/potatosalad/erlang-libsodium) * `jose_jwa_curve25519` - only supported when `crypto_fallback/0` is `true` Additional modules that implement the `jose_curve25519` behavior may also be used. """ @spec curve25519_module(module()) :: :ok defdelegate curve25519_module(module), to: :jose @doc """ Gets the current Curve448 module used by `jose_curve448` See `curve448_module/1` for default. """ @spec curve448_module() :: module() defdelegate curve448_module(), to: :jose @doc """ Sets the current Curve448 module used by `jose_curve448`. Currently supported Curve448 modules (first found is used as default): * [`libdecaf`](https://github.com/potatosalad/erlang-libdecaf) * `jose_jwa_curve448` - only supported when `crypto_fallback/0` is `true` Additional modules that implement the `jose_curve448` behavior may also be used. """ @spec curve448_module(module()) :: :ok defdelegate curve448_module(module), to: :jose @doc """ Decodes JSON to a term using the module returned by `json_module/0`. Returns the decoded term, or raises if `binary` contains invalid JSON. """ @spec decode(binary()) :: term() defdelegate decode(binary), to: :jose @doc """ Encodes a term to JSON using the module returned by `json_module/0`. Returns the encoded JSON, or raises if `term` cannot be encoded. """ @spec encode(term()) :: binary() defdelegate encode(term), to: :jose @doc """ Gets the current JSON module used by `decode/1` and `encode/1`, see `json_module/1` for default. """ @spec json_module() :: module() defdelegate json_module(), to: :jose @doc """ Sets the current JSON module used by `decode/1` and `encode/1`. Currently supported JSON modules (first found is used as default): * [`ojson`](https://github.com/potatosalad/erlang-ojson) * [`Jason`](https://github.com/michalmuskala/jason) * [`Poison`](https://github.com/devinus/poison) * [`jiffy`](https://github.com/davisp/jiffy) * [`jsone`](https://github.com/sile/jsone) * [`jsx`](https://github.com/talentdeficit/jsx) Additional modules that implement the `:jose_json` behavior may also be used. """ @spec json_module(module()) :: :ok defdelegate json_module(module), to: :jose @doc """ Gets the current SHA3 module used by `jose_sha3`, see `sha3_module/1` for default. """ @spec sha3_module() :: module() defdelegate sha3_module(), to: :jose @doc """ Sets the current SHA3 module used by `jose_sha3`. Currently supported SHA3 modules (first found is used as default): * [`keccakf1600`](https://github.com/potatosalad/erlang-keccakf1600) * [`libdecaf`](https://github.com/potatosalad/erlang-libdecaf) * `jose_jwa_sha3` - only supported when `crypto_fallback/0` is `true` Additional modules that implement the `jose_sha3` behavior may also be used. """ @spec sha3_module(module()) :: :ok defdelegate sha3_module(module), to: :jose @doc """ Gets the current Unsecured Signing state, defaults to `false`. """ @spec unsecured_signing() :: boolean() defdelegate unsecured_signing(), to: :jose @doc """ Sets the current Unsecured Signing state. Enables/disables the `"none"` algorithm used for signing and verifying. See [Critical vulnerabilities in JSON Web Token libraries](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/) for more information. """ @spec unsecured_signing(boolean()) :: :ok defdelegate unsecured_signing(boolean), to: :jose end