pgp 0.19.0

OpenPGP implementation in Rust
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
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
extern crate rand;
#[macro_use]
extern crate pretty_assertions;
extern crate serde_json;
#[macro_use]
extern crate serde;
extern crate pgp;
extern crate pretty_env_logger;
#[macro_use]
extern crate log;

use std::{collections::VecDeque, fs::File, io::BufReader};

use pgp::{
    composed::{
        CleartextSignedMessage, Deserializable, DetachedSignature, KeyType, Message,
        MessageBuilder, PlainSessionKey, SecretKeyParamsBuilder, SignedPublicKey, SignedSecretKey,
    },
    crypto::{hash::HashAlgorithm, sym::SymmetricKeyAlgorithm},
    packet::{LiteralData, Packet, PacketParser},
    ser::Serialize,
    types::{KeyDetails, KeyId, Password},
};
use rand::SeedableRng;
use rand_chacha::ChaCha8Rng;

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct Testcase {
    typ: Option<String>,
    decrypt_key: String,
    passphrase: String,
    verify_key: Option<String>,
    filename: Option<String>,
    timestamp: Option<u64>,
    textcontent: Option<String>,
    keyid: Option<String>,
}

fn test_parse_msg(entry: &str, base_path: &str, _is_normalized: bool) {
    let _ = pretty_env_logger::try_init();

    // TODO: verify filename
    let n = format!("{base_path}/{entry}");
    let mut file = File::open(&n).unwrap_or_else(|_| panic!("no file: {}", &n));

    let details: Testcase = serde_json::from_reader(&mut file).unwrap();
    info!(
        "Testcase: {}",
        serde_json::to_string_pretty(&details).unwrap()
    );

    let mut decrypt_key_file =
        File::open(format!("{}/{}", base_path, details.decrypt_key)).unwrap();
    let (decrypt_key, _headers) = SignedSecretKey::from_armor_single(&mut decrypt_key_file)
        .expect("failed to read decryption key");
    decrypt_key
        .verify_bindings()
        .expect("invalid decryption key");

    let decrypt_id = hex::encode(decrypt_key.legacy_key_id());

    info!("decrypt key (ID={})", &decrypt_id);
    if let Some(id) = &details.keyid {
        assert_eq!(id, &decrypt_id, "invalid keyid");
    }

    let verify_key = if let Some(verify_key_str) = details.verify_key.clone() {
        let mut verify_key_file = File::open(format!("{base_path}/{verify_key_str}")).unwrap();
        let (verify_key, _headers) = SignedPublicKey::from_armor_single(&mut verify_key_file)
            .expect("failed to read verification key");
        verify_key
            .verify_bindings()
            .expect("invalid verification key");

        let verify_id = hex::encode(verify_key.legacy_key_id());
        info!("verify key (ID={})", &verify_id);
        Some(verify_key)
    } else {
        None
    };

    let file_name = entry.replace(".json", ".asc");
    let cipher_file_path = format!("{base_path}/{file_name}");
    // let cipher_file = File::open(&cipher_file_path).unwrap();

    let (message, _headers) =
        Message::from_armor_file(cipher_file_path).expect("failed to parse message");
    info!("message: {:?}", &message);

    match &message {
        Message::Encrypted { .. } => {
            let decrypted = message
                .decrypt(&details.passphrase.into(), &decrypt_key)
                .expect("failed to init decryption");

            let mut msg = decrypted.decompress().expect("compression");
            dbg!(&msg);
            let data = msg.as_data_string().unwrap();

            // TODO: figure out how to do roundtrips
            // serialize and check we get the same thing
            // let serialized = decrypted.to_armored_bytes(None.into()).unwrap();
            // // and parse them again
            // let (decrypted2, _headers) =
            //     Message::from_armor(&serialized[..]).expect("failed to parse round2");
            // assert_eq!(decrypted, decrypted2);

            assert_eq!(data, details.textcontent.unwrap_or_default());

            if let Some(verify_key) = verify_key {
                msg.verify(&verify_key.primary_key)
                    .expect("message verification failed");
            }
        }
        Message::Signed { reader, .. } => {
            println!("signature: {:?}", reader.signatures());
        }
        _ => {
            // TODO: some other checks?
            panic!("this test should not have anything else?");
        }
    }

    // TODO: how to roundtrip?
    // // serialize and check we get the same thing
    // let serialized = message.to_armored_string(Some(&headers).into()).unwrap();
    // if is_normalized {
    //     let mut cipher_file = File::open(&cipher_file_path).unwrap();
    //     let mut expected_bytes = String::new();
    //     cipher_file.read_to_string(&mut expected_bytes).unwrap();
    //     // normalize read in line endings to unix
    //     assert_eq!(serialized, expected_bytes.replace("\r\n", "\n"));
    // }
    // // and parse them again
    // let (message2, headers2) =
    //     Message::from_armor(serialized.as_bytes()).expect("failed to parse round2");
    // assert_eq!(headers, headers2);
    // assert_eq!(message, message2);
}

macro_rules! msg_test {
    ($name:ident, $pos:expr, $normalized:expr) => {
        #[test]
        fn $name() {
            test_parse_msg(
                &format!("{}.json", $pos),
                "./tests/openpgp-interop/testcases/messages",
                $normalized,
            );
        }
    };
}

// RSA
msg_test!(msg_gnupg_v1_001, "gnupg-v1-001", false);
// Elgamal
// msg_test!(msg_gnupg_v1_002, "gnupg-v1-002", true);
// RSA
msg_test!(msg_gnupg_v1_003, "gnupg-v1-003", false);

msg_test!(msg_gnupg_v1_4_11_001, "gnupg-v1-4-11-001", true);
msg_test!(msg_gnupg_v1_4_11_002, "gnupg-v1-4-11-002", false);
msg_test!(msg_gnupg_v1_4_11_003, "gnupg-v1-4-11-003", true);
msg_test!(msg_gnupg_v1_4_11_004, "gnupg-v1-4-11-004", true);
msg_test!(msg_gnupg_v1_4_11_005, "gnupg-v1-4-11-005", true);
msg_test!(msg_gnupg_v1_4_11_006, "gnupg-v1-4-11-006", false);
msg_test!(msg_gnupg_v2_0_17_001, "gnupg-v2-0-17-001", true);
msg_test!(msg_gnupg_v2_0_17_002, "gnupg-v2-0-17-002", false);
msg_test!(msg_gnupg_v2_0_17_003, "gnupg-v2-0-17-003", true);
msg_test!(msg_gnupg_v2_0_17_004, "gnupg-v2-0-17-004", true);
msg_test!(msg_gnupg_v2_0_17_005, "gnupg-v2-0-17-005", true);
msg_test!(msg_gnupg_v2_0_17_006, "gnupg-v2-0-17-006", true);
// parsing error
// ECDH key - nist p256
// msg_test!(msg_gnupg_v2_1_5_001, "gnupg-v2-1-5-001", true);

