Skip to main content

CRYPTO

Constant CRYPTO 

Source
pub const CRYPTO: &str = "//! Hashes, message authentication codes and secure random values.\n//!\n//! `sha256` and `sha512` hash `Bytes`, `hmac_sha256` computes an HMAC-SHA-256\n//! tag, and `hmac_sha256_verify` checks one. `random_bytes` and `random_int`\n//! draw from the operating system\'s cryptographically secure random source.\n//! Hashes and tags are raw `Bytes`: nothing here encodes them as hex or\n//! base64. Every module sees these names without an import, and a module\'s\n//! own definition of one of them shadows the one here.\n//!\n//! There is no encryption, no hash other than SHA-256 and SHA-512, and no way\n//! to hash input that arrives in pieces: each function takes all of its\n//! input in one `Bytes`.\n//!\n//! Hashing bytes, tagging a message with HMAC and checking the tag, and\n//! drawing random values:\n//!\n//! ```\n//! let digest = sha256(bytes_from_string(\"abc\"))\n//! assert_eq(digest.len(), 32) // raw bytes, not hex\n//! assert_eq(digest.byte_at(0).unwrap(), 0xba) // SHA-256(\"abc\") is ba7816bf\u{2026}\n//!\n//! // An HMAC tag proves a message came from someone holding the key.\n//! let key = bytes_from_string(\"secret key\")\n//! let msg = bytes_from_string(\"amount=10\")\n//! let tag = hmac_sha256(key, msg)\n//! assert(hmac_sha256_verify(key, msg, tag), \"the tag matches its message\")\n//! assert(\n//!     !hmac_sha256_verify(key, bytes_from_string(\"amount=99\"), tag),\n//!     \"a changed message does not\",\n//! )\n//!\n//! let roll = random_int(1, 6).unwrap()\n//! assert(roll >= 1 && roll <= 6, \"both ends are included\")\n//! assert_eq(random_bytes(16).unwrap().len(), 16)\n//! ```\n\n// `std/crypto`: hashes, HMAC and random, over three runtime intrinsics.\n//\n// **Digest lengths are documented, not typed.** `nova-spec/20-STDLIB.md`\n// section 8 declares these as `[u8; 32]` and `[u8; 64]`; neither is\n// writable, because `u8` does not name a type and a fixed-length array type\n// does not parse. So a digest is a `Bytes` whose length is stated in a\n// comment, and nothing can force a caller to hold exactly 32 bytes.\n//\n// **What this module does not provide:** BLAKE3, which the `ring` backing\n// this module does not implement; AEAD; and any streaming or incremental\n// hasher, so a caller with more data than fits in one `Bytes` has no route\n// here.\n\n/// Why `random_bytes` or `random_int` failed.\npub type CryptoErrorKind =\n    /// The operating system\'s random source failed.\n    | EntropyUnavailable\n    /// `random_bytes` was asked for a negative number of bytes.\n    | InvalidLength\n    /// `random_bytes` was asked for more than 65,536 bytes.\n    | RequestTooLarge\n    /// `random_int` was given a `min` greater than its `max`.\n    | InvalidRange\n\n/// The error `random_bytes` and `random_int` return.\npub record CryptoError {\n    /// Why the call failed.\n    pub kind: CryptoErrorKind,\n    /// A short message naming the function that failed, such as\n    /// `random_bytes failed`; `kind` says why.\n    pub message: String,\n}\n\n// These four values are duplicated in `crates/nova-runtime/src/crypto.rs`.\n// Nothing ties the two sides together, so the fixtures assert a known vector\n// per operation: swapping two of these must fail a fixture.\nconst OP_SHA256: Int = 0\n\nconst OP_SHA512: Int = 1\n\nconst OP_HMAC_SHA256: Int = 2\n\nconst OP_HMAC_SHA256_VERIFY: Int = 3\n\n// The `1 => EntropyUnavailable` arm below is not exercised by any test in\n// this project: it is reachable only when the OS entropy source itself\n// fails, and no fixture or Rust unit test can force that failure without\n// breaking the entropy source out from under the whole runtime, so the gap\n// is disclosed here rather than covered.\nfn crypto_error_kind_of(status: Int) -> CryptoErrorKind {\n    match 0 - status {\n        1 => EntropyUnavailable\n        2 => InvalidLength\n        3 => RequestTooLarge\n        4 => InvalidRange\n        // `Int` cannot be matched exhaustively, so a wildcard arm must\n        // exist. It is unreachable for the kinds\n        // `crates/nova-runtime/src/crypto.rs` currently defines, and exists\n        // so that set can grow without this function failing to compile.\n        _ => InvalidRange\n    }\n}\n\nfn no_bytes() -> Bytes { bytes_from_ints([]) }\n\n// SHA-256 of `data`, 32 bytes.\n//\n// Infallible: the intrinsic\'s status is always 0 for this operation, because\n// no input this surface can produce makes a digest fail.\n/// Returns the SHA-256 hash of `data`, as 32 raw bytes.\n///\n/// Any `Bytes` is accepted, empty included, and it cannot fail.\npub fn sha256(data: Bytes) -> Bytes {\n    let _ = crypto_hash(OP_SHA256, no_bytes(), data, no_bytes())\n    fs_take_bytes()\n}\n\n// SHA-512 of `data`, 64 bytes. Infallible, for the reason `sha256` gives.\n/// Returns the SHA-512 hash of `data`, as 64 raw bytes.\n///\n/// Any `Bytes` is accepted, empty included, and it cannot fail.\npub fn sha512(data: Bytes) -> Bytes {\n    let _ = crypto_hash(OP_SHA512, no_bytes(), data, no_bytes())\n    fs_take_bytes()\n}\n\n// HMAC-SHA-256 of `data` under `key`, 32 bytes. Infallible.\n/// Returns the HMAC-SHA-256 tag of `data` under `key`, as 32 raw bytes.\n///\n/// `key` may be any length, empty included, and it cannot fail. To check a\n/// tag a message arrived with, use `hmac_sha256_verify` rather than comparing\n/// it with this function\'s result yourself.\npub fn hmac_sha256(key: Bytes, data: Bytes) -> Bytes {\n    let _ = crypto_hash(OP_HMAC_SHA256, key, data, no_bytes())\n    fs_take_bytes()\n}\n\n// Whether `tag` is the HMAC-SHA-256 of `data` under `key`.\n//\n// **Constant-time, and that property is INHERITED from `ring::hmac::verify`\n// rather than demonstrated here.** No test in this project observes timing.\n// Prefer this over comparing tags yourself: `Bytes::eq` is available and\n// exact, but it is not constant-time -- it returns early on a length\n// mismatch and then calls `memcmp` -- so a hand-written check leaks.\n/// Returns `true` if `tag` is the HMAC-SHA-256 tag of `data` under `key`.\n///\n/// A `tag` of the wrong length gives `false`, not an error. The comparison\n/// is made in constant time, so how long it takes does not reveal how much of\n/// a wrong `tag` matched; comparing tags with `Bytes`\'s `eq` gives no such\n/// guarantee.\npub fn hmac_sha256_verify(key: Bytes, data: Bytes, tag: Bytes) -> Bool {\n    crypto_hash(OP_HMAC_SHA256_VERIFY, key, data, tag) == 0\n}\n\n// `n` cryptographically random bytes.\n//\n// Fallible where section 8 declared it infallible: the OS entropy source can\n// fail, `n` can be negative or above the runtime\'s cap, and no panic may\n// cross a generated poll boundary, so the alternative to a `Result` is\n// ending the process.\n/// Returns `n` cryptographically secure random bytes from the operating\n/// system.\n///\n/// An `n` of 0 gives empty `Bytes`. Returns a `CryptoError` of kind\n/// `InvalidLength` if `n` is negative, `RequestTooLarge` if `n` is more than\n/// 65,536, and `EntropyUnavailable` if the operating system\'s random source\n/// fails.\npub fn random_bytes(n: Int) -> Result<Bytes, CryptoError> {\n    let status = crypto_random_bytes(n)\n    if status == 0 { return Ok(fs_take_bytes()) }\n    Err(CryptoError { kind: crypto_error_kind_of(status), message: \"random_bytes failed\" })\n}\n\n// A uniform integer in `min ..= max`.\n//\n// `min == max` is a valid degenerate range and returns `min`; a range with\n// `min` above `max` is `InvalidRange`.\n/// Returns a random `Int` from `min` to `max`, both included, each value as\n/// likely as any other.\n///\n/// It draws from the operating system\'s cryptographically secure random\n/// source. A `min` equal to `max` gives `min`, and the range may span every\n/// `Int`. Returns a `CryptoError` of kind `InvalidRange` if `min` is greater\n/// than `max`, and `EntropyUnavailable` if the random source fails.\npub fn random_int(min: Int, max: Int) -> Result<Int, CryptoError> {\n    let status = crypto_random_int(min, max)\n    if status == 0 { return Ok(decode_count(fs_take_bytes())) }\n    Err(CryptoError { kind: crypto_error_kind_of(status), message: \"random_int failed\" })\n}\n";
Expand description

std/crypto/lib.nova.