MetamorphicCrypto.Mac (metamorphic_crypto v0.8.0)

Copy Markdown View Source

Message authentication codes — HMAC-SHA256 (RFC 2104 / FIPS 198-1).

A thin, idiomatic wrapper over the audited metamorphic-crypto Rust core — the same one-shot HMAC primitive used by the browser WASM build, so a tag computed here is byte-for-byte identical to one computed in the browser or native Rust.

Why this exists

The IETF KEYTRANS protocol (draft-ietf-keytrans-protocol) specifies that commitments in its standard cipher suites are computed as HMAC(Kc, CommitmentValue) using the suite hash (SHA-256 for both currently defined suites). MetamorphicLog owns the KEYTRANS-specific framing; this module supplies only the generic HMAC-SHA256 primitive, keeping metamorphic_crypto the single source of truth for cryptographic primitives.

Encoding (base64 in, base64 out)

key and msg are base64-encoded and the 32-byte tag is returned base64-encoded, matching the rest of this package and the WASM wire format. This is standard HMAC-SHA256: any conformant implementation computes an identical tag for the same (key, msg). If you have raw binaries, encode them first with Base.encode64/1.

The key may be any length: HMAC internally hashes an over-long key and zero-pads a short one, so no length validation is required or performed.

Security note

HMAC security depends on how you use it

HMAC's authenticator security rests on the key being secret. In the KEYTRANS commitment construction the "key" is a fixed, public per-suite constant and hiding comes from the random opening inside the message — there HMAC is a committing PRF, not an authenticator. This primitive is generic; callers are responsible for using it in a construction whose security properties they understand.

Summary

Functions

Compute HMAC-SHA256(key, msg), returning a base64-encoded 32-byte tag.

Same as hmac_sha256/2 but returns the tag directly, raising on invalid input.

HMAC-SHA256 output length in bytes (32, a SHA-256 digest).

Functions

hmac_sha256(key_b64, msg_b64)

@spec hmac_sha256(key_b64 :: String.t(), msg_b64 :: String.t()) ::
  {:ok, String.t()} | {:error, String.t()}

Compute HMAC-SHA256(key, msg), returning a base64-encoded 32-byte tag.

key and msg are base64-encoded. Returns {:ok, tag_b64} or {:error, reason} (invalid base64 input). Hashing itself is infallible.

Example

# RFC 4231 Test Case 2: key "Jefe", message "what do ya want for nothing?"
{:ok, tag} =
  MetamorphicCrypto.Mac.hmac_sha256(Base.encode64("Jefe"), Base.encode64("what do ya want for nothing?"))
# Base.decode64!(tag) == <<0x5b, 0xdc, 0xc1, 0x46, ...>>

hmac_sha256!(key_b64, msg_b64)

@spec hmac_sha256!(key_b64 :: String.t(), msg_b64 :: String.t()) :: String.t()

Same as hmac_sha256/2 but returns the tag directly, raising on invalid input.

hmac_sha256_len()

@spec hmac_sha256_len() :: pos_integer()

HMAC-SHA256 output length in bytes (32, a SHA-256 digest).

Example

iex> MetamorphicCrypto.Mac.hmac_sha256_len()
32