// parsing error
// ECDH key - nist p384
// msg_test!(msg_gnupg_v2_1_5_002, "gnupg-v2-1-5-002", true);
// parsing error
// ECDH key - nist p512
// msg_test!(msg_gnupg_v2_1_5_003, "gnupg-v2-1-5-003", true);

msg_test!(msg_gnupg_v2_10_001, "gnupg-v2-10-001", true);
msg_test!(msg_gnupg_v2_10_002, "gnupg-v2-10-002", true);
msg_test!(msg_gnupg_v2_10_003, "gnupg-v2-10-003", true);
msg_test!(msg_gnupg_v2_10_004, "gnupg-v2-10-004", false);
msg_test!(msg_gnupg_v2_10_005, "gnupg-v2-10-005", true);
msg_test!(msg_gnupg_v2_10_006, "gnupg-v2-10-006", true);
msg_test!(msg_gnupg_v2_10_007, "gnupg-v2-10-007", true);

// ECDH
// msg_test!(msg_e2e_001, "e2e-001", true);
// ECDH
// msg_test!(msg_e2e_002, "e2e-001", true);

msg_test!(msg_pgp_10_0_001, "pgp-10-0-001", false);
msg_test!(msg_pgp_10_0_002, "pgp-10-0-002", false);
msg_test!(msg_pgp_10_0_003, "pgp-10-0-003", false);
msg_test!(msg_pgp_10_0_004, "pgp-10-0-004", false);
msg_test!(msg_pgp_10_0_005, "pgp-10-0-005", false);
msg_test!(msg_pgp_10_0_006, "pgp-10-0-006", false);
msg_test!(msg_pgp_10_0_007, "pgp-10-0-007", false);

msg_test!(msg_camellia128_001, "camellia128-001", false);
msg_test!(msg_camellia192_001, "camellia192-001", false);
msg_test!(msg_camellia256_001, "camellia256-001", false);

// ECDH
// msg_test!(msg_openkeychain_001, "openkeychain-001", true);

msg_test!(msg_openpgp_001, "openpgp-001", false);

macro_rules! msg_test_js {
    ($name:ident, $pos:expr, $normalized:expr) => {
        #[test]
        fn $name() {
            test_parse_msg(&format!("{}.json", $pos), "./tests/openpgpjs", $normalized);
        }
    };
}

msg_test_js!(msg_openpgpjs_x25519, "x25519", true);

#[test]
fn msg_partial_body_len() {
    let msg_file = "./tests/partial.asc";
    Message::from_armor_file(msg_file).expect("failed to parse message");
}

#[test]
fn msg_regression_01() {
    let msg_file = "./tests/regression-01.asc";
    Message::from_armor_file(msg_file).expect("failed to parse message");
}

