Current section

Files

Jump to
ywt_core src ywt algorithm.gleam
Raw

src/ywt/algorithm.gleam

import gleam/dynamic/decode.{type Decoder}
import gleam/json.{type Json}
import ywt/internal/core
/// A JWT signing algorithm.
///
/// The algorithm fixes both the signing primitive and digest. Prefer asymmetric
/// algorithms when multiple services need to verify tokens; HMAC keys are shared
/// secrets and any verifier can also forge tokens. If you do not have a
/// compatibility constraint, `es384` is a good default.
pub opaque type Algorithm {
Hs256
Hs384
Hs512
Es256
Es384
Es512
Rs256
Rs384
Rs512
Ps256
Ps384
Ps512
}
/// HMAC with SHA-256.
pub const hs256 = Hs256
/// HMAC with SHA-384.
pub const hs384 = Hs384
/// HMAC with SHA-512.
pub const hs512 = Hs512
/// ECDSA with the P-256 curve and SHA-256.
pub const es256 = Es256
/// ECDSA with the P-384 curve and SHA-384.
pub const es384 = Es384
/// ECDSA with the P-521 curve and SHA-512.
pub const es512 = Es512
/// RSA PKCS#1 v1.5 with SHA-256.
pub const rs256 = Rs256
/// RSA PKCS#1 v1.5 with SHA-384.
pub const rs384 = Rs384
/// RSA PKCS#1 v1.5 with SHA-512.
pub const rs512 = Rs512
/// RSA PSS with SHA-256.
pub const ps256 = Ps256
/// RSA PSS with SHA-384.
pub const ps384 = Ps384
/// RSA PSS with SHA-512.
pub const ps512 = Ps512
// -- INTERNALS ---------------------------------------------------------------
@internal
pub fn decoder() -> Decoder(Algorithm) {
use variant <- decode.then(decode.string)
case variant {
"HS256" -> decode.success(Hs256)
"HS384" -> decode.success(Hs384)
"HS512" -> decode.success(Hs512)
"ES256" -> decode.success(Es256)
"ES384" -> decode.success(Es384)
"ES512" -> decode.success(Es512)
"RS256" -> decode.success(Rs256)
"RS384" -> decode.success(Rs384)
"RS512" -> decode.success(Rs512)
"PS256" -> decode.success(Ps256)
"PS384" -> decode.success(Ps384)
"PS512" -> decode.success(Ps512)
_ -> decode.failure(Hs256, "alg")
}
}
@internal
pub fn to_json(alg: Algorithm) -> Json {
case alg {
Hs256 -> json.string("HS256")
Hs384 -> json.string("HS384")
Hs512 -> json.string("HS512")
Es256 -> json.string("ES256")
Es384 -> json.string("ES384")
Es512 -> json.string("ES512")
Rs256 -> json.string("RS256")
Rs384 -> json.string("RS384")
Rs512 -> json.string("RS512")
Ps256 -> json.string("PS256")
Ps384 -> json.string("PS384")
Ps512 -> json.string("PS512")
}
}
@internal
pub fn is_hmac(alg: Algorithm) -> Bool {
case alg {
Hs256 | Hs384 | Hs512 -> True
_ -> False
}
}
@internal
pub fn digest_type(alg: Algorithm) -> core.DigestType {
case alg {
Hs256 | Es256 | Rs256 | Ps256 -> core.Sha256
Hs384 | Es384 | Rs384 | Ps384 -> core.Sha384
Hs512 | Es512 | Rs512 | Ps512 -> core.Sha512
}
}
@internal
pub fn padding(alg: Algorithm) -> Result(core.Padding, Nil) {
case alg {
Rs256 | Rs384 | Rs512 -> Ok(core.RsaPkcs1Padding)
Ps256 | Ps384 | Ps512 -> Ok(core.RsaPkcs1PssPadding)
_ -> Error(Nil)
}
}
@internal
pub fn ec_algorithm(curve: core.NamedCurve) -> Algorithm {
case curve {
core.Secp256r1 -> Es256
core.Secp384r1 -> Es384
core.Secp521r1 -> Es512
}
}
@internal
pub fn generate_key(
alg: Algorithm,
generate_hmac: fn(core.DigestType) -> result,
generate_ecdsa: fn(core.NamedCurve, core.DigestType) -> result,
generate_rsa: fn(core.DigestType, Int, core.Padding) -> result,
) -> result {
case alg {
Es256 -> generate_ecdsa(core.Secp256r1, core.Sha256)
Es384 -> generate_ecdsa(core.Secp384r1, core.Sha384)
Es512 -> generate_ecdsa(core.Secp521r1, core.Sha512)
Hs256 -> generate_hmac(core.Sha256)
Hs384 -> generate_hmac(core.Sha384)
Hs512 -> generate_hmac(core.Sha512)
Ps256 -> generate_rsa(core.Sha256, 4096, core.RsaPkcs1PssPadding)
Ps384 -> generate_rsa(core.Sha384, 4096, core.RsaPkcs1PssPadding)
Ps512 -> generate_rsa(core.Sha512, 4096, core.RsaPkcs1PssPadding)
Rs256 -> generate_rsa(core.Sha256, 4096, core.RsaPkcs1Padding)
Rs384 -> generate_rsa(core.Sha384, 4096, core.RsaPkcs1Padding)
Rs512 -> generate_rsa(core.Sha512, 4096, core.RsaPkcs1Padding)
}
}