]> git.proxmox.com Git - rustc.git/blob - vendor/elliptic-curve/src/jwk.rs
New upstream version 1.71.1+dfsg1
[rustc.git] / vendor / elliptic-curve / src / jwk.rs
1 //! JSON Web Key (JWK) Support.
2 //!
3 //! Specified in RFC 7518 Section 6: Cryptographic Algorithms for Keys:
4 //! <https://tools.ietf.org/html/rfc7518#section-6>
5
6 use crate::{
7 sec1::{Coordinates, EncodedPoint, ModulusSize, ValidatePublicKey},
8 secret_key::SecretKey,
9 Curve, Error, FieldBytes, FieldBytesSize, Result,
10 };
11 use alloc::{
12 borrow::ToOwned,
13 format,
14 string::{String, ToString},
15 };
16 use base64ct::{Base64UrlUnpadded as Base64Url, Encoding};
17 use core::{
18 fmt::{self, Debug},
19 marker::PhantomData,
20 str::{self, FromStr},
21 };
22 use serdect::serde::{de, ser, Deserialize, Serialize};
23 use zeroize::{Zeroize, ZeroizeOnDrop};
24
25 #[cfg(feature = "arithmetic")]
26 use crate::{
27 public_key::PublicKey,
28 sec1::{FromEncodedPoint, ToEncodedPoint},
29 AffinePoint, CurveArithmetic,
30 };
31
32 /// Key Type (`kty`) for elliptic curve keys.
33 pub const EC_KTY: &str = "EC";
34
35 /// Deserialization error message.
36 const DE_ERROR_MSG: &str = "struct JwkEcKey with 5 elements";
37
38 /// Name of the JWK type
39 const JWK_TYPE_NAME: &str = "JwkEcKey";
40
41 /// Field names
42 const FIELDS: &[&str] = &["kty", "crv", "x", "y", "d"];
43
44 /// Elliptic curve parameters used by JSON Web Keys.
45 pub trait JwkParameters: Curve {
46 /// The `crv` parameter which identifies a particular elliptic curve
47 /// as defined in RFC 7518 Section 6.2.1.1:
48 /// <https://tools.ietf.org/html/rfc7518#section-6.2.1.1>
49 ///
50 /// Curve values are registered in the IANA "JSON Web Key Elliptic Curve"
51 /// registry defined in RFC 7518 Section 7.6:
52 /// <https://tools.ietf.org/html/rfc7518#section-7.6>
53 const CRV: &'static str;
54 }
55
56 /// JSON Web Key (JWK) with a `kty` of `"EC"` (elliptic curve).
57 ///
58 /// Specified in [RFC 7518 Section 6: Cryptographic Algorithms for Keys][1].
59 ///
60 /// This type can represent either a public/private keypair, or just a
61 /// public key, depending on whether or not the `d` parameter is present.
62 ///
63 /// [1]: https://tools.ietf.org/html/rfc7518#section-6
64 // TODO(tarcieri): eagerly decode or validate `x`, `y`, and `d` as Base64
65 #[derive(Clone)]
66 pub struct JwkEcKey {
67 /// The `crv` parameter which identifies a particular elliptic curve
68 /// as defined in RFC 7518 Section 6.2.1.1:
69 /// <https://tools.ietf.org/html/rfc7518#section-6.2.1.1>
70 crv: String,
71
72 /// The x-coordinate of the elliptic curve point which is the public key
73 /// value associated with this JWK as defined in RFC 7518 6.2.1.2:
74 /// <https://tools.ietf.org/html/rfc7518#section-6.2.1.2>
75 x: String,
76
77 /// The y-coordinate of the elliptic curve point which is the public key
78 /// value associated with this JWK as defined in RFC 7518 6.2.1.3:
79 /// <https://tools.ietf.org/html/rfc7518#section-6.2.1.3>
80 y: String,
81
82 /// The `d` ECC private key parameter as described in RFC 7518 6.2.2.1:
83 /// <https://tools.ietf.org/html/rfc7518#section-6.2.2.1>
84 ///
85 /// Value is optional and if omitted, this JWK represents a private key.
86 ///
87 /// Inner value is encoded according to the `Integer-to-Octet-String`
88 /// conversion as defined in SEC1 section 2.3.7:
89 /// <https://www.secg.org/sec1-v2.pdf>
90 d: Option<String>,
91 }
92
93 impl JwkEcKey {
94 /// Get the `crv` parameter for this JWK.
95 pub fn crv(&self) -> &str {
96 &self.crv
97 }
98
99 /// Is this JWK a keypair that includes a private key?
100 pub fn is_keypair(&self) -> bool {
101 self.d.is_some()
102 }
103
104 /// Does this JWK contain only a public key?
105 pub fn is_public_key(&self) -> bool {
106 self.d.is_none()
107 }
108
109 /// Decode a JWK into a [`PublicKey`].
110 #[cfg(feature = "arithmetic")]
111 pub fn to_public_key<C>(&self) -> Result<PublicKey<C>>
112 where
113 C: CurveArithmetic + JwkParameters,
114 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
115 FieldBytesSize<C>: ModulusSize,
116 {
117 PublicKey::from_sec1_bytes(self.to_encoded_point::<C>()?.as_bytes())
118 }
119
120 /// Create a JWK from a SEC1 [`EncodedPoint`].
121 pub fn from_encoded_point<C>(point: &EncodedPoint<C>) -> Option<Self>
122 where
123 C: Curve + JwkParameters,
124 FieldBytesSize<C>: ModulusSize,
125 {
126 match point.coordinates() {
127 Coordinates::Uncompressed { x, y } => Some(JwkEcKey {
128 crv: C::CRV.to_owned(),
129 x: Base64Url::encode_string(x),
130 y: Base64Url::encode_string(y),
131 d: None,
132 }),
133 _ => None,
134 }
135 }
136
137 /// Get the public key component of this JWK as a SEC1 [`EncodedPoint`].
138 pub fn to_encoded_point<C>(&self) -> Result<EncodedPoint<C>>
139 where
140 C: Curve + JwkParameters,
141 FieldBytesSize<C>: ModulusSize,
142 {
143 if self.crv != C::CRV {
144 return Err(Error);
145 }
146
147 let x = decode_base64url_fe::<C>(&self.x)?;
148 let y = decode_base64url_fe::<C>(&self.y)?;
149 Ok(EncodedPoint::<C>::from_affine_coordinates(&x, &y, false))
150 }
151
152 /// Decode a JWK into a [`SecretKey`].
153 #[cfg(feature = "arithmetic")]
154 pub fn to_secret_key<C>(&self) -> Result<SecretKey<C>>
155 where
156 C: Curve + JwkParameters + ValidatePublicKey,
157 FieldBytesSize<C>: ModulusSize,
158 {
159 self.try_into()
160 }
161 }
162
163 impl FromStr for JwkEcKey {
164 type Err = Error;
165
166 fn from_str(s: &str) -> Result<Self> {
167 serde_json::from_str(s).map_err(|_| Error)
168 }
169 }
170
171 impl ToString for JwkEcKey {
172 fn to_string(&self) -> String {
173 serde_json::to_string(self).expect("JWK encoding error")
174 }
175 }
176
177 impl<C> TryFrom<JwkEcKey> for SecretKey<C>
178 where
179 C: Curve + JwkParameters + ValidatePublicKey,
180 FieldBytesSize<C>: ModulusSize,
181 {
182 type Error = Error;
183
184 fn try_from(jwk: JwkEcKey) -> Result<SecretKey<C>> {
185 (&jwk).try_into()
186 }
187 }
188
189 impl<C> TryFrom<&JwkEcKey> for SecretKey<C>
190 where
191 C: Curve + JwkParameters + ValidatePublicKey,
192 FieldBytesSize<C>: ModulusSize,
193 {
194 type Error = Error;
195
196 fn try_from(jwk: &JwkEcKey) -> Result<SecretKey<C>> {
197 if let Some(d_base64) = &jwk.d {
198 let pk = jwk.to_encoded_point::<C>()?;
199 let mut d_bytes = decode_base64url_fe::<C>(d_base64)?;
200 let result = SecretKey::from_slice(&d_bytes);
201 d_bytes.zeroize();
202
203 result.and_then(|secret_key| {
204 C::validate_public_key(&secret_key, &pk)?;
205 Ok(secret_key)
206 })
207 } else {
208 Err(Error)
209 }
210 }
211 }
212
213 #[cfg(feature = "arithmetic")]
214 impl<C> From<SecretKey<C>> for JwkEcKey
215 where
216 C: CurveArithmetic + JwkParameters,
217 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
218 FieldBytesSize<C>: ModulusSize,
219 {
220 fn from(sk: SecretKey<C>) -> JwkEcKey {
221 (&sk).into()
222 }
223 }
224
225 #[cfg(feature = "arithmetic")]
226 impl<C> From<&SecretKey<C>> for JwkEcKey
227 where
228 C: CurveArithmetic + JwkParameters,
229 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
230 FieldBytesSize<C>: ModulusSize,
231 {
232 fn from(sk: &SecretKey<C>) -> JwkEcKey {
233 let mut jwk = sk.public_key().to_jwk();
234 let mut d = sk.to_bytes();
235 jwk.d = Some(Base64Url::encode_string(&d));
236 d.zeroize();
237 jwk
238 }
239 }
240
241 #[cfg(feature = "arithmetic")]
242 impl<C> TryFrom<JwkEcKey> for PublicKey<C>
243 where
244 C: CurveArithmetic + JwkParameters,
245 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
246 FieldBytesSize<C>: ModulusSize,
247 {
248 type Error = Error;
249
250 fn try_from(jwk: JwkEcKey) -> Result<PublicKey<C>> {
251 (&jwk).try_into()
252 }
253 }
254
255 #[cfg(feature = "arithmetic")]
256 impl<C> TryFrom<&JwkEcKey> for PublicKey<C>
257 where
258 C: CurveArithmetic + JwkParameters,
259 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
260 FieldBytesSize<C>: ModulusSize,
261 {
262 type Error = Error;
263
264 fn try_from(jwk: &JwkEcKey) -> Result<PublicKey<C>> {
265 PublicKey::from_sec1_bytes(jwk.to_encoded_point::<C>()?.as_bytes())
266 }
267 }
268
269 #[cfg(feature = "arithmetic")]
270 impl<C> From<PublicKey<C>> for JwkEcKey
271 where
272 C: CurveArithmetic + JwkParameters,
273 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
274 FieldBytesSize<C>: ModulusSize,
275 {
276 fn from(pk: PublicKey<C>) -> JwkEcKey {
277 (&pk).into()
278 }
279 }
280
281 #[cfg(feature = "arithmetic")]
282 impl<C> From<&PublicKey<C>> for JwkEcKey
283 where
284 C: CurveArithmetic + JwkParameters,
285 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
286 FieldBytesSize<C>: ModulusSize,
287 {
288 fn from(pk: &PublicKey<C>) -> JwkEcKey {
289 Self::from_encoded_point::<C>(&pk.to_encoded_point(false)).expect("JWK encoding error")
290 }
291 }
292
293 impl Debug for JwkEcKey {
294 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295 let d = if self.d.is_some() {
296 "Some(...)"
297 } else {
298 "None"
299 };
300
301 // NOTE: this implementation omits the `d` private key parameter
302 f.debug_struct(JWK_TYPE_NAME)
303 .field("crv", &self.crv)
304 .field("x", &self.x)
305 .field("y", &self.y)
306 .field("d", &d)
307 .finish()
308 }
309 }
310
311 impl PartialEq for JwkEcKey {
312 fn eq(&self, other: &Self) -> bool {
313 use subtle::ConstantTimeEq;
314
315 // Compare private key in constant time
316 let d_eq = match &self.d {
317 Some(d1) => match &other.d {
318 Some(d2) => d1.as_bytes().ct_eq(d2.as_bytes()).into(),
319 None => other.d.is_none(),
320 },
321 None => other.d.is_none(),
322 };
323
324 self.crv == other.crv && self.x == other.x && self.y == other.y && d_eq
325 }
326 }
327
328 impl Eq for JwkEcKey {}
329
330 impl ZeroizeOnDrop for JwkEcKey {}
331
332 impl Drop for JwkEcKey {
333 fn drop(&mut self) {
334 self.zeroize();
335 }
336 }
337
338 impl Zeroize for JwkEcKey {
339 fn zeroize(&mut self) {
340 if let Some(d) = &mut self.d {
341 d.zeroize();
342 }
343 }
344 }
345
346 impl<'de> Deserialize<'de> for JwkEcKey {
347 fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
348 where
349 D: de::Deserializer<'de>,
350 {
351 /// Field positions
352 enum Field {
353 Kty,
354 Crv,
355 X,
356 Y,
357 D,
358 }
359
360 /// Field visitor
361 struct FieldVisitor;
362
363 impl<'de> de::Visitor<'de> for FieldVisitor {
364 type Value = Field;
365
366 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
367 fmt::Formatter::write_str(formatter, "field identifier")
368 }
369
370 fn visit_u64<E>(self, value: u64) -> core::result::Result<Self::Value, E>
371 where
372 E: de::Error,
373 {
374 match value {
375 0 => Ok(Field::Kty),
376 1 => Ok(Field::Crv),
377 2 => Ok(Field::X),
378 3 => Ok(Field::Y),
379 4 => Ok(Field::D),
380 _ => Err(de::Error::invalid_value(
381 de::Unexpected::Unsigned(value),
382 &"field index 0 <= i < 5",
383 )),
384 }
385 }
386
387 fn visit_str<E>(self, value: &str) -> core::result::Result<Self::Value, E>
388 where
389 E: de::Error,
390 {
391 self.visit_bytes(value.as_bytes())
392 }
393
394 fn visit_bytes<E>(self, value: &[u8]) -> core::result::Result<Self::Value, E>
395 where
396 E: de::Error,
397 {
398 match value {
399 b"kty" => Ok(Field::Kty),
400 b"crv" => Ok(Field::Crv),
401 b"x" => Ok(Field::X),
402 b"y" => Ok(Field::Y),
403 b"d" => Ok(Field::D),
404 _ => Err(de::Error::unknown_field(
405 &String::from_utf8_lossy(value),
406 FIELDS,
407 )),
408 }
409 }
410 }
411
412 impl<'de> Deserialize<'de> for Field {
413 #[inline]
414 fn deserialize<D>(__deserializer: D) -> core::result::Result<Self, D::Error>
415 where
416 D: de::Deserializer<'de>,
417 {
418 de::Deserializer::deserialize_identifier(__deserializer, FieldVisitor)
419 }
420 }
421
422 struct Visitor<'de> {
423 marker: PhantomData<JwkEcKey>,
424 lifetime: PhantomData<&'de ()>,
425 }
426
427 impl<'de> de::Visitor<'de> for Visitor<'de> {
428 type Value = JwkEcKey;
429
430 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
431 fmt::Formatter::write_str(formatter, "struct JwkEcKey")
432 }
433
434 #[inline]
435 fn visit_seq<A>(self, mut seq: A) -> core::result::Result<Self::Value, A::Error>
436 where
437 A: de::SeqAccess<'de>,
438 {
439 let kty = de::SeqAccess::next_element::<String>(&mut seq)?
440 .ok_or_else(|| de::Error::invalid_length(0, &DE_ERROR_MSG))?;
441
442 if kty != EC_KTY {
443 return Err(de::Error::custom(format!("unsupported JWK kty: {kty:?}")));
444 }
445
446 let crv = de::SeqAccess::next_element::<String>(&mut seq)?
447 .ok_or_else(|| de::Error::invalid_length(1, &DE_ERROR_MSG))?;
448
449 let x = de::SeqAccess::next_element::<String>(&mut seq)?
450 .ok_or_else(|| de::Error::invalid_length(2, &DE_ERROR_MSG))?;
451
452 let y = de::SeqAccess::next_element::<String>(&mut seq)?
453 .ok_or_else(|| de::Error::invalid_length(3, &DE_ERROR_MSG))?;
454
455 let d = de::SeqAccess::next_element::<Option<String>>(&mut seq)?
456 .ok_or_else(|| de::Error::invalid_length(4, &DE_ERROR_MSG))?;
457
458 Ok(JwkEcKey { crv, x, y, d })
459 }
460
461 #[inline]
462 fn visit_map<A>(self, mut map: A) -> core::result::Result<Self::Value, A::Error>
463 where
464 A: de::MapAccess<'de>,
465 {
466 let mut kty: Option<String> = None;
467 let mut crv: Option<String> = None;
468 let mut x: Option<String> = None;
469 let mut y: Option<String> = None;
470 let mut d: Option<String> = None;
471
472 while let Some(key) = de::MapAccess::next_key::<Field>(&mut map)? {
473 match key {
474 Field::Kty => {
475 if kty.is_none() {
476 kty = Some(de::MapAccess::next_value::<String>(&mut map)?);
477 } else {
478 return Err(de::Error::duplicate_field(FIELDS[0]));
479 }
480 }
481 Field::Crv => {
482 if crv.is_none() {
483 crv = Some(de::MapAccess::next_value::<String>(&mut map)?);
484 } else {
485 return Err(de::Error::duplicate_field(FIELDS[1]));
486 }
487 }
488 Field::X => {
489 if x.is_none() {
490 x = Some(de::MapAccess::next_value::<String>(&mut map)?);
491 } else {
492 return Err(de::Error::duplicate_field(FIELDS[2]));
493 }
494 }
495 Field::Y => {
496 if y.is_none() {
497 y = Some(de::MapAccess::next_value::<String>(&mut map)?);
498 } else {
499 return Err(de::Error::duplicate_field(FIELDS[3]));
500 }
501 }
502 Field::D => {
503 if d.is_none() {
504 d = de::MapAccess::next_value::<Option<String>>(&mut map)?;
505 } else {
506 return Err(de::Error::duplicate_field(FIELDS[4]));
507 }
508 }
509 }
510 }
511
512 let kty = kty.ok_or_else(|| de::Error::missing_field("kty"))?;
513
514 if kty != EC_KTY {
515 return Err(de::Error::custom(format!("unsupported JWK kty: {kty}")));
516 }
517
518 let crv = crv.ok_or_else(|| de::Error::missing_field("crv"))?;
519 let x = x.ok_or_else(|| de::Error::missing_field("x"))?;
520 let y = y.ok_or_else(|| de::Error::missing_field("y"))?;
521
522 Ok(JwkEcKey { crv, x, y, d })
523 }
524 }
525
526 de::Deserializer::deserialize_struct(
527 deserializer,
528 JWK_TYPE_NAME,
529 FIELDS,
530 Visitor {
531 marker: PhantomData::<JwkEcKey>,
532 lifetime: PhantomData,
533 },
534 )
535 }
536 }
537
538 impl Serialize for JwkEcKey {
539 fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
540 where
541 S: ser::Serializer,
542 {
543 use ser::SerializeStruct;
544
545 let mut state = serializer.serialize_struct(JWK_TYPE_NAME, 5)?;
546
547 for (i, field) in [EC_KTY, &self.crv, &self.x, &self.y].iter().enumerate() {
548 state.serialize_field(FIELDS[i], field)?;
549 }
550
551 if let Some(d) = &self.d {
552 state.serialize_field("d", d)?;
553 }
554
555 ser::SerializeStruct::end(state)
556 }
557 }
558
559 /// Decode a Base64url-encoded field element
560 fn decode_base64url_fe<C: Curve>(s: &str) -> Result<FieldBytes<C>> {
561 let mut result = FieldBytes::<C>::default();
562 Base64Url::decode(s, &mut result).map_err(|_| Error)?;
563 Ok(result)
564 }
565
566 #[cfg(test)]
567 mod tests {
568 use super::*;
569
570 #[cfg(feature = "dev")]
571 use crate::dev::MockCurve;
572
573 /// Example private key. From RFC 7518 Appendix C:
574 /// <https://tools.ietf.org/html/rfc7518#appendix-C>
575 const JWK_PRIVATE_KEY: &str = r#"
576 {
577 "kty":"EC",
578 "crv":"P-256",
579 "x":"gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0",
580 "y":"SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps",
581 "d":"0_NxaRPUMQoAJt50Gz8YiTr8gRTwyEaCumd-MToTmIo"
582 }
583 "#;
584
585 /// Example public key.
586 const JWK_PUBLIC_KEY: &str = r#"
587 {
588 "kty":"EC",
589 "crv":"P-256",
590 "x":"gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0",
591 "y":"SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps"
592 }
593 "#;
594
595 /// Example unsupported JWK (RSA key)
596 const UNSUPPORTED_JWK: &str = r#"
597 {
598 "kty":"RSA",
599 "kid":"cc34c0a0-bd5a-4a3c-a50d-a2a7db7643df",
600 "use":"sig",
601 "n":"pjdss8ZaDfEH6K6U7GeW2nxDqR4IP049fk1fK0lndimbMMVBdPv_hSpm8T8EtBDxrUdi1OHZfMhUixGaut-3nQ4GG9nM249oxhCtxqqNvEXrmQRGqczyLxuh-fKn9Fg--hS9UpazHpfVAFnB5aCfXoNhPuI8oByyFKMKaOVgHNqP5NBEqabiLftZD3W_lsFCPGuzr4Vp0YS7zS2hDYScC2oOMu4rGU1LcMZf39p3153Cq7bS2Xh6Y-vw5pwzFYZdjQxDn8x8BG3fJ6j8TGLXQsbKH1218_HcUJRvMwdpbUQG5nvA2GXVqLqdwp054Lzk9_B_f1lVrmOKuHjTNHq48w",
602 "e":"AQAB",
603 "d":"ksDmucdMJXkFGZxiomNHnroOZxe8AmDLDGO1vhs-POa5PZM7mtUPonxwjVmthmpbZzla-kg55OFfO7YcXhg-Hm2OWTKwm73_rLh3JavaHjvBqsVKuorX3V3RYkSro6HyYIzFJ1Ek7sLxbjDRcDOj4ievSX0oN9l-JZhaDYlPlci5uJsoqro_YrE0PRRWVhtGynd-_aWgQv1YzkfZuMD-hJtDi1Im2humOWxA4eZrFs9eG-whXcOvaSwO4sSGbS99ecQZHM2TcdXeAs1PvjVgQ_dKnZlGN3lTWoWfQP55Z7Tgt8Nf1q4ZAKd-NlMe-7iqCFfsnFwXjSiaOa2CRGZn-Q",
604 "p":"4A5nU4ahEww7B65yuzmGeCUUi8ikWzv1C81pSyUKvKzu8CX41hp9J6oRaLGesKImYiuVQK47FhZ--wwfpRwHvSxtNU9qXb8ewo-BvadyO1eVrIk4tNV543QlSe7pQAoJGkxCia5rfznAE3InKF4JvIlchyqs0RQ8wx7lULqwnn0",
605 "q":"ven83GM6SfrmO-TBHbjTk6JhP_3CMsIvmSdo4KrbQNvp4vHO3w1_0zJ3URkmkYGhz2tgPlfd7v1l2I6QkIh4Bumdj6FyFZEBpxjE4MpfdNVcNINvVj87cLyTRmIcaGxmfylY7QErP8GFA-k4UoH_eQmGKGK44TRzYj5hZYGWIC8",
606 "dp":"lmmU_AG5SGxBhJqb8wxfNXDPJjf__i92BgJT2Vp4pskBbr5PGoyV0HbfUQVMnw977RONEurkR6O6gxZUeCclGt4kQlGZ-m0_XSWx13v9t9DIbheAtgVJ2mQyVDvK4m7aRYlEceFh0PsX8vYDS5o1txgPwb3oXkPTtrmbAGMUBpE",
607 "dq":"mxRTU3QDyR2EnCv0Nl0TCF90oliJGAHR9HJmBe__EjuCBbwHfcT8OG3hWOv8vpzokQPRl5cQt3NckzX3fs6xlJN4Ai2Hh2zduKFVQ2p-AF2p6Yfahscjtq-GY9cB85NxLy2IXCC0PF--Sq9LOrTE9QV988SJy_yUrAjcZ5MmECk",
608 "qi":"ldHXIrEmMZVaNwGzDF9WG8sHj2mOZmQpw9yrjLK9hAsmsNr5LTyqWAqJIYZSwPTYWhY4nu2O0EY9G9uYiqewXfCKw_UngrJt8Xwfq1Zruz0YY869zPN4GiE9-9rzdZB33RBw8kIOquY3MK74FMwCihYx_LiU2YTHkaoJ3ncvtvg"
609 }
610 "#;
611
612 #[test]
613 fn parse_private_key() {
614 let jwk = JwkEcKey::from_str(JWK_PRIVATE_KEY).unwrap();
615 assert_eq!(jwk.crv, "P-256");
616 assert_eq!(jwk.x, "gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0");
617 assert_eq!(jwk.y, "SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps");
618 assert_eq!(
619 jwk.d.as_ref().unwrap(),
620 "0_NxaRPUMQoAJt50Gz8YiTr8gRTwyEaCumd-MToTmIo"
621 );
622 }
623
624 #[test]
625 fn parse_public_key() {
626 let jwk = JwkEcKey::from_str(JWK_PUBLIC_KEY).unwrap();
627 assert_eq!(jwk.crv, "P-256");
628 assert_eq!(jwk.x, "gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0");
629 assert_eq!(jwk.y, "SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps");
630 assert_eq!(jwk.d, None);
631 }
632
633 #[test]
634 fn parse_unsupported() {
635 assert_eq!(JwkEcKey::from_str(UNSUPPORTED_JWK), Err(Error));
636 }
637
638 #[test]
639 fn serialize_private_key() {
640 let actual = JwkEcKey::from_str(JWK_PRIVATE_KEY).unwrap().to_string();
641 let expected: String = JWK_PRIVATE_KEY.split_whitespace().collect();
642 assert_eq!(actual, expected);
643 }
644
645 #[test]
646 fn serialize_public_key() {
647 let actual = JwkEcKey::from_str(JWK_PUBLIC_KEY).unwrap().to_string();
648 let expected: String = JWK_PUBLIC_KEY.split_whitespace().collect();
649 assert_eq!(actual, expected);
650 }
651
652 #[cfg(feature = "dev")]
653 #[test]
654 fn jwk_into_encoded_point() {
655 let jwk = JwkEcKey::from_str(JWK_PUBLIC_KEY).unwrap();
656 let point = jwk.to_encoded_point::<MockCurve>().unwrap();
657 let (x, y) = match point.coordinates() {
658 Coordinates::Uncompressed { x, y } => (x, y),
659 other => panic!("unexpected coordinates: {:?}", other),
660 };
661
662 assert_eq!(&decode_base64url_fe::<MockCurve>(&jwk.x).unwrap(), x);
663 assert_eq!(&decode_base64url_fe::<MockCurve>(&jwk.y).unwrap(), y);
664 }
665
666 #[cfg(feature = "dev")]
667 #[test]
668 fn encoded_point_into_jwk() {
669 let jwk = JwkEcKey::from_str(JWK_PUBLIC_KEY).unwrap();
670 let point = jwk.to_encoded_point::<MockCurve>().unwrap();
671 let jwk2 = JwkEcKey::from_encoded_point::<MockCurve>(&point).unwrap();
672 assert_eq!(jwk, jwk2);
673 }
674 }