#[test]
fn msg_large_indeterminate_len() {
    let _ = pretty_env_logger::try_init();

    let msg_file = "./tests/indeterminate.asc";
    let (message, _headers) = Message::from_armor_file(msg_file).expect("failed to parse message");

    let mut key_file = File::open("./tests/openpgpjs/x25519.sec.asc").unwrap();
    let (decrypt_key, _headers) =
        SignedSecretKey::from_armor_single(&mut key_file).expect("failed to parse key");

    let decrypted = message
        .decrypt(&"moon".into(), &decrypt_key)
        .expect("failed to decrypt message");

    let mut msg = decrypted.decompress().unwrap();
    let raw = msg.as_data_string().unwrap();

    assert_eq!(
        raw,
        "Content-Type: text/plain; charset=us-ascii
Autocrypt-Gossip: addr=deltabot@codespeak.net; keydata=
  xsDNBFur7GMBDACeGJhpeP4xGZCUQcjFj1pPSXjWeFlezAo5Jkw5VivJoJRByJxO2dzg9HtAIYcgg2
  WR6b57rx/v9CyU6Ev653j4DMLghoKdyC/kGm/44pi9At4hXtXzgfp6ixKNuJnMfRC3fe0G5oRQY40c
  1AdaPDpfYaKT+dlFQLZpFXr+Jz+Y8Br717NXAYJUUOAWnH0oRkI1EfdttwF7kki0gLB93BvVc2hmE5
  xMiWEUHV+OlyqYeIJEtopGiqRRAKKZXmwkiQktiUTB+SaixAReXJmJQ1LW6lzceV7eqPC+NIUplv0N
  fTI4YcFCAbZr1Jl1Wo70oEXOidrH4LEOGLKlj9z6FoPRnPu3PhpHbCE0emimADSnc17t5m935emnMk
  6Bo0zl6ODzaqAYti6TMxCOcYtL+ypERweaprgL3BqQF7au7abCGM1QuOWObInQRLkO+hoXbSTIUhBo
  Ount8oa/BVwoWcxQaupI45IvT3TvTfFrW52zyxKTbfrA3MEi0SwBB4ZK4t8AEQEAAc0YPGRlbHRhYm
  90QGNvZGVzcGVhay5uZXQ+wsD8BBMBCAAmBQJbq+xjBQkAAAAAAhkBAhsDBgsJBwMCAQYVCAkKCwIC
  FgICHgEACgkQouc5Q3Wnbc/I+Qv9EDxYA1buPKfN42OcIhCnnMfc/r4uCtXjJri+/gxHRjkpPMWW9o
  /sRMPWKiFV9UUYeDKkln1Eh4mdI/RdyO6Q47znsBcwJzyddZoFD6VeSi3+oRM1q1ykDlczJZ639mfO
  eVH+ebPGUX/3apMPSUlflphQ1PKJo6Nwm6/oTfi+XQWwdj8IhHh801XEdqUlizVAWNAsy50COI5a+F
  Kxslfz6I1ce5ezsHNUCtVw0YP6/+YaeIsv+nazB1038jgjpeVJz2Xt4svWTpkgFF/LLeEXgdcZnI8Z
  u+IWdPSzz434YAynr68VdTjJoc2B+YPfqP38lkqnPAqaavwq/5/NLwJ6WCyVa/HCEu7OiYVEkXC4JX
  ZD4xdejrWG9p4JVQcwUv1rewbVqBMQ30ZlsBMAmEOh4+wkML+U+00/9LlQEv2wsLZMQ1OQVjxfncGb
  /tsOOavm25jhQnytwyM2j3eItnNni93Echqa0Fb3vQIB5ZrRtFVx15LomgsNWPHJN/BSeGuBzsDNBF
  ur7GMBDADPo8r8T2sDHaJ7NnVxxh5+dc9jgQkKdMmAba+RyJ2k0w5G4zKYQ5IZ1LEK5hXMkJ8dOOPW
  lUxvMqD732C2AwllLden4ZZNnMG/sXBNJXFcIOHMjG+Q8SzJ1q5tOQsqXGZ3+MRR9mfvJ8KLfaWWyY
  +I2Ow5gCkrueo/mTkCnVjOzQltuqUi6aG0f8B44A5+S0EfA4tFF0b0zJgReH4DfhQV7g+nUgbCmb3w
  EdRnrXL01JkDw5Zjy1Fx9QYNYzXk1hzWZugU9pSrMw7Sx4Zox+wWVCYTKfBvuJVVgNUDqv+B7RejeP
  OnMm2bI+AG3DgAOTaeTLa0xOqYF3n7tegFJTLCXYG9wUO8M76jttAjb8J3l9D/wiM+F+UPQcBFdRYZ
  JySUITyakgt8BrKzhtTKj/7lPdMYp+jglFFvvspnCZ3OJt0fHc9r58fFIdpuF/Wb7kEQkemoAZev2t
  1ZIEhFQFDFwWzJA3ymiRLwV/51JeH41N9TvKbG+bSxybIGIjZ26ccAEQEAAcLA5QQYAQgADwUCW6vs
  YwUJAAAAAAIbDAAKCRCi5zlDdadtz9U0C/0f+DIxh2IKK64MyWsCmv7BHIHEETtrXwQtYL9edOqrd2
  ty3f+QZ0MDS6/9f0/h4BWNa2WOxpUlamilAW1q2+JvLwKwwm7RVSOfmpJ0fVJn+d6E2LW8iz7rELza
  +6/SIivXkBHxZK9ykMdk4k1QlT6dA32mHzR+O7qL42htifHlzU7RTZio29oF0wOC2MHX96qMFXKS6z
  4s/6syEdrV4OZsyGo+/IrQubahrDE7/vDEHU0ez2AzmZuptJ6P3XcbzvEN1qwvrWO11DE22aCj7Iuv
  OoWICXyPb0u5DjSeejj5YoJ9frBiOSN5a/2Np4EII/3BY16cKDMEcE8104vIVEhmjzUWEWRP+BfUQm
  wU1xKr4A8VD/4iJzTOJr8wmsmyUyfrBJ378AoJrw3buuaOMxGX58RkN7Nv0djnfnmpwr73hmLlw9sr
  BS0T8vAI6psuMcmu/Oh2MUfnExZdYryW+/zOYWnGeEOi0ZiP/0KEZ5ePlchn/DlE549gB2Ht+U97na
  I=
Autocrypt-Gossip: addr=holger@merlinux.eu; keydata=
  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
  1CvHBWNRmnPMaFMeSpgJ

test1
"
    );
}

#[test]
fn msg_literal_signature() {
    let (pkey, _) = SignedPublicKey::from_armor_single(
        File::open("./tests/autocrypt/alice@autocrypt.example.pub.asc").unwrap(),
    )
    .unwrap();
    let (msg, _) = Message::from_armor_file("./tests/literal-text-signed.asc")
        .expect("failed to parse message");

    let mut msg = msg.decompress().unwrap();
    msg.verify_read(&pkey).unwrap();
}

#[test]
fn binary_msg_password() {
    // encrypted README.md using gpg
    let message = Message::from_file("./tests/binary_password.pgp").unwrap();
    let decrypted = message.decrypt_with_password(&"1234".into()).unwrap();
    let decompressed = decrypted.decompress().unwrap();

    assert!(decompressed.is_literal());
    assert_eq!(
        decompressed.literal_data_header().unwrap().file_name(),
        "README.md"
    );
}

/// Tests decryption of a message that uses the Wildcard KeyID "0000000000000000" is its PKESK.
///
/// Test message comes from the "Recipient IDs" test in the OpenPGP interoperability test suite.
#[test]
fn wildcard_id_decrypt() {
    let (skey, _headers) = SignedSecretKey::from_armor_single(
        std::fs::File::open("./tests/draft-bre-openpgp-samples-00/bob.sec.asc").unwrap(),
    )
    .unwrap();

    let (msg, _) = Message::from_armor_file("./tests/wildcard.msg").expect("msg");

    let mut dec = msg.decrypt(&Password::empty(), &skey).expect("decrypt");

    let decrypted = dec.as_data_string().unwrap();
    assert_eq!(&decrypted, "Hello World :)");
}

/// Tests decryption of a message that is encrypted to a symmetrical secret.
#[test]
fn skesk_decrypt() {
    let (msg, _) = Message::from_armor_file("./tests/sym-password.msg").expect("msg");

    let mut dec = msg
        .decrypt_with_password(&Password::from("password"))
        .expect("decrypt_with_password");

    let decrypted = dec.as_data_string().unwrap();
    assert_eq!(&decrypted, "hello world");
}

/// Tests decryption of a message that was encrypted by PGP 6.5.8 to a v3 RSA key.
/// The message uses a historical SED encryption container.
#[test]
fn pgp6_decrypt() {
    let (skey, _headers) = SignedSecretKey::from_armor_single(
        std::fs::File::open("./tests/pgp6/alice.sec.asc").unwrap(),
    )
    .unwrap();

    let (msg, _) = Message::from_armor_file("./tests/pgp6/hello.msg").expect("msg");
    dbg!(&msg);

    let dec = msg
        .decrypt_legacy(&Password::empty(), &skey)
        .expect("decrypt");
    let mut dec = dec.decompress().expect("decompress");

    let decrypted = dec.as_data_string().unwrap();
    assert_eq!(&decrypted, "hello world\n");
}

