polyjose 0.2.0

Pure-Rust JOSE: JWA, JWK, JWS, JWE and JWT per the RFCs, with no OpenSSL
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
//! The public JWE API (RFC 7516): the decrypt policy, the decrypt result,
//! the encryptor and its recipients.
//!
//! The bodies here only delegate to `core`, `compact` and `json` (which
//! call `zip`), so the tasks that fill those in never edit this file.

use core::fmt;

use zeroize::Zeroizing;

use super::keymgmt::RecipientOptions;
use super::random::OsRandom;
use super::{compact, core as pipeline, json};
use crate::header::Header;
#[cfg(feature = "insecure-test-vectors")]
use crate::insecure_test_vectors::FixedRng;
use crate::jwa::{ContentEncAlg, KeyMgmtAlg};
use crate::jwk::Jwk;
use crate::{Error, Result};

/// What [`decrypt_compact`] and
/// [`decrypt_json`] accept (RFC 8725 §3.1,
/// ADR 0007).
///
/// A JWE is decrypted only if its `alg` and `enc` are both in the policy's
/// allow-lists: the header alone is never trusted. The limits bound the work
/// an attacker-chosen JWE can cause before it fails.
///
/// **Pin every JWE key** (ADR 0050 §3, RFC 8725 §3.1). The policy's lists
/// say which algorithms the caller accepts at all; the key's JWK `alg`
/// (RFC 7517 §4.4), and `use` (§4.2) or `key_ops` (§4.3), say which one
/// *this key* may serve, and polyjose enforces them on decrypt. A key
/// without `alg`, symmetric keys above all, may be tried under every `alg`
/// and `enc` the policy allows, which is what cross-mode attacks rely on
/// (ADR 0050 context). So keep the allow-lists to what you use, and give
/// each key its `alg` (for a `dir` key, `"dir"` or its `enc` value, RFC
/// 7518 §4.5), plus `"use":"enc"` or `key_ops` (`["decrypt"]` for `dir`,
/// `["unwrapKey"]` for key wrapping and encryption, `["deriveKey"]` for
/// ECDH-ES).
///
/// ```
/// use polyjose::jwa::{ContentEncAlg, KeyMgmtAlg};
/// use polyjose::jwe::DecryptPolicy;
///
/// let policy = DecryptPolicy::new(&[KeyMgmtAlg::A256KW], &[ContentEncAlg::A256GCM])?
///     .max_recipients(4);
/// # Ok::<(), polyjose::Error>(())
/// ```
#[derive(Debug, Clone)]
pub struct DecryptPolicy {
    // core.rs, keymgmt.rs and the families read these fields directly.
    pub(super) allowed_algs: Vec<KeyMgmtAlg>,
    pub(super) allowed_enc: Vec<ContentEncAlg>,
    pub(super) max_pbes2_count: u32,
    pub(super) max_decompressed_len: usize,
    pub(super) understood_crit: Vec<String>,
    pub(super) max_input_len: usize,
    pub(super) max_recipients: usize,
}

impl DecryptPolicy {
    /// The default [`max_pbes2_count`](DecryptPolicy::max_pbes2_count):
    /// 10,000 PBKDF2 iterations (ADR 0023).
    pub const DEFAULT_MAX_PBES2_COUNT: u32 = 10_000;

    /// The hard ceiling of [`max_pbes2_count`](DecryptPolicy::max_pbes2_count):
    /// 1,000,000 iterations (ADR 0023, ADR 0007).
    pub const MAX_PBES2_COUNT_CEILING: u32 = 1_000_000;

    /// The default
    /// [`max_decompressed_len`](DecryptPolicy::max_decompressed_len):
    /// 250,000 bytes (ADR 0017).
    pub const DEFAULT_MAX_DECOMPRESSED_LEN: usize = 250_000;

    /// The default [`max_input_len`](DecryptPolicy::max_input_len): 1 MiB,
    /// as for JWS (ADR 0026 §7).
    pub const DEFAULT_MAX_INPUT_LEN: usize = 1024 * 1024;

    /// The default [`max_recipients`](DecryptPolicy::max_recipients): 16,
    /// as the JWS signature limit (ADR 0026 §7).
    pub const DEFAULT_MAX_RECIPIENTS: usize = 16;

