ReferencePreviewFor developerFor agency

Use SHA-256 and HMAC in Rust applications

Add pinned RustCrypto dependencies without changing SDK 1.1 or app-support locks.

Last updated 2026-10-30

What you'll achieve

  • Add the complete pinned offline dependency graph
  • Use SHA-256 and HMAC without native password calls
  • Preserve authorization and uncertain-write recovery

SHA-256 and HMAC in Rust applications

The rust-crypto-v1 compiler bundle provides pinned RustCrypto sha2 0.10.9 and hmac 0.12.1 alongside SDK 1.1.0 and app-support 0.1.0. They compile into the application's WASM and add no host imports, external network access or native password-operation load. Earlier compiler bundles without these vendored crates cannot build this opt-in. Existing applications and their lockfiles are not rewritten.

Use SHA-256 for a digest of a high-entropy token or bounded payload, and HMAC-SHA-256 when a secret key must authenticate or deterministically derive a value. Continue using blinkhost_app_support::hash_password and verify_password for human passwords: SHA-256 and HMAC are not password-storage replacements. These libraries do not generate randomness. Use app-support's OS-backed secure_random, never WASI random_get, timestamps or counters, for authentication secrets.

Add the dependencies explicitly

Preserve your existing package name and version. The complete manifest and lock below are a matched example for one package with exactly the four listed dependencies and no workspace, patch or additional feature settings. For a different graph, preserve every existing dependency and generate a compatible complete lock using the compiler's available vendored sources. Never paste this lock over unrelated packages, invent checksums, delete entries, or bypass --offline --locked.

[package]
name = "your-app"
version = "0.1.0"
edition = "2021"
rust-version = "1.88"
publish = false

[dependencies]
blinkhost-sdk = { path = "/opt/blinkhost-sdk/rust", version = "=1.1.0" }
blinkhost-app-support = { path = "/opt/blinkhost-sdk/app-support", version = "=0.1.0" }
sha2 = { version = "=0.10.9", default-features = false }
hmac = { version = "=0.12.1", default-features = false }

[profile.release]
codegen-units = 1
lto = true
opt-level = "s"
panic = "abort"
strip = true

Complete matching Cargo.lock

# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4

[[package]]
name = "blinkhost-app-support"
version = "0.1.0"
dependencies = [
 "serde_json",
]

[[package]]
name = "your-app"
version = "0.1.0"
dependencies = [
 "blinkhost-app-support",
 "blinkhost-sdk",
 "hmac",
 "sha2",
]

[[package]]
name = "blinkhost-sdk"
version = "1.1.0"

[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
 "generic-array",
]

[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"

[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
 "libc",
]

[[package]]
name = "crypto-common"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
 "generic-array",
 "typenum",
]

[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
 "block-buffer",
 "crypto-common",
 "subtle",
]

[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
 "typenum",
 "version_check",
]

[[package]]
name = "hmac"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
 "digest",
]

[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"

[[package]]
name = "libc"
version = "0.2.189"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"

[[package]]
name = "memchr"
version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"

[[package]]
name = "proc-macro2"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [
 "unicode-ident",
]

[[package]]
name = "quote"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
 "proc-macro2",
]

[[package]]
name = "serde"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
dependencies = [
 "serde_core",
]

[[package]]
name = "serde_core"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
dependencies = [
 "serde_derive",
]

[[package]]
name = "serde_derive"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "serde_json"
version = "1.0.154"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6"
dependencies = [
 "itoa",
 "memchr",
 "serde",
 "serde_core",
 "zmij",
]

[[package]]
name = "sha2"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
 "cfg-if",
 "cpufeatures",
 "digest",
]

[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"

[[package]]
name = "syn"
version = "3.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b"
dependencies = [
 "proc-macro2",
 "quote",
 "unicode-ident",
]

[[package]]
name = "typenum"
version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"

[[package]]
name = "unicode-ident"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563"

[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"

[[package]]
name = "zmij"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"

Supported APIs

use sha2::{Digest, Sha256};
use hmac::{Hmac, Mac};
type HmacSha256 = Hmac<Sha256>;

// A digest is 32 bytes. Encode every byte as two lowercase hex characters
// if the application schema requires exactly 64 lowercase hex characters.
let digest = Sha256::digest(b"bounded input");

// app_key is a securely provisioned per-application random key, at least 32 bytes.
// Keep it in server-side secret storage. Do not log or send it to the browser.
let mut mac = HmacSha256::new_from_slice(&app_key)?;
mac.update(b"blinkhost:session:v1\0");
mac.update(&(app_id.len() as u64).to_be_bytes());
mac.update(app_id.as_bytes());
mac.update(&(operation_id.len() as u64).to_be_bytes());
mac.update(operation_id.as_bytes());
let token = mac.finalize().into_bytes();

// To authenticate an incoming HMAC tag, recompute the same keyed message
// and call verify_slice(received_tag), which performs constant-time verification.
// An invalid length or altered tag is an error and must fail closed.

This is an API fragment, not a complete login implementation. The application must bound inputs, authorize the requested operation and obtain its own key. blinkhost_sdk::secret(name) reads an authorized server secret when the module's signed secret policy permits it. Do not hardcode a fallback key. Use separate domain labels for sessions, CSRF and unrelated uses, and unambiguous length-prefixed context fields. Scope keys to the application and record key/version identifiers so rotation does not silently change a pending operation's result.

Sessions and uncertain database writes

An operation ID, CSRF token or derived-value recipe is not authorization. Require credentials or an existing valid session, exact Origin validation, an appropriately bound CSRF challenge, expiry and record authorization before accepting or recovering an authentication operation. Store only the session token's secure digest, not its bearer value. Never store raw passwords or setup secrets in a journal, logs, URLs or browser storage.

Persist the original database scope, operation ID, exact SQL/parameters and fixed generated values before the business mutation. A keyed deterministic token can be recovered from the original authorized context and key version without retaining a plaintext bearer. It does not reconstruct a lost random password salt, timestamp, challenge or other journal field: those still need the original durable intent. Following an ambiguous journal write, a primary read that finds the matching intent can recover it; a missing row remains unknown. Never treat absence as rollback, create a replacement operation ID automatically, regenerate salted hashes, or retry a changed payload under the old ID.

Issue an authenticated cookie only after a confirmed commit and a primary check that the session exists, belongs to the authenticated user, is active and has not expired or been revoked. Revalidate recovery authorization before returning a cookie. The transport permits one secure __Host- cookie per response. See secure application sessions and the native Rust database contract.

Compatibility and verification

The compiler remains Rust 1.88.0, target wasm32-wasip1, with offline locked dependencies. The existing SDK/app-support manifests and dependency locks are unchanged. SHA-256/HMAC runs inside ordinary guest resource limits; it does not consume the native Argon2 call quota or bypass application permissions.

The maintained WASM fixture verifies SHA-256 of the empty string and abc, RFC 4231 HMAC-SHA-256 cases 1 and 2, and rejection of altered or truncated tags. This demonstrates the primitives and compilation contract. Published-app authentication, database recovery, secret provisioning and customer authorization need their own application verification.

Help improve this page

Sign in to send page-specific feedback. For account-specific help, email support@blinkhost.me.