sd-jwt-0.1.0.0: Selective Disclosure for JSON Web Tokens (RFC 9901)
Safe HaskellNone
LanguageHaskell2010

SDJWT.Internal.Utils

Description

Utility functions for SD-JWT operations (low-level).

This module provides base64url encoding/decoding, salt generation, and text/ByteString conversions used throughout the SD-JWT library.

Usage

This module contains low-level utilities that are typically used internally by other SD-JWT modules. Most users should use the higher-level APIs in:

These utilities may be useful for: * Advanced use cases requiring custom implementations * Library developers building on top of SD-JWT * Testing and debugging

Synopsis

Documentation

base64urlEncode :: ByteString -> Text Source #

Base64url encode a ByteString (without padding).

This function encodes a ByteString using base64url encoding as specified in RFC 4648 Section 5. The result is URL-safe and does not include padding.

>>> base64urlEncode "Hello, World!"
"SGVsbG8sIFdvcmxkIQ"

base64urlDecode :: Text -> Either Text ByteString Source #

Base64url decode a Text (handles padding).

This function decodes a base64url-encoded Text back to a ByteString. It handles both padded and unpadded input.

Returns Left with an error message if decoding fails.

textToByteString :: Text -> ByteString Source #

Convert Text to ByteString (UTF-8 encoding).

This is a convenience function that encodes Text as UTF-8 ByteString.

byteStringToText :: ByteString -> Text Source #

Convert ByteString to Text (UTF-8 decoding).

This is a convenience function that decodes a UTF-8 ByteString to Text. Note: This will throw an exception if the ByteString is not valid UTF-8. For safe decoding, use decodeUtf8' instead.

hashToBytes :: HashAlgorithm -> ByteString -> ByteString Source #

Hash bytes using the specified hash algorithm.

This function computes a cryptographic hash of the input ByteString using the specified hash algorithm (SHA-256, SHA-384, or SHA-512). Returns the hash digest as a ByteString.

splitJSONPointer :: Text -> [Text] Source #

Split JSON Pointer path by "/", respecting escapes (RFC 6901).

This function properly handles JSON Pointer escaping:

  • "~1" represents a literal forward slash "/"
  • "~0" represents a literal tilde "~"

Examples:

  • "a/b" → ["a", "b"]
  • "a~1b" → ["a/b"] (escaped slash)
  • "a~0b" → ["a~b"] (escaped tilde)
  • "a~1/b" → ["a/", "b"] (escaped slash becomes "/", then "/" is separator)

Note: This function is designed for relative JSON Pointer paths (without leading "/"). Leading slashes are stripped, trailing slashes don't create empty segments, and consecutive slashes are collapsed.

unescapeJSONPointer :: Text -> Text Source #

Unescape JSON Pointer segment (RFC 6901).

Converts escape sequences back to literal characters:

  • "~1" → "/"
  • "~0" → "~"

Note: Order matters - must replace ~1 before ~0 to avoid double-replacement.

constantTimeEq :: ByteString -> ByteString -> Bool Source #

Constant-time equality comparison for ByteStrings.

This function performs a constant-time comparison to prevent timing attacks. It compares two ByteStrings byte-by-byte and always takes the same amount of time regardless of where the first difference occurs.

SECURITY: Use this function when comparing cryptographic values like digests, hashes, or other sensitive data that could be exploited via timing attacks.

Implementation uses cryptonite's constEq which provides constant-time comparison for ByteArray instances. ByteString is a ByteArray instance.

generateSalt :: MonadIO m => m ByteString Source #

Generate a cryptographically secure random salt.

Generates 128 bits (16 bytes) of random data as recommended by RFC 9901. This salt is used when creating disclosures to ensure that digests cannot be guessed or brute-forced.

The salt is generated using cryptonite's secure random number generator.

groupPathsByFirstSegment :: [[Text]] -> Map Text [[Text]] Source #

Group paths by their first segment.

This is a common pattern for processing nested JSON Pointer paths. Empty paths are grouped under an empty string key.

Example: groupPathsByFirstSegment [["a", "b"], ["a", "c"], ["x"]] = Map.fromList [("a", [["b"], ["c"]]), ("x", [[]])]