    /// A policy allowing exactly the key management algorithms
    /// `allowed_algs` and the content encryption algorithms `allowed_enc`,
    /// with the default limits and no understood `crit` extensions.
    ///
    /// An algorithm polyjose does not implement may be listed but never
    /// decrypts: a JWE using it fails with
    /// [`Error::UnsupportedAlgorithm`] before the allow-list is consulted
    /// (the support check comes first; RFC 7516 §5.2, ADR 0007). That
    /// includes `RSA-OAEP` (SHA-1, ADR 0022) and `RSA1_5` (ADR 0067 §3).
    ///
    /// # Errors
    ///
    /// [`Error::AlgorithmNotAllowed`] if either list is empty (RFC 8725
    /// §3.1, ADR 0007).
    pub fn new(allowed_algs: &[KeyMgmtAlg], allowed_enc: &[ContentEncAlg]) -> Result<Self> {
        if allowed_algs.is_empty() || allowed_enc.is_empty() {
            return Err(Error::AlgorithmNotAllowed);
        }
        Ok(Self {
            allowed_algs: allowed_algs.to_vec(),
            allowed_enc: allowed_enc.to_vec(),
            max_pbes2_count: Self::DEFAULT_MAX_PBES2_COUNT,
            max_decompressed_len: Self::DEFAULT_MAX_DECOMPRESSED_LEN,
            understood_crit: Vec::new(),
            max_input_len: Self::DEFAULT_MAX_INPUT_LEN,
            max_recipients: Self::DEFAULT_MAX_RECIPIENTS,
        })
    }

    /// The PBES2 iteration budget (RFC 7518 §4.8.1.2 `p2c`) **per token,
    /// summed over all PBES2 candidates tried** in one decrypt call
    /// (ADR 0050 §2, amending ADR 0023).
    ///
    /// Each PBES2 recipient that is a candidate for the key spends its `p2c`
    /// from the budget just before its PBKDF2 derivation runs. A candidate
    /// whose `p2c` exceeds what is left fails, without any PBKDF2 work, and
    /// the next candidate is tried; if none decrypts, the result is
    /// [`Error::DecryptionFailed`], as for any other cause. So a JWE with 16
    /// PBES2 recipients of `p2c` 1,000 and no `kid` runs at most 10
    /// derivations under the default, and its 16th recipient decrypts only
    /// with a budget of 16,000 or more. A `kid` on the key and the
    /// recipients narrows the candidates (RFC 7516 §4.1.6).
    ///
    /// The default is [`DEFAULT_MAX_PBES2_COUNT`](Self::DEFAULT_MAX_PBES2_COUNT)
    /// (10,000). A value above
    /// [`MAX_PBES2_COUNT_CEILING`](Self::MAX_PBES2_COUNT_CEILING) (1,000,000)
    /// is clamped to it: the ceiling bounds the CPU one token can cost
    /// (ADR 0023). Prefer high-entropy secrets over high iteration counts.
    ///
    /// ECDH-ES has the same up-to-[`max_recipients`](Self::max_recipients)
    /// multiplier: each candidate ECDH-ES recipient costs one scalar
    /// multiplication. That is cheap next to PBKDF2, so it is bounded only
    /// by the recipient count, not budgeted (ADR 0050 §2).
    ///
    /// RSA-OAEP-256 has the same multiplier and no budget: each candidate
    /// RSA-OAEP-256 recipient costs one private-key build (validation and
    /// CRT precomputation) and one private-key operation (RFC 8017
    /// §7.1.2), so a decrypt call costs at most
    /// [`max_recipients`](Self::max_recipients) of each. Both grow roughly
    /// with the cube of the modulus size, so with 4096-bit or larger keys,
    /// give the key and the recipients a `kid`, which narrows the
    /// candidates (RFC 7516 §4.1.6), or lower `max_recipients`.
    #[must_use]
    pub fn max_pbes2_count(mut self, n: u32) -> Self {
        self.max_pbes2_count = n.min(Self::MAX_PBES2_COUNT_CEILING);
        self
    }

    /// The largest plaintext a `zip: DEF` JWE may inflate to, in bytes
    /// (RFC 7516 §4.1.3, ADR 0017). Inflation stops, with
    /// [`Error::DecryptionFailed`], as soon as the output would exceed it.
    /// `0` rejects every compressed JWE. The default is
    /// [`DEFAULT_MAX_DECOMPRESSED_LEN`](Self::DEFAULT_MAX_DECOMPRESSED_LEN)
    /// (250,000). Only used with the `deflate` feature; without it `zip` is
    /// always [`Error::UnsupportedAlgorithm`].
    ///
    /// The value bounds the memory inflation may use, but is not reserved
    /// up front (ADR 0050 §4): the output buffer starts at most at
    /// [`DEFAULT_MAX_DECOMPRESSED_LEN`](Self::DEFAULT_MAX_DECOMPRESSED_LEN)
    /// bytes (less when the compressed input is too short to expand that
    /// far, RFC 1951 §3.2.5) and grows by doubling only as the output does,
    /// wiping each buffer it leaves. A growth step briefly holds the old and
    /// the new buffer, under 3× the output so far. Raising the value raises
    /// the memory one authenticated JWE can claim: up to about twice it
    /// during the last growth step.
    #[must_use]
    pub fn max_decompressed_len(mut self, n: usize) -> Self {
        self.max_decompressed_len = n;
        self
    }

