1use super::{Jwk, JwkError};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13#[non_exhaustive]
14pub enum KeyCodec {
15 Secp256k1,
17 X25519,
19 Ed25519,
21 P256,
23 Rsa,
25}
26
27impl KeyCodec {
28 pub const fn code(self) -> u64 {
30 match self {
31 Self::Secp256k1 => 0xe7,
32 Self::X25519 => 0xec,
33 Self::Ed25519 => 0xed,
34 Self::P256 => 0x1200,
35 Self::Rsa => 0x1205,
36 }
37 }
38
39 pub const fn name(self) -> &'static str {
41 match self {
42 Self::Secp256k1 => "secp256k1-pub",
43 Self::X25519 => "x25519-pub",
44 Self::Ed25519 => "ed25519-pub",
45 Self::P256 => "p256-pub",
46 Self::Rsa => "rsa-pub",
47 }
48 }
49
50 pub const fn from_code(code: u64) -> Option<Self> {
52 Some(match code {
53 0xe7 => Self::Secp256k1,
54 0xec => Self::X25519,
55 0xed => Self::Ed25519,
56 0x1200 => Self::P256,
57 0x1205 => Self::Rsa,
58 _ => return None,
59 })
60 }
61
62 pub const fn key_len(self) -> Option<usize> {
64 match self {
65 Self::Secp256k1 | Self::P256 => Some(33),
66 Self::X25519 | Self::Ed25519 => Some(32),
67 Self::Rsa => None,
68 }
69 }
70
71 const fn key_name(self) -> &'static str {
72 match self {
73 Self::Secp256k1 => "secp256k1",
74 Self::X25519 => "X25519",
75 Self::Ed25519 => "Ed25519",
76 Self::P256 => "P-256",
77 Self::Rsa => "RSA",
78 }
79 }
80}
81
82impl Jwk {
83 pub fn from_multikey(multikey: &str) -> Result<Self, JwkError> {
111 let (codec, raw) = decode(multikey)?;
112 if let Some(expected) = codec.key_len()
113 && raw.len() != expected
114 {
115 return Err(JwkError::InvalidKeyLength {
116 key: codec.key_name(),
117 expected,
118 actual: raw.len(),
119 });
120 }
121 match codec {
122 KeyCodec::Ed25519 => Self::from_ed25519_public(&raw),
123 KeyCodec::X25519 => Self::from_x25519_public(&raw),
124 KeyCodec::P256 => Self::from_p256_public(&raw),
125 #[cfg(feature = "secp256k1")]
126 KeyCodec::Secp256k1 => Self::from_secp256k1_public(&raw),
127 #[cfg(not(feature = "secp256k1"))]
128 KeyCodec::Secp256k1 => Err(JwkError::FeatureDisabled {
129 key: "secp256k1",
130 feature: "secp256k1",
131 }),
132 KeyCodec::Rsa => Self::from_pkcs1_der(&raw),
133 }
134 }
135
136 pub fn to_multikey(&self) -> Result<String, JwkError> {
147 let (codec, raw) = match (self.kty(), self.crv()) {
148 ("OKP", Some("Ed25519")) => (KeyCodec::Ed25519, self.ed25519_public_bytes()?.to_vec()),
149 ("OKP", Some("X25519")) => (KeyCodec::X25519, self.x25519_public_bytes()?.to_vec()),
150 ("EC", Some("P-256")) => {
151 use p256::elliptic_curve::sec1::ToSec1Point as _;
152 let point = self.p256_public_key()?.to_sec1_point(true);
153 (KeyCodec::P256, point.as_bytes().to_vec())
154 }
155 #[cfg(feature = "secp256k1")]
156 ("EC", Some("secp256k1")) => {
157 use k256::elliptic_curve::sec1::ToSec1Point as _;
158 let point = self.secp256k1_public_key()?.to_sec1_point(true);
159 (KeyCodec::Secp256k1, point.as_bytes().to_vec())
160 }
161 ("RSA", _) => (KeyCodec::Rsa, self.to_pkcs1_der()?),
162 _ => return Err(self.unsupported()),
163 };
164 Ok(encode(codec, &raw))
165 }
166}
167
168pub(crate) fn encode(codec: KeyCodec, raw: &[u8]) -> String {
170 let mut bytes = varint::encode(codec.code());
171 bytes.extend_from_slice(raw);
172 format!("z{}", bs58::encode(bytes).into_string())
173}
174
175pub(crate) fn decode(multikey: &str) -> Result<(KeyCodec, Vec<u8>), JwkError> {
177 let Some(base58) = multikey.strip_prefix('z') else {
178 return Err(JwkError::InvalidMultikey(
179 "expected the base58btc multibase prefix `z`".to_owned(),
180 ));
181 };
182 let bytes = bs58::decode(base58)
183 .into_vec()
184 .map_err(|e| JwkError::InvalidMultikey(format!("invalid base58btc: {e}")))?;
185 let (code, header_len) = varint::decode(&bytes)?;
186 let codec = KeyCodec::from_code(code).ok_or(JwkError::UnsupportedMulticodec(code))?;
187 Ok((codec, bytes[header_len..].to_vec()))
188}
189
190mod varint {
192 use crate::jose::JwkError;
193
194 const MAX_LEN: usize = 9;
196
197 pub(super) fn encode(mut value: u64) -> Vec<u8> {
198 let mut out = Vec::with_capacity(2);
199 loop {
200 let byte = (value & 0x7f) as u8;
201 value >>= 7;
202 if value == 0 {
203 out.push(byte);
204 return out;
205 }
206 out.push(byte | 0x80);
207 }
208 }
209
210 pub(super) fn decode(bytes: &[u8]) -> Result<(u64, usize), JwkError> {
212 let mut value = 0u64;
213 for (i, &byte) in bytes.iter().enumerate().take(MAX_LEN) {
214 value |= u64::from(byte & 0x7f) << (7 * i);
215 if byte & 0x80 == 0 {
216 if i > 0 && byte == 0 {
217 return Err(JwkError::InvalidMultikey(
218 "multicodec header is not minimally encoded".to_owned(),
219 ));
220 }
221 return Ok((value, i + 1));
222 }
223 }
224 Err(JwkError::InvalidMultikey(
225 "multicodec header is truncated or too long".to_owned(),
226 ))
227 }
228}
229
230#[cfg(test)]
231mod tests {
232 use super::*;
233
234 #[test]
235 fn varint_round_trips_multicodec_headers() {
236 assert_eq!(varint::encode(0xed), [0xed, 0x01]);
238 assert_eq!(varint::encode(0x1200), [0x80, 0x24]);
239 assert_eq!(varint::encode(0x1205), [0x85, 0x24]);
240 assert_eq!(varint::encode(0x7f), [0x7f]);
241 for code in [0u64, 1, 0x7f, 0x80, 0xed, 0x1200, 0x1205, u64::MAX >> 1] {
242 let encoded = varint::encode(code);
243 assert_eq!(varint::decode(&encoded).unwrap(), (code, encoded.len()));
244 }
245 assert!(varint::decode(&[0x80]).is_err(), "truncated");
246 assert!(varint::decode(&[0xed, 0x80, 0x00]).is_err(), "not minimal");
247 assert!(varint::decode(&[0x80; 10]).is_err(), "too long");
248 }
249}