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 => {
133 let (n, e) = pkcs1::decode(&raw)?;
134 Self::from_rsa_public(&n, &e)
135 }
136 }
137 }
138
139 pub fn to_multikey(&self) -> Result<String, JwkError> {
150 let (codec, raw) = match (self.kty(), self.crv()) {
151 ("OKP", Some("Ed25519")) => (KeyCodec::Ed25519, self.ed25519_public_bytes()?.to_vec()),
152 ("OKP", Some("X25519")) => (KeyCodec::X25519, self.x25519_public_bytes()?.to_vec()),
153 ("EC", Some("P-256")) => {
154 use p256::elliptic_curve::sec1::ToSec1Point as _;
155 let point = self.p256_public_key()?.to_sec1_point(true);
156 (KeyCodec::P256, point.as_bytes().to_vec())
157 }
158 #[cfg(feature = "secp256k1")]
159 ("EC", Some("secp256k1")) => {
160 use k256::elliptic_curve::sec1::ToSec1Point as _;
161 let point = self.secp256k1_public_key()?.to_sec1_point(true);
162 (KeyCodec::Secp256k1, point.as_bytes().to_vec())
163 }
164 ("RSA", _) => {
165 let (n, e) = self.rsa_public_components()?;
166 (KeyCodec::Rsa, pkcs1::encode(&n, &e))
167 }
168 _ => return Err(self.unsupported()),
169 };
170 Ok(encode(codec, &raw))
171 }
172}
173
174pub(crate) fn encode(codec: KeyCodec, raw: &[u8]) -> String {
176 let mut bytes = varint::encode(codec.code());
177 bytes.extend_from_slice(raw);
178 format!("z{}", bs58::encode(bytes).into_string())
179}
180
181pub(crate) fn decode(multikey: &str) -> Result<(KeyCodec, Vec<u8>), JwkError> {
183 let Some(base58) = multikey.strip_prefix('z') else {
184 return Err(JwkError::InvalidMultikey(
185 "expected the base58btc multibase prefix `z`".to_owned(),
186 ));
187 };
188 let bytes = bs58::decode(base58)
189 .into_vec()
190 .map_err(|e| JwkError::InvalidMultikey(format!("invalid base58btc: {e}")))?;
191 let (code, header_len) = varint::decode(&bytes)?;
192 let codec = KeyCodec::from_code(code).ok_or(JwkError::UnsupportedMulticodec(code))?;
193 Ok((codec, bytes[header_len..].to_vec()))
194}
195
196mod varint {
198 use crate::jose::JwkError;
199
200 const MAX_LEN: usize = 9;
202
203 pub(super) fn encode(mut value: u64) -> Vec<u8> {
204 let mut out = Vec::with_capacity(2);
205 loop {
206 let byte = (value & 0x7f) as u8;
207 value >>= 7;
208 if value == 0 {
209 out.push(byte);
210 return out;
211 }
212 out.push(byte | 0x80);
213 }
214 }
215
216 pub(super) fn decode(bytes: &[u8]) -> Result<(u64, usize), JwkError> {
218 let mut value = 0u64;
219 for (i, &byte) in bytes.iter().enumerate().take(MAX_LEN) {
220 value |= u64::from(byte & 0x7f) << (7 * i);
221 if byte & 0x80 == 0 {
222 if i > 0 && byte == 0 {
223 return Err(JwkError::InvalidMultikey(
224 "multicodec header is not minimally encoded".to_owned(),
225 ));
226 }
227 return Ok((value, i + 1));
228 }
229 }
230 Err(JwkError::InvalidMultikey(
231 "multicodec header is truncated or too long".to_owned(),
232 ))
233 }
234}
235
236mod pkcs1 {
239 use crate::jose::JwkError;
240
241 const SEQUENCE: u8 = 0x30;
242 const INTEGER: u8 = 0x02;
243
244 pub(super) fn encode(n: &[u8], e: &[u8]) -> Vec<u8> {
245 let mut body = integer(n);
246 body.extend(integer(e));
247 tlv(SEQUENCE, &body)
248 }
249
250 pub(super) fn decode(der: &[u8]) -> Result<(Vec<u8>, Vec<u8>), JwkError> {
252 let (body, rest) = read_tlv(der, SEQUENCE)?;
253 if !rest.is_empty() {
254 return Err(invalid("trailing bytes after RSAPublicKey"));
255 }
256 let (n, body) = read_tlv(body, INTEGER)?;
257 let (e, body) = read_tlv(body, INTEGER)?;
258 if !body.is_empty() {
259 return Err(invalid("trailing bytes inside RSAPublicKey"));
260 }
261 Ok((unsigned(n)?.to_vec(), unsigned(e)?.to_vec()))
262 }
263
264 fn integer(value: &[u8]) -> Vec<u8> {
266 let first = value.iter().position(|&b| b != 0).unwrap_or(value.len());
267 let trimmed = &value[first..];
268 let mut body = Vec::with_capacity(trimmed.len() + 1);
269 if trimmed.first().is_none_or(|&b| b & 0x80 != 0) {
270 body.push(0);
271 }
272 body.extend_from_slice(trimmed);
273 tlv(INTEGER, &body)
274 }
275
276 fn tlv(tag: u8, body: &[u8]) -> Vec<u8> {
277 let mut out = vec![tag];
278 let len = body.len();
279 if len < 0x80 {
280 out.push(len as u8);
281 } else {
282 let be = len.to_be_bytes();
283 let first = be.iter().position(|&b| b != 0).unwrap_or(be.len() - 1);
284 out.push(0x80 | (be.len() - first) as u8);
285 out.extend_from_slice(&be[first..]);
286 }
287 out.extend_from_slice(body);
288 out
289 }
290
291 fn read_tlv(der: &[u8], tag: u8) -> Result<(&[u8], &[u8]), JwkError> {
293 let (&found, rest) = der.split_first().ok_or_else(|| invalid("unexpected end"))?;
294 if found != tag {
295 return Err(invalid("unexpected ASN.1 tag"));
296 }
297 let (&first, rest) = rest
298 .split_first()
299 .ok_or_else(|| invalid("unexpected end"))?;
300 let (len, rest) = if first < 0x80 {
301 (usize::from(first), rest)
302 } else {
303 let count = usize::from(first & 0x7f);
304 if count == 0 || count > size_of::<usize>() || rest.len() < count {
305 return Err(invalid("invalid length"));
306 }
307 let (len_bytes, rest) = rest.split_at(count);
308 if len_bytes[0] == 0 || (count == 1 && len_bytes[0] < 0x80) {
309 return Err(invalid("length is not minimally encoded"));
310 }
311 let len = len_bytes
312 .iter()
313 .fold(0usize, |acc, &b| (acc << 8) | usize::from(b));
314 (len, rest)
315 };
316 if rest.len() < len {
317 return Err(invalid("length exceeds input"));
318 }
319 Ok(rest.split_at(len))
320 }
321
322 fn unsigned(body: &[u8]) -> Result<&[u8], JwkError> {
324 match body {
325 [] => Err(invalid("empty INTEGER")),
326 [first, ..] if first & 0x80 != 0 => Err(invalid("negative INTEGER")),
327 [0, second, ..] if second & 0x80 == 0 => Err(invalid("INTEGER has a leading zero")),
328 [0, rest @ ..] => Ok(rest),
329 _ => Ok(body),
330 }
331 }
332
333 fn invalid(reason: &str) -> JwkError {
334 JwkError::InvalidKey {
335 key: "RSA",
336 reason: format!("malformed RSAPublicKey: {reason}"),
337 }
338 }
339}
340
341#[cfg(test)]
342mod tests {
343 use super::*;
344
345 #[test]
346 fn varint_round_trips_multicodec_headers() {
347 assert_eq!(varint::encode(0xed), [0xed, 0x01]);
349 assert_eq!(varint::encode(0x1200), [0x80, 0x24]);
350 assert_eq!(varint::encode(0x1205), [0x85, 0x24]);
351 assert_eq!(varint::encode(0x7f), [0x7f]);
352 for code in [0u64, 1, 0x7f, 0x80, 0xed, 0x1200, 0x1205, u64::MAX >> 1] {
353 let encoded = varint::encode(code);
354 assert_eq!(varint::decode(&encoded).unwrap(), (code, encoded.len()));
355 }
356 assert!(varint::decode(&[0x80]).is_err(), "truncated");
357 assert!(varint::decode(&[0xed, 0x80, 0x00]).is_err(), "not minimal");
358 assert!(varint::decode(&[0x80; 10]).is_err(), "too long");
359 }
360
361 #[test]
362 fn pkcs1_round_trips_and_is_minimal() {
363 let n = [0x80, 0x01];
365 let e = [0x01, 0x00, 0x01];
366 let der = pkcs1::encode(&n, &e);
367 assert_eq!(
368 der,
369 [
370 0x30, 0x0a, 0x02, 0x03, 0x00, 0x80, 0x01, 0x02, 0x03, 0x01, 0x00, 0x01
371 ]
372 );
373 assert_eq!(pkcs1::decode(&der).unwrap(), (n.to_vec(), e.to_vec()));
374 assert_eq!(pkcs1::encode(&[0, 0, 0x80, 0x01], &[0, 1, 0, 1]), der);
376 let big = vec![0x7f; 300];
378 let der = pkcs1::encode(&big, &e);
379 assert_eq!(&der[..4], [0x30, 0x82, 0x01, 0x35]);
381 assert_eq!(pkcs1::decode(&der).unwrap().0, big);
382 for broken in [
383 &[0x30, 0x00][..],
384 &[0x30, 0x04, 0x02, 0x01, 0xff, 0x02][..],
385 &[0x30, 0x06, 0x02, 0x01, 0x80, 0x02, 0x01, 0x01][..],
386 &[0x30, 0x07, 0x02, 0x02, 0x00, 0x01, 0x02, 0x01, 0x01][..],
387 &[0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x00][..],
388 &[0x30, 0x81, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01][..],
389 ] {
390 assert!(pkcs1::decode(broken).is_err(), "{broken:02x?}");
391 }
392 }
393}