Skip to main content

crypto_vote/
error.rs

1//! Error type returned by every fallible public function.
2//!
3//! Errors are intentionally coarse-grained: the host never needs to do
4//! anything with them other than reject the request. We give them
5//! descriptive `Display` strings so logs are useful, but there is no
6//! `#[non_exhaustive]` machinery, no error codes, no nested causes — the
7//! crate's job is to say "valid" or "not valid", and these variants only
8//! exist to explain *why* an input could not even be parsed.
9
10use core::fmt;
11
12/// Anything that can go wrong when *parsing* or *processing* inputs.
13///
14/// Note that a signature being mathematically invalid is not an error —
15/// it is a `false` return from [`crate::verify_vote`]. An `Error` is only
16/// produced when the caller hands us malformed bytes (wrong length, not
17/// on the curve, etc.) or asks for an operation that does not make sense
18/// (e.g. signing with a key that is not in the authorised ring).
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub enum Error {
21    /// A byte slice did not have the size required for the type it was
22    /// supposed to decode into.
23    InvalidLength {
24        /// Human-readable name of the value being decoded.
25        what: &'static str,
26        /// Number of bytes that were expected.
27        expected: usize,
28        /// Number of bytes that were actually provided.
29        got: usize,
30    },
31    /// 32 bytes that do not represent a valid Ristretto255 point.
32    InvalidPoint,
33    /// A Ristretto255 point decoded correctly but is the identity point,
34    /// which is not a valid public protocol value.
35    InvalidIdentityPoint,
36    /// 32 bytes that do not represent a canonical scalar (mod ℓ).
37    InvalidScalar,
38    /// A scalar decoded correctly but is not usable as a secret key.
39    InvalidSecretKey,
40    /// The hex string handed to a `*_from_hex` constructor could not be
41    /// decoded.
42    InvalidHex,
43    /// A prefixed string (e.g. `pk_…`) handed to a `*_from_prefixed`
44    /// constructor was not in the expected three-part shape, or carried
45    /// the wrong tag for the type being decoded (e.g. a `ki_` value where
46    /// a `pk_` one was required).
47    InvalidPrefix {
48        /// The tag the constructor required, without the trailing `_`
49        /// (`"pk"`, `"sk"`, `"ki"` or `"blsag"`).
50        expected: &'static str,
51        /// The tag actually found, or empty when the string was not even
52        /// in `tag_body_checksum` shape. Never echoes the value's body,
53        /// so it cannot leak secret-key material.
54        got: String,
55    },
56    /// A prefixed string decoded structurally but its trailing checksum
57    /// did not match the body — the value was almost certainly mistyped,
58    /// truncated or corrupted in transit.
59    InvalidChecksum,
60    /// `sign_vote` was called with a secret key whose public key is not
61    /// in the supplied authorised ring. Signing would still mathematically
62    /// produce something, but it would not verify, so we refuse it early.
63    SignerNotInRing,
64    /// `sign_vote` or `verify_vote` was called with an authorised ring
65    /// containing fewer than two members. A ring of one trivially
66    /// de-anonymises the signer, so we reject it.
67    RingTooSmall,
68    /// `sign_vote` was given a ring containing the same public key twice.
69    /// The protocol's anonymity guarantees assume distinct members, and
70    /// we refuse to silently de-duplicate.
71    DuplicateRingMember,
72    /// `sign_vote` was called with a zero-byte ballot. The library has
73    /// no opinion on the payload format, but an empty payload is almost
74    /// always a caller bug (forgot to serialise the form, fed in the
75    /// wrong variable, …) so we surface it instead of silently signing
76    /// nothing.
77    EmptyVote,
78    /// `sign_vote` was called with a zero-byte election identifier.
79    /// Allowing it would defeat the whole point of binding signatures
80    /// to an election context, so we refuse early.
81    EmptyElectionId,
82}
83
84impl fmt::Display for Error {
85    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86        match self {
87            Error::InvalidLength {
88                what,
89                expected,
90                got,
91            } => write!(
92                f,
93                "invalid length for {what}: expected {expected} bytes, got {got}"
94            ),
95            Error::InvalidPoint => f.write_str("bytes do not encode a valid Ristretto255 point"),
96            Error::InvalidIdentityPoint => f.write_str("point must not be the Ristretto identity"),
97            Error::InvalidScalar => f.write_str("bytes do not encode a canonical scalar"),
98            Error::InvalidSecretKey => f.write_str("secret key must be a non-zero scalar"),
99            Error::InvalidHex => f.write_str("input is not valid hexadecimal"),
100            Error::InvalidPrefix { expected, got } => {
101                if got.is_empty() {
102                    write!(f, "expected a \"{expected}_\"-prefixed value")
103                } else {
104                    write!(f, "expected a \"{expected}_\" prefix, got \"{got}_\"")
105                }
106            }
107            Error::InvalidChecksum => {
108                f.write_str("checksum mismatch: the value was mistyped or corrupted")
109            }
110            Error::SignerNotInRing => {
111                f.write_str("signer's public key is not part of the authorised ring")
112            }
113            Error::RingTooSmall => f.write_str("authorised ring must contain at least two members"),
114            Error::DuplicateRingMember => {
115                f.write_str("authorised ring contains a duplicate public key")
116            }
117            Error::EmptyVote => f.write_str("ballot is empty"),
118            Error::EmptyElectionId => f.write_str("election identifier is empty"),
119        }
120    }
121}
122
123impl std::error::Error for Error {}
124
125/// Local `Result` alias to keep signatures readable.
126pub type Result<T> = core::result::Result<T, Error>;