Current section

Files

Jump to
ywt_core src ywt internal core.gleam
Raw

src/ywt/internal/core.gleam

import bigi.{type BigInt}
import gleam/bit_array
import gleam/dynamic/decode.{type Decoder}
import gleam/json.{type Json}
import gleam/order
import gleam/time/timestamp
pub type ParseError {
MalformedToken
InvalidHeaderEncoding
InvalidPayloadEncoding
InvalidSignatureEncoding
InvalidHeaderJson(json.DecodeError)
InvalidPayloadJson(json.DecodeError)
NoMatchingKey
InvalidSignature
TokenExpired(expired_at: timestamp.Timestamp)
TokenNotYetValid(not_before: timestamp.Timestamp)
InvalidIssuer(expected: List(String), actual: String)
InvalidAudience(expected: List(String), actual: String)
InvalidSubject(expected: List(String), actual: String)
InvalidId(expected: List(String), actual: String)
MissingClaim(claim_name: String)
ClaimDecodingError(claim_name: String, error: List(decode.DecodeError))
InvalidCustomClaim(claim_name: String)
PayloadDecodingError(List(decode.DecodeError))
}
// -- INTEGERS -----------------------------------------------------------------
pub fn int_decoder() -> Decoder(BigInt) {
use bits <- decode.then(bits_decoder())
case bigi.from_bytes(bits, bigi.BigEndian, bigi.Unsigned) {
Ok(int) -> decode.success(int)
Error(_) -> decode.failure(bigi.zero(), "BigInt")
}
}
pub fn bits_to_json(bits: BitArray) -> Json {
json.string(bit_array.base64_url_encode(bits, False))
}
pub fn int_to_json(int: BigInt) -> Json {
let size = int_byte_size(int, 0)
let assert Ok(bytes) = bigi.to_bytes(int, bigi.BigEndian, bigi.Unsigned, size)
json.string(bit_array.base64_url_encode(bytes, False))
}
fn int_byte_size(int: BigInt, bytes: Int) -> Int {
case bigi.compare(int, bigi.zero()) {
order.Gt -> int_byte_size(bigi.bitwise_shift_right(int, 8), bytes + 1)
_ -> bytes
}
}
pub fn bits_decoder() {
use str <- decode.then(decode.string)
case bit_array.base64_url_decode(str) {
Ok(bits) -> decode.success(bits)
Error(_) -> decode.failure(<<>>, "Base64Url")
}
}
// -- INTERNAL KEY TYPES -------------------------------------------------------
/// The names the Erlang public_key application uses for Ecdsa curves.
pub type NamedCurve {
Secp256r1
Secp384r1
Secp521r1
}
pub fn curve_decoder() -> Decoder(NamedCurve) {
// The "crv" (curve) parameter identifies the cryptographic curve used
// with the key. Curve values from [DSS] used by this specification
// are:
// o "P-256"
// o "P-384"
// o "P-521"
use crv <- decode.then(decode.string)
case crv {
"P-256" -> decode.success(Secp256r1)
"P-384" -> decode.success(Secp384r1)
"P-521" -> decode.success(Secp521r1)
_ -> decode.failure(Secp256r1, "crv")
}
}
pub fn named_curve(curve: NamedCurve) -> String {
case curve {
Secp256r1 -> "P-256"
Secp384r1 -> "P-384"
Secp521r1 -> "P-521"
}
}
pub fn named_curve_size(curve: NamedCurve) -> Int {
case curve {
Secp256r1 -> 32
Secp384r1 -> 48
Secp521r1 -> 66
}
}
/// The RSA Padding
pub type Padding {
RsaPkcs1Padding
RsaPkcs1PssPadding
}
pub fn rsa_padding(padding: Padding) {
case padding {
RsaPkcs1Padding -> "RSASSA-PKCS1-v1_5"
RsaPkcs1PssPadding -> "RSA-PSS"
}
}
/// We use our own type here to not add a dependency to gleam_crypto
pub type DigestType {
Sha256
Sha384
Sha512
}
pub fn digest_type(digest_type: DigestType) {
case digest_type {
Sha256 -> "SHA-256"
Sha384 -> "SHA-384"
Sha512 -> "SHA-512"
}
}
pub fn digest_size(digest_type: DigestType) -> Int {
case digest_type {
Sha256 -> 32
Sha384 -> 48
Sha512 -> 64
}
}