/// Tests that decompressing compression quine does not result in stack overflow.
/// quine.out comes from <https://mumble.net/~campbell/misc/pgp-quine/>
/// See <https://mumble.net/~campbell/2013/10/08/compression> for details.
#[test]
fn test_compression_quine() {
    // Public key does not matter as the message is not signed.
    let (skey, _headers) = SignedSecretKey::from_armor_single(
        std::fs::File::open("./tests/autocrypt/alice@autocrypt.example.sec.asc").unwrap(),
    )
    .unwrap();
    let pkey = skey.public_key();

    let msg = Message::from_file("./tests/quine.out").unwrap();
    let mut msg = msg.decompress().unwrap();
    let res = msg.as_data_vec().unwrap();
    assert_eq!(res.len(), 176);

    let msg = Message::from_file("./tests/quine.out").unwrap();
    assert!(msg.verify(pkey).is_err());
}

#[test]
fn test_text_signature_normalization() {
    // Test verifying an inlined signed message.
    //
    // The signature type is 0x01 ("Signature of a canonical text document").
    //
    // The literal data packet (which is in binary mode) contains the output of:
    // echo -en "foo\nbar\r\nbaz"
    //
    // RFC 9580 mandates that the hash for signature type 0x01 has to be calculated over normalized line endings,
    // so the hash for this message is calculated over "foo\r\nbar\r\nbaz".
    //
    // So it must also be verified against a hash digest over this normalized format.
    let (mut signed_msg, _header) =
        Message::from_armor_file("./tests/unit-tests/text_signature_normalization.msg").unwrap();

    let (skey, _headers) = SignedSecretKey::from_armor_single(
        std::fs::File::open("./tests/unit-tests/text_signature_normalization_alice.key").unwrap(),
    )
    .unwrap();

    // Manually find the signing subkey
    let signing = skey
        .secret_subkeys
        .iter()
        .find(|key| {
            key.legacy_key_id() == KeyId::from([0x64, 0x35, 0x7E, 0xB6, 0xBB, 0x55, 0xDE, 0x12])
        })
        .unwrap();

    // And transform it into a public subkey for signature verification
    let verify = signing.public_key();

    // verify the signature with alice's signing subkey
    signed_msg
        .verify_read(&verify)
        .expect("signature seems bad");
}

// Sample Version 6 Certificate (Transferable Public Key)
// https://www.rfc-editor.org/rfc/rfc9580.html#name-sample-version-6-certificat
const ANNEX_A_3: &str = "-----BEGIN PGP PUBLIC KEY BLOCK-----

xioGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laPCsQYf
GwoAAABCBYJjh3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxy
KwwfHifBilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lw
gyU2kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE
QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBM4qBmOHf+MZAAAAIIaTJINn
+eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1wpsGGBsKAAAALAWCY4d/4wKbDCIh
BssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8
j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805
I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg==
-----END PGP PUBLIC KEY BLOCK-----";

// Sample Version 6 Secret Key (Transferable Secret Key)
// https://www.rfc-editor.org/rfc/rfc9580.html#name-sample-version-6-secret-key
const ANNEX_A_4: &str = "-----BEGIN PGP PRIVATE KEY BLOCK-----

xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB
exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ
BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh
RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe
7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/
LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG
GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE
M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr
k0mXubZvyl4GBg==
-----END PGP PRIVATE KEY BLOCK-----";

/// Verify Cleartext Signed Message
///
/// Test data from RFC 9580, see
/// https://www.rfc-editor.org/rfc/rfc9580.html#name-sample-cleartext-signed-mes
#[test]
fn test_v6_annex_a_6() {
    let (ssk, _) = SignedPublicKey::from_string(ANNEX_A_3).expect("SSK from armor");

    let msg = "-----BEGIN PGP SIGNED MESSAGE-----

What we need from the grocery store:

- - tofu
- - vegetables
- - noodles

-----BEGIN PGP SIGNATURE-----

wpgGARsKAAAAKQWCY5ijYyIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
2azJAAAAAGk2IHZJX1AhiJD39eLuPBgiUU9wUA9VHYblySHkBONKU/usJ9BvuAqo
/FvLFuGWMbKAdA+epq7V4HOtAPlBWmU8QOd6aud+aSunHQaaEJ+iTFjP2OMW0KBr
NK2ay45cX1IVAQ==
-----END PGP SIGNATURE-----";

    let (msg, _) = CleartextSignedMessage::from_string(msg).unwrap();

    msg.verify(&ssk).expect("verify");
}

/// Verify Inline Signed Message
///
/// Test data from RFC 9580, see
/// https://www.rfc-editor.org/rfc/rfc9580.html#name-sample-inline-signed-messag
#[test]
fn test_v6_annex_a_7() {
    pretty_env_logger::try_init().ok();
    let (ssk, _) = SignedPublicKey::from_string(ANNEX_A_3).expect("SSK from armor");
    assert_eq!(ssk.details.direct_signatures.len(), 1);

    let msg = "-----BEGIN PGP MESSAGE-----

xEYGAQobIHZJX1AhiJD39eLuPBgiUU9wUA9VHYblySHkBONKU/usyxhsTwYJppfk
1S36bHIrDB8eJ8GKVnCPZSXsJ7rZrMkBy0p1AAAAAABXaGF0IHdlIG5lZWQgZnJv
bSB0aGUgZ3JvY2VyeSBzdG9yZToKCi0gdG9mdQotIHZlZ2V0YWJsZXMKLSBub29k
bGVzCsKYBgEbCgAAACkFgmOYo2MiIQbLGGxPBgmml+TVLfpscisMHx4nwYpWcI9l
JewnutmsyQAAAABpNiB2SV9QIYiQ9/Xi7jwYIlFPcFAPVR2G5ckh5ATjSlP7rCfQ
b7gKqPxbyxbhljGygHQPnqau1eBzrQD5QVplPEDnemrnfmkrpx0GmhCfokxYz9jj
FtCgazStmsuOXF9SFQE=
-----END PGP MESSAGE-----";

    let (mut msg, _) = Message::from_string(msg).unwrap();
    dbg!(&msg);
    msg.verify_read(&ssk).expect("verify");
}

