Skip to content

feat(crypto): sha256/sha512 return raw bytes; add ard/hex module#83

Merged
akonwi merged 1 commit intomainfrom
feat/raw-hash-bytes
Apr 5, 2026
Merged

feat(crypto): sha256/sha512 return raw bytes; add ard/hex module#83
akonwi merged 1 commit intomainfrom
feat/raw-hash-bytes

Conversation

@akonwi
Copy link
Copy Markdown
Owner

@akonwi akonwi commented Apr 5, 2026

Summary

Two paired changes:

  1. crypto::sha256 and crypto::sha512 now return raw digest bytes instead of hex strings. This is a breaking change but aligns Ard with Node/Python defaults where digests are bytes, and lets the hash output feed directly into base64/hex without a decoding dance.

  2. New ard/hex stdlib module with encode / decode, added in the same release so the previous hex behavior is still one function call away.

Motivation

PKCE S256 (OAuth 2.1) needs base64url(sha256(verifier)) with no padding. Before this change, you had to hex-decode the sha256 result to get bytes, then re-encode as base64url:

// Before (awkward)
let digest_hex = crypto::sha256(verifier)
let digest_bytes = try hex::decode(digest_hex)
let challenge = base64::encode_url(digest_bytes, true)

After:

// After (one line, semantically correct)
base64::encode_url(crypto::sha256(verifier), true)

Verified end-to-end against RFC 7636 Appendix B — produces the expected challenge E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM.

This unblocks MCP server OAuth work in Ranger.

Breaking Change

// Before: returns hex string directly
crypto::sha256("hello")
// → "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"

// After: returns raw 32-byte digest
crypto::sha256("hello")
// → 32 raw bytes as Str

Migration

Wrap existing calls in the new hex::encode to get the same hex output as before:

use ard/crypto
use ard/hex

let digest = hex::encode(crypto::sha256(input))

What's unchanged

  • crypto::md5 still returns a hex string. The TODO only listed sha256/sha512, and md5 has different (and rarer) usage patterns. Happy to change md5 for consistency in a follow-up if you want.
  • crypto::hash / verify (bcrypt) — unchanged (they return opaque strings, not bytes).
  • crypto::scrypt_* — unchanged (they still return <salt>:<hex> format).

New module: ard/hex

hex::encode(bytes: Str) Str       // raw bytes → lowercase hex string
hex::decode(input: Str) Str!Str   // hex string → raw bytes, or err on invalid input

Symmetric with ard/base64. Decode returns Str!Str so bad input is a recoverable error, not a panic.

Tests

  • std_lib/hex.ard: 7 inline tests (roundtrip, lowercase output, empty, odd-length rejection, non-hex character rejection)
  • std_lib/crypto.ard: sha256/sha512 tests updated to wrap with hex::encode for the hex assertion, plus 2 new tests verifying raw-byte length (32 and 64)
  • Manual smoke test: PKCE S256 against RFC 7636 Appendix B test vector — ✅ matches exactly

All 66 stdlib tests pass (was 55). All Go tests pass.

Docs

  • New page: website/src/content/docs/stdlib/hex.md
  • Updated: website/src/content/docs/stdlib/crypto.md — sha256/sha512 sections describe new behavior, added PKCE example
  • Sidebar: added ard/hex entry

Release note

Needs a minor bump (pre-1.0, breaking change). The migration is a single wrap with hex::encode, so the blast radius is small.

BREAKING CHANGE: crypto::sha256 and crypto::sha512 now return the raw
digest bytes instead of a hex string. Raw bytes are the semantically
correct output and match Node/Python defaults.

To migrate existing hex output, wrap calls with the new ard/hex module:

  // Before
  let digest = crypto::sha256(input)  // hex string

  // After
  let digest = hex::encode(crypto::sha256(input))  // same hex string

md5 is unchanged for now (still returns hex).

The new ard/hex module provides:

  hex::encode(bytes: Str) Str       // raw bytes -> lowercase hex
  hex::decode(input: Str) Str!Str   // hex -> raw bytes (Result)

Together these make PKCE S256 a one-liner:

  base64::encode_url(crypto::sha256(verifier), true)

Verified end-to-end against RFC 7636 Appendix B.
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 5, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ard-lang-dev Ready Ready Preview, Comment Apr 5, 2026 9:05pm

@akonwi akonwi merged commit 784db3b into main Apr 5, 2026
4 checks passed
@akonwi akonwi deleted the feat/raw-hash-bytes branch April 5, 2026 21:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant