1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
use thiserror::Error;
/// Errors returned by all pqfile operations.
#[non_exhaustive]
#[derive(Debug, Error)]
pub enum PqfileError {
/// Underlying I/O error.
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
/// File does not begin with the `PQFL` magic bytes.
#[error("invalid magic bytes: expected PQFL")]
InvalidMagic,
/// Version byte in the header is not recognized.
#[error("unsupported version: {0:#04x}")]
UnsupportedVersion(u8),
/// KEM variant field in the header is not recognized.
#[error("unsupported KEM variant: {0}")]
UnsupportedKem(u16),
/// The decryption key uses a different KEM variant than the file was encrypted with.
#[error("KEM variant mismatch: key is {key}, file uses {file}")]
KemVariantMismatch {
/// KEM variant identifier of the supplied private key.
key: u16,
/// KEM variant identifier recorded in the file header.
file: u16,
},
/// An encryption or key-encapsulation operation failed.
#[error("encryption failure")]
EncryptionFailure,
/// AEAD authentication tag did not verify; ciphertext is corrupt or tampered.
#[error("decryption failure: authentication tag mismatch")]
DecryptionFailure,
/// PEM data could not be parsed.
#[error("invalid PEM: {0}")]
InvalidPem(String),
/// Key material has an unexpected byte length.
#[error("invalid key length: expected {expected}, got {got}")]
InvalidKeyLength {
/// Expected byte length.
expected: usize,
/// Actual byte length encountered.
got: usize,
},
/// Output file already exists and `--force` was not specified.
#[error("output file already exists (use --force to overwrite): {0}")]
OutputExists(std::path::PathBuf),
/// Passphrase did not decrypt the key, or the encrypted key body is corrupt.
#[error("wrong passphrase or corrupted key")]
WrongPassphrase,
/// Operation requires a passphrase but none was supplied.
#[error("private key is passphrase-protected; provide a passphrase to decrypt it")]
PassphraseRequired,
/// New-passphrase confirmation did not match.
#[error("passphrases do not match")]
PassphraseMismatch,
/// Signature bytes are malformed (wrong length or structure).
#[error("invalid signature: malformed bytes")]
InvalidSignature,
/// ML-DSA signature is well-formed but does not verify against the data.
#[error("signature verification failed")]
SignatureVerificationFailed,
/// None of the recipient slots in a v4/v7 file matched the supplied private key.
#[error("no matching recipient found in this file for the given private key")]
NoMatchingRecipient,
/// The key has been explicitly revoked.
#[error("key has been revoked (fingerprint: {fingerprint}): {reason}")]
KeyRevoked {
/// Colon-separated hex fingerprint of the revoked key.
fingerprint: String,
/// Human-readable reason supplied at revocation time.
reason: String,
},
/// v6 compressed files require the `zstd` feature, which is absent in this build.
#[error("compressed .pqf files (v6) are not supported in this build")]
CompressionNotSupported,
/// The private key was encrypted with legacy Argon2id parameters (p=1, pqfile < 4.0).
/// Run `pqfile repassphrase --from-legacy` to upgrade it before use.
#[error(
"private key uses legacy Argon2id parameters (p=1); \
run `pqfile repassphrase --from-legacy` to upgrade it to p=4 before use"
)]
LegacyKeyFormat,
/// Shamir share fingerprints did not agree; shares may be from different keys.
#[error("key share reconstruction failed: fingerprint mismatch, ensure you have the correct shares for this key")]
ShareVerificationFailed,
/// The `.pqf` stream ended without a terminal chunk (is_last flag never seen).
/// The file was most likely truncated during download or transfer; re-downloading
/// is the appropriate recovery action. Distinguished from [`DecryptionFailure`]
/// which indicates bytes are present but the authentication tag is wrong.
#[error("stream truncated: file ended before the final authenticated chunk was seen; re-download and try again")]
Truncated,
}