/// Decrypt an X25519-AEAD-OCB Encrypted Packet Sequence
///
/// Test data from RFC 9580, see
/// https://www.rfc-editor.org/rfc/rfc9580.html#name-sample-x25519-aead-ocb-encr
#[test]
fn test_v6_annex_a_8() {
    let (ssk, _) = SignedSecretKey::from_string(ANNEX_A_4).expect("SSK from armor");

    // A.8. Sample X25519-AEAD-OCB Decryption
    let msg = "-----BEGIN PGP MESSAGE-----

wV0GIQYSyD8ecG9jCP4VGkF3Q6HwM3kOk+mXhIjR2zeNqZMIhRmHzxjV8bU/gXzO
WgBM85PMiVi93AZfJfhK9QmxfdNnZBjeo1VDeVZheQHgaVf7yopqR6W1FT6NOrfS
aQIHAgZhZBZTW+CwcW1g4FKlbExAf56zaw76/prQoN+bAzxpohup69LA7JW/Vp0l
yZnuSj3hcFj0DfqLTGgr4/u717J+sPWbtQBfgMfG9AOIwwrUBqsFE9zW+f1zdlYo
bhF30A+IitsxxA==
-----END PGP MESSAGE-----";

    let (message, _) = Message::from_string(msg).expect("ok");
    let mut dec = message.decrypt(&Password::empty(), &ssk).expect("decrypt");

    let decrypted = dec.as_data_string().unwrap();
    assert_eq!(&decrypted, "Hello, world!");
}

#[test]
fn test_invalid_partial_messages() {
    pretty_env_logger::try_init().ok();

    let (ssk, _headers) =
        SignedSecretKey::from_armor_file("./tests/draft-bre-openpgp-samples-00/bob.sec.asc")
            .expect("ssk");

    // 512 bytes, f256 p128 f128
    let (message, _) =
        Message::from_armor_file("./tests/partial_invalid_two_fixed.asc").expect("ok");

    dbg!(&message);
    let mut msg = message.decrypt(&Password::empty(), &ssk).expect("decrypt");

    let err = msg.as_data_vec().unwrap_err();
    dbg!(&err);

    assert!(
        err.to_string().contains("unexpected trailing"),
        "found error: {err}"
    );

    // 512 bytes, p512 f0 f0
    let (message, _) =
        Message::from_armor_file("./tests/partial_invalid_two_fixed_empty.asc").expect("ok");

    dbg!(&message);
    let mut msg = message.decrypt(&Password::empty(), &ssk).expect("decrypt");

    let err = msg.as_data_vec().unwrap_err();
    dbg!(&err);

    assert!(
        err.to_string().contains("unexpected trailing"),
        "found error: {err}"
    );

    // 512 bytes, p512 f1
    let (message, _) =
        Message::from_armor_file("./tests/partial_invalid_short_last.asc").expect("ok");

    dbg!(&message);
    let mut msg = message.decrypt(&Password::empty(), &ssk).expect("decrypt");

    let err = msg.as_data_vec().unwrap_err();
    dbg!(&err);

    assert!(
        err.to_string()
            .contains("Fixed chunk was shorter than expected"),
        "found error: {err}"
    );
}

#[test]
fn test_invalid_multi_message() {
    pretty_env_logger::try_init().ok();

    let (ssk, _headers) =
        SignedSecretKey::from_armor_file("./tests/draft-bre-openpgp-samples-00/bob.sec.asc")
            .expect("ssk");

    // compressed, followed by literal
    let (message, _) = Message::from_armor_file("./tests/multi_message_1.asc").expect("ok");

    dbg!(&message);
    let mut msg = message.decrypt(&Password::empty(), &ssk).expect("decrypt");

    dbg!(&msg);
    let err = msg.as_data_vec().unwrap_err();
    dbg!(&err);

    let err_string = err.to_string();
    assert!(
        err_string.contains("unexpected trailing") && err_string.contains("LiteralData"),
        "found error: {err_string}"
    );
}

#[test]
fn test_packet_excess_data() {
    // Message from the test "Packet excess consumption" in the interop suite.

    // The message contains extra tailing data inside the compressed packet, which the decompressor
    // ignores. The test checks that the consumer skips this data and successfully processes the
    // message.

    pretty_env_logger::try_init().ok();

    let (ssk, _headers) =
        SignedSecretKey::from_armor_file("./tests/draft-bre-openpgp-samples-00/bob.sec.asc")
            .expect("ssk");

    // 100kbyte of excess trailing data in the compressed packet
    let (message, _) = Message::from_armor_file("./tests/tests/excess_100k.msg").expect("ok");

    dbg!(&message);
    let msg = message.decrypt(&Password::empty(), &ssk).expect("decrypt");
    let mut msg = msg.decompress().unwrap();

    dbg!(&msg);
    let data = msg.as_data_vec().unwrap();

    assert_eq!(&data, b"Hello World :)");
}

#[test]
fn test_two_messages() {
    // "Two messages, concatenated" from the OpenPGP interoperability test suite

    pretty_env_logger::try_init().ok();

    let (ssk, _headers) =
        SignedSecretKey::from_armor_file("./tests/draft-bre-openpgp-samples-00/bob.sec.asc")
            .expect("ssk");

    let (message, _) = Message::from_armor_file("./tests/two_messages.asc").expect("ok");

    dbg!(&message);
    let mut msg = message.decrypt(&Password::empty(), &ssk).expect("decrypt");

    let res = msg.as_data_string();
    dbg!(&res);

    let err = res.unwrap_err();
    assert!(
        err.to_string().contains("unexpected trailing"),
        "found error: {err}"
    );
}

#[test]
fn test_two_literals_first_compressed_no_decompression() {
    // "Two literals, 1st compressed 1 times" from the OpenPGP interoperability test suite

    pretty_env_logger::try_init().ok();

    let (ssk, _headers) =
        SignedSecretKey::from_armor_file("./tests/draft-bre-openpgp-samples-00/bob.sec.asc")
            .expect("ssk");

    let (message, _) =
        Message::from_armor_file("./tests/two_literals_first_compressed.asc").expect("ok");

    dbg!(&message);
    let mut msg = message.decrypt(&Password::empty(), &ssk).expect("decrypt");

    let err = msg.as_data_vec().unwrap_err();
    dbg!(&err);

    assert!(
        err.to_string().contains("unexpected trailing"),
        "found error: {err}"
    );
}

