Skip to main content

codec_multikey/
error.rs

1// SPDX-FileCopyrightText: Copyright © 2026 ReallyMe LLC. All rights reserved
2//
3// SPDX-License-Identifier: Apache-2.0
4
5use thiserror::Error;
6
7/// Reason a codec name could not be resolved, used in error messages.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9#[non_exhaustive]
10pub enum CodecNameReason {
11    /// The codec name is not one of the supported multicodec names.
12    Unsupported,
13}
14
15impl core::fmt::Display for CodecNameReason {
16    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
17        let detail = match self {
18            CodecNameReason::Unsupported => "unsupported codec name",
19        };
20        write!(f, "{detail}")
21    }
22}
23
24/// Classified multicodec name used for deterministic, non-string error context.
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26#[non_exhaustive]
27pub enum MultikeyCodecKind {
28    /// Ed25519 public key.
29    Ed25519,
30    /// Ed448 public key.
31    Ed448,
32    /// X25519 public key.
33    X25519,
34    /// NIST P-256 public key.
35    P256,
36    /// NIST P-384 public key.
37    P384,
38    /// NIST P-521 public key.
39    P521,
40    /// RSA public key.
41    Rsa,
42    /// secp256k1 public key.
43    Secp256k1,
44    /// ML-DSA-44 public key.
45    MlDsa44,
46    /// ML-DSA-65 public key.
47    MlDsa65,
48    /// ML-DSA-87 public key.
49    MlDsa87,
50    /// ML-KEM-512 public key.
51    MlKem512,
52    /// ML-KEM-768 public key.
53    MlKem768,
54    /// ML-KEM-1024 public key.
55    MlKem1024,
56    /// A codec outside the supported public-key set.
57    Unsupported,
58}
59
60impl core::fmt::Display for MultikeyCodecKind {
61    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
62        let detail = match self {
63            MultikeyCodecKind::Ed25519 => "ed25519-pub",
64            MultikeyCodecKind::Ed448 => "ed448-pub",
65            MultikeyCodecKind::X25519 => "x25519-pub",
66            MultikeyCodecKind::P256 => "p256-pub",
67            MultikeyCodecKind::P384 => "p384-pub",
68            MultikeyCodecKind::P521 => "p521-pub",
69            MultikeyCodecKind::Rsa => "rsa-pub",
70            MultikeyCodecKind::Secp256k1 => "secp256k1-pub",
71            MultikeyCodecKind::MlDsa44 => "mldsa-44-pub",
72            MultikeyCodecKind::MlDsa65 => "mldsa-65-pub",
73            MultikeyCodecKind::MlDsa87 => "mldsa-87-pub",
74            MultikeyCodecKind::MlKem512 => "mlkem-512-pub",
75            MultikeyCodecKind::MlKem768 => "mlkem-768-pub",
76            MultikeyCodecKind::MlKem1024 => "mlkem-1024-pub",
77            MultikeyCodecKind::Unsupported => "unsupported codec",
78        };
79        write!(f, "{detail}")
80    }
81}
82
83/// Classified binding-type label, used for stable error reporting.
84#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85#[non_exhaustive]
86pub enum BindingTypeKind {
87    /// Generic `Multikey` binding, allowing any supported algorithm.
88    Multikey,
89    /// `P256Key2024` profile-specific binding.
90    P256Key2024,
91    /// `P384Key2024` profile-specific binding.
92    P384Key2024,
93    /// `P521Key2024` profile-specific binding.
94    P521Key2024,
95    /// `RsaVerificationKey2024` profile-specific binding.
96    RsaVerificationKey2024,
97    /// `ML_DSA_44Key2024` profile-specific binding.
98    MlDsa44Key2024,
99    /// `ML_DSA_65Key2024` profile-specific binding.
100    MlDsa65Key2024,
101    /// `ML_DSA_87Key2024` profile-specific binding.
102    MlDsa87Key2024,
103    /// `MLKEM512Key2024` profile-specific binding.
104    MlKem512Key2024,
105    /// `MLKEM768Key2024` profile-specific binding.
106    MlKem768Key2024,
107    /// `MLKEM1024Key2024` profile-specific binding.
108    MlKem1024Key2024,
109    /// An unrecognized binding-type label.
110    Unsupported,
111}
112
113impl core::fmt::Display for BindingTypeKind {
114    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
115        let detail = match self {
116            BindingTypeKind::Multikey => "Multikey",
117            BindingTypeKind::P256Key2024 => "P256Key2024",
118            BindingTypeKind::P384Key2024 => "P384Key2024",
119            BindingTypeKind::P521Key2024 => "P521Key2024",
120            BindingTypeKind::RsaVerificationKey2024 => "RsaVerificationKey2024",
121            BindingTypeKind::MlDsa44Key2024 => "ML_DSA_44Key2024",
122            BindingTypeKind::MlDsa65Key2024 => "ML_DSA_65Key2024",
123            BindingTypeKind::MlDsa87Key2024 => "ML_DSA_87Key2024",
124            BindingTypeKind::MlKem512Key2024 => "MLKEM512Key2024",
125            BindingTypeKind::MlKem768Key2024 => "MLKEM768Key2024",
126            BindingTypeKind::MlKem1024Key2024 => "MLKEM1024Key2024",
127            BindingTypeKind::Unsupported => "unsupported binding type",
128        };
129        write!(f, "{detail}")
130    }
131}
132
133/// Classified algorithm label, used for stable error reporting.
134#[derive(Debug, Clone, Copy, PartialEq, Eq)]
135#[non_exhaustive]
136pub enum BindingAlgorithmKind {
137    /// Ed25519 signature algorithm.
138    Ed25519,
139    /// Ed448 signature algorithm.
140    Ed448,
141    /// X25519 key-agreement algorithm.
142    X25519,
143    /// NIST P-256 algorithm.
144    P256,
145    /// NIST P-384 algorithm.
146    P384,
147    /// NIST P-521 algorithm.
148    P521,
149    /// RSA signature verification algorithm.
150    Rsa,
151    /// secp256k1 algorithm.
152    Secp256k1,
153    /// ML-DSA-44 post-quantum signature algorithm.
154    MlDsa44,
155    /// ML-DSA-65 post-quantum signature algorithm.
156    MlDsa65,
157    /// ML-DSA-87 post-quantum signature algorithm.
158    MlDsa87,
159    /// ML-KEM-512 post-quantum KEM.
160    MlKem512,
161    /// ML-KEM-768 post-quantum KEM.
162    MlKem768,
163    /// ML-KEM-1024 post-quantum KEM.
164    MlKem1024,
165    /// An unrecognized algorithm label.
166    Unsupported,
167}
168
169impl core::fmt::Display for BindingAlgorithmKind {
170    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
171        let detail = match self {
172            BindingAlgorithmKind::Ed25519 => "Ed25519",
173            BindingAlgorithmKind::Ed448 => "Ed448",
174            BindingAlgorithmKind::X25519 => "X25519",
175            BindingAlgorithmKind::P256 => "P-256",
176            BindingAlgorithmKind::P384 => "P-384",
177            BindingAlgorithmKind::P521 => "P-521",
178            BindingAlgorithmKind::Rsa => "RSA",
179            BindingAlgorithmKind::Secp256k1 => "secp256k1",
180            BindingAlgorithmKind::MlDsa44 => "ML-DSA-44",
181            BindingAlgorithmKind::MlDsa65 => "ML-DSA-65",
182            BindingAlgorithmKind::MlDsa87 => "ML-DSA-87",
183            BindingAlgorithmKind::MlKem512 => "ML-KEM-512",
184            BindingAlgorithmKind::MlKem768 => "ML-KEM-768",
185            BindingAlgorithmKind::MlKem1024 => "ML-KEM-1024",
186            BindingAlgorithmKind::Unsupported => "unsupported algorithm",
187        };
188        write!(f, "{detail}")
189    }
190}
191
192/// Error returned when parsing, encoding, or validating a multikey fails.
193///
194/// Parsing fails closed: malformed input yields one of these variants.
195#[derive(Debug, Error)]
196#[non_exhaustive]
197pub enum MultikeyError {
198    /// The input is not a valid multibase string.
199    #[error("multikey: invalid multibase string")]
200    InvalidMultibase,
201
202    /// The decoded bytes are too short to hold a codec prefix and key.
203    #[error("multikey: decoded key too short: length={0}")]
204    DecodedTooShort(usize),
205
206    /// The multicodec prefix is not a recognized key type.
207    #[error("multikey: unknown multicodec prefix")]
208    UnknownCodecPrefix,
209
210    /// The given codec name is not one of the supported names.
211    #[error("multikey: unknown codec name: {reason}")]
212    UnknownCodecName {
213        /// Which class of unknown codec name was supplied.
214        reason: CodecNameReason,
215    },
216
217    /// The key length does not match the length required by the codec.
218    #[error("multikey: key length mismatch for {codec}: expected {expected}, got {actual}")]
219    KeyLengthMismatch {
220        /// Canonical codec name whose length requirement was violated.
221        codec: MultikeyCodecKind,
222        /// Key length in bytes the codec requires.
223        expected: usize,
224        /// Key length in bytes that was supplied.
225        actual: usize,
226    },
227
228    /// A variable-length key exceeded this crate's semantic safety limit.
229    #[error("multikey: key too large for {codec}: maximum {max}, got {actual}")]
230    KeyTooLarge {
231        /// Canonical codec name whose length limit was violated.
232        codec: MultikeyCodecKind,
233        /// Maximum accepted key length in bytes.
234        max: usize,
235        /// Key length in bytes that was supplied.
236        actual: usize,
237    },
238
239    /// The final multikey payload exceeded the base58btc safety limit.
240    #[error("multikey: encoded payload too large")]
241    EncodedPayloadTooLarge,
242
243    /// The binding type is incompatible with the key's multicodec.
244    #[error(
245        "multikey: binding type '{binding_type}' does not match codec '{codec}' (alg={algorithm})"
246    )]
247    BindingTypeCodecMismatch {
248        /// Declared binding type.
249        binding_type: BindingTypeKind,
250        /// Canonical codec name implied by the key.
251        codec: MultikeyCodecKind,
252        /// Algorithm string implied by the codec.
253        algorithm: BindingAlgorithmKind,
254    },
255
256    /// The declared binding algorithm disagrees with the codec's algorithm.
257    #[error(
258        "multikey: algorithm mismatch: binding.algorithm='{binding_alg}' but codec implies '{codec_algorithm}'"
259    )]
260    BindingAlgorithmMismatch {
261        /// Algorithm declared in the binding.
262        binding_alg: BindingAlgorithmKind,
263        /// Algorithm string implied by the codec.
264        codec_algorithm: BindingAlgorithmKind,
265    },
266
267    /// A required explicit algorithm was omitted for this binding type.
268    #[error(
269        "multikey: binding.algorithm missing but binding type '{binding_type}' requires an explicit algorithm"
270    )]
271    BindingAlgorithmMissing {
272        /// Binding type that requires an explicit algorithm.
273        binding_type: BindingTypeKind,
274    },
275}
276
277pub(crate) fn classify_binding_type(binding_type: &str) -> BindingTypeKind {
278    match binding_type {
279        "Multikey" => BindingTypeKind::Multikey,
280        "P256Key2024" => BindingTypeKind::P256Key2024,
281        "P384Key2024" => BindingTypeKind::P384Key2024,
282        "P521Key2024" => BindingTypeKind::P521Key2024,
283        "RsaVerificationKey2024" => BindingTypeKind::RsaVerificationKey2024,
284        "ML_DSA_44Key2024" => BindingTypeKind::MlDsa44Key2024,
285        "ML_DSA_65Key2024" => BindingTypeKind::MlDsa65Key2024,
286        "ML_DSA_87Key2024" => BindingTypeKind::MlDsa87Key2024,
287        "MLKEM512Key2024" => BindingTypeKind::MlKem512Key2024,
288        "MLKEM768Key2024" => BindingTypeKind::MlKem768Key2024,
289        "MLKEM1024Key2024" => BindingTypeKind::MlKem1024Key2024,
290        _ => BindingTypeKind::Unsupported,
291    }
292}
293
294pub(crate) fn classify_binding_algorithm(algorithm: &str) -> BindingAlgorithmKind {
295    match algorithm {
296        "Ed25519" => BindingAlgorithmKind::Ed25519,
297        "Ed448" => BindingAlgorithmKind::Ed448,
298        "X25519" => BindingAlgorithmKind::X25519,
299        "P-256" => BindingAlgorithmKind::P256,
300        "P-384" => BindingAlgorithmKind::P384,
301        "P-521" => BindingAlgorithmKind::P521,
302        "RSA" => BindingAlgorithmKind::Rsa,
303        "secp256k1" => BindingAlgorithmKind::Secp256k1,
304        "ML-DSA-44" => BindingAlgorithmKind::MlDsa44,
305        "ML-DSA-65" => BindingAlgorithmKind::MlDsa65,
306        "ML-DSA-87" => BindingAlgorithmKind::MlDsa87,
307        "ML-KEM-512" => BindingAlgorithmKind::MlKem512,
308        "ML-KEM-768" => BindingAlgorithmKind::MlKem768,
309        "ML-KEM-1024" => BindingAlgorithmKind::MlKem1024,
310        _ => BindingAlgorithmKind::Unsupported,
311    }
312}
313
314pub(crate) fn classify_multikey_codec(codec_name: &str) -> MultikeyCodecKind {
315    match codec_name {
316        "ed25519-pub" => MultikeyCodecKind::Ed25519,
317        "ed448-pub" => MultikeyCodecKind::Ed448,
318        "x25519-pub" => MultikeyCodecKind::X25519,
319        "p256-pub" => MultikeyCodecKind::P256,
320        "p384-pub" => MultikeyCodecKind::P384,
321        "p521-pub" => MultikeyCodecKind::P521,
322        "rsa-pub" => MultikeyCodecKind::Rsa,
323        "secp256k1-pub" => MultikeyCodecKind::Secp256k1,
324        "mldsa-44-pub" => MultikeyCodecKind::MlDsa44,
325        "mldsa-65-pub" => MultikeyCodecKind::MlDsa65,
326        "mldsa-87-pub" => MultikeyCodecKind::MlDsa87,
327        "mlkem-512-pub" => MultikeyCodecKind::MlKem512,
328        "mlkem-768-pub" => MultikeyCodecKind::MlKem768,
329        "mlkem-1024-pub" => MultikeyCodecKind::MlKem1024,
330        _ => MultikeyCodecKind::Unsupported,
331    }
332}