    /// The `crit` extension names the caller understands and processes
    /// (RFC 7516 §4.1.13). A JWE whose `crit` names anything else fails with
    /// [`Error::UnknownCritical`]. The default is none. Replaces any earlier
    /// list.
    #[must_use]
    pub fn understood_crit(mut self, names: &[&str]) -> Self {
        self.understood_crit = names.iter().map(|&name| name.to_owned()).collect();
        self
    }

    /// The largest input accepted, in bytes; a longer one fails with
    /// [`Error::InvalidFormat`] before any parsing (ADR 0026 §7). The
    /// default is [`DEFAULT_MAX_INPUT_LEN`](Self::DEFAULT_MAX_INPUT_LEN)
    /// (1 MiB).
    #[must_use]
    pub fn max_input_len(mut self, n: usize) -> Self {
        self.max_input_len = n;
        self
    }

    /// The most recipients a JSON JWE may have (RFC 7516 §7.2.1); more fail
    /// with [`Error::InvalidFormat`] (ADR 0026 §7). The default is
    /// [`DEFAULT_MAX_RECIPIENTS`](Self::DEFAULT_MAX_RECIPIENTS) (16).
    ///
    /// The count also bounds the asymmetric work of one decrypt call: each
    /// candidate ECDH-ES recipient costs one scalar multiplication, and
    /// each candidate RSA-OAEP-256 recipient one private-key build and
    /// operation. For RSA keys of 4096 bits or more, give the key and the
    /// recipients a `kid` (RFC 7516 §4.1.6), so only the matching recipients
    /// are candidates, or lower this bound. PBES2 has its own budget,
    /// [`max_pbes2_count`](Self::max_pbes2_count).
    #[must_use]
    pub fn max_recipients(mut self, n: usize) -> Self {
        self.max_recipients = n;
        self
    }
}

/// A successfully decrypted JWE (RFC 7516 §5.2).
///
/// The plaintext is wiped on drop and `Debug` never prints it. The header
/// parts are returned as received, together with their union, the JOSE
/// Header that was used (RFC 7516 §5.2 step 4).
///
/// **Only [`protected_header`](Self::protected_header) is
/// integrity-protected** (RFC 7516 §5.1 steps 14–15: of the header parts,
/// it alone is in the AAD).
/// The shared and per-recipient unprotected headers, and therefore
/// [`header`](Self::header), which merges them in, can be changed by anyone
/// in transit without making decryption fail. Do not base security
/// decisions, such as key selection, content typing or trust, on `kid`,
/// `cty` or `jku` (or any other parameter) taken from them: read those from
/// the protected header (ADR 0050 §3, the JWE counterpart of ADR 0049 §2).
pub struct Decrypted {
    // core.rs builds this value.
    pub(super) plaintext: Zeroizing<Vec<u8>>,
    pub(super) protected_header: Option<Header>,
    pub(super) shared_header: Option<Header>,
    pub(super) recipient_header: Option<Header>,
    pub(super) header: Header,
    /// Wiped on drop: the AAD can carry application secrets (ADR 0050 §4,
    /// ADR 0030 §7.2).
    pub(super) aad: Option<Zeroizing<Vec<u8>>>,
    pub(super) recipient: usize,
}

impl Decrypted {
    /// The plaintext (RFC 7516 §5.2 step 16), decompressed if the JWE used
    /// `zip` (§5.2 step 17).
    pub fn plaintext(&self) -> &[u8] {
        &self.plaintext
    }

    /// The plaintext, in a buffer that is wiped on drop.
    pub fn into_plaintext(self) -> Zeroizing<Vec<u8>> {
        self.plaintext
    }

    /// The JWE Protected Header (RFC 7516 §2), if the JWE has one.
    pub fn protected_header(&self) -> Option<&Header> {
        self.protected_header.as_ref()
    }

    /// The JWE Shared Unprotected Header (RFC 7516 §7.2.1 `unprotected`),
    /// JSON only.
    ///
    /// **Not integrity-protected** (RFC 7516 §7.2.1): do not base security
    /// decisions on `kid`, `cty`, `jku` or any other parameter in it
    /// (ADR 0050 §3).
    pub fn shared_header(&self) -> Option<&Header> {
        self.shared_header.as_ref()
    }

    /// The JWE Per-Recipient Unprotected Header (RFC 7516 §7.2.1 `header`)
    /// of the recipient that decrypted, JSON only.
    ///
    /// **Not integrity-protected** (RFC 7516 §7.2.1): do not base security
    /// decisions on `kid`, `cty`, `jku` or any other parameter in it
    /// (ADR 0050 §3).
    pub fn recipient_header(&self) -> Option<&Header> {
        self.recipient_header.as_ref()
    }

