1use num_bigint::{BigInt, Sign};
2
3use crate::{
4 crypto::Error as CryptoError,
5 jwk::{Bytes, Ec, EcCurves, Jwk, Key, Okp, OkpCurves, Parameters},
6};
7
8use multibase::Base::Base58Btc;
9use serde::{Deserialize, Serialize};
10use thiserror::Error;
11
12#[derive(Debug, Copy, Clone, PartialEq)]
14#[allow(unused, clippy::upper_case_acronyms)]
15pub enum Algorithm {
16 Ed25519,
17 X25519,
18 Secp256k1,
19 BLS12381,
20 P256,
21 P384,
22 P521,
23 RSA,
24}
25
26use Algorithm::*;
27
28impl Algorithm {
32 pub fn muticodec_prefix(&self) -> [u8; 2] {
38 match self {
39 Ed25519 => [0xed, 0x01],
40 X25519 => [0xec, 0x01],
41 Secp256k1 => [0xe7, 0x01],
42 BLS12381 => [0xeb, 0x01],
43 P256 => [0x80, 0x24],
44 P384 => [0x81, 0x24],
45 P521 => [0x82, 0x24],
46 RSA => [0x85, 0x24],
47 }
48 }
49
50 pub fn from_muticodec_prefix(prefix: &[u8; 2]) -> Option<Self> {
60 match prefix {
61 [0xed, 0x01] => Some(Ed25519),
62 [0xec, 0x01] => Some(X25519),
63 [0xe7, 0x01] => Some(Secp256k1),
64 [0xeb, 0x01] => Some(BLS12381),
65 [0x80, 0x24] => Some(P256),
66 [0x81, 0x24] => Some(P384),
67 [0x82, 0x24] => Some(P521),
68 [0x85, 0x24] => Some(RSA),
69 _ => None,
70 }
71 }
72
73 pub fn public_key_length(&self) -> Option<usize> {
79 match self {
80 Ed25519 => Some(32),
81 X25519 => Some(32),
82 Secp256k1 => Some(33),
83 BLS12381 => None,
84 P256 => Some(33),
85 P384 => Some(49),
86 P521 => None,
87 RSA => None,
88 }
89 }
90
91 pub fn build_jwk(&self, raw_public_key_bytes: &[u8]) -> Result<Jwk, CryptoError> {
101 match self {
102 Ed25519 => Ok(Jwk {
103 key: Key::Okp(Okp {
104 crv: OkpCurves::Ed25519,
105 x: Bytes::from(raw_public_key_bytes.to_vec()),
106 d: None,
107 }),
108 prm: Parameters::default(),
109 }),
110 X25519 => Ok(Jwk {
111 key: Key::Okp(Okp {
112 crv: OkpCurves::X25519,
113 x: Bytes::from(raw_public_key_bytes.to_vec()),
114 d: None,
115 }),
116 prm: Parameters::default(),
117 }),
118 Secp256k1 => {
119 let uncompressed = self.uncompress_public_key(raw_public_key_bytes)?;
120 Ok(Jwk {
121 key: Key::Ec(Ec {
122 crv: EcCurves::P256K,
123 d: None,
124 x: Bytes::from(uncompressed[1..33].to_vec()),
125 y: Bytes::from(uncompressed[33..].to_vec()),
126 }),
127 prm: Parameters::default(),
128 })
129 }
130 P256 => {
131 let uncompressed = self.uncompress_public_key(raw_public_key_bytes)?;
132 Ok(Jwk {
133 key: Key::Ec(Ec {
134 crv: EcCurves::P256,
135 d: None,
136 x: Bytes::from(uncompressed[1..33].to_vec()),
137 y: Bytes::from(uncompressed[33..].to_vec()),
138 }),
139 prm: Parameters::default(),
140 })
141 }
142 _ => Err(CryptoError::Unsupported),
144 }
145 }
146
147 pub fn uncompress_public_key(&self, compressed_key_bytes: &[u8]) -> Result<Vec<u8>, CryptoError> {
157 if let Some(required_length) = self.public_key_length() {
158 if required_length != compressed_key_bytes.len() {
159 return Err(CryptoError::InvalidKeyLength);
160 }
161 }
162
163 let sec1_generic = |p: BigInt, a: BigInt, b: BigInt| {
164 let sign_byte = compressed_key_bytes[0];
165 let sign = match sign_byte {
166 0x02 => 0u8,
167 0x03 => 1u8,
168 _ => return Err(CryptoError::InvalidPublicKey),
169 };
170
171 let x = BigInt::from_bytes_be(Sign::Plus, &compressed_key_bytes[1..]);
172 let y_sq = (x.modpow(&BigInt::from(3u32), &p) + &a * &x + &b) % &p;
173 let mut y = y_sq.modpow(&((&p + BigInt::from(1)) / BigInt::from(4)), &p);
174
175 if &y % BigInt::from(2) != (sign % 2).into() {
176 y = &p - &y;
177 }
178
179 let mut z = vec![0x04];
180 z.append(&mut x.to_bytes_be().1);
181 z.append(&mut y.to_bytes_be().1);
182
183 Ok(z)
184 };
185
186 match self {
187 Secp256k1 => {
188 let p = "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f";
189 let p = BigInt::from_bytes_be(Sign::Plus, &hex::decode(p).unwrap());
190
191 let a = BigInt::from(0);
192 let b = BigInt::from(7);
193
194 sec1_generic(p, a, b)
195 }
196 P256 => {
197 let p = "ffffffff00000001000000000000000000000000ffffffffffffffffffffffff";
198 let p = BigInt::from_bytes_be(Sign::Plus, &hex::decode(p).unwrap());
199
200 let a = &p - BigInt::from(3);
201
202 let b = "5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b";
203 let b = BigInt::from_bytes_be(Sign::Plus, &hex::decode(b).unwrap());
204
205 sec1_generic(p, a, b)
206 }
207 _ => Err(CryptoError::Unsupported),
208 }
209 }
210}
211
212#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Error)]
213pub(crate) enum DecodeMultikeyError {
214 #[error("error to multibase decode")]
215 MultibaseDecodeError,
216 #[error("not multibase-encoded in Base58")]
217 NotBase58MultibaseEncoded,
218 #[error("assumed multicodec too short")]
219 MulticodecTooShort,
220 #[error("unknown algorithm")]
221 UnknownAlgorithm,
222}
223
224pub(crate) fn decode_multikey(multikey: &str) -> Result<(Algorithm, Vec<u8>), DecodeMultikeyError> {
226 let (base, multicodec) = multibase::decode(multikey).map_err(|_| DecodeMultikeyError::MultibaseDecodeError)?;
227
228 if base != Base58Btc {
230 return Err(DecodeMultikeyError::NotBase58MultibaseEncoded);
231 }
232
233 if multicodec.len() < 2 {
235 return Err(DecodeMultikeyError::MulticodecTooShort);
236 }
237
238 let multicodec_prefix: &[u8; 2] = &multicodec[..2].try_into().unwrap();
240 let raw_public_key_bytes = &multicodec[2..];
241
242 let alg = Algorithm::from_muticodec_prefix(multicodec_prefix).ok_or(DecodeMultikeyError::UnknownAlgorithm)?;
244
245 Ok((alg, raw_public_key_bytes.to_vec()))
247}
248
249#[cfg(test)]
250mod tests {
251 use super::*;
252 use multibase::Base::Base64Url;
253 use serde_json::Value;
254
255 #[test]
256 fn test_can_build_secp256k1_jwk() {
257 let (alg, bytes) = decode_multibase_key("zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme");
258 assert_eq!(alg, Secp256k1);
259
260 let uncompressed = alg.uncompress_public_key(&bytes).unwrap();
261 assert_eq!(uncompressed.len(), 65);
262
263 let jwk = alg.build_jwk(&bytes).unwrap();
264 let expected: Value = serde_json::from_str(
265 r#"{
266 "kty": "EC",
267 "crv": "secp256k1",
268 "x": "h0wVx_2iDlOcblulc8E5iEw1EYh5n1RYtLQfeSTyNc0",
269 "y": "O2EATIGbu6DezKFptj5scAIRntgfecanVNXxat1rnwE"
270 }"#,
271 )
272 .unwrap();
273
274 assert_eq!(
275 json_canon::to_string(&jwk).unwrap(), json_canon::to_string(&expected).unwrap(), )
278 }
279
280 #[test]
281 fn test_can_build_p256_jwk() {
282 let (alg, bytes) = decode_multibase_key("zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169");
283 assert_eq!(alg, P256);
284
285 let uncompressed = alg.uncompress_public_key(&bytes).unwrap();
286 assert_eq!(uncompressed.len(), 65);
287
288 let jwk = alg.build_jwk(&bytes).unwrap();
289 let expected: Value = serde_json::from_str(
290 r#"{
291 "kty": "EC",
292 "crv": "P-256",
293 "x": "fyNYMN0976ci7xqiSdag3buk-ZCwgXU4kz9XNkBlNUI",
294 "y": "hW2ojTNfH7Jbi8--CJUo3OCbH3y5n91g-IMA9MLMbTU"
295 }"#,
296 )
297 .unwrap();
298
299 assert_eq!(
300 json_canon::to_string(&jwk).unwrap(), json_canon::to_string(&expected).unwrap(), )
303 }
304
305 #[test]
306 fn test_cannot_build_unsupported_jwk() {
307 let multibase_keys = [
308 (
309 BLS12381, concat!(
311 "zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAk",
312 "ERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY"
313 ),
314 ),
315 (
316 P384, "z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9",
318 ),
319 (
320 P521, "z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7",
322 ),
323 (
324 RSA, concat!(
326 "z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsu",
327 "VNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC",
328 "6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6",
329 "CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMN",
330 "UYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i"
331 ),
332 ),
333 ];
334
335 for (expected_alg, multibase_key) in multibase_keys {
336 let (alg, bytes) = decode_multibase_key(multibase_key);
337 assert_eq!(alg, expected_alg);
338 assert!(matches!(alg.build_jwk(&bytes).unwrap_err(), CryptoError::Unsupported));
339 }
340 }
341
342 #[test]
343 fn test_key_decompression_fails_on_invalid_key_length() {
344 let bytes = hex::decode("023d4de48a477e309548a0ed8ceee086d1aaeceb11f0a8e3a0ffb3e5f44602de1800").unwrap();
345 let uncompressed = P256.uncompress_public_key(&bytes);
346 assert!(matches!(uncompressed.unwrap_err(), CryptoError::InvalidKeyLength));
347 }
348
349 #[test]
350 fn test_key_decompression_fails_on_invalid_sign_byte() {
351 let bytes = hex::decode("113d4de48a477e309548a0ed8ceee086d1aaeceb11f0a8e3a0ffb3e5f44602de18").unwrap();
352 let uncompressed = P256.uncompress_public_key(&bytes);
353 assert!(matches!(uncompressed.unwrap_err(), CryptoError::InvalidPublicKey));
354 }
355
356 fn decode_multibase_key(key: &str) -> (Algorithm, Vec<u8>) {
357 let (_, multicodec) = multibase::decode(key).unwrap();
358
359 let prefix: &[u8; 2] = &multicodec[..2].try_into().unwrap();
360 let bytes = &multicodec[2..];
361
362 (Algorithm::from_muticodec_prefix(prefix).unwrap(), bytes.to_vec())
363 }
364
365 #[test]
366 fn test_decode_multikey() {
367 let multikey = "z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp";
368 let (alg, bytes) = decode_multikey(multikey).unwrap();
369 assert_eq!(alg, Algorithm::Ed25519);
370 assert_eq!(bytes, Base64Url.decode("O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik").unwrap());
371
372 let multikey = "z6LSbuUXWSgPfpiDBjUK6E7yiCKMN2eKJsXn5b55ZgqGz6Mr";
373 let (alg, bytes) = decode_multikey(multikey).unwrap();
374 assert_eq!(alg, Algorithm::X25519);
375 assert_eq!(bytes, Base64Url.decode("A2gufB762KKDkbTX0usDbekRJ-_PPBeVhc2gNgjpswU").unwrap());
376 }
377
378 #[test]
379 fn test_decode_multikey_negative_cases() {
380 let cases = [
381 (
382 "z#6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWpd", DecodeMultikeyError::MultibaseDecodeError,
384 ),
385 (
386 "Z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK", DecodeMultikeyError::NotBase58MultibaseEncoded,
388 ),
389 (
390 "z6", DecodeMultikeyError::MulticodecTooShort,
392 ),
393 (
394 "z7MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWpd", DecodeMultikeyError::UnknownAlgorithm,
396 ),
397 ];
398
399 for (multikey, expected_err) in cases {
400 let err = decode_multikey(multikey).unwrap_err();
401 assert_eq!(err, expected_err);
402 }
403 }
404}