#[test]
fn test_two_literals_first_compressed_two_times() {
    // "Two literals, 1st compressed 2 times" from the OpenPGP interoperability test suite

    pretty_env_logger::try_init().ok();

    let (ssk, _headers) =
        SignedSecretKey::from_armor_file("./tests/draft-bre-openpgp-samples-00/bob.sec.asc")
            .expect("ssk");

    let (message, _) =
        Message::from_armor_file("./tests/two_literals_first_compressed_two_times.asc")
            .expect("ok");

    dbg!(&message);
    let mut msg = message.decrypt(&Password::empty(), &ssk).expect("decrypt");

    let err = msg.as_data_vec().unwrap_err();
    dbg!(&err);

    assert!(
        err.to_string().contains("unexpected trailing"),
        "found error: {err}"
    );
}

#[test]
fn test_two_literals_first_compressed_explicit_decompression() {
    // "Two literals, 1st compressed 1 times" from the OpenPGP interoperability test suite,
    // Explicitly decompressing the compressed packet.

    // FIXME: this test should probably error somewhere?

    pretty_env_logger::try_init().ok();

    let (ssk, _headers) =
        SignedSecretKey::from_armor_file("./tests/draft-bre-openpgp-samples-00/bob.sec.asc")
            .expect("ssk");

    let (message, _) =
        Message::from_armor_file("./tests/two_literals_first_compressed.asc").expect("ok");

    dbg!(&message);
    let msg = message.decrypt(&Password::empty(), &ssk).expect("decrypt");

    let mut msg = msg.decompress().unwrap();

    let err = msg.as_data_string().unwrap_err();
    dbg!(&err);

    assert!(
        err.to_string().contains("unexpected trailing"),
        "found error: {err}"
    );
}

#[test]
fn test_two_literals_first_compressed_two_times_explicit_decompression() {
    // "Two literals, 1st compressed 2 times" from the OpenPGP interoperability test suite,
    // Explicitly decompressing the compressed packet.

    // FIXME: this test should probably error somewhere?

    pretty_env_logger::try_init().ok();

    let (ssk, _headers) =
        SignedSecretKey::from_armor_file("./tests/draft-bre-openpgp-samples-00/bob.sec.asc")
            .expect("ssk");

    let (message, _) =
        Message::from_armor_file("./tests/two_literals_first_compressed_two_times.asc")
            .expect("ok");

    dbg!(&message);
    let msg = message.decrypt(&Password::empty(), &ssk).expect("decrypt");

    let msg = msg.decompress().unwrap();
    dbg!(&msg);
    let mut msg = msg.decompress().unwrap();
    dbg!(&msg);

    let res = msg.as_data_string();
    dbg!(&res);

    let err = res.unwrap_err();
    assert!(
        err.to_string().contains("unexpected trailing"),
        "found error: {err}"
    );
}

#[test]
fn test_literal_eating_mdc() {
    // "Literal eating MDC" from the OpenPGP interoperability test suite

    pretty_env_logger::try_init().ok();

    let (ssk, _headers) =
        SignedSecretKey::from_armor_file("./tests/draft-bre-openpgp-samples-00/bob.sec.asc")
            .expect("ssk");

    let (message, _) = Message::from_armor_file("./tests/literal_eating_mdc.asc").expect("ok");

    dbg!(&message);
    let mut msg = message.decrypt(&Password::empty(), &ssk).expect("decrypt");

    let res = msg.as_data_vec();
    dbg!(&res);

    let err = res.unwrap_err();
    assert!(
        err.to_string()
            .contains("Fixed chunk was shorter than expected"),
        "found error: {err}"
    );
}

#[test]
fn test_unknown_hash() {
    pretty_env_logger::try_init().ok();
    let (msg, _) = Message::from_armor_file("tests/sigs/unknown_hash.sig.asc").unwrap();
    dbg!(&msg);

    let mut msg = msg
        .decrypt_with_session_key(PlainSessionKey::V3_4 {
            sym_alg: SymmetricKeyAlgorithm::AES256,
            key: hex::decode("0A62FC3D10FA134E8C3C915C68AA4B6C6E081D68A9ED1578735AC4743D0381F8")
                .unwrap()
                .into(),
        })
        .expect("failed to decrypt");

    dbg!(&msg);
    let content = msg.as_data_string().expect("failed to read");
    assert_eq!(content, "Encrypted, signed message.");
}

#[test]
fn test_unknown_one_pass() {
    pretty_env_logger::try_init().ok();
    let (ssk, _headers) =
        SignedSecretKey::from_armor_file("./tests/draft-bre-openpgp-samples-00/bob.sec.asc")
            .expect("ssk");

    let (msg, _) = Message::from_armor_file("tests/sigs/unknown_one_pass.sig.asc").unwrap();
    dbg!(&msg);

    let mut msg = msg
        .decrypt(&Password::empty(), &ssk)
        .expect("failed to decrypt");

    dbg!(&msg);
    let content = msg.as_data_string().expect("failed to read");
    assert_eq!(content, "Encrypted, signed message.");
    dbg!(&msg);
}

#[test]
fn test_signature_leniency() {
    // Test graceful handling of signatures with unknown elements.
    // Test vectors from "Messages with unknown packets" in OpenPGP interoperability test suite.

    pretty_env_logger::try_init().ok();

    let (ssk, _headers) =
        SignedSecretKey::from_armor_file("./tests/draft-bre-openpgp-samples-00/bob.sec.asc")
            .expect("ssk");

    // "PKESK3 SEIPDv1 [OPS3[H99] Literal Sig4[H99]]" from the OpenPGP interoperability test suite
    let (message, _) = Message::from_armor_file("./tests/message_other_hash.asc").expect("ok");

    dbg!(&message);
    let mut msg = message.decrypt(&Password::empty(), &ssk).expect("decrypt");

    let res = msg.as_data_vec();
    dbg!(&res);

    assert!(res.is_ok());

    // "PKESK3 SEIPDv1 [OPS3[P99] Literal Sig4[P99]]" from the OpenPGP interoperability test suite
    let (message, _) = Message::from_armor_file("./tests/message_other_pub_algo.asc").expect("ok");

    dbg!(&message);
    let mut msg = message.decrypt(&Password::empty(), &ssk).expect("decrypt");

    let res = msg.as_data_vec();
    dbg!(&res);

    assert!(res.is_ok());

    // "PKESK3 SEIP [OPS23 OPS3 Literal Sig4 Sig23]" from the OpenPGP interoperability test suite
    let (message, _) =
        Message::from_armor_file("./tests/message_future_signature.asc").expect("ok");

    dbg!(&message);
    let mut msg = message.decrypt(&Password::empty(), &ssk).expect("decrypt");

    let res = msg.as_data_vec();
    dbg!(&res);

    assert!(res.is_ok());
}

