%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*-
%% vim: ts=4 sw=4 ft=erlang noet
%%%-------------------------------------------------------------------
%%% @author Andrew Bennett <potatosaladx@gmail.com>
%%% @copyright 2014-2022, Andrew Bennett
%%% @doc
%%%
%%% @end
%%% Created :  24 Jul 2015 by Andrew Bennett <potatosaladx@gmail.com>
%%%-------------------------------------------------------------------
-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(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));
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
%%%-------------------------------------------------------------------