    /// The JOSE Header used: the union of the three parts above (RFC 7516
    /// §5.2 step 4).
    ///
    /// **Not integrity-protected as a whole**: any parameter may come from
    /// an unprotected part, which anyone can change in transit. Do not base
    /// security decisions on `kid`, `cty` or `jku` taken from it; use
    /// [`protected_header`](Self::protected_header) (ADR 0050 §3).
    pub fn header(&self) -> &Header {
        &self.header
    }

    /// The JWE AAD (RFC 7516 §7.2.1 `aad`), decoded, JSON only. It is
    /// held in a buffer wiped on drop, as it can carry application secrets
    /// (ADR 0050 §4, ADR 0030 §7.2), and `Debug` never prints it.
    pub fn aad(&self) -> Option<&[u8]> {
        self.aad.as_deref().map(Vec::as_slice)
    }

    /// The index of the recipient that decrypted, in the order of the JWE's
    /// `recipients` (RFC 7516 §7.2.1); 0 for compact and flattened JWEs.
    pub fn recipient(&self) -> usize {
        self.recipient
    }
}

impl fmt::Debug for Decrypted {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Decrypted")
            .field("plaintext", &"<redacted>")
            .field("protected_header", &self.protected_header)
            .field("shared_header", &self.shared_header)
            .field("recipient_header", &self.recipient_header)
            .field("header", &self.header)
            .field("aad", &self.aad.as_ref().map(|_| "<redacted>"))
            .field("recipient", &self.recipient)
            .finish()
    }
}

/// The two JWE JSON Serialization syntaxes (RFC 7516 §7.2).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum JsonForm {
    /// General (RFC 7516 §7.2.1): a `recipients` array, any number of
    /// recipients.
    General,
    /// Flattened (RFC 7516 §7.2.2): one recipient, at the top level.
    Flattened,
}

/// A JWE serialization (RFC 7516 §3, §7).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Serialization {
    /// The JWE Compact Serialization (RFC 7516 §7.1).
    Compact,
    /// The JWE JSON Serialization (RFC 7516 §7.2), in either form.
    Json(JsonForm),
}

/// One recipient of an [`Encryptor`]: its key management algorithm, its
/// key and its options (RFC 7516 §5.1 steps 1–8).
///
/// The key is a public key (RSA, EC, OKP) or a symmetric key (`oct`: the
/// key for `dir`, A*KW and A*GCMKW, the password for PBES2). It is checked
/// against `alg` when encrypting (RFC 8725 §3.1, ADR 0007).
#[derive(Debug, Clone)]
pub struct Recipient {
    // core.rs reads these fields directly.
    pub(super) alg: KeyMgmtAlg,
    pub(super) key: Jwk,
    pub(super) header: Option<Header>,
    pub(super) options: RecipientOptions,
}

impl Recipient {
    /// A recipient that `alg` encrypts to with `key`.
    pub fn new(alg: KeyMgmtAlg, key: &Jwk) -> Self {
        Self {
            alg,
            key: key.clone(),
            header: None,
            options: RecipientOptions::default(),
        }
    }

    /// This recipient's Per-Recipient Unprotected Header (RFC 7516 §7.2.1
    /// `header`), JSON only. Replaces any earlier one. Not
    /// integrity-protected: the receiver must not trust its contents
    /// (ADR 0050 §3).
    #[must_use]
    pub fn header(mut self, header: Header) -> Self {
        self.header = Some(header);
        self
    }

    /// ECDH-ES Agreement PartyUInfo, raw bytes; sent base64url-encoded as
    /// `apu` (RFC 7518 §4.6.1.2). It must differ from `apv`.
    #[must_use]
    pub fn apu(mut self, apu: impl Into<Vec<u8>>) -> Self {
        self.options.apu = Some(apu.into());
        self
    }

    /// ECDH-ES Agreement PartyVInfo, raw bytes; sent base64url-encoded as
    /// `apv` (RFC 7518 §4.6.1.3). It must differ from `apu`.
    #[must_use]
    pub fn apv(mut self, apv: impl Into<Vec<u8>>) -> Self {
        self.options.apv = Some(apv.into());
        self
    }

    /// The PBES2 iteration count `p2c` (RFC 7518 §4.8.1.2). The default is
    /// 10,000 (ADR 0023), inside every mainstream library's decrypt bound.
    ///
    /// Encrypting accepts 1,000 (RFC 7518 §4.8.1.2's recommended minimum)
    /// to 1,000,000
    /// ([`DecryptPolicy::MAX_PBES2_COUNT_CEILING`], the most any decrypt
    /// policy allows; ADR 0050 §2). Outside that range encryption fails with
    /// [`Error::InvalidKey`]. A count above the decrypting side's
    /// [`DecryptPolicy::max_pbes2_count`] (10,000 by default) is
    /// [`Error::DecryptionFailed`] there.
    #[must_use]
    pub fn p2c(mut self, p2c: u32) -> Self {
        self.options.p2c = Some(p2c);
        self
    }