#[test]
fn test_packet_leniency() {
    // Tests graceful handling of a certificate with an unknown packet.
    // Test vector "P U UB S SB X B" from "Perturbed certificates" in OpenPGP interoperability test suite.

    pretty_env_logger::try_init().ok();

    let (key, _) = SignedPublicKey::from_armor_file("./tests/perturbed.pub.asc").unwrap();
    dbg!(&key);
}

// Tests graceful handling of a certificates with a subkey that has unknown features.
// Test vectors from "Mock PQ subkey" in OpenPGP interoperability test suite.

#[test]
fn test_mock_pq_cert_leniency_unkown_algo_mpi() {
    pretty_env_logger::try_init().ok();
    let (key, _) =
        SignedPublicKey::from_armor_file("./tests/mock_pq/unknown_algo_mpi.pub.asc").unwrap();
    dbg!(&key);
}

#[test]
fn test_mock_pq_cert_leniency_ecdsa_opaque() {
    pretty_env_logger::try_init().ok();
    let (key, _) =
        SignedPublicKey::from_armor_file("./tests/mock_pq/ecdsa_opaque_small.pub.asc").unwrap();
    dbg!(&key);
}
#[test]
fn test_mock_pq_cert_leniency_eddsa_opaque() {
    pretty_env_logger::try_init().ok();
    let (key, _) =
        SignedPublicKey::from_armor_file("./tests/mock_pq/eddsa_opaque_small.pub.asc").unwrap();
    dbg!(&key);
}
#[test]
fn test_mock_pq_cert_leniency_ecdh_opaque() {
    pretty_env_logger::try_init().ok();
    let (key, _) =
        SignedPublicKey::from_armor_file("./tests/mock_pq/ecdh_opaque_small.pub.asc").unwrap();
    dbg!(key);
}

#[test]
fn fuzz_msg_reader() {
    // those are fuzzer-generated "messages" that each contain a nonsensical series of packets
    // (surely with nonsensical package contents, as well)
    pretty_env_logger::try_init().ok();

    for file in [
        // OPS SIG PKESK SED BAD
        "./tests/fuzz/minimized-from-7585e756306047aba2218ebf70b24c6373e82e2a",
        // OPS SKESK SED
        "./tests/fuzz/minimized-from-82b02bbac39a10c7b98d020f78153ffb75c94607",
    ] {
        let _ = Message::from_file(file).unwrap().as_data_vec();
    }
}

#[test]
fn fuzz_msg_reader_fail() {
    // those are fuzzer-generated "messages" that each contain a nonsensical series of packets
    // (surely with nonsensical package contents, as well)
    pretty_env_logger::try_init().ok();

    for file in [
        // many OPS, followed by some odd packet(s)
        "./tests/fuzz/crash-1b1482d11c52075aabfc75256626a56c607787f3",
        "./tests/fuzz/minimized-from-e2a02fea22523e47a4d74b66bda8f455533bcfbb",
    ] {
        let _ = Message::from_file(file).unwrap_err();
    }
}

#[test]
fn message_parsing_pqc_pkesk() {
    pretty_env_logger::try_init().ok();

    // This is a message that is encrypted to one traditional PKESK and one PQC PKESK
    let (message, _) = Message::from_armor_file("./tests/message_pqc.asc").expect("ok");

    let Message::Encrypted { esk, .. } = message else {
        panic!("destructure encrypted message")
    };

    assert_eq!(esk.len(), 2);
}

/// inline-sign a message with quite a lot of keys, producing a deeply nested message structure
#[test]
fn message_many_one_pass_signatures() {
    pretty_env_logger::try_init().ok();

    const PLAIN: &str = "hello";

    let mut rng = ChaCha8Rng::seed_from_u64(0);

    // make NUM keys that can produce data signatures
    const NUM: usize = 2000;
    let keys = make_signing_keys(&mut rng, NUM);

    let mut builder = MessageBuilder::from_bytes("", PLAIN.as_bytes());

    warn!("signing message");
    // sign the message with all the keys
    for skey in &keys {
        builder.sign(&skey.primary_key, Password::empty(), HashAlgorithm::Sha256);
    }

    let signed = builder.to_vec(&mut rng).expect("writing");

    // parse the message ...
    warn!("parsing message");
    let mut message = Message::from_bytes(&signed[..]).expect("reading");

    let plain = message.as_data_string().unwrap();
    assert_eq!(plain, PLAIN);

    // ... and try to verify one of the signatures in it
    warn!("verifying message");
    assert!(message.is_one_pass_signed());

    let Message::Signed { reader, .. } = &message else {
        panic!("invalid message type");
    };
    assert_eq!(reader.num_signatures(), keys.len());
    assert_eq!(reader.num_one_pass_signatures(), keys.len());
    assert_eq!(reader.num_regular_signatures(), 0);

    for (i, key) in keys.iter().enumerate() {
        message
            .verify_nested_explicit(i, key.primary_key.public_key())
            .expect("signed");
    }
}

