Skip to main content

miden_node_store/allowlist/
invitation.rs

1use std::fmt;
2
3use sha2::{Digest, Sha256};
4use thiserror::Error;
5
6/// A nonempty invitation code represented by its SHA-256 digest.
7///
8/// The digest permits code matching without storing a code that an attacker can redeem after a database leak.
9/// Construction does not retain the original text. Debug output hides the digest.
10/// Callers must use random invitation codes with enough entropy to resist guessing.
11#[derive(Clone, PartialEq, Eq)]
12pub struct InvitationCode([u8; 32]);
13
14impl InvitationCode {
15    /// Computes a digest of the exact UTF-8 text without trimming or normalization.
16    pub fn new(code: &str) -> Result<Self, InvalidInvitationCode> {
17        if code.is_empty() {
18            return Err(InvalidInvitationCode);
19        }
20        Ok(Self(Sha256::digest(code.as_bytes()).into()))
21    }
22
23    /// Uses a caller-computed SHA-256 digest without hashing it again. The caller must compute the
24    /// digest from a nonempty invitation code.
25    pub fn from_digest(digest: [u8; 32]) -> Self {
26        Self(digest)
27    }
28
29    /// Parses a SHA-256 digest from 64 hexadecimal characters without a prefix. This method does
30    /// not hash the digest again.
31    pub fn from_hex_digest(value: &str) -> Result<Self, hex::FromHexError> {
32        let mut digest = [0; 32];
33        hex::decode_to_slice(value, &mut digest)?;
34        Ok(Self::from_digest(digest))
35    }
36
37    /// Returns the SHA-256 digest as lowercase hexadecimal without a prefix.
38    pub fn to_hex_digest(&self) -> String {
39        hex::encode(self.0)
40    }
41
42    pub(crate) fn digest(&self) -> &[u8] {
43        &self.0
44    }
45}
46
47impl fmt::Debug for InvitationCode {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        f.write_str("InvitationCode([REDACTED])")
50    }
51}
52
53/// An invitation code must not be empty.
54#[derive(Debug, Error, PartialEq, Eq)]
55#[error("invitation code must not be empty")]
56pub struct InvalidInvitationCode;