Current section

Files

Jump to
ywt_core src ywt@sign_key.erl
Raw

src/ywt@sign_key.erl

-module(ywt@sign_key).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/ywt/sign_key.gleam").
-export([hs256/1, hs384/1, hs512/1, with_random_id/1, id/1, match/5, algorithm/1, decoder/0, to_jwk/1]).
-export_type([sign_key/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" <style>\n"
" .goto-top {\n"
" display: block;\n"
" font-size: 0.85em;\n"
" text-align: right;\n"
" }\n"
" </style>\n"
" <script>\n"
" (callback => document.readyState !== 'loading' ? callback() : document.addEventListener('DOMContentLoaded', callback, { once: true }))(() => {\n"
" const goToTop = document.createElement('a')\n"
" goToTop.classList.add('goto-top')\n"
" goToTop.setAttribute('href', '#')\n"
" goToTop.textContent = 'Back to top ↑'\n"
" for (const member of document.querySelectorAll('.member')) {\n"
" member.insertBefore(goToTop.cloneNode(true), null)\n"
" }\n"
" })\n"
" </script>\n"
).
-opaque sign_key() :: {sign_ecdsa,
gleam@option:option(binary()),
ywt@internal@core:named_curve(),
ywt@internal@core:digest_type(),
bitstring(),
bitstring()} |
{sign_rsa_simple,
gleam@option:option(binary()),
ywt@internal@core:digest_type(),
bigi:big_int(),
bigi:big_int(),
bigi:big_int(),
ywt@internal@core:padding()} |
{sign_rsa_full,
gleam@option:option(binary()),
ywt@internal@core:digest_type(),
bigi:big_int(),
bigi:big_int(),
bigi:big_int(),
bigi:big_int(),
bigi:big_int(),
bigi:big_int(),
bigi:big_int(),
bigi:big_int(),
list({bigi:big_int(), bigi:big_int(), bigi:big_int()}),
ywt@internal@core:padding()} |
{sign_hmac,
gleam@option:option(binary()),
ywt@internal@core:digest_type(),
bitstring()}.
-file("src/ywt/sign_key.gleam", 92).
?DOC(
" Creates a symmetric HMAC-SHA256 signing key for JWT authentication.\n"
"\n"
" This is the most commonly used algorithm for simple applications where you have a shared\n"
" secret between your application and the service that will verify JWTs. The same key is\n"
" used for both signing and verification.\n"
"\n"
" ## Security Considerations\n"
" - The secret must be cryptographically random and at least 256 bits (32 bytes) long\n"
" - Keep the secret absolutely confidential - anyone with access can forge tokens\n"
" - Rotate secrets regularly and have a key rotation strategy\n"
" - Never store secrets in code or version control\n"
"\n"
" ## Example\n"
" ```gleam\n"
" import gleam/crypto\n"
"\n"
" // Generate a secure random secret\n"
" let secret = crypto.strong_random_bytes(32)\n"
" let assert Ok(key) = sign_key.hs256(secret)\n"
"\n"
" // Use this key to sign JWTs\n"
" ywt.encode(payload: [#(\"sub\", json.string(\"user123\"))], claims: [], key: key)\n"
" ```\n"
"\n"
" ⚠️ **Warning:** HMAC keys are symmetric - the same key signs and verifies tokens.\n"
" This means every service that needs to verify tokens must have the secret,\n"
" increasing the attack surface.\n"
).
-spec hs256(bitstring()) -> {ok, sign_key()} | {error, nil}.
hs256(Secret) ->
case erlang:bit_size(Secret) >= 256 of
true ->
{ok, {sign_hmac, none, sha256, Secret}};
false ->
{error, nil}
end.
-file("src/ywt/sign_key.gleam", 127).
?DOC(
" Creates a symmetric HMAC-SHA384 signing key for for JWT authentication.\n"
"\n"
" Choose this over HS256 when you need additional security margins or your security\n"
" policy requires SHA-384.\n"
"\n"
" ## Security Considerations\n"
" - The secret must be cryptographically random and at least 384 bits (48 bytes) long\n"
" - Keep the secret absolutely confidential - anyone with access can forge tokens\n"
" - Rotate secrets regularly and have a key rotation strategy\n"
" - Never store secrets in code or version control\n"
"\n"
" ## Example\n"
" ```gleam\n"
" import gleam/crypto\n"
"\n"
" // Generate a secure random secret\n"
" let secret = crypto.strong_random_bytes(48)\n"
" let assert Ok(key) = sign_key.hs384(secret)\n"
"\n"
" // Use this key to sign JWTs\n"
" ywt.encode(payload: [#(\"sub\", json.string(\"user123\"))], claims: [], key: key)\n"
" ```\n"
"\n"
" ⚠️ **Warning:** HMAC keys are symmetric - the same key signs and verifies tokens.\n"
" This means every service that needs to verify tokens must have the secret,\n"
" increasing the attack surface.\n"
).
-spec hs384(bitstring()) -> {ok, sign_key()} | {error, nil}.
hs384(Secret) ->
case erlang:bit_size(Secret) >= 384 of
true ->
{ok, {sign_hmac, none, sha384, Secret}};
false ->
{error, nil}
end.
-file("src/ywt/sign_key.gleam", 161).
?DOC(
" Creates a symmetric HMAC-SHA512 signing key for JWT authentication.\n"
"\n"
" This is the most commonly used algorithm for simple applications where you have a shared\n"
" secret between your application and the service that will verify JWTs. The same key is\n"
" used for both signing and verification.\n"
"\n"
" ## Security Considerations\n"
" - The secret must be cryptographically random and at least 512 bits (64 bytes) long\n"
" - Keep the secret absolutely confidential - anyone with access can forge tokens\n"
" - Rotate secrets regularly and have a key rotation strategy\n"
" - Never store secrets in code or version control\n"
"\n"
" ## Example\n"
" ```gleam\n"
" import gleam/crypto\n"
"\n"
" // Generate a secure random secret\n"
" let secret = crypto.strong_random_bytes(64)\n"
" let assert Ok(key) = sign_key.hs512(secret)\n"
"\n"
" // Use this key to sign JWTs\n"
" ywt.encode(payload: [#(\"sub\", json.string(\"user123\"))], claims: [], key: key)\n"
" ```\n"
"\n"
" ⚠️ **Warning:** HMAC keys are symmetric - the same key signs and verifies tokens.\n"
" This means every service that needs to verify tokens must have the secret,\n"
" increasing the attack surface.\n"
).
-spec hs512(bitstring()) -> {ok, sign_key()} | {error, nil}.
hs512(Secret) ->
case erlang:bit_size(Secret) >= 512 of
true ->
{ok, {sign_hmac, none, sha512, Secret}};
false ->
{error, nil}
end.
-file("src/ywt/sign_key.gleam", 178).
?DOC(
" Assigns a random key identifier to a signing key for key management and rotation.\n"
"\n"
" This function generates a cryptographically random identifier and assigns it to\n"
" the key's `id` field. Key identifiers enable proper key selection during JWT\n"
" verification and are essential for implementing key rotation strategies.\n"
"\n"
" ## Usage\n"
" Use this when you need to uniquely identify keys for distribution, storage,\n"
" or rotation purposes. The identifier will be included in JWT headers as the\n"
" `kid` (Key ID) field, allowing verifiers to select the correct key.\n"
).
-spec with_random_id(sign_key()) -> sign_key().
with_random_id(Key) ->
Id = {some, ywt_core_ffi:random_id()},
case Key of
{sign_ecdsa, _, _, _, _, _} ->
{sign_ecdsa,
Id,
erlang:element(3, Key),
erlang:element(4, Key),
erlang:element(5, Key),
erlang:element(6, Key)};
{sign_hmac, _, _, _} ->
{sign_hmac, Id, erlang:element(3, Key), erlang:element(4, Key)};
{sign_rsa_full, _, _, _, _, _, _, _, _, _, _, _, _} ->
{sign_rsa_full,
Id,
erlang:element(3, Key),
erlang:element(4, Key),
erlang:element(5, Key),
erlang:element(6, Key),
erlang:element(7, Key),
erlang:element(8, Key),
erlang:element(9, Key),
erlang:element(10, Key),
erlang:element(11, Key),
erlang:element(12, Key),
erlang:element(13, Key)};
{sign_rsa_simple, _, _, _, _, _, _} ->
{sign_rsa_simple,
Id,
erlang:element(3, Key),
erlang:element(4, Key),
erlang:element(5, Key),
erlang:element(6, Key),
erlang:element(7, Key)}
end.
-file("src/ywt/sign_key.gleam", 244).
-spec ec_decoder(gleam@option:option(binary())) -> gleam@dynamic@decode:decoder(sign_key()).
ec_decoder(Id) ->
gleam@dynamic@decode:field(
<<"crv"/utf8>>,
ywt@internal@core:curve_decoder(),
fun(Crv) ->
gleam@dynamic@decode:field(
<<"x"/utf8>>,
ywt@internal@core:bits_decoder(),
fun(X) ->
gleam@dynamic@decode:field(
<<"y"/utf8>>,
ywt@internal@core:bits_decoder(),
fun(Y) ->
gleam@dynamic@decode:field(
<<"d"/utf8>>,
ywt@internal@core:bits_decoder(),
fun(Private_key) ->
Public_key = <<16#4,
X/bitstring,
Y/bitstring>>,
Digest_type = case Crv of
secp256r1 ->
sha256;
secp384r1 ->
sha384;
secp521r1 ->
sha512
end,
gleam@dynamic@decode:success(
{sign_ecdsa,
Id,
Crv,
Digest_type,
Public_key,
Private_key}
)
end
)
end
)
end
)
end
).
-file("src/ywt/sign_key.gleam", 402).
-spec other_primes_info_decoder() -> gleam@dynamic@decode:decoder({bigi:big_int(),
bigi:big_int(),
bigi:big_int()}).
other_primes_info_decoder() ->
Field_decoder = ywt@internal@core:int_decoder(),
gleam@dynamic@decode:field(
<<"r"/utf8>>,
Field_decoder,
fun(Factor) ->
gleam@dynamic@decode:field(
<<"d"/utf8>>,
Field_decoder,
fun(Exponent) ->
gleam@dynamic@decode:field(
<<"t"/utf8>>,
Field_decoder,
fun(Coefficient) ->
gleam@dynamic@decode:success(
{Factor, Exponent, Coefficient}
)
end
)
end
)
end
).
-file("src/ywt/sign_key.gleam", 341).
-spec rsa_full_decoder(
gleam@option:option(binary()),
ywt@internal@core:digest_type(),
ywt@internal@core:padding(),
bigi:big_int(),
bigi:big_int(),
bigi:big_int()
) -> gleam@dynamic@decode:decoder(sign_key()).
rsa_full_decoder(
Id,
Digest_type,
Padding,
Public_exponent,
Modulus,
Private_exponent
) ->
Field_decoder = ywt@internal@core:int_decoder(),
gleam@dynamic@decode:field(
<<"p"/utf8>>,
Field_decoder,
fun(First_prime_factor) ->
gleam@dynamic@decode:field(
<<"q"/utf8>>,
Field_decoder,
fun(Second_prime_factor) ->
gleam@dynamic@decode:field(
<<"dp"/utf8>>,
Field_decoder,
fun(First_factor_crt_exponent) ->
gleam@dynamic@decode:field(
<<"dq"/utf8>>,
Field_decoder,
fun(Second_factor_crt_exponent) ->
gleam@dynamic@decode:field(
<<"qi"/utf8>>,
Field_decoder,
fun(First_crt_coefficient) ->
gleam@dynamic@decode:optional_field(
<<"oth"/utf8>>,
[],
gleam@dynamic@decode:list(
other_primes_info_decoder()
),
fun(Other_primes_info) ->
gleam@dynamic@decode:success(
{sign_rsa_full,
Id,
Digest_type,
Public_exponent,
Modulus,
Private_exponent,
First_prime_factor,
Second_prime_factor,
First_factor_crt_exponent,
Second_factor_crt_exponent,
First_crt_coefficient,
Other_primes_info,
Padding}
)
end
)
end
)
end
)
end
)
end
)
end
).
-file("src/ywt/sign_key.gleam", 424).
?DOC(
" Extracts the key identifier from a signing key, if present.\n"
"\n"
" This function retrieves the optional key identifier that was assigned to a key.\n"
" Key identifiers are used for key selection during JWT verification.\n"
).
-spec id(sign_key()) -> {ok, binary()} | {error, nil}.
id(Key) ->
gleam@option:to_result(erlang:element(2, Key), nil).
-file("src/ywt/sign_key.gleam", 431).
?DOC(false).
-spec match(
sign_key(),
fun((gleam@option:option(binary()), ywt@internal@core:named_curve(), ywt@internal@core:digest_type(), bitstring(), bitstring()) -> EUY),
fun((gleam@option:option(binary()), ywt@internal@core:digest_type(), bigi:big_int(), bigi:big_int(), bigi:big_int(), ywt@internal@core:padding()) -> EUY),
fun((gleam@option:option(binary()), ywt@internal@core:digest_type(), bigi:big_int(), bigi:big_int(), bigi:big_int(), bigi:big_int(), bigi:big_int(), bigi:big_int(), bigi:big_int(), bigi:big_int(), list({bigi:big_int(),
bigi:big_int(),
bigi:big_int()}), ywt@internal@core:padding()) -> EUY),
fun((gleam@option:option(binary()), ywt@internal@core:digest_type(), bitstring()) -> EUY)
) -> EUY.
match(Sign_key, On_ecdsa, On_rsa_simple, On_rsa_full, On_hmac) ->
case Sign_key of
{sign_ecdsa, Id, Curve, Digest_type, Public_key, Private_key} ->
On_ecdsa(Id, Curve, Digest_type, Public_key, Private_key);
{sign_hmac, Id@1, Digest_type@1, Secret} ->
On_hmac(Id@1, Digest_type@1, Secret);
{sign_rsa_full,
Id@2,
Digest_type@2,
Public_exponent,
Modulus,
Private_exponent,
First_prime_factor,
Second_prime_factor,
First_factor_crt_exponent,
Second_factor_crt_exponent,
First_crt_coefficient,
Other_primes_info,
Padding} ->
On_rsa_full(
Id@2,
Digest_type@2,
Public_exponent,
Modulus,
Private_exponent,
First_prime_factor,
Second_prime_factor,
First_factor_crt_exponent,
Second_factor_crt_exponent,
First_crt_coefficient,
Other_primes_info,
Padding
);
{sign_rsa_simple,
Id@3,
Digest_type@3,
Public_exponent@1,
Modulus@1,
Private_exponent@1,
Padding@1} ->
On_rsa_simple(
Id@3,
Digest_type@3,
Public_exponent@1,
Modulus@1,
Private_exponent@1,
Padding@1
)
end.
-file("src/ywt/sign_key.gleam", 484).
?DOC(false).
-spec algorithm(sign_key()) -> ywt@algorithm:algorithm().
algorithm(Key) ->
case Key of
{sign_ecdsa, _, _, sha256, _, _} ->
es256;
{sign_ecdsa, _, _, sha384, _, _} ->
es384;
{sign_ecdsa, _, _, sha512, _, _} ->
es512;
{sign_hmac, _, sha256, _} ->
hs256;
{sign_hmac, _, sha384, _} ->
hs384;
{sign_hmac, _, sha512, _} ->
hs512;
{sign_rsa_simple, _, sha256, _, _, _, rsa_pkcs1_padding} ->
rs256;
{sign_rsa_full, _, sha256, _, _, _, _, _, _, _, _, _, rsa_pkcs1_padding} ->
rs256;
{sign_rsa_simple, _, sha384, _, _, _, rsa_pkcs1_padding} ->
rs384;
{sign_rsa_full, _, sha384, _, _, _, _, _, _, _, _, _, rsa_pkcs1_padding} ->
rs384;
{sign_rsa_simple, _, sha512, _, _, _, rsa_pkcs1_padding} ->
rs512;
{sign_rsa_full, _, sha512, _, _, _, _, _, _, _, _, _, rsa_pkcs1_padding} ->
rs512;
{sign_rsa_simple, _, sha256, _, _, _, rsa_pkcs1_pss_padding} ->
ps256;
{sign_rsa_full,
_,
sha256,
_,
_,
_,
_,
_,
_,
_,
_,
_,
rsa_pkcs1_pss_padding} ->
ps256;
{sign_rsa_simple, _, sha384, _, _, _, rsa_pkcs1_pss_padding} ->
ps384;
{sign_rsa_full,
_,
sha384,
_,
_,
_,
_,
_,
_,
_,
_,
_,
rsa_pkcs1_pss_padding} ->
ps384;
{sign_rsa_simple, _, sha512, _, _, _, rsa_pkcs1_pss_padding} ->
ps512;
{sign_rsa_full,
_,
sha512,
_,
_,
_,
_,
_,
_,
_,
_,
_,
rsa_pkcs1_pss_padding} ->
ps512
end.
-file("src/ywt/sign_key.gleam", 235).
-spec oct_decoder(gleam@option:option(binary())) -> gleam@dynamic@decode:decoder(sign_key()).
oct_decoder(Id) ->
gleam@dynamic@decode:field(
<<"alg"/utf8>>,
ywt@algorithm:decoder(),
fun(Alg) ->
gleam@dynamic@decode:field(
<<"k"/utf8>>,
ywt@internal@core:bits_decoder(),
fun(K) ->
Digest_type = ywt@algorithm:digest_type(Alg),
gleam@dynamic@decode:success(
{sign_hmac, Id, Digest_type, K}
)
end
)
end
).
-file("src/ywt/sign_key.gleam", 287).
-spec rsa_decoder(gleam@option:option(binary())) -> gleam@dynamic@decode:decoder(sign_key()).
rsa_decoder(Id) ->
gleam@dynamic@decode:field(
<<"alg"/utf8>>,
ywt@algorithm:decoder(),
fun(Alg) ->
Field_decoder = ywt@internal@core:int_decoder(),
gleam@dynamic@decode:field(
<<"e"/utf8>>,
Field_decoder,
fun(Public_exponent) ->
gleam@dynamic@decode:field(
<<"n"/utf8>>,
Field_decoder,
fun(Modulus) ->
gleam@dynamic@decode:field(
<<"d"/utf8>>,
Field_decoder,
fun(Private_exponent) ->
Digest_type = ywt@algorithm:digest_type(Alg),
gleam@dynamic@decode:then(
case ywt@algorithm:padding(Alg) of
{ok, Padding} ->
gleam@dynamic@decode:success(
Padding
);
{error, _} ->
gleam@dynamic@decode:failure(
rsa_pkcs1_padding,
<<"alg"/utf8>>
)
end,
fun(Padding@1) ->
Full = rsa_full_decoder(
Id,
Digest_type,
Padding@1,
Public_exponent,
Modulus,
Private_exponent
),
gleam@dynamic@decode:one_of(
Full,
[gleam@dynamic@decode:success(
{sign_rsa_simple,
Id,
Digest_type,
Public_exponent,
Modulus,
Private_exponent,
Padding@1}
)]
)
end
)
end
)
end
)
end
)
end
).
-file("src/ywt/sign_key.gleam", 221).
?DOC(
" Decodes a JSON Web Key (JWK) containing a private key into a signing key.\n"
"\n"
" Use this for loading private keys from secure configuration or key management systems\n"
" when implementing JWT signing services.\n"
"\n"
" ## Security Considerations\n"
" - Only accept keys from trusted sources\n"
" - Store private keys in secure key management systems, never in code\n"
" - Only keep private keys in your auth service, they should never leave secure boundaries\n"
" - Rotate private keys regularly and have a key rotation strategy\n"
"\n"
" ## Example\n"
" ```gleam\n"
" // Load private key from secure configuration (never hardcode!)\n"
" let private_jwk = load_from_secure_vault(\"jwt-signing-key\")\n"
"\n"
" case json.parse(private_jwk, sign_key.decoder()) {\n"
" Ok(sign_key) -> {\n"
" // Use to create signed JWTs\n"
" encode(payload: [#(\"sub\", json.string(\"user123\"))],\n"
" claims: [],\n"
" key: sign_key)\n"
" }\n"
" Error(_) -> // Handle key loading error\n"
" }\n"
" ```\n"
"\n"
" 🔐 **Critical:** Sign keys are the most important pieces of your security.\n"
" Treat them accordingly.\n"
).
-spec decoder() -> gleam@dynamic@decode:decoder(sign_key()).
decoder() ->
gleam@dynamic@decode:optional_field(
<<"kid"/utf8>>,
none,
gleam@dynamic@decode:map(
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Field@0) -> {some, Field@0} end
),
fun(Id) ->
gleam@dynamic@decode:field(
<<"kty"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Kty) -> case Kty of
<<"oct"/utf8>> ->
oct_decoder(Id);
<<"EC"/utf8>> ->
ec_decoder(Id);
<<"RSA"/utf8>> ->
rsa_decoder(Id);
_ ->
gleam@dynamic@decode:failure(
{sign_hmac, Id, sha256, <<>>},
<<"kty"/utf8>>
)
end end
)
end
).
-file("src/ywt/sign_key.gleam", 535).
?DOC(false).
-spec to_jwk(sign_key()) -> gleam@json:json().
to_jwk(Key) ->
Fields@1 = case Key of
{sign_hmac, _, _, _} ->
[{<<"kty"/utf8>>, gleam@json:string(<<"oct"/utf8>>)},
{<<"k"/utf8>>,
ywt@internal@core:bits_to_json(erlang:element(4, Key))}];
{sign_ecdsa, _, _, _, _, _} ->
Coord_size = ywt@internal@core:named_curve_size(
erlang:element(3, Key)
),
{X@1, Y@1} = case erlang:element(5, Key) of
<<16#4, X:Coord_size/binary, Y:Coord_size/binary>> -> {X, Y};
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"ywt/sign_key"/utf8>>,
function => <<"to_jwk"/utf8>>,
line => 543,
value => _assert_fail,
start => 19801,
'end' => 19896,
pattern_start => 19812,
pattern_end => 19871})
end,
[{<<"kty"/utf8>>, gleam@json:string(<<"EC"/utf8>>)},
{<<"crv"/utf8>>,
gleam@json:string(
ywt@internal@core:named_curve(erlang:element(3, Key))
)},
{<<"x"/utf8>>, ywt@internal@core:bits_to_json(X@1)},
{<<"y"/utf8>>, ywt@internal@core:bits_to_json(Y@1)},
{<<"d"/utf8>>,
ywt@internal@core:bits_to_json(erlang:element(6, Key))}];
{sign_rsa_simple, _, _, _, _, _, _} ->
[{<<"kty"/utf8>>, gleam@json:string(<<"RSA"/utf8>>)},
{<<"e"/utf8>>,
ywt@internal@core:int_to_json(erlang:element(4, Key))},
{<<"n"/utf8>>,
ywt@internal@core:int_to_json(erlang:element(5, Key))},
{<<"d"/utf8>>,
ywt@internal@core:int_to_json(erlang:element(6, Key))}];
{sign_rsa_full, _, _, _, _, _, _, _, _, _, _, _, _} ->
Fields = [{<<"kty"/utf8>>, gleam@json:string(<<"RSA"/utf8>>)},
{<<"e"/utf8>>,
ywt@internal@core:int_to_json(erlang:element(4, Key))},
{<<"n"/utf8>>,
ywt@internal@core:int_to_json(erlang:element(5, Key))},
{<<"d"/utf8>>,
ywt@internal@core:int_to_json(erlang:element(6, Key))},
{<<"p"/utf8>>,
ywt@internal@core:int_to_json(erlang:element(7, Key))},
{<<"q"/utf8>>,
ywt@internal@core:int_to_json(erlang:element(8, Key))},
{<<"dp"/utf8>>,
ywt@internal@core:int_to_json(erlang:element(9, Key))},
{<<"dq"/utf8>>,
ywt@internal@core:int_to_json(erlang:element(10, Key))},
{<<"qi"/utf8>>,
ywt@internal@core:int_to_json(erlang:element(11, Key))}],
case erlang:element(12, Key) of
[] ->
Fields;
[_ | _] ->
[{<<"oth"/utf8>>,
gleam@json:array(
erlang:element(12, Key),
fun(Info) ->
gleam@json:object(
[{<<"r"/utf8>>,
ywt@internal@core:int_to_json(
erlang:element(1, Info)
)},
{<<"d"/utf8>>,
ywt@internal@core:int_to_json(
erlang:element(2, Info)
)},
{<<"t"/utf8>>,
ywt@internal@core:int_to_json(
erlang:element(3, Info)
)}]
)
end
)} |
Fields]
end
end,
Fields@2 = [{<<"alg"/utf8>>, ywt@algorithm:to_json(algorithm(Key))} |
Fields@1],
Fields@3 = case erlang:element(2, Key) of
{some, Id} ->
[{<<"kid"/utf8>>, gleam@json:string(Id)} | Fields@2];
none ->
Fields@2
end,
gleam@json:object(Fields@3).