    /// The length of the random PBES2 salt input `p2s`, in bytes. The
    /// default is 16.
    ///
    /// Encrypting accepts 8 (RFC 7518 §4.8.1.1's minimum) to 256 (ADR 0050
    /// §2, which bounds the salt PBKDF2 hashes); outside that range
    /// encryption fails with [`Error::InvalidKey`].
    #[must_use]
    pub fn p2s_len(mut self, len: usize) -> Self {
        self.options.p2s_len = Some(len);
        self
    }
}

/// Builds and encrypts JWEs (RFC 7516 §5.1).
///
/// `enc` always goes in the JWE Protected Header. For compact and flattened
/// output, each recipient's `alg` and key management parameters do too; for
/// general JSON they go in the recipient's `header` (as RFC 7520 §5.13),
/// except for `dir` and ECDH-ES, which are protected in every form (ADR
/// 0050 §1: a direct mode's `alg` and `enc` must be protected to decrypt).
/// Every random value comes from the OS CSPRNG (ADR 0005).
///
/// ```no_run
/// use polyjose::jwa::{ContentEncAlg, KeyMgmtAlg};
/// use polyjose::jwe::{Encryptor, Recipient};
/// use polyjose::jwk::Jwk;
///
/// let key = Jwk::from_json(br#"{"kty":"oct","k":"GawgguFyGrWKav7AX4VKUg"}"#)?;
/// let jwe = Encryptor::new(ContentEncAlg::A128GCM)
///     .recipient(Recipient::new(KeyMgmtAlg::A128KW, &key))
///     .encrypt_compact(b"hello")?;
/// # Ok::<(), polyjose::Error>(())
/// ```
#[derive(Clone)]
pub struct Encryptor {
    // core.rs reads these fields directly.
    pub(super) enc: ContentEncAlg,
    pub(super) protected: Option<Header>,
    pub(super) shared: Option<Header>,
    /// Wiped on drop, in every clone: the AAD can carry application
    /// secrets (ADR 0050 §4, ADR 0030 §7.2).
    pub(super) aad: Option<Zeroizing<Vec<u8>>>,
    /// Set only with the `deflate` feature (RFC 7516 §4.1.3).
    pub(super) compress: bool,
    pub(super) recipients: Vec<Recipient>,
}

impl Encryptor {
    /// An encryptor for content encryption algorithm `enc` (RFC 7516
    /// §4.1.2), with no recipients yet.
    pub fn new(enc: ContentEncAlg) -> Self {
        Self {
            enc,
            protected: None,
            shared: None,
            aad: None,
            compress: false,
            recipients: Vec::new(),
        }
    }

    /// Extra parameters for the JWE Protected Header (RFC 7516 §4), such
    /// as `typ`, `cty` or `kid`. `alg`, `enc`, `zip` and the key management
    /// parameters are set by polyjose and must not be given here.
    #[must_use]
    pub fn protected_header(mut self, header: Header) -> Self {
        self.protected = Some(header);
        self
    }

    /// The extra JWE Protected Header parameters the caller set with
    /// [`protected_header`](Self::protected_header), if any.
    ///
    /// Crate-internal: the nested-JWT issuer reads it to add `cty: "JWT"`
    /// (RFC 7519 §5.2, §7.1 step 5) to a copy of the caller's encryptor,
    /// keeping the caller's other parameters; in the compact serialization
    /// the protected header is the whole JOSE Header (RFC 7516 §7.1).
    pub(crate) fn protected(&self) -> Option<&Header> {
        self.protected.as_ref()
    }

    /// Whether [`compress`](Self::compress) was called, so that encrypting
    /// would set `zip: "DEF"` in the JWE Protected Header (RFC 7516
    /// §4.1.3).
    ///
    /// Crate-internal: the nested-JWT issuer refuses compression before
    /// encryption (RFC 8725 §3.6: it "SHOULD NOT" be done).
    pub(crate) fn compresses(&self) -> bool {
        self.compress
    }

    /// The JWE Shared Unprotected Header (RFC 7516 §7.2.1 `unprotected`),
    /// JSON only. Not integrity-protected: the receiver must not trust its
    /// contents (ADR 0050 §3).
    #[must_use]
    pub fn shared_unprotected_header(mut self, header: Header) -> Self {
        self.shared = Some(header);
        self
    }

    /// The JWE AAD (RFC 7516 §7.2.1 `aad`), integrity protected but not
    /// encrypted, JSON only.
    ///
    /// The encryptor keeps it in a buffer wiped on drop, as it can carry
    /// application secrets (ADR 0050 §4, ADR 0030 §7.2), and `Debug` prints
    /// only its length. A `Vec<u8>` passed here is moved in, not copied.
    #[must_use]
    pub fn aad(mut self, aad: impl Into<Vec<u8>>) -> Self {
        self.aad = Some(Zeroizing::new(aad.into()));
        self
    }

