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)]
9pub enum CodecNameReason {
10    /// The codec name is not one of the supported multicodec names.
11    Unsupported,
12}
13
14impl core::fmt::Display for CodecNameReason {
15    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
16        let detail = match self {
17            CodecNameReason::Unsupported => "unsupported codec name",
18        };
19        write!(f, "{detail}")
20    }
21}
22
23/// Classified binding-type label, used for stable error reporting.
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum BindingTypeKind {
26    /// Generic `Multikey` binding, allowing any supported algorithm.
27    Multikey,
28    /// `P256Key2024` profile-specific binding.
29    P256Key2024,
30    /// `P384Key2024` profile-specific binding.
31    P384Key2024,
32    /// `P521Key2024` profile-specific binding.
33    P521Key2024,
34    /// `RsaVerificationKey2024` profile-specific binding.
35    RsaVerificationKey2024,
36    /// `ML_DSA_44Key2024` profile-specific binding.
37    MlDsa44Key2024,
38    /// `ML_DSA_65Key2024` profile-specific binding.
39    MlDsa65Key2024,
40    /// `ML_DSA_87Key2024` profile-specific binding.
41    MlDsa87Key2024,
42    /// `MLKEM512Key2024` profile-specific binding.
43    MlKem512Key2024,
44    /// `MLKEM768Key2024` profile-specific binding.
45    MlKem768Key2024,
46    /// `MLKEM1024Key2024` profile-specific binding.
47    MlKem1024Key2024,
48    /// An unrecognized binding-type label.
49    Unsupported,
50}
51
52impl core::fmt::Display for BindingTypeKind {
53    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
54        let detail = match self {
55            BindingTypeKind::Multikey => "Multikey",
56            BindingTypeKind::P256Key2024 => "P256Key2024",
57            BindingTypeKind::P384Key2024 => "P384Key2024",
58            BindingTypeKind::P521Key2024 => "P521Key2024",
59            BindingTypeKind::RsaVerificationKey2024 => "RsaVerificationKey2024",
60            BindingTypeKind::MlDsa44Key2024 => "ML_DSA_44Key2024",
61            BindingTypeKind::MlDsa65Key2024 => "ML_DSA_65Key2024",
62            BindingTypeKind::MlDsa87Key2024 => "ML_DSA_87Key2024",
63            BindingTypeKind::MlKem512Key2024 => "MLKEM512Key2024",
64            BindingTypeKind::MlKem768Key2024 => "MLKEM768Key2024",
65            BindingTypeKind::MlKem1024Key2024 => "MLKEM1024Key2024",
66            BindingTypeKind::Unsupported => "unsupported binding type",
67        };
68        write!(f, "{detail}")
69    }
70}
71
72/// Classified algorithm label, used for stable error reporting.
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74pub enum BindingAlgorithmKind {
75    /// Ed25519 signature algorithm.
76    Ed25519,
77    /// Ed448 signature algorithm.
78    Ed448,
79    /// X25519 key-agreement algorithm.
80    X25519,
81    /// NIST P-256 algorithm.
82    P256,
83    /// NIST P-384 algorithm.
84    P384,
85    /// NIST P-521 algorithm.
86    P521,
87    /// RSA signature verification algorithm.
88    Rsa,
89    /// secp256k1 algorithm.
90    Secp256k1,
91    /// ML-DSA-44 post-quantum signature algorithm.
92    MlDsa44,
93    /// ML-DSA-65 post-quantum signature algorithm.
94    MlDsa65,
95    /// ML-DSA-87 post-quantum signature algorithm.
96    MlDsa87,
97    /// ML-KEM-512 post-quantum KEM.
98    MlKem512,
99    /// ML-KEM-768 post-quantum KEM.
100    MlKem768,
101    /// ML-KEM-1024 post-quantum KEM.
102    MlKem1024,
103    /// An unrecognized algorithm label.
104    Unsupported,
105}
106
107impl core::fmt::Display for BindingAlgorithmKind {
108    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
109        let detail = match self {
110            BindingAlgorithmKind::Ed25519 => "Ed25519",
111            BindingAlgorithmKind::Ed448 => "Ed448",
112            BindingAlgorithmKind::X25519 => "X25519",
113            BindingAlgorithmKind::P256 => "P-256",
114            BindingAlgorithmKind::P384 => "P-384",
115            BindingAlgorithmKind::P521 => "P-521",
116            BindingAlgorithmKind::Rsa => "RSA",
117            BindingAlgorithmKind::Secp256k1 => "secp256k1",
118            BindingAlgorithmKind::MlDsa44 => "ML-DSA-44",
119            BindingAlgorithmKind::MlDsa65 => "ML-DSA-65",
120            BindingAlgorithmKind::MlDsa87 => "ML-DSA-87",
121            BindingAlgorithmKind::MlKem512 => "ML-KEM-512",
122            BindingAlgorithmKind::MlKem768 => "ML-KEM-768",
123            BindingAlgorithmKind::MlKem1024 => "ML-KEM-1024",
124            BindingAlgorithmKind::Unsupported => "unsupported algorithm",
125        };
126        write!(f, "{detail}")
127    }
128}
129
130/// Error returned when parsing, encoding, or validating a multikey fails.
131///
132/// Parsing fails closed: malformed input yields one of these variants.
133#[derive(Debug, Error)]
134pub enum MultikeyError {
135    /// The input is not a valid multibase string.
136    #[error("multikey: invalid multibase string")]
137    InvalidMultibase,
138
139    /// The decoded bytes are too short to hold a codec prefix and key.
140    #[error("multikey: decoded key too short: length={0}")]
141    DecodedTooShort(usize),
142
143    /// The multicodec prefix is not a recognized key type.
144    #[error("multikey: unknown multicodec prefix")]
145    UnknownCodecPrefix,
146
147    /// The given codec name is not one of the supported names.
148    #[error("multikey: unknown codec name: {reason}")]
149    UnknownCodecName {
150        /// Which class of unknown codec name was supplied.
151        reason: CodecNameReason,
152    },
153
154    /// The key length does not match the length required by the codec.
155    #[error("multikey: key length mismatch for {codec_name}: expected {expected}, got {actual}")]
156    KeyLengthMismatch {
157        /// Canonical codec name whose length requirement was violated.
158        codec_name: &'static str,
159        /// Key length in bytes the codec requires.
160        expected: usize,
161        /// Key length in bytes that was supplied.
162        actual: usize,
163    },
164
165    /// A variable-length key exceeded this crate's semantic safety limit.
166    #[error("multikey: key too large for {codec_name}: maximum {max}, got {actual}")]
167    KeyTooLarge {
168        /// Canonical codec name whose length limit was violated.
169        codec_name: &'static str,
170        /// Maximum accepted key length in bytes.
171        max: usize,
172        /// Key length in bytes that was supplied.
173        actual: usize,
174    },
175
176    /// The final multikey payload exceeded the base58btc safety limit.
177    #[error("multikey: encoded payload too large")]
178    EncodedPayloadTooLarge,
179
180    /// The binding type is incompatible with the key's multicodec.
181    #[error(
182        "multikey: binding type '{binding_type}' does not match codec '{codec_name}' (alg={alg})"
183    )]
184    BindingTypeCodecMismatch {
185        /// Declared binding type.
186        binding_type: BindingTypeKind,
187        /// Canonical codec name implied by the key.
188        codec_name: &'static str,
189        /// Algorithm string implied by the codec.
190        alg: &'static str,
191    },
192
193    /// The declared binding algorithm disagrees with the codec's algorithm.
194    #[error(
195        "multikey: algorithm mismatch: binding.algorithm='{binding_alg}' but codec implies '{codec_alg}'"
196    )]
197    BindingAlgorithmMismatch {
198        /// Algorithm declared in the binding.
199        binding_alg: BindingAlgorithmKind,
200        /// Algorithm string implied by the codec.
201        codec_alg: &'static str,
202    },
203
204    /// A required explicit algorithm was omitted for this binding type.
205    #[error(
206        "multikey: binding.algorithm missing but binding type '{binding_type}' requires an explicit algorithm"
207    )]
208    BindingAlgorithmMissing {
209        /// Binding type that requires an explicit algorithm.
210        binding_type: BindingTypeKind,
211    },
212}
213
214pub(crate) fn classify_binding_type(binding_type: &str) -> BindingTypeKind {
215    match binding_type {
216        "Multikey" => BindingTypeKind::Multikey,
217        "P256Key2024" => BindingTypeKind::P256Key2024,
218        "P384Key2024" => BindingTypeKind::P384Key2024,
219        "P521Key2024" => BindingTypeKind::P521Key2024,
220        "RsaVerificationKey2024" => BindingTypeKind::RsaVerificationKey2024,
221        "ML_DSA_44Key2024" => BindingTypeKind::MlDsa44Key2024,
222        "ML_DSA_65Key2024" => BindingTypeKind::MlDsa65Key2024,
223        "ML_DSA_87Key2024" => BindingTypeKind::MlDsa87Key2024,
224        "MLKEM512Key2024" => BindingTypeKind::MlKem512Key2024,
225        "MLKEM768Key2024" => BindingTypeKind::MlKem768Key2024,
226        "MLKEM1024Key2024" => BindingTypeKind::MlKem1024Key2024,
227        _ => BindingTypeKind::Unsupported,
228    }
229}
230
231pub(crate) fn classify_binding_algorithm(algorithm: &str) -> BindingAlgorithmKind {
232    match algorithm {
233        "Ed25519" => BindingAlgorithmKind::Ed25519,
234        "Ed448" => BindingAlgorithmKind::Ed448,
235        "X25519" => BindingAlgorithmKind::X25519,
236        "P-256" => BindingAlgorithmKind::P256,
237        "P-384" => BindingAlgorithmKind::P384,
238        "P-521" => BindingAlgorithmKind::P521,
239        "RSA" => BindingAlgorithmKind::Rsa,
240        "secp256k1" => BindingAlgorithmKind::Secp256k1,
241        "ML-DSA-44" => BindingAlgorithmKind::MlDsa44,
242        "ML-DSA-65" => BindingAlgorithmKind::MlDsa65,
243        "ML-DSA-87" => BindingAlgorithmKind::MlDsa87,
244        "ML-KEM-512" => BindingAlgorithmKind::MlKem512,
245        "ML-KEM-768" => BindingAlgorithmKind::MlKem768,
246        "ML-KEM-1024" => BindingAlgorithmKind::MlKem1024,
247        _ => BindingAlgorithmKind::Unsupported,
248    }
249}