/// produce a message that has been prefix-signed by quite a lot of keys,
/// then attempt to verify the signatures in the message
#[test]
fn message_many_prefix_signatures() {
    pretty_env_logger::try_init().ok();

    let mut rng = ChaCha8Rng::seed_from_u64(0);

    // make NUM keys that can produce data signatures
    const NUM: usize = 2000;
    let keys = make_signing_keys(&mut rng, NUM);

    const PLAIN: &str = "hello";

    let sigs: Vec<_> = keys
        .iter()
        .map(|k| {
            DetachedSignature::sign_binary_data(
                &mut rng,
                &k.primary_key,
                &Password::empty(),
                HashAlgorithm::Sha256,
                PLAIN.as_bytes(),
            )
            .expect("sign")
        })
        .collect();

    // a prefix-signed is a series of signature packets followed by a literal data packet
    let mut packets: Vec<Packet> = sigs.into_iter().map(|ds| ds.signature.into()).collect();

    let lit = LiteralData::from_bytes(&[][..], PLAIN.as_bytes().into()).expect("literal");
    packets.push(lit.into());

    let signed = packets.to_bytes().expect("bytes");

    // parse the message again ...
    warn!("parsing message");
    let mut message = Message::from_bytes(&signed[..]).expect("reading");

    let plain = message.as_data_string().unwrap();
    assert_eq!(plain, PLAIN);

    // ... and try to verify one of the signatures in it
    warn!("verifying message");
    assert!(message.is_signed());

    let Message::Signed { reader, .. } = &message else {
        panic!("invalid message type");
    };
    assert_eq!(reader.num_signatures(), keys.len());
    assert_eq!(reader.num_one_pass_signatures(), 0);
    assert_eq!(reader.num_regular_signatures(), keys.len());

    for (i, key) in keys.iter().enumerate() {
        message
            .verify_nested_explicit(i, key.primary_key.public_key())
            .expect("signed");
    }
}

/// produce a message that mixes prefix-signed and one pass signatures by quite a lot of keys,
/// then attempt to verify the signatures in the message
#[test]
fn message_many_mixed_signatures() {
    pretty_env_logger::try_init().ok();

    let mut rng = ChaCha8Rng::seed_from_u64(0);

    // number of private keys to generate and sign with
    const NUM: usize = 2000;
    let keys = make_signing_keys(&mut rng, NUM);

    // order: prefixed first vs. OPS first
    for order in 0..=1 {
        let mut packets: Vec<Packet> = Vec::new();

        const PLAIN: &str = "hello";
        let lit = LiteralData::from_bytes(&[][..], PLAIN.as_bytes().into()).expect("literal");

        let mut regular_sigs: VecDeque<Packet> = VecDeque::new();
        let mut ops: VecDeque<Packet> = VecDeque::new();
        let mut op_sigs: VecDeque<Packet> = VecDeque::new();
        for (i, k) in keys.iter().enumerate() {
            if i % 2 == order {
                println!("prefixed sig: {i}");
                // prefixed sig
                let sig = DetachedSignature::sign_binary_data(
                    &mut rng,
                    &k.primary_key,
                    &Password::empty(),
                    HashAlgorithm::Sha256,
                    PLAIN.as_bytes(),
                )
                .expect("sign");

                regular_sigs.push_back(sig.signature.into());
            } else {
                println!("ops sig: {i}");
                // make ops sig

                let mut builder = MessageBuilder::from_bytes("", PLAIN.as_bytes());

                warn!("signing message");
                // sign the message with this key
                builder.sign(&k.primary_key, Password::empty(), HashAlgorithm::Sha256);

                let signed = builder.to_vec(&mut rng).expect("writing");
                let mut pp = PacketParser::new(BufReader::new(signed.as_slice()));
                let Ok(Packet::OnePassSignature(op)) = pp.next().unwrap() else {
                    panic!("expected OPS");
                };
                let _ = pp.next().unwrap();
                let Ok(Packet::Signature(sig)) = pp.next().unwrap() else {
                    panic!("expected Signature");
                };

                ops.push_back(op.into());
                op_sigs.push_back(sig.into());
            }
        }

        // build packet structure

        // Sig
        // OPS
        // Sig
        // OPS
        // ..
        // LIT
        // Sig (from OPS)
        // Sig (from OPS)
        // ..

        let ops_sigs = ops.len();
        let prefix_sigs = regular_sigs.len();
        assert_eq!(ops_sigs, op_sigs.len());

        for i in 0..keys.len() {
            if i % 2 == order {
                // regular
                println!("insert regular sig: {i}");
                packets.push(regular_sigs.pop_front().unwrap());
            } else {
                println!("insert ops: {i}");
                packets.push(ops.pop_front().unwrap());
            }
        }

        packets.push(lit.into());
        for (i, sig) in op_sigs.into_iter().enumerate().rev() {
            println!("insert ops sig: {i}");
            packets.push(sig);
        }

        let signed = packets.to_bytes().expect("bytes");

        // let mut armored = Vec::new();
        // pgp::armor::write(
        //     &packets,
        //     pgp::armor::BlockType::Message,
        //     &mut armored,
        //     None,
        //     false,
        // )
        // .expect("armor");
        // eprintln!("{}", String::from_utf8_lossy(&armored));

        // parse the message again ...
        warn!("parsing message");
        let mut message = Message::from_bytes(&signed[..]).expect("reading");

        let plain = message.as_data_string().unwrap();
        assert_eq!(plain, PLAIN);

        // ... and try to verify one of the signatures in it
        warn!("verifying message");
        assert!(message.is_signed());

        let Message::Signed { reader, .. } = &message else {
            panic!("invalid message type");
        };
        assert_eq!(reader.num_signatures(), keys.len());
        assert_eq!(reader.num_one_pass_signatures(), ops_sigs);
        assert_eq!(reader.num_regular_signatures(), prefix_sigs);

        for (i, key) in keys.iter().enumerate() {
            println!("-- verifying key {i}");
            message
                .verify_nested_explicit(i, key.primary_key.public_key())
                .expect("signed");
        }
    }
}

fn make_signing_keys(mut rng: &mut ChaCha8Rng, num: usize) -> Vec<SignedSecretKey> {
    // make NUM keys that can produce data signatures
    (0..num)
        .map(|count| {
            // legal v4 key, with user id
            let key_params = SecretKeyParamsBuilder::default()
                .key_type(KeyType::Ed25519)
                .can_sign(true)
                .primary_user_id(format!("test{}", count))
                .build()
                .unwrap();

            key_params
                .generate(&mut rng)
                .expect("failed to generate secret key")
        })
        .collect()
}

#[test]
fn test_two_signed_messages() {
    pretty_env_logger::try_init().ok();

    // let (psk, _headers) =
    //     SignedPublicKey::from_armor_file("./tests/two_signed_cert.pub.asc").expect("psk");

    let (mut message, _) = Message::from_armor_file("./tests/two_signed.asc").expect("ok");

    dbg!(&message);
    let err = message.as_data_string().unwrap_err();
    dbg!(&err);
    // let sig = message.verify(&psk).unwrap();

    assert!(
        err.to_string().contains("unexpected trailing"),
        "found error: {err}"
    );
}