1#![allow(clippy::needless_return)]
6
7use crate::AlgorithmError;
8#[cfg(any(
9 feature = "ed25519",
10 feature = "x25519",
11 feature = "secp256k1",
12 feature = "p256",
13 feature = "p384",
14 feature = "p521",
15 feature = "ml-dsa-44",
16 feature = "ml-dsa-65",
17 feature = "ml-dsa-87",
18 feature = "ml-kem-512",
19 feature = "ml-kem-768",
20 feature = "ml-kem-1024"
21))]
22use codec_multikey::encode_multikey;
23use crypto_core::Algorithm;
24
25#[cfg(feature = "p256")]
26fn compress_p256_public_key(input: &[u8]) -> Result<Vec<u8>, AlgorithmError> {
27 #[cfg(all(
28 feature = "p256",
29 feature = "native",
30 not(all(feature = "wasm", target_arch = "wasm32"))
31 ))]
32 {
33 return crypto_p256::compress_p256(input)
34 .map_err(|_| AlgorithmError::InvalidKey(Algorithm::P256));
35 }
36
37 #[cfg(all(feature = "p256", feature = "wasm", target_arch = "wasm32"))]
38 {
39 return crypto_p256::compress_p256(input)
40 .map_err(|_| AlgorithmError::InvalidKey(Algorithm::P256));
41 }
42
43 #[cfg(not(any(
44 all(
45 feature = "p256",
46 feature = "native",
47 not(all(feature = "wasm", target_arch = "wasm32"))
48 ),
49 all(feature = "p256", feature = "wasm", target_arch = "wasm32")
50 )))]
51 {
52 let _ = input;
53 Err(AlgorithmError::UnsupportedAlgorithm(Algorithm::P256))
54 }
55}
56
57#[cfg(feature = "p384")]
58fn compress_p384_public_key(input: &[u8]) -> Result<Vec<u8>, AlgorithmError> {
59 #[cfg(all(
60 feature = "p384",
61 feature = "native",
62 not(all(feature = "wasm", target_arch = "wasm32"))
63 ))]
64 {
65 return crypto_p384::compress_p384(input)
66 .map_err(|_| AlgorithmError::InvalidKey(Algorithm::P384));
67 }
68
69 #[cfg(all(feature = "p384", feature = "wasm", target_arch = "wasm32"))]
70 {
71 return crypto_p384::compress_p384(input)
72 .map_err(|_| AlgorithmError::InvalidKey(Algorithm::P384));
73 }
74
75 #[cfg(not(any(
76 all(
77 feature = "p384",
78 feature = "native",
79 not(all(feature = "wasm", target_arch = "wasm32"))
80 ),
81 all(feature = "p384", feature = "wasm", target_arch = "wasm32")
82 )))]
83 {
84 let _ = input;
85 Err(AlgorithmError::UnsupportedAlgorithm(Algorithm::P384))
86 }
87}
88
89#[cfg(feature = "p521")]
90fn compress_p521_public_key(input: &[u8]) -> Result<Vec<u8>, AlgorithmError> {
91 #[cfg(all(
92 feature = "p521",
93 feature = "native",
94 not(all(feature = "wasm", target_arch = "wasm32"))
95 ))]
96 {
97 return crypto_p521::compress_p521(input)
98 .map_err(|_| AlgorithmError::InvalidKey(Algorithm::P521));
99 }
100
101 #[cfg(all(feature = "p521", feature = "wasm", target_arch = "wasm32"))]
102 {
103 return crypto_p521::compress_p521(input)
104 .map_err(|_| AlgorithmError::InvalidKey(Algorithm::P521));
105 }
106
107 #[cfg(not(any(
108 all(
109 feature = "p521",
110 feature = "native",
111 not(all(feature = "wasm", target_arch = "wasm32"))
112 ),
113 all(feature = "p521", feature = "wasm", target_arch = "wasm32")
114 )))]
115 {
116 let _ = input;
117 Err(AlgorithmError::UnsupportedAlgorithm(Algorithm::P521))
118 }
119}
120
121#[cfg(any(feature = "p256", feature = "p384", feature = "p521"))]
122fn canonicalize_sec1_public_key(
123 alg: Algorithm,
124 public_key: &[u8],
125 compressed_len: usize,
126 uncompressed_len: usize,
127 raw_len: usize,
128 compress: fn(&[u8]) -> Result<Vec<u8>, AlgorithmError>,
129) -> Result<Vec<u8>, AlgorithmError> {
130 match public_key.len() {
131 len if len == compressed_len => {
132 crate::sec1_identity::validate_compressed_public_key(alg, public_key)?;
133 Ok(public_key.to_vec())
134 }
135 len if len == uncompressed_len => compress(public_key),
136 len if len == raw_len => {
137 let capacity = raw_len
138 .checked_add(1)
139 .ok_or(AlgorithmError::InvalidKey(alg))?;
140 let mut sec1 = Vec::with_capacity(capacity);
141 sec1.push(0x04);
142 sec1.extend_from_slice(public_key);
143 compress(&sec1)
144 }
145 _ => Err(AlgorithmError::InvalidKey(alg)),
146 }
147}
148
149#[cfg(not(any(
152 feature = "ed25519",
153 feature = "x25519",
154 feature = "secp256k1",
155 feature = "p256",
156 feature = "p384",
157 feature = "p521",
158 feature = "ml-dsa-44",
159 feature = "ml-dsa-65",
160 feature = "ml-dsa-87",
161 feature = "ml-kem-512",
162 feature = "ml-kem-768",
163 feature = "ml-kem-1024"
164)))]
165pub fn public_key_to_multikey(alg: Algorithm, public_key: &[u8]) -> Result<String, AlgorithmError> {
166 let _ = public_key;
167 Err(AlgorithmError::UnsupportedAlgorithm(alg))
168}
169
170#[cfg(any(
173 feature = "ed25519",
174 feature = "x25519",
175 feature = "secp256k1",
176 feature = "p256",
177 feature = "p384",
178 feature = "p521",
179 feature = "ml-dsa-44",
180 feature = "ml-dsa-65",
181 feature = "ml-dsa-87",
182 feature = "ml-kem-512",
183 feature = "ml-kem-768",
184 feature = "ml-kem-1024"
185))]
186pub fn public_key_to_multikey(alg: Algorithm, public_key: &[u8]) -> Result<String, AlgorithmError> {
187 let (codec, key_bytes) = match alg {
188 Algorithm::Ed25519 => {
189 #[cfg(feature = "ed25519")]
190 {
191 crypto_ed25519::validate_public_key_identity(public_key)
192 .map_err(|_| AlgorithmError::InvalidKey(alg))?;
193 ("ed25519-pub", public_key.to_vec())
194 }
195 #[cfg(not(feature = "ed25519"))]
196 {
197 return Err(AlgorithmError::UnsupportedAlgorithm(alg));
198 }
199 }
200 Algorithm::X25519 => {
201 #[cfg(feature = "x25519")]
202 {
203 if !crate::x25519_identity::is_canonical(public_key) {
204 return Err(AlgorithmError::InvalidKey(alg));
205 }
206 ("x25519-pub", public_key.to_vec())
207 }
208 #[cfg(not(feature = "x25519"))]
209 {
210 return Err(AlgorithmError::UnsupportedAlgorithm(alg));
211 }
212 }
213 Algorithm::Secp256k1 => {
214 #[cfg(feature = "secp256k1")]
215 {
216 crate::sec1_identity::validate_compressed_public_key(alg, public_key)?;
217 ("secp256k1-pub", public_key.to_vec())
218 }
219 #[cfg(not(feature = "secp256k1"))]
220 {
221 return Err(AlgorithmError::UnsupportedAlgorithm(alg));
222 }
223 }
224 Algorithm::P384 => {
225 #[cfg(feature = "p384")]
226 {
227 (
228 "p384-pub",
229 canonicalize_sec1_public_key(
230 alg,
231 public_key,
232 crypto_p384::P384_PUBLIC_KEY_COMPRESSED_LEN,
233 crypto_p384::P384_PUBLIC_KEY_UNCOMPRESSED_LEN,
234 crypto_p384::P384_PUBLIC_KEY_RAW_LEN,
235 compress_p384_public_key,
236 )?,
237 )
238 }
239 #[cfg(not(feature = "p384"))]
240 {
241 return Err(AlgorithmError::UnsupportedAlgorithm(alg));
242 }
243 }
244 Algorithm::P521 => {
245 #[cfg(feature = "p521")]
246 {
247 (
248 "p521-pub",
249 canonicalize_sec1_public_key(
250 alg,
251 public_key,
252 crypto_p521::P521_PUBLIC_KEY_COMPRESSED_LEN,
253 crypto_p521::P521_PUBLIC_KEY_UNCOMPRESSED_LEN,
254 crypto_p521::P521_PUBLIC_KEY_RAW_LEN,
255 compress_p521_public_key,
256 )?,
257 )
258 }
259 #[cfg(not(feature = "p521"))]
260 {
261 return Err(AlgorithmError::UnsupportedAlgorithm(alg));
262 }
263 }
264 Algorithm::MlDsa44 => {
265 #[cfg(feature = "ml-dsa-44")]
266 {
267 ("mldsa-44-pub", public_key.to_vec())
268 }
269 #[cfg(not(feature = "ml-dsa-44"))]
270 {
271 return Err(AlgorithmError::UnsupportedAlgorithm(alg));
272 }
273 }
274 Algorithm::MlDsa65 => {
275 #[cfg(feature = "ml-dsa-65")]
276 {
277 ("mldsa-65-pub", public_key.to_vec())
278 }
279 #[cfg(not(feature = "ml-dsa-65"))]
280 {
281 return Err(AlgorithmError::UnsupportedAlgorithm(alg));
282 }
283 }
284 Algorithm::MlDsa87 => {
285 #[cfg(feature = "ml-dsa-87")]
286 {
287 ("mldsa-87-pub", public_key.to_vec())
288 }
289 #[cfg(not(feature = "ml-dsa-87"))]
290 {
291 return Err(AlgorithmError::UnsupportedAlgorithm(alg));
292 }
293 }
294 Algorithm::SlhDsaSha2_128s => {
295 return Err(AlgorithmError::UnsupportedAlgorithm(alg));
296 }
297 Algorithm::MlKem512 => {
298 #[cfg(feature = "ml-kem-512")]
299 {
300 ("mlkem-512-pub", public_key.to_vec())
301 }
302 #[cfg(not(feature = "ml-kem-512"))]
303 {
304 return Err(AlgorithmError::UnsupportedAlgorithm(alg));
305 }
306 }
307 Algorithm::MlKem768 => {
308 #[cfg(feature = "ml-kem-768")]
309 {
310 ("mlkem-768-pub", public_key.to_vec())
311 }
312 #[cfg(not(feature = "ml-kem-768"))]
313 {
314 return Err(AlgorithmError::UnsupportedAlgorithm(alg));
315 }
316 }
317 Algorithm::MlKem1024 => {
318 #[cfg(feature = "ml-kem-1024")]
319 {
320 ("mlkem-1024-pub", public_key.to_vec())
321 }
322 #[cfg(not(feature = "ml-kem-1024"))]
323 {
324 return Err(AlgorithmError::UnsupportedAlgorithm(alg));
325 }
326 }
327 Algorithm::XWing768 => {
328 return Err(AlgorithmError::UnsupportedAlgorithm(alg));
329 }
330
331 Algorithm::P256 => {
333 #[cfg(not(feature = "p256"))]
334 {
335 return Err(AlgorithmError::UnsupportedAlgorithm(alg));
336 }
337
338 #[cfg(feature = "p256")]
339 {
340 let compressed = canonicalize_sec1_public_key(
341 alg,
342 public_key,
343 33,
344 65,
345 64,
346 compress_p256_public_key,
347 )?;
348
349 ("p256-pub", compressed)
350 }
351 }
352 };
353
354 encode_multikey(codec, &key_bytes).map_err(|_| AlgorithmError::InvalidKey(alg))
355}