    /// Compresses the plaintext with `zip: DEF` before encrypting it
    /// (RFC 7516 §4.1.3, §5.1 step 11). Compression can reveal information
    /// about the plaintext (RFC 8725 §3.6); use it only for data an attacker
    /// cannot influence.
    ///
    /// This method exists in every build, so the binding surface's
    /// `compress` option is the same on every platform (ADR 0030 §4). The
    /// codec is the off-by-default `deflate` feature (ADR 0017): without
    /// it, encrypting after `compress()` fails with
    /// [`Error::UnsupportedAlgorithm`], among the support checks and so
    /// before anything is drawn from the random source, and never produces
    /// an uncompressed JWE in its place (ADR 0017 §5, ADR 0030 §4).
    #[must_use]
    pub fn compress(mut self) -> Self {
        self.compress = true;
        self
    }

    /// Adds a recipient (RFC 7516 §5.1 step 8).
    #[must_use]
    pub fn recipient(mut self, recipient: Recipient) -> Self {
        self.recipients.push(recipient);
        self
    }

    /// Encrypts `plaintext` in the JWE Compact Serialization (RFC 7516
    /// §7.1).
    ///
    /// # Errors
    ///
    /// [`Error::InvalidFormat`] unless there is exactly one recipient and
    /// no shared unprotected header, per-recipient header or AAD (RFC 7516
    /// §7.1); otherwise the header, key and algorithm errors of
    /// [`crate::jwe`].
    pub fn encrypt_compact(&self, plaintext: &[u8]) -> Result<String> {
        let parts =
            pipeline::encrypt(self, plaintext, Serialization::Compact, &mut OsRandom, None)?;
        compact::serialize(&parts)
    }

    /// Encrypts `plaintext` in the JWE JSON Serialization (RFC 7516 §7.2),
    /// general or flattened.
    ///
    /// # Errors
    ///
    /// [`Error::InvalidFormat`] for no recipients, or a flattened JWE with
    /// more than one (RFC 7516 §7.2.2); otherwise the header, key and
    /// algorithm errors of [`crate::jwe`].
    pub fn encrypt_json(&self, plaintext: &[u8], form: JsonForm) -> Result<String> {
        let form_ = Serialization::Json(form);
        let parts = pipeline::encrypt(self, plaintext, form_, &mut OsRandom, None)?;
        json::serialize(&parts, form)
    }

    /// **Insecure: reproduces published test vectors only.** Encrypts like
    /// [`encrypt_compact`](Self::encrypt_compact) or
    /// [`encrypt_json`](Self::encrypt_json), drawing every random value from
    /// `rng` in the documented order, so an RFC 7516 Appendix A or RFC 7520
    /// §5 output can be reproduced byte for byte (ADR 0005, ADR 0041 §3).
    ///
    /// `protected_b64`, if given, is the RFC's exact encoded protected
    /// header. It is used verbatim for the AAD and the output, but only if
    /// it decodes to a header JSON-equal to the one polyjose builds (member
    /// order aside), so a test cannot make polyjose emit a header it would
    /// not produce.
    ///
    /// # Errors
    ///
    /// As the method for `form`; [`Error::RandomnessUnavailable`] when
    /// `rng` runs out; [`Error::InvalidFormat`] when `protected_b64` does
    /// not match the built header.
    #[cfg(feature = "insecure-test-vectors")]
    pub fn encrypt_reproducible(
        &self,
        plaintext: &[u8],
        form: Serialization,
        rng: &mut FixedRng,
        protected_b64: Option<&str>,
    ) -> Result<String> {
        let parts = pipeline::encrypt(self, plaintext, form, rng, protected_b64)?;
        match form {
            Serialization::Compact => compact::serialize(&parts),
            Serialization::Json(json_form) => json::serialize(&parts, json_form),
        }
    }
}

/// Written by hand so the JWE AAD (RFC 7516 §7.2.1 `aad`) is never
/// printed: it can carry application secrets, so only whether it is set and
/// its length are shown (ADR 0050 §4; secrets are never in `Debug`, ADR
/// 0030 §7.5). Recipients are counted, not listed; the headers are shown
/// only as set or not set. For example:
///
/// ```text
/// Encryptor { enc: A128GCM, recipients: 1, protected_header: false,
///     shared_header: false, aad: Some(<redacted, len=13>), compress: false }
/// ```
impl fmt::Debug for Encryptor {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        /// The AAD's length, in place of its bytes.
        struct RedactedLen(usize);
        impl fmt::Debug for RedactedLen {
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                write!(f, "<redacted, len={}>", self.0)
            }
        }
        f.debug_struct("Encryptor")
            .field("enc", &self.enc)
            .field("recipients", &self.recipients.len())
            .field("protected_header", &self.protected.is_some())
            .field("shared_header", &self.shared.is_some())
            .field("aad", &self.aad.as_ref().map(|aad| RedactedLen(aad.len())))
            .field("compress", &self.compress)
            .finish()
    }
}

