1 //! JSON Web Key (JWK) Support.
3 //! Specified in RFC 7518 Section 6: Cryptographic Algorithms for Keys:
4 //! <https://tools.ietf.org/html/rfc7518#section-6>
7 sec1
::{Coordinates, EncodedPoint, ModulusSize, ValidatePublicKey}
,
9 Curve
, Error
, FieldBytes
, FieldBytesSize
, Result
,
14 string
::{String, ToString}
,
16 use base64ct
::{Base64UrlUnpadded as Base64Url, Encoding}
;
22 use serdect
::serde
::{de, ser, Deserialize, Serialize}
;
23 use zeroize
::{Zeroize, ZeroizeOnDrop}
;
25 #[cfg(feature = "arithmetic")]
27 public_key
::PublicKey
,
28 sec1
::{FromEncodedPoint, ToEncodedPoint}
,
29 AffinePoint
, CurveArithmetic
,
32 /// Key Type (`kty`) for elliptic curve keys.
33 pub const EC_KTY
: &str = "EC";
35 /// Deserialization error message.
36 const DE_ERROR_MSG
: &str = "struct JwkEcKey with 5 elements";
38 /// Name of the JWK type
39 const JWK_TYPE_NAME
: &str = "JwkEcKey";
42 const FIELDS
: &[&str] = &["kty", "crv", "x", "y", "d"];
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>
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;
56 /// JSON Web Key (JWK) with a `kty` of `"EC"` (elliptic curve).
58 /// Specified in [RFC 7518 Section 6: Cryptographic Algorithms for Keys][1].
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.
63 /// [1]: https://tools.ietf.org/html/rfc7518#section-6
64 // TODO(tarcieri): eagerly decode or validate `x`, `y`, and `d` as Base64
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>
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>
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>
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>
85 /// Value is optional and if omitted, this JWK represents a private key.
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>
94 /// Get the `crv` parameter for this JWK.
95 pub fn crv(&self) -> &str {
99 /// Is this JWK a keypair that includes a private key?
100 pub fn is_keypair(&self) -> bool
{
104 /// Does this JWK contain only a public key?
105 pub fn is_public_key(&self) -> bool
{
109 /// Decode a JWK into a [`PublicKey`].
110 #[cfg(feature = "arithmetic")]
111 pub fn to_public_key
<C
>(&self) -> Result
<PublicKey
<C
>>
113 C
: CurveArithmetic
+ JwkParameters
,
114 AffinePoint
<C
>: FromEncodedPoint
<C
> + ToEncodedPoint
<C
>,
115 FieldBytesSize
<C
>: ModulusSize
,
117 PublicKey
::from_sec1_bytes(self.to_encoded_point
::<C
>()?
.as_bytes())
120 /// Create a JWK from a SEC1 [`EncodedPoint`].
121 pub fn from_encoded_point
<C
>(point
: &EncodedPoint
<C
>) -> Option
<Self>
123 C
: Curve
+ JwkParameters
,
124 FieldBytesSize
<C
>: ModulusSize
,
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
),
137 /// Get the public key component of this JWK as a SEC1 [`EncodedPoint`].
138 pub fn to_encoded_point
<C
>(&self) -> Result
<EncodedPoint
<C
>>
140 C
: Curve
+ JwkParameters
,
141 FieldBytesSize
<C
>: ModulusSize
,
143 if self.crv
!= C
::CRV
{
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))
152 /// Decode a JWK into a [`SecretKey`].
153 #[cfg(feature = "arithmetic")]
154 pub fn to_secret_key
<C
>(&self) -> Result
<SecretKey
<C
>>
156 C
: Curve
+ JwkParameters
+ ValidatePublicKey
,
157 FieldBytesSize
<C
>: ModulusSize
,
163 impl FromStr
for JwkEcKey
{
166 fn from_str(s
: &str) -> Result
<Self> {
167 serde_json
::from_str(s
).map_err(|_
| Error
)
171 impl ToString
for JwkEcKey
{
172 fn to_string(&self) -> String
{
173 serde_json
::to_string(self).expect("JWK encoding error")
177 impl<C
> TryFrom
<JwkEcKey
> for SecretKey
<C
>
179 C
: Curve
+ JwkParameters
+ ValidatePublicKey
,
180 FieldBytesSize
<C
>: ModulusSize
,
184 fn try_from(jwk
: JwkEcKey
) -> Result
<SecretKey
<C
>> {
189 impl<C
> TryFrom
<&JwkEcKey
> for SecretKey
<C
>
191 C
: Curve
+ JwkParameters
+ ValidatePublicKey
,
192 FieldBytesSize
<C
>: ModulusSize
,
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
);
203 result
.and_then(|secret_key
| {
204 C
::validate_public_key(&secret_key
, &pk
)?
;
213 #[cfg(feature = "arithmetic")]
214 impl<C
> From
<SecretKey
<C
>> for JwkEcKey
216 C
: CurveArithmetic
+ JwkParameters
,
217 AffinePoint
<C
>: FromEncodedPoint
<C
> + ToEncodedPoint
<C
>,
218 FieldBytesSize
<C
>: ModulusSize
,
220 fn from(sk
: SecretKey
<C
>) -> JwkEcKey
{
225 #[cfg(feature = "arithmetic")]
226 impl<C
> From
<&SecretKey
<C
>> for JwkEcKey
228 C
: CurveArithmetic
+ JwkParameters
,
229 AffinePoint
<C
>: FromEncodedPoint
<C
> + ToEncodedPoint
<C
>,
230 FieldBytesSize
<C
>: ModulusSize
,
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
));
241 #[cfg(feature = "arithmetic")]
242 impl<C
> TryFrom
<JwkEcKey
> for PublicKey
<C
>
244 C
: CurveArithmetic
+ JwkParameters
,
245 AffinePoint
<C
>: FromEncodedPoint
<C
> + ToEncodedPoint
<C
>,
246 FieldBytesSize
<C
>: ModulusSize
,
250 fn try_from(jwk
: JwkEcKey
) -> Result
<PublicKey
<C
>> {
255 #[cfg(feature = "arithmetic")]
256 impl<C
> TryFrom
<&JwkEcKey
> for PublicKey
<C
>
258 C
: CurveArithmetic
+ JwkParameters
,
259 AffinePoint
<C
>: FromEncodedPoint
<C
> + ToEncodedPoint
<C
>,
260 FieldBytesSize
<C
>: ModulusSize
,
264 fn try_from(jwk
: &JwkEcKey
) -> Result
<PublicKey
<C
>> {
265 PublicKey
::from_sec1_bytes(jwk
.to_encoded_point
::<C
>()?
.as_bytes())
269 #[cfg(feature = "arithmetic")]
270 impl<C
> From
<PublicKey
<C
>> for JwkEcKey
272 C
: CurveArithmetic
+ JwkParameters
,
273 AffinePoint
<C
>: FromEncodedPoint
<C
> + ToEncodedPoint
<C
>,
274 FieldBytesSize
<C
>: ModulusSize
,
276 fn from(pk
: PublicKey
<C
>) -> JwkEcKey
{
281 #[cfg(feature = "arithmetic")]
282 impl<C
> From
<&PublicKey
<C
>> for JwkEcKey
284 C
: CurveArithmetic
+ JwkParameters
,
285 AffinePoint
<C
>: FromEncodedPoint
<C
> + ToEncodedPoint
<C
>,
286 FieldBytesSize
<C
>: ModulusSize
,
288 fn from(pk
: &PublicKey
<C
>) -> JwkEcKey
{
289 Self::from_encoded_point
::<C
>(&pk
.to_encoded_point(false)).expect("JWK encoding error")
293 impl Debug
for JwkEcKey
{
294 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
295 let d
= if self.d
.is_some() {
301 // NOTE: this implementation omits the `d` private key parameter
302 f
.debug_struct(JWK_TYPE_NAME
)
303 .field("crv", &self.crv
)
311 impl PartialEq
for JwkEcKey
{
312 fn eq(&self, other
: &Self) -> bool
{
313 use subtle
::ConstantTimeEq
;
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(),
321 None
=> other
.d
.is_none(),
324 self.crv
== other
.crv
&& self.x
== other
.x
&& self.y
== other
.y
&& d_eq
328 impl Eq
for JwkEcKey {}
330 impl ZeroizeOnDrop
for JwkEcKey {}
332 impl Drop
for JwkEcKey
{
338 impl Zeroize
for JwkEcKey
{
339 fn zeroize(&mut self) {
340 if let Some(d
) = &mut self.d
{
346 impl<'de
> Deserialize
<'de
> for JwkEcKey
{
347 fn deserialize
<D
>(deserializer
: D
) -> core
::result
::Result
<Self, D
::Error
>
349 D
: de
::Deserializer
<'de
>,
363 impl<'de
> de
::Visitor
<'de
> for FieldVisitor
{
366 fn expecting(&self, formatter
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
367 fmt
::Formatter
::write_str(formatter
, "field identifier")
370 fn visit_u64
<E
>(self, value
: u64) -> core
::result
::Result
<Self::Value
, E
>
380 _
=> Err(de
::Error
::invalid_value(
381 de
::Unexpected
::Unsigned(value
),
382 &"field index 0 <= i < 5",
387 fn visit_str
<E
>(self, value
: &str) -> core
::result
::Result
<Self::Value
, E
>
391 self.visit_bytes(value
.as_bytes())
394 fn visit_bytes
<E
>(self, value
: &[u8]) -> core
::result
::Result
<Self::Value
, E
>
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
),
412 impl<'de
> Deserialize
<'de
> for Field
{
414 fn deserialize
<D
>(__deserializer
: D
) -> core
::result
::Result
<Self, D
::Error
>
416 D
: de
::Deserializer
<'de
>,
418 de
::Deserializer
::deserialize_identifier(__deserializer
, FieldVisitor
)
422 struct Visitor
<'de
> {
423 marker
: PhantomData
<JwkEcKey
>,
424 lifetime
: PhantomData
<&'
de ()>,
427 impl<'de
> de
::Visitor
<'de
> for Visitor
<'de
> {
428 type Value
= JwkEcKey
;
430 fn expecting(&self, formatter
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
431 fmt
::Formatter
::write_str(formatter
, "struct JwkEcKey")
435 fn visit_seq
<A
>(self, mut seq
: A
) -> core
::result
::Result
<Self::Value
, A
::Error
>
437 A
: de
::SeqAccess
<'de
>,
439 let kty
= de
::SeqAccess
::next_element
::<String
>(&mut seq
)?
440 .ok_or_else(|| de
::Error
::invalid_length(0, &DE_ERROR_MSG
))?
;
443 return Err(de
::Error
::custom(format
!("unsupported JWK kty: {kty:?}")));
446 let crv
= de
::SeqAccess
::next_element
::<String
>(&mut seq
)?
447 .ok_or_else(|| de
::Error
::invalid_length(1, &DE_ERROR_MSG
))?
;
449 let x
= de
::SeqAccess
::next_element
::<String
>(&mut seq
)?
450 .ok_or_else(|| de
::Error
::invalid_length(2, &DE_ERROR_MSG
))?
;
452 let y
= de
::SeqAccess
::next_element
::<String
>(&mut seq
)?
453 .ok_or_else(|| de
::Error
::invalid_length(3, &DE_ERROR_MSG
))?
;
455 let d
= de
::SeqAccess
::next_element
::<Option
<String
>>(&mut seq
)?
456 .ok_or_else(|| de
::Error
::invalid_length(4, &DE_ERROR_MSG
))?
;
458 Ok(JwkEcKey { crv, x, y, d }
)
462 fn visit_map
<A
>(self, mut map
: A
) -> core
::result
::Result
<Self::Value
, A
::Error
>
464 A
: de
::MapAccess
<'de
>,
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
;
472 while let Some(key
) = de
::MapAccess
::next_key
::<Field
>(&mut map
)?
{
476 kty
= Some(de
::MapAccess
::next_value
::<String
>(&mut map
)?
);
478 return Err(de
::Error
::duplicate_field(FIELDS
[0]));
483 crv
= Some(de
::MapAccess
::next_value
::<String
>(&mut map
)?
);
485 return Err(de
::Error
::duplicate_field(FIELDS
[1]));
490 x
= Some(de
::MapAccess
::next_value
::<String
>(&mut map
)?
);
492 return Err(de
::Error
::duplicate_field(FIELDS
[2]));
497 y
= Some(de
::MapAccess
::next_value
::<String
>(&mut map
)?
);
499 return Err(de
::Error
::duplicate_field(FIELDS
[3]));
504 d
= de
::MapAccess
::next_value
::<Option
<String
>>(&mut map
)?
;
506 return Err(de
::Error
::duplicate_field(FIELDS
[4]));
512 let kty
= kty
.ok_or_else(|| de
::Error
::missing_field("kty"))?
;
515 return Err(de
::Error
::custom(format
!("unsupported JWK kty: {kty}")));
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"))?
;
522 Ok(JwkEcKey { crv, x, y, d }
)
526 de
::Deserializer
::deserialize_struct(
531 marker
: PhantomData
::<JwkEcKey
>,
532 lifetime
: PhantomData
,
538 impl Serialize
for JwkEcKey
{
539 fn serialize
<S
>(&self, serializer
: S
) -> core
::result
::Result
<S
::Ok
, S
::Error
>
543 use ser
::SerializeStruct
;
545 let mut state
= serializer
.serialize_struct(JWK_TYPE_NAME
, 5)?
;
547 for (i
, field
) in [EC_KTY
, &self.crv
, &self.x
, &self.y
].iter().enumerate() {
548 state
.serialize_field(FIELDS
[i
], field
)?
;
551 if let Some(d
) = &self.d
{
552 state
.serialize_field("d", d
)?
;
555 ser
::SerializeStruct
::end(state
)
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
)?
;
570 #[cfg(feature = "dev")]
571 use crate::dev
::MockCurve
;
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
#"
579 "x":"gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0",
580 "y":"SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps",
581 "d":"0_NxaRPUMQoAJt50Gz8YiTr8gRTwyEaCumd-MToTmIo"
585 /// Example public key.
586 const JWK_PUBLIC_KEY
: &str = r
#"
590 "x":"gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0",
591 "y":"SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps"
595 /// Example unsupported JWK (RSA key)
596 const UNSUPPORTED_JWK
: &str = r
#"
599 "kid":"cc34c0a0-bd5a-4a3c-a50d-a2a7db7643df",
601 "n":"pjdss8ZaDfEH6K6U7GeW2nxDqR4IP049fk1fK0lndimbMMVBdPv_hSpm8T8EtBDxrUdi1OHZfMhUixGaut-3nQ4GG9nM249oxhCtxqqNvEXrmQRGqczyLxuh-fKn9Fg--hS9UpazHpfVAFnB5aCfXoNhPuI8oByyFKMKaOVgHNqP5NBEqabiLftZD3W_lsFCPGuzr4Vp0YS7zS2hDYScC2oOMu4rGU1LcMZf39p3153Cq7bS2Xh6Y-vw5pwzFYZdjQxDn8x8BG3fJ6j8TGLXQsbKH1218_HcUJRvMwdpbUQG5nvA2GXVqLqdwp054Lzk9_B_f1lVrmOKuHjTNHq48w",
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"
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");
619 jwk
.d
.as_ref().unwrap(),
620 "0_NxaRPUMQoAJt50Gz8YiTr8gRTwyEaCumd-MToTmIo"
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
);
634 fn parse_unsupported() {
635 assert_eq
!(JwkEcKey
::from_str(UNSUPPORTED_JWK
), Err(Error
));
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
);
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
);
652 #[cfg(feature = "dev")]
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
),
662 assert_eq
!(&decode_base64url_fe
::<MockCurve
>(&jwk
.x
).unwrap(), x
);
663 assert_eq
!(&decode_base64url_fe
::<MockCurve
>(&jwk
.y
).unwrap(), y
);
666 #[cfg(feature = "dev")]
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
);