libsignify_rs/
error.rs

1//
2// signify-rs: cryptographically sign and verify files
3// src/error.rs: Error handling functions
4//
5// Copyright (c) 2025, 2026 Ali Polatel <alip@chesswob.org>
6// Based in part upon OpenBSD's signify which is:
7//   Copyright (c) 2013 Ted Unangst <tedu@openbsd.org>
8//   Copyright (c) 2016 Marc Espie <espie@openbsd.org>
9//   Copyright (c) 2019 Adrian Perez de Castro <aperez@igalia.com>
10//   Copyright (c) 2019 Scott Bennett and other contributors
11//   SPDX-License-Identifier: ISC
12//
13// SPDX-License-Identifier: ISC
14
15use std::fmt;
16use std::io;
17use std::path::PathBuf;
18
19/// Custom Error type for Signify operations.
20#[derive(Debug)]
21pub enum Error {
22    /// I/O error.
23    Io(io::Error),
24    /// Key length is invalid.
25    InvalidKeyLength,
26    /// Unsupported public key algorithm (expected Ed).
27    UnsupportedPkAlgo,
28    /// Unsupported KDF algorithm (expected BK).
29    UnsupportedKdfAlgo,
30    /// File is too short.
31    FileTooShort,
32    /// File is too large (exceeds 1GB limit for embedded verification).
33    FileTooLarge,
34    /// Invalid comment header (expected "untrusted comment: ...").
35    InvalidCommentHeader,
36    /// Invalid comment (not utf8)
37    InvalidCommentUtf8,
38    /// Password confirmation failed.
39    PasswordMismatch,
40    /// Failed to read password.
41    PasswordReadFailed,
42    /// Incorrect passphrase (checksum mismatch).
43    IncorrectPassphrase,
44    /// Missing public key (no comment or autolocate failed).
45    MissingPubKey,
46    /// Key fingerprint mismatch.
47    KeyMismatch,
48    /// Signature verification failed.
49    VerifyFailed,
50    /// Autolocate failed to find the key.
51    AutolocateFailed(PathBuf, Box<Error>),
52    /// Key name compliance check failed.
53    InvalidKeyName,
54    /// Path does not contain a filename.
55    InvalidPath,
56    /// Checksum verification failed.
57    CheckFailed,
58    /// Integer overflow.
59    Overflow,
60    /// Base64 decoding failed.
61    Base64Decode(base64ct::Error),
62    /// Argument parsing error.
63    Arg(lexopt::Error),
64    /// Invalid signature length.
65    InvalidSignatureLength,
66    /// Missing Gzip header.
67    MissingGzipHeader,
68    /// Missing signature in Gzip comment.
69    MissingGzipSignature,
70    /// Missing newline in signature.
71    MissingSignatureNewline,
72    /// Keyring support is disabled.
73    KeyringDisabled,
74    /// Invalid key ID.
75    InvalidKeyId,
76    /// Required argument missing.
77    RequiredArg(&'static str),
78    /// Mode not specified.
79    MissingMode,
80    /// Invalid UTF-8 in signature.
81    InvalidSignatureUtf8,
82    /// Keyring error.
83    #[cfg(any(target_os = "linux", target_os = "android"))]
84    Keyring(linux_keyutils::KeyError),
85    /// Cryptographic error (e.g., invalid key format).
86    Crypto(ed25519_compact::Error),
87    /// RNG error.
88    Rng(rand_core::OsError),
89}
90
91impl std::error::Error for Error {
92    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
93        match self {
94            Self::Io(err) => Some(err),
95            Self::Base64Decode(err) => Some(err),
96            Self::Arg(err) => Some(err),
97            _ => None,
98        }
99    }
100}
101
102impl fmt::Display for Error {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        match self {
105            Self::Io(err) => write!(f, "IO error: {err}"),
106            Self::InvalidKeyLength => write!(f, "invalid key length"),
107            Self::UnsupportedPkAlgo => write!(f, "unsupported public key algorithm"),
108            Self::UnsupportedKdfAlgo => write!(f, "unsupported KDF algorithm"),
109            Self::FileTooShort => write!(f, "file too short"),
110            Self::FileTooLarge => write!(f, "file too large (memory limit exceeded)"),
111            Self::InvalidCommentHeader => write!(f, "invalid comment header"),
112            Self::InvalidCommentUtf8 => write!(f, "invalid comment (not utf8)"),
113            Self::InvalidSignatureUtf8 => write!(f, "invalid signature (base64 is not utf8)"),
114            Self::Base64Decode(err) => write!(f, "base64 decode error: {err}"),
115            Self::Arg(err) => write!(f, "argument error: {err}"),
116            Self::PasswordMismatch => write!(f, "password mismatch"),
117            Self::PasswordReadFailed => write!(f, "failed to read password"),
118            Self::IncorrectPassphrase => write!(f, "incorrect passphrase"),
119            Self::MissingPubKey => write!(f, "public key not found"),
120            Self::KeyMismatch => write!(f, "verification failed: checked against wrong key"),
121            Self::VerifyFailed => write!(f, "signature verification failed"),
122            Self::AutolocateFailed(path, err) => {
123                write!(f, "autolocate failed loading {}: {err}", path.display())
124            }
125            Self::InvalidKeyName => write!(f, "invalid key name"),
126            Self::InvalidPath => write!(f, "invalid path"),
127            Self::CheckFailed => write!(f, "checksum check failed"),
128            Self::Overflow => write!(f, "limit exceeded"),
129            Self::InvalidSignatureLength => write!(f, "invalid signature length"),
130            Self::MissingGzipHeader => write!(f, "missing gzip header"),
131            Self::MissingGzipSignature => write!(f, "missing signature in gzip comment"),
132            Self::MissingSignatureNewline => write!(f, "missing newline in signature"),
133            Self::KeyringDisabled => write!(f, "keyring support disabled"),
134            Self::InvalidKeyId => write!(f, "invalid key id"),
135            Self::RequiredArg(arg) => write!(f, "missing required argument: {arg}"),
136            Self::MissingMode => write!(f, "must specify mode"),
137            #[cfg(any(target_os = "linux", target_os = "android"))]
138            Self::Keyring(err) => write!(f, "keyring error: {err:?}"),
139            Self::Crypto(err) => write!(f, "crypto error: {err}"),
140            Self::Rng(err) => write!(f, "rng error: {err}"),
141        }
142    }
143}
144
145impl From<ed25519_compact::Error> for Error {
146    fn from(err: ed25519_compact::Error) -> Self {
147        Self::Crypto(err)
148    }
149}
150
151impl From<io::Error> for Error {
152    fn from(err: io::Error) -> Self {
153        Self::Io(err)
154    }
155}
156
157impl From<base64ct::Error> for Error {
158    fn from(err: base64ct::Error) -> Self {
159        Self::Base64Decode(err)
160    }
161}
162
163impl From<lexopt::Error> for Error {
164    fn from(err: lexopt::Error) -> Self {
165        Self::Arg(err)
166    }
167}
168
169#[cfg(any(target_os = "linux", target_os = "android"))]
170impl From<linux_keyutils::KeyError> for Error {
171    fn from(err: linux_keyutils::KeyError) -> Self {
172        Self::Keyring(err)
173    }
174}
175
176impl From<rand_core::OsError> for Error {
177    fn from(err: rand_core::OsError) -> Self {
178        Self::Rng(err)
179    }
180}
181
182/// Result alias for Signify operations.
183pub type Result<T> = std::result::Result<T, Error>;