/// Decrypts a JWE in the Compact Serialization (RFC 7516 §5.2, §7.1) with
/// `key`, if `policy` allows its `alg` and `enc`.
///
/// # Errors
///
/// The error contract of [`crate::jwe`]: [`Error::InvalidFormat`],
/// [`Error::InvalidBase64`], [`Error::InvalidJson`],
/// [`Error::InvalidHeader`] and [`Error::UnknownCritical`] for malformed
/// input; [`Error::UnsupportedAlgorithm`], [`Error::AlgorithmNotAllowed`]
/// and [`Error::KeyAlgorithmMismatch`] before any cryptography; and a
/// detail-free [`Error::DecryptionFailed`] for everything after.
pub fn decrypt_compact(token: &str, key: &Jwk, policy: &DecryptPolicy) -> Result<Decrypted> {
    let parts = compact::parse(token, policy)?;
    pipeline::decrypt(parts, key, policy)
}

/// Decrypts a JWE in the JSON Serialization (RFC 7516 §5.2, §7.2),
/// general or flattened, with `key`, if `policy` allows its `alg` and
/// `enc`. With several recipients, each one `key` can serve is tried in
/// order and the first that decrypts wins ([`Decrypted::recipient`]).
///
/// # Errors
///
/// As [`decrypt_compact`].
pub fn decrypt_json(jwe: &str, key: &Jwk, policy: &DecryptPolicy) -> Result<Decrypted> {
    let parts = json::parse(jwe, policy)?;
    pipeline::decrypt(parts, key, policy)
}

#[cfg(test)]
mod tests {
    use super::*;

    fn policy() -> DecryptPolicy {
        DecryptPolicy::new(&[KeyMgmtAlg::A128KW], &[ContentEncAlg::A128GCM])
            .unwrap_or_else(|e| panic!("{e}"))
    }

    #[test]
    fn empty_allow_lists_are_rejected() {
        let alg = [KeyMgmtAlg::A128KW];
        let enc = [ContentEncAlg::A128GCM];
        let not_allowed = Err(Error::AlgorithmNotAllowed);
        assert_eq!(DecryptPolicy::new(&[], &enc).map(|_| ()), not_allowed);
        assert_eq!(DecryptPolicy::new(&alg, &[]).map(|_| ()), not_allowed);
        assert_eq!(DecryptPolicy::new(&[], &[]).map(|_| ()), not_allowed);
        assert!(DecryptPolicy::new(&alg, &enc).is_ok());
    }

    #[test]
    fn defaults_follow_adr_0023_0017_and_0026() {
        let p = policy();
        assert_eq!(p.allowed_algs, [KeyMgmtAlg::A128KW]);
        assert_eq!(p.allowed_enc, [ContentEncAlg::A128GCM]);
        assert_eq!(p.max_pbes2_count, 10_000);
        assert_eq!(p.max_decompressed_len, 250_000);
        assert_eq!(p.max_input_len, 1 << 20);
        assert_eq!(p.max_recipients, 16);
        assert!(p.understood_crit.is_empty());
    }

    #[test]
    fn max_pbes2_count_clamps_at_the_ceiling() {
        assert_eq!(policy().max_pbes2_count(1_000).max_pbes2_count, 1_000);
        assert_eq!(policy().max_pbes2_count(0).max_pbes2_count, 0);
        let ceiling = DecryptPolicy::MAX_PBES2_COUNT_CEILING;
        assert_eq!(ceiling, 1_000_000);
        assert_eq!(policy().max_pbes2_count(ceiling).max_pbes2_count, ceiling);
        assert_eq!(
            policy().max_pbes2_count(ceiling + 1).max_pbes2_count,
            ceiling
        );
        assert_eq!(policy().max_pbes2_count(u32::MAX).max_pbes2_count, ceiling);
    }

    #[test]
    fn builders_set_their_limit() {
        let p = policy()
            .max_decompressed_len(0)
            .max_input_len(10)
            .max_recipients(1)
            .understood_crit(&["exp"])
            .understood_crit(&["a", "b"]);
        assert_eq!(p.max_decompressed_len, 0);
        assert_eq!(p.max_input_len, 10);
        assert_eq!(p.max_recipients, 1);
        assert_eq!(p.understood_crit, ["a", "b"]);
    }

    #[test]
    fn unimplemented_algorithms_may_be_listed() {
        // They fail later with UnsupportedAlgorithm (support before allow-list).
        let algs = [KeyMgmtAlg::from("RSA1_5"), KeyMgmtAlg::RsaOaep256];
        let p = DecryptPolicy::new(&algs, &[ContentEncAlg::from("A128CTR")]);
        assert!(p.is_ok());
    }

