Skip to main content

jwt_simple/algorithms/jwe/
ecdh_es.rs

1//! ECDH-ES key agreement algorithms for JWE.
2//!
3//! Implements ECDH-ES+A256KW and ECDH-ES+A128KW (Elliptic Curve Diffie-Hellman
4//! Ephemeral Static key agreement with AES Key Wrap).
5
6use ct_codecs::{Base64UrlSafeNoPadding, Decoder, Encoder};
7use p256::ecdh::EphemeralSecret;
8use p256::elliptic_curve::sec1::{FromSec1Point, ToSec1Point};
9use p256::elliptic_curve::Generate as _;
10use p256::pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey};
11use p256::{NonZeroScalar, PublicKey, Sec1Point, SecretKey};
12use rand::rng;
13use serde::{de::DeserializeOwned, Serialize};
14use serde_json::json;
15use zeroize::Zeroize;
16
17use crate::algorithms::jwe::aes_kw::{A128KWKey, A256KWKey};
18use crate::claims::*;
19use crate::error::*;
20use crate::jwe_header::JWEHeader;
21use crate::jwe_token::{DecryptionOptions, EncryptionOptions, JWEToken, JWETokenMetadata};
22
23/// Derive a key using Concat KDF as specified in NIST SP 800-56A.
24fn concat_kdf(
25    shared_secret: &[u8],
26    key_len: usize,
27    alg: &str,
28    apu: Option<&[u8]>,
29    apv: Option<&[u8]>,
30) -> Vec<u8> {
31    use hmac_sha256::Hash as SHA256;
32
33    let apu = apu.unwrap_or(&[]);
34    let apv = apv.unwrap_or(&[]);
35
36    // AlgorithmID || PartyUInfo || PartyVInfo || SuppPubInfo
37    let alg_bytes = alg.as_bytes();
38    let alg_len = (alg_bytes.len() as u32).to_be_bytes();
39    let apu_len = (apu.len() as u32).to_be_bytes();
40    let apv_len = (apv.len() as u32).to_be_bytes();
41    let key_bits = ((key_len * 8) as u32).to_be_bytes();
42
43    let mut derived_key = Vec::with_capacity(key_len);
44    let mut counter: u32 = 1;
45
46    while derived_key.len() < key_len {
47        let counter_bytes = counter.to_be_bytes();
48
49        // Hash: counter || Z || OtherInfo
50        let mut hasher = SHA256::new();
51        hasher.update(counter_bytes);
52        hasher.update(shared_secret);
53        // OtherInfo = AlgorithmID || PartyUInfo || PartyVInfo || SuppPubInfo
54        hasher.update(alg_len);
55        hasher.update(alg_bytes);
56        hasher.update(apu_len);
57        hasher.update(apu);
58        hasher.update(apv_len);
59        hasher.update(apv);
60        hasher.update(key_bits);
61
62        let hash = hasher.finalize();
63        derived_key.extend_from_slice(&hash);
64        counter += 1;
65    }
66
67    derived_key.truncate(key_len);
68    derived_key
69}
70
71/// P-256 public key for ECDH-ES+A256KW encryption.
72#[derive(Debug, Clone)]
73pub struct EcdhEsA256KWEncryptionKey {
74    pk: PublicKey,
75    key_id: Option<String>,
76}
77
78impl EcdhEsA256KWEncryptionKey {
79    const ALG_NAME: &'static str = "ECDH-ES+A256KW";
80    const KEY_WRAP_SIZE: usize = 32;
81
82    /// Create from SEC1-encoded bytes (compressed or uncompressed).
83    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
84        let point = Sec1Point::from_bytes(bytes).map_err(|_| JWTError::InvalidPublicKey)?;
85        let pk = PublicKey::from_sec1_point(&point);
86        if pk.is_none().into() {
87            bail!(JWTError::InvalidPublicKey);
88        }
89        Ok(EcdhEsA256KWEncryptionKey {
90            pk: pk.unwrap(),
91            key_id: None,
92        })
93    }
94
95    /// Create from DER-encoded public key.
96    pub fn from_der(der: &[u8]) -> Result<Self, Error> {
97        let pk = PublicKey::from_public_key_der(der).map_err(|_| JWTError::InvalidPublicKey)?;
98        Ok(EcdhEsA256KWEncryptionKey { pk, key_id: None })
99    }
100
101    /// Create from PEM-encoded public key.
102    pub fn from_pem(pem: &str) -> Result<Self, Error> {
103        let pk = PublicKey::from_public_key_pem(pem).map_err(|_| JWTError::InvalidPublicKey)?;
104        Ok(EcdhEsA256KWEncryptionKey { pk, key_id: None })
105    }
106
107    /// Export as SEC1 compressed bytes.
108    pub fn to_bytes(&self) -> Vec<u8> {
109        self.pk.to_sec1_point(true).as_bytes().to_vec()
110    }
111
112    /// Export as SEC1 uncompressed bytes.
113    pub fn to_bytes_uncompressed(&self) -> Vec<u8> {
114        self.pk.to_sec1_point(false).as_bytes().to_vec()
115    }
116
117    /// Export as DER.
118    pub fn to_der(&self) -> Result<Vec<u8>, Error> {
119        Ok(self
120            .pk
121            .to_public_key_der()
122            .map_err(|_| JWTError::InvalidPublicKey)?
123            .as_ref()
124            .to_vec())
125    }
126
127    /// Export as PEM.
128    pub fn to_pem(&self) -> Result<String, Error> {
129        Ok(self
130            .pk
131            .to_public_key_pem(Default::default())
132            .map_err(|_| JWTError::InvalidPublicKey)?)
133    }
134
135    /// Set the key ID.
136    pub fn with_key_id(mut self, key_id: impl Into<String>) -> Self {
137        self.key_id = Some(key_id.into());
138        self
139    }
140
141    /// Get the key ID.
142    pub fn key_id(&self) -> Option<&str> {
143        self.key_id.as_deref()
144    }
145
146    fn build_epk_jwk(&self, ephemeral_pk: &PublicKey) -> serde_json::Value {
147        let point = ephemeral_pk.to_sec1_point(false);
148        let x = Base64UrlSafeNoPadding::encode_to_string(point.x().unwrap()).unwrap();
149        let y = Base64UrlSafeNoPadding::encode_to_string(point.y().unwrap()).unwrap();
150        json!({
151            "kty": "EC",
152            "crv": "P-256",
153            "x": x,
154            "y": y
155        })
156    }
157
158    /// Encrypt claims into a JWE token.
159    pub fn encrypt<CustomClaims: Serialize>(
160        &self,
161        claims: JWTClaims<CustomClaims>,
162    ) -> Result<String, Error> {
163        self.encrypt_with_options(claims, &EncryptionOptions::default())
164    }
165
166    /// Encrypt claims into a JWE token with options.
167    pub fn encrypt_with_options<CustomClaims: Serialize>(
168        &self,
169        claims: JWTClaims<CustomClaims>,
170        options: &EncryptionOptions,
171    ) -> Result<String, Error> {
172        let content_encryption = options.content_encryption;
173
174        let ephemeral_secret = EphemeralSecret::generate_from_rng(&mut rng());
175        let ephemeral_pk = ephemeral_secret.public_key();
176
177        let shared_secret = ephemeral_secret.diffie_hellman(&self.pk);
178
179        let mut kek = concat_kdf(
180            shared_secret.raw_secret_bytes(),
181            Self::KEY_WRAP_SIZE,
182            Self::ALG_NAME,
183            None,
184            None,
185        );
186
187        let wrap_key = A256KWKey::from_bytes(&kek)?;
188        kek.zeroize();
189
190        let mut header = JWEHeader::new(Self::ALG_NAME, content_encryption.alg_name());
191        header.ephemeral_public_key = Some(self.build_epk_jwk(&ephemeral_pk));
192
193        if let Some(key_id) = &self.key_id {
194            header.key_id = Some(key_id.clone());
195        }
196        if let Some(key_id) = &options.key_id {
197            header.key_id = Some(key_id.clone());
198        }
199        if let Some(cty) = &options.content_type {
200            header.content_type = Some(cty.clone());
201        }
202
203        JWEToken::build_from_claims(&header, &claims, content_encryption, |cek| {
204            wrap_key.wrap_key(cek)
205        })
206    }
207}
208
209/// P-256 key pair for ECDH-ES+A256KW decryption.
210#[derive(Clone)]
211pub struct EcdhEsA256KWDecryptionKey {
212    sk: SecretKey,
213    key_id: Option<String>,
214}
215
216impl std::fmt::Debug for EcdhEsA256KWDecryptionKey {
217    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
218        f.debug_struct("EcdhEsA256KWDecryptionKey")
219            .field("key_id", &self.key_id)
220            .finish_non_exhaustive()
221    }
222}
223
224impl EcdhEsA256KWDecryptionKey {
225    const ALG_NAME: &'static str = "ECDH-ES+A256KW";
226    const KEY_WRAP_SIZE: usize = 32;
227
228    /// Create from raw scalar bytes.
229    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
230        let sk = SecretKey::from_slice(bytes).map_err(|_| JWTError::InvalidKeyPair)?;
231        Ok(EcdhEsA256KWDecryptionKey { sk, key_id: None })
232    }
233
234    /// Create from DER-encoded private key.
235    pub fn from_der(der: &[u8]) -> Result<Self, Error> {
236        let sk = SecretKey::from_pkcs8_der(der).map_err(|_| JWTError::InvalidKeyPair)?;
237        Ok(EcdhEsA256KWDecryptionKey { sk, key_id: None })
238    }
239
240    /// Create from PEM-encoded private key.
241    pub fn from_pem(pem: &str) -> Result<Self, Error> {
242        let sk = SecretKey::from_pkcs8_pem(pem).map_err(|_| JWTError::InvalidKeyPair)?;
243        Ok(EcdhEsA256KWDecryptionKey { sk, key_id: None })
244    }
245
246    /// Generate a new key pair.
247    pub fn generate() -> Self {
248        let sk = SecretKey::generate_from_rng(&mut rng());
249        EcdhEsA256KWDecryptionKey { sk, key_id: None }
250    }
251
252    /// Export private key as raw bytes.
253    pub fn to_bytes(&self) -> Vec<u8> {
254        self.sk.to_bytes().to_vec()
255    }
256
257    /// Export private key as DER.
258    pub fn to_der(&self) -> Result<Vec<u8>, Error> {
259        let scalar = NonZeroScalar::from_repr(self.sk.to_bytes());
260        if bool::from(scalar.is_none()) {
261            return Err(JWTError::InvalidKeyPair.into());
262        }
263        let sk = SecretKey::from(NonZeroScalar::from_repr(scalar.unwrap().into()).unwrap());
264        Ok(sk
265            .to_pkcs8_der()
266            .map_err(|_| JWTError::InvalidKeyPair)?
267            .as_bytes()
268            .to_vec())
269    }
270
271    /// Export private key as PEM.
272    pub fn to_pem(&self) -> Result<String, Error> {
273        let scalar = NonZeroScalar::from_repr(self.sk.to_bytes());
274        if bool::from(scalar.is_none()) {
275            return Err(JWTError::InvalidKeyPair.into());
276        }
277        let sk = SecretKey::from(NonZeroScalar::from_repr(scalar.unwrap().into()).unwrap());
278        Ok(sk
279            .to_pkcs8_pem(Default::default())
280            .map_err(|_| JWTError::InvalidKeyPair)?
281            .to_string())
282    }
283
284    /// Get the public encryption key.
285    pub fn encryption_key(&self) -> EcdhEsA256KWEncryptionKey {
286        EcdhEsA256KWEncryptionKey {
287            pk: self.sk.public_key(),
288            key_id: self.key_id.clone(),
289        }
290    }
291
292    /// Set the key ID.
293    pub fn with_key_id(mut self, key_id: impl Into<String>) -> Self {
294        self.key_id = Some(key_id.into());
295        self
296    }
297
298    /// Get the key ID.
299    pub fn key_id(&self) -> Option<&str> {
300        self.key_id.as_deref()
301    }
302
303    fn parse_epk(epk: &serde_json::Value) -> Result<PublicKey, Error> {
304        let kty = epk.get("kty").and_then(|v| v.as_str());
305        ensure!(kty == Some("EC"), JWTError::InvalidEphemeralKey);
306
307        let crv = epk.get("crv").and_then(|v| v.as_str());
308        ensure!(crv == Some("P-256"), JWTError::InvalidEphemeralKey);
309
310        let x = epk
311            .get("x")
312            .and_then(|v| v.as_str())
313            .ok_or(JWTError::InvalidEphemeralKey)?;
314        let y = epk
315            .get("y")
316            .and_then(|v| v.as_str())
317            .ok_or(JWTError::InvalidEphemeralKey)?;
318
319        let x_bytes = Base64UrlSafeNoPadding::decode_to_vec(x, None)
320            .map_err(|_| JWTError::InvalidEphemeralKey)?;
321        let y_bytes = Base64UrlSafeNoPadding::decode_to_vec(y, None)
322            .map_err(|_| JWTError::InvalidEphemeralKey)?;
323
324        // Build uncompressed point: 0x04 || x || y
325        let mut point_bytes = vec![0x04];
326        point_bytes.extend_from_slice(&x_bytes);
327        point_bytes.extend_from_slice(&y_bytes);
328
329        let point =
330            Sec1Point::from_bytes(&point_bytes).map_err(|_| JWTError::InvalidEphemeralKey)?;
331        let pk = PublicKey::from_sec1_point(&point);
332        if pk.is_none().into() {
333            bail!(JWTError::InvalidEphemeralKey);
334        }
335
336        Ok(pk.unwrap())
337    }
338
339    /// Encrypt claims into a JWE token.
340    pub fn encrypt<CustomClaims: Serialize>(
341        &self,
342        claims: JWTClaims<CustomClaims>,
343    ) -> Result<String, Error> {
344        self.encryption_key().encrypt(claims)
345    }
346
347    /// Encrypt claims into a JWE token with options.
348    pub fn encrypt_with_options<CustomClaims: Serialize>(
349        &self,
350        claims: JWTClaims<CustomClaims>,
351        options: &EncryptionOptions,
352    ) -> Result<String, Error> {
353        self.encryption_key().encrypt_with_options(claims, options)
354    }
355
356    /// Decrypt a JWE token and return the claims.
357    pub fn decrypt_token<CustomClaims: DeserializeOwned>(
358        &self,
359        token: &str,
360        options: Option<DecryptionOptions>,
361    ) -> Result<JWTClaims<CustomClaims>, Error> {
362        JWEToken::decrypt(Self::ALG_NAME, token, options, |header, encrypted_key| {
363            let epk = header
364                .ephemeral_public_key
365                .as_ref()
366                .ok_or(JWTError::MissingEphemeralKey)?;
367            let ephemeral_pk = Self::parse_epk(epk)?;
368
369            let shared_secret =
370                p256::ecdh::diffie_hellman(self.sk.to_nonzero_scalar(), ephemeral_pk.as_affine());
371
372            let mut kek = concat_kdf(
373                shared_secret.raw_secret_bytes(),
374                Self::KEY_WRAP_SIZE,
375                Self::ALG_NAME,
376                None,
377                None,
378            );
379
380            let wrap_key = A256KWKey::from_bytes(&kek)?;
381            kek.zeroize();
382            wrap_key.unwrap_key(encrypted_key)
383        })
384    }
385
386    /// Decode token metadata without decrypting.
387    pub fn decode_metadata(token: &str) -> Result<JWETokenMetadata, Error> {
388        JWEToken::decode_metadata(token)
389    }
390}
391
392/// P-256 public key for ECDH-ES+A128KW encryption.
393#[derive(Debug, Clone)]
394pub struct EcdhEsA128KWEncryptionKey {
395    pk: PublicKey,
396    key_id: Option<String>,
397}
398
399impl EcdhEsA128KWEncryptionKey {
400    const ALG_NAME: &'static str = "ECDH-ES+A128KW";
401    const KEY_WRAP_SIZE: usize = 16;
402
403    /// Create from SEC1-encoded bytes (compressed or uncompressed).
404    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
405        let point = Sec1Point::from_bytes(bytes).map_err(|_| JWTError::InvalidPublicKey)?;
406        let pk = PublicKey::from_sec1_point(&point);
407        if pk.is_none().into() {
408            bail!(JWTError::InvalidPublicKey);
409        }
410        Ok(EcdhEsA128KWEncryptionKey {
411            pk: pk.unwrap(),
412            key_id: None,
413        })
414    }
415
416    /// Create from DER-encoded public key.
417    pub fn from_der(der: &[u8]) -> Result<Self, Error> {
418        let pk = PublicKey::from_public_key_der(der).map_err(|_| JWTError::InvalidPublicKey)?;
419        Ok(EcdhEsA128KWEncryptionKey { pk, key_id: None })
420    }
421
422    /// Create from PEM-encoded public key.
423    pub fn from_pem(pem: &str) -> Result<Self, Error> {
424        let pk = PublicKey::from_public_key_pem(pem).map_err(|_| JWTError::InvalidPublicKey)?;
425        Ok(EcdhEsA128KWEncryptionKey { pk, key_id: None })
426    }
427
428    /// Export as SEC1 compressed bytes.
429    pub fn to_bytes(&self) -> Vec<u8> {
430        self.pk.to_sec1_point(true).as_bytes().to_vec()
431    }
432
433    /// Export as DER.
434    pub fn to_der(&self) -> Result<Vec<u8>, Error> {
435        Ok(self
436            .pk
437            .to_public_key_der()
438            .map_err(|_| JWTError::InvalidPublicKey)?
439            .as_ref()
440            .to_vec())
441    }
442
443    /// Export as PEM.
444    pub fn to_pem(&self) -> Result<String, Error> {
445        Ok(self
446            .pk
447            .to_public_key_pem(Default::default())
448            .map_err(|_| JWTError::InvalidPublicKey)?)
449    }
450
451    /// Set the key ID.
452    pub fn with_key_id(mut self, key_id: impl Into<String>) -> Self {
453        self.key_id = Some(key_id.into());
454        self
455    }
456
457    /// Get the key ID.
458    pub fn key_id(&self) -> Option<&str> {
459        self.key_id.as_deref()
460    }
461
462    fn build_epk_jwk(&self, ephemeral_pk: &PublicKey) -> serde_json::Value {
463        let point = ephemeral_pk.to_sec1_point(false);
464        let x = Base64UrlSafeNoPadding::encode_to_string(point.x().unwrap()).unwrap();
465        let y = Base64UrlSafeNoPadding::encode_to_string(point.y().unwrap()).unwrap();
466        json!({
467            "kty": "EC",
468            "crv": "P-256",
469            "x": x,
470            "y": y
471        })
472    }
473
474    /// Encrypt claims into a JWE token.
475    pub fn encrypt<CustomClaims: Serialize>(
476        &self,
477        claims: JWTClaims<CustomClaims>,
478    ) -> Result<String, Error> {
479        self.encrypt_with_options(claims, &EncryptionOptions::default())
480    }
481
482    /// Encrypt claims into a JWE token with options.
483    pub fn encrypt_with_options<CustomClaims: Serialize>(
484        &self,
485        claims: JWTClaims<CustomClaims>,
486        options: &EncryptionOptions,
487    ) -> Result<String, Error> {
488        let content_encryption = options.content_encryption;
489
490        let ephemeral_secret = EphemeralSecret::generate_from_rng(&mut rng());
491        let ephemeral_pk = ephemeral_secret.public_key();
492
493        let shared_secret = ephemeral_secret.diffie_hellman(&self.pk);
494
495        let mut kek = concat_kdf(
496            shared_secret.raw_secret_bytes(),
497            Self::KEY_WRAP_SIZE,
498            Self::ALG_NAME,
499            None,
500            None,
501        );
502
503        let wrap_key = A128KWKey::from_bytes(&kek)?;
504        kek.zeroize();
505
506        let mut header = JWEHeader::new(Self::ALG_NAME, content_encryption.alg_name());
507        header.ephemeral_public_key = Some(self.build_epk_jwk(&ephemeral_pk));
508
509        if let Some(key_id) = &self.key_id {
510            header.key_id = Some(key_id.clone());
511        }
512        if let Some(key_id) = &options.key_id {
513            header.key_id = Some(key_id.clone());
514        }
515        if let Some(cty) = &options.content_type {
516            header.content_type = Some(cty.clone());
517        }
518
519        JWEToken::build_from_claims(&header, &claims, content_encryption, |cek| {
520            wrap_key.wrap_key(cek)
521        })
522    }
523}
524
525/// P-256 key pair for ECDH-ES+A128KW decryption.
526#[derive(Clone)]
527pub struct EcdhEsA128KWDecryptionKey {
528    sk: SecretKey,
529    key_id: Option<String>,
530}
531
532impl std::fmt::Debug for EcdhEsA128KWDecryptionKey {
533    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
534        f.debug_struct("EcdhEsA128KWDecryptionKey")
535            .field("key_id", &self.key_id)
536            .finish_non_exhaustive()
537    }
538}
539
540impl EcdhEsA128KWDecryptionKey {
541    const ALG_NAME: &'static str = "ECDH-ES+A128KW";
542    const KEY_WRAP_SIZE: usize = 16;
543
544    /// Create from raw scalar bytes.
545    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
546        let sk = SecretKey::from_slice(bytes).map_err(|_| JWTError::InvalidKeyPair)?;
547        Ok(EcdhEsA128KWDecryptionKey { sk, key_id: None })
548    }
549
550    /// Create from DER-encoded private key.
551    pub fn from_der(der: &[u8]) -> Result<Self, Error> {
552        let sk = SecretKey::from_pkcs8_der(der).map_err(|_| JWTError::InvalidKeyPair)?;
553        Ok(EcdhEsA128KWDecryptionKey { sk, key_id: None })
554    }
555
556    /// Create from PEM-encoded private key.
557    pub fn from_pem(pem: &str) -> Result<Self, Error> {
558        let sk = SecretKey::from_pkcs8_pem(pem).map_err(|_| JWTError::InvalidKeyPair)?;
559        Ok(EcdhEsA128KWDecryptionKey { sk, key_id: None })
560    }
561
562    /// Generate a new key pair.
563    pub fn generate() -> Self {
564        let sk = SecretKey::generate_from_rng(&mut rng());
565        EcdhEsA128KWDecryptionKey { sk, key_id: None }
566    }
567
568    /// Export private key as raw bytes.
569    pub fn to_bytes(&self) -> Vec<u8> {
570        self.sk.to_bytes().to_vec()
571    }
572
573    /// Export private key as DER.
574    pub fn to_der(&self) -> Result<Vec<u8>, Error> {
575        let scalar = NonZeroScalar::from_repr(self.sk.to_bytes());
576        if bool::from(scalar.is_none()) {
577            return Err(JWTError::InvalidKeyPair.into());
578        }
579        let sk = SecretKey::from(NonZeroScalar::from_repr(scalar.unwrap().into()).unwrap());
580        Ok(sk
581            .to_pkcs8_der()
582            .map_err(|_| JWTError::InvalidKeyPair)?
583            .as_bytes()
584            .to_vec())
585    }
586
587    /// Export private key as PEM.
588    pub fn to_pem(&self) -> Result<String, Error> {
589        let scalar = NonZeroScalar::from_repr(self.sk.to_bytes());
590        if bool::from(scalar.is_none()) {
591            return Err(JWTError::InvalidKeyPair.into());
592        }
593        let sk = SecretKey::from(NonZeroScalar::from_repr(scalar.unwrap().into()).unwrap());
594        Ok(sk
595            .to_pkcs8_pem(Default::default())
596            .map_err(|_| JWTError::InvalidKeyPair)?
597            .to_string())
598    }
599
600    /// Get the public encryption key.
601    pub fn encryption_key(&self) -> EcdhEsA128KWEncryptionKey {
602        EcdhEsA128KWEncryptionKey {
603            pk: self.sk.public_key(),
604            key_id: self.key_id.clone(),
605        }
606    }
607
608    /// Set the key ID.
609    pub fn with_key_id(mut self, key_id: impl Into<String>) -> Self {
610        self.key_id = Some(key_id.into());
611        self
612    }
613
614    /// Get the key ID.
615    pub fn key_id(&self) -> Option<&str> {
616        self.key_id.as_deref()
617    }
618
619    fn parse_epk(epk: &serde_json::Value) -> Result<PublicKey, Error> {
620        let kty = epk.get("kty").and_then(|v| v.as_str());
621        ensure!(kty == Some("EC"), JWTError::InvalidEphemeralKey);
622
623        let crv = epk.get("crv").and_then(|v| v.as_str());
624        ensure!(crv == Some("P-256"), JWTError::InvalidEphemeralKey);
625
626        let x = epk
627            .get("x")
628            .and_then(|v| v.as_str())
629            .ok_or(JWTError::InvalidEphemeralKey)?;
630        let y = epk
631            .get("y")
632            .and_then(|v| v.as_str())
633            .ok_or(JWTError::InvalidEphemeralKey)?;
634
635        let x_bytes = Base64UrlSafeNoPadding::decode_to_vec(x, None)
636            .map_err(|_| JWTError::InvalidEphemeralKey)?;
637        let y_bytes = Base64UrlSafeNoPadding::decode_to_vec(y, None)
638            .map_err(|_| JWTError::InvalidEphemeralKey)?;
639
640        // Build uncompressed point: 0x04 || x || y
641        let mut point_bytes = vec![0x04];
642        point_bytes.extend_from_slice(&x_bytes);
643        point_bytes.extend_from_slice(&y_bytes);
644
645        let point =
646            Sec1Point::from_bytes(&point_bytes).map_err(|_| JWTError::InvalidEphemeralKey)?;
647        let pk = PublicKey::from_sec1_point(&point);
648        if pk.is_none().into() {
649            bail!(JWTError::InvalidEphemeralKey);
650        }
651
652        Ok(pk.unwrap())
653    }
654
655    /// Encrypt claims into a JWE token.
656    pub fn encrypt<CustomClaims: Serialize>(
657        &self,
658        claims: JWTClaims<CustomClaims>,
659    ) -> Result<String, Error> {
660        self.encryption_key().encrypt(claims)
661    }
662
663    /// Encrypt claims into a JWE token with options.
664    pub fn encrypt_with_options<CustomClaims: Serialize>(
665        &self,
666        claims: JWTClaims<CustomClaims>,
667        options: &EncryptionOptions,
668    ) -> Result<String, Error> {
669        self.encryption_key().encrypt_with_options(claims, options)
670    }
671
672    /// Decrypt a JWE token and return the claims.
673    pub fn decrypt_token<CustomClaims: DeserializeOwned>(
674        &self,
675        token: &str,
676        options: Option<DecryptionOptions>,
677    ) -> Result<JWTClaims<CustomClaims>, Error> {
678        JWEToken::decrypt(Self::ALG_NAME, token, options, |header, encrypted_key| {
679            let epk = header
680                .ephemeral_public_key
681                .as_ref()
682                .ok_or(JWTError::MissingEphemeralKey)?;
683            let ephemeral_pk = Self::parse_epk(epk)?;
684
685            let shared_secret =
686                p256::ecdh::diffie_hellman(self.sk.to_nonzero_scalar(), ephemeral_pk.as_affine());
687
688            let mut kek = concat_kdf(
689                shared_secret.raw_secret_bytes(),
690                Self::KEY_WRAP_SIZE,
691                Self::ALG_NAME,
692                None,
693                None,
694            );
695
696            let wrap_key = A128KWKey::from_bytes(&kek)?;
697            kek.zeroize();
698            wrap_key.unwrap_key(encrypted_key)
699        })
700    }
701
702    /// Decode token metadata without decrypting.
703    pub fn decode_metadata(token: &str) -> Result<JWETokenMetadata, Error> {
704        JWEToken::decode_metadata(token)
705    }
706}