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    /// The binding type is incompatible with the key's multicodec.
166    #[error(
167        "multikey: binding type '{binding_type}' does not match codec '{codec_name}' (alg={alg})"
168    )]
169    BindingTypeCodecMismatch {
170        /// Declared binding type.
171        binding_type: BindingTypeKind,
172        /// Canonical codec name implied by the key.
173        codec_name: &'static str,
174        /// Algorithm string implied by the codec.
175        alg: &'static str,
176    },
177
178    /// The declared binding algorithm disagrees with the codec's algorithm.
179    #[error(
180        "multikey: algorithm mismatch: binding.algorithm='{binding_alg}' but codec implies '{codec_alg}'"
181    )]
182    BindingAlgorithmMismatch {
183        /// Algorithm declared in the binding.
184        binding_alg: BindingAlgorithmKind,
185        /// Algorithm string implied by the codec.
186        codec_alg: &'static str,
187    },
188
189    /// A required explicit algorithm was omitted for this binding type.
190    #[error(
191        "multikey: binding.algorithm missing but binding type '{binding_type}' requires an explicit algorithm"
192    )]
193    BindingAlgorithmMissing {
194        /// Binding type that requires an explicit algorithm.
195        binding_type: BindingTypeKind,
196    },
197}
198
199pub(crate) fn classify_binding_type(binding_type: &str) -> BindingTypeKind {
200    match binding_type {
201        "Multikey" => BindingTypeKind::Multikey,
202        "P256Key2024" => BindingTypeKind::P256Key2024,
203        "P384Key2024" => BindingTypeKind::P384Key2024,
204        "P521Key2024" => BindingTypeKind::P521Key2024,
205        "RsaVerificationKey2024" => BindingTypeKind::RsaVerificationKey2024,
206        "ML_DSA_44Key2024" => BindingTypeKind::MlDsa44Key2024,
207        "ML_DSA_65Key2024" => BindingTypeKind::MlDsa65Key2024,
208        "ML_DSA_87Key2024" => BindingTypeKind::MlDsa87Key2024,
209        "MLKEM512Key2024" => BindingTypeKind::MlKem512Key2024,
210        "MLKEM768Key2024" => BindingTypeKind::MlKem768Key2024,
211        "MLKEM1024Key2024" => BindingTypeKind::MlKem1024Key2024,
212        _ => BindingTypeKind::Unsupported,
213    }
214}
215
216pub(crate) fn classify_binding_algorithm(algorithm: &str) -> BindingAlgorithmKind {
217    match algorithm {
218        "Ed25519" => BindingAlgorithmKind::Ed25519,
219        "Ed448" => BindingAlgorithmKind::Ed448,
220        "X25519" => BindingAlgorithmKind::X25519,
221        "P-256" => BindingAlgorithmKind::P256,
222        "P-384" => BindingAlgorithmKind::P384,
223        "P-521" => BindingAlgorithmKind::P521,
224        "RSA" => BindingAlgorithmKind::Rsa,
225        "secp256k1" => BindingAlgorithmKind::Secp256k1,
226        "ML-DSA-44" => BindingAlgorithmKind::MlDsa44,
227        "ML-DSA-65" => BindingAlgorithmKind::MlDsa65,
228        "ML-DSA-87" => BindingAlgorithmKind::MlDsa87,
229        "ML-KEM-512" => BindingAlgorithmKind::MlKem512,
230        "ML-KEM-768" => BindingAlgorithmKind::MlKem768,
231        "ML-KEM-1024" => BindingAlgorithmKind::MlKem1024,
232        _ => BindingAlgorithmKind::Unsupported,
233    }
234}