    fn oct_key() -> Jwk {
        Jwk::from_json(br#"{"kty":"oct","k":"GawgguFyGrWKav7AX4VKUg"}"#)
            .unwrap_or_else(|e| panic!("{e}"))
    }

    #[test]
    fn builders_record_what_they_are_given() {
        let r = Recipient::new(KeyMgmtAlg::EcdhEs, &oct_key())
            .apu(b"Alice".to_vec())
            .apv(*b"Bob")
            .p2c(4096)
            .p2s_len(8)
            .header(Header::new());
        assert_eq!(r.alg, KeyMgmtAlg::EcdhEs);
        assert_eq!(r.options.apu.as_deref(), Some(&b"Alice"[..]));
        assert_eq!(r.options.apv.as_deref(), Some(&b"Bob"[..]));
        assert_eq!((r.options.p2c, r.options.p2s_len), (Some(4096), Some(8)));
        assert!(r.header.is_some());
        let e = Encryptor::new(ContentEncAlg::A128GCM)
            .protected_header(Header::new())
            .shared_unprotected_header(Header::new())
            .aad(*b"aad")
            .recipient(r.clone())
            .recipient(r);
        assert_eq!(e.enc, ContentEncAlg::A128GCM);
        assert!(e.protected.is_some() && e.shared.is_some());
        assert_eq!(e.aad.as_deref().map(Vec::as_slice), Some(&b"aad"[..]));
        assert_eq!(e.recipients.len(), 2);
        assert!(!e.compress);
        assert!(e.compress().compress);
    }

    /// ADR 0050 §4, ADR 0030 §7.2: the JWE AAD (RFC 7516 §7.2.1 `aad`) can
    /// carry application secrets, so the encryptor holds it, and every
    /// clone of it, in a buffer wiped on drop (0006 security audit L3).
    /// `Debug` still prints only its length (ADR 0030 §7.5).
    #[test]
    fn encryptor_holds_the_aad_in_a_buffer_wiped_on_drop() {
        fn is_zeroizing(_: &Option<Zeroizing<Vec<u8>>>) {}
        let e = Encryptor::new(ContentEncAlg::A128GCM).aad(b"MARKERaad".to_vec());
        is_zeroizing(&e.aad);
        let copy = e.clone();
        is_zeroizing(&copy.aad);
        assert_eq!(
            copy.aad.as_deref().map(Vec::as_slice),
            Some(&b"MARKERaad"[..])
        );
        let debug = format!("{e:?}");
        assert!(!debug.contains("MARKER"), "{debug}");
        assert!(debug.contains("aad: Some(<redacted, len=9>)"), "{debug}");
    }

    #[test]
    fn decrypted_redacts_plaintext_and_aad() {
        let d = Decrypted {
            plaintext: Zeroizing::new(b"MARKERsecret".to_vec()),
            protected_header: None,
            shared_header: None,
            recipient_header: None,
            header: Header::new(),
            aad: Some(Zeroizing::new(b"MARKERaad".to_vec())),
            recipient: 2,
        };
        // ADR 0050 §4: the AAD is held in a buffer wiped on drop.
        let _: &Option<Zeroizing<Vec<u8>>> = &d.aad;
        let debug = format!("{d:?}");
        assert!(!debug.contains("MARKER"), "{debug}");
        assert!(debug.contains("<redacted>"), "{debug}");
        assert_eq!(
            (d.plaintext(), d.aad(), d.recipient()),
            (&b"MARKERsecret"[..], Some(&b"MARKERaad"[..]), 2)
        );
        assert!(d.protected_header().is_none() && d.shared_header().is_none());
        assert!(d.recipient_header().is_none() && d.header().alg.is_none());
        assert_eq!(d.into_plaintext().as_slice(), b"MARKERsecret");
    }

    #[test]
    fn compact_round_trips_through_the_public_entry_points() {
        let key = oct_key();
        let policy = policy();
        let e = Encryptor::new(ContentEncAlg::A128GCM)
            .recipient(Recipient::new(KeyMgmtAlg::A128KW, &key));
        let token = e.encrypt_compact(b"x").unwrap_or_else(|e| panic!("{e}"));
        assert_eq!(token.split('.').count(), 5);
        let d = decrypt_compact(&token, &key, &policy).unwrap_or_else(|e| panic!("{e}"));
        assert_eq!((d.plaintext(), d.recipient()), (&b"x"[..], 0));
        assert!(d.shared_header().is_none() && d.recipient_header().is_none());
        assert!(d.aad().is_none());
        assert_eq!(d.protected_header(), Some(d.header()));
        assert!(decrypt_compact("a.b.c.d.e", &key, &policy).is_err());
    }
}