Welcome to mirror list, hosted at ThFree Co, Russian Federation.

openpgp.js « general « test - github.com/openpgpjs/openpgpjs.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: eb37933323b488c3d47cc597384ead29003d70f0 (plain)
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
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
/* eslint-disable max-lines */
/* globals tryTests: true */

const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..');
const crypto = require('../../src/crypto');
const random = require('../../src/crypto/random');
const util = require('../../src/util');
const keyIDType = require('../../src/type/keyid');
const { isAEADSupported } = require('../../src/key');

const stream = require('@openpgp/web-stream-tools');

const spy = require('sinon/lib/sinon/spy');
const input = require('./testInputs.js');
const chai = require('chai');
chai.use(require('chai-as-promised'));

const expect = chai.expect;

const pub_key = [
  '-----BEGIN PGP PUBLIC KEY BLOCK-----',
  'Version: GnuPG v2.0.19 (GNU/Linux)',
  '',
  'mI0EUmEvTgEEANyWtQQMOybQ9JltDqmaX0WnNPJeLILIM36sw6zL0nfTQ5zXSS3+',
  'fIF6P29lJFxpblWk02PSID5zX/DYU9/zjM2xPO8Oa4xo0cVTOTLj++Ri5mtr//f5',
  'GLsIXxFrBJhD/ghFsL3Op0GXOeLJ9A5bsOn8th7x6JucNKuaRB6bQbSPABEBAAG0',
  'JFRlc3QgTWNUZXN0aW5ndG9uIDx0ZXN0QGV4YW1wbGUuY29tPoi5BBMBAgAjBQJS',
  'YS9OAhsvBwsJCAcDAgEGFQgCCQoLBBYCAwECHgECF4AACgkQSmNhOk1uQJQwDAP6',
  'AgrTyqkRlJVqz2pb46TfbDM2TDF7o9CBnBzIGoxBhlRwpqALz7z2kxBDmwpQa+ki',
  'Bq3jZN/UosY9y8bhwMAlnrDY9jP1gdCo+H0sD48CdXybblNwaYpwqC8VSpDdTndf',
  '9j2wE/weihGp/DAdy/2kyBCaiOY1sjhUfJ1GogF49rC4jQRSYS9OAQQA6R/PtBFa',
  'JaT4jq10yqASk4sqwVMsc6HcifM5lSdxzExFP74naUMMyEsKHP53QxTF0Grqusag',
  'Qg/ZtgT0CN1HUM152y7ACOdp1giKjpMzOTQClqCoclyvWOFB+L/SwGEIJf7LSCEr',
  'woBuJifJc8xAVr0XX0JthoW+uP91eTQ3XpsAEQEAAYkBPQQYAQIACQUCUmEvTgIb',
  'LgCoCRBKY2E6TW5AlJ0gBBkBAgAGBQJSYS9OAAoJEOCE90RsICyXuqIEANmmiRCA',
  'SF7YK7PvFkieJNwzeK0V3F2lGX+uu6Y3Q/Zxdtwc4xR+me/CSBmsURyXTO29OWhP',
  'GLszPH9zSJU9BdDi6v0yNprmFPX/1Ng0Abn/sCkwetvjxC1YIvTLFwtUL/7v6NS2',
  'bZpsUxRTg9+cSrMWWSNjiY9qUKajm1tuzPDZXAUEAMNmAN3xXN/Kjyvj2OK2ck0X',
  'W748sl/tc3qiKPMJ+0AkMF7Pjhmh9nxqE9+QCEl7qinFqqBLjuzgUhBU4QlwX1GD',
  'AtNTq6ihLMD5v1d82ZC7tNatdlDMGWnIdvEMCv2GZcuIqDQ9rXWs49e7tq1NncLY',
  'hz3tYjKhoFTKEIq3y3Pp',
  '=h/aX',
  '-----END PGP PUBLIC KEY BLOCK-----'
].join('\n');

const priv_key = [
  '-----BEGIN PGP PRIVATE KEY BLOCK-----',
  'Version: GnuPG v2.0.19 (GNU/Linux)',
  '',
  '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',
  '=lw5e',
  '-----END PGP PRIVATE KEY BLOCK-----'
].join('\n');

const pub_key_de = [
  '-----BEGIN PGP PUBLIC KEY BLOCK-----',
  'Version: GnuPG v2.0.22 (GNU/Linux)',
  '',
  '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',
  'og2umGfGng==',
  '=v3+L',
  '-----END PGP PUBLIC KEY BLOCK-----'
].join('\n');

const priv_key_de = [
  '-----BEGIN PGP PRIVATE KEY BLOCK-----',
  'Version: GnuPG v2.0.22 (GNU/Linux)',
  '',
  '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',
  'DHTORVqAOdoOcu5Qh7AQg1GdSmfFAsx2',
  '=kyeP',
  '-----END PGP PRIVATE KEY BLOCK-----'
].join('\n');

const priv_key_2000_2008 = `-----BEGIN PGP PRIVATE KEY BLOCK-----

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
t/ia1kMpSEiOVLlX5dfHZzhR3WNtBqU=
=C0fJ
-----END PGP PRIVATE KEY BLOCK-----`;

const priv_key_2038_2045 = `-----BEGIN PGP PRIVATE KEY BLOCK-----

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
0uyeibPdgJTjARMuQzDFA8bdbM540vBf5Q==
=WLIN
-----END PGP PRIVATE KEY BLOCK-----`;

const priv_key_expires_1337 = `-----BEGIN PGP PRIVATE KEY BLOCK-----

xcA4BAAAAAEBAgCgONc0J8rfO6cJw5YTP38x1ze2tAYIO7EcmRCNYwMkXngb
0Qdzg34Q5RW0rNiR56VB6KElPUhePRPVklLFiIvHABEBAAEAAf9qabYMzsz/
/LeRVZSsTgTljmJTdzd2ambUbpi+vt8MXJsbaWh71vjoLMWSXajaKSPDjVU5
waFNt9kLqwGGGLqpAQD5ZdMH2XzTq6GU9Ka69iZs6Pbnzwdz59Vc3i8hXlUj
zQEApHargCTsrtvSrm+hK/pN51/BHAy9lxCAw9f2etx+AeMA/RGrijkFZtYt
jeWdv/usXL3mgHvEcJv63N5zcEvDX5X4W1bND3Rlc3QxIDxhQGIuY29tPsJ7
BBABCAAvBQIAAAABBQMAAAU5BgsJBwgDAgkQzcF99nGrkAkEFQgKAgMWAgEC
GQECGwMCHgEAABAlAfwPehmLZs+gOhOTTaSslqQ50bl/REjmv42Nyr1ZBlQS
DECl1Qu4QyeXin29uEXWiekMpNlZVsEuc8icCw6ABhIZ
=/7PI
-----END PGP PRIVATE KEY BLOCK-----`;

const passphrase = 'hello world';
const plaintext = input.createSomeMessage();
const password1 = 'I am a password';
const password2 = 'I am another password';

const twoPasswordGPGFail = [
  '-----BEGIN PGP MESSAGE-----',
  'Version: OpenPGP.js v3.0.0',
  'Comment: https://openpgpjs.org',
  '',
  'wy4ECQMIWjj3WEfWxGpgrfb3vXu0TS9L8UNTBvNZFIjltGjMVkLFD+/afgs5',
  'aXt0wy4ECQMIrFo3TFN5xqtgtB+AaAjBcWJrA4bvIPBpJ38PbMWeF0JQgrqg',
  'j3uehxXy0mUB5i7B61g0ho+YplyFGM0s9XayJCnu40tWmr5LqqsRxuwrhJKR',
  'migslOF/l6Y9F0F9xGIZWGhxp3ugQPjVKjj8fOH7ap14mLm60C8q8AOxiSmL',
  'ubsd/hL7FPZatUYAAZVA0a6hmQ==',
  '=cHCV',
  '-----END PGP MESSAGE-----'
].join('\n');

const ecdh_msg_bad = `-----BEGIN PGP MESSAGE-----
Version: ProtonMail
Comment: https://protonmail.com

wV4DlF328rtCW+wSAQdA9FsAz4rCdoxY/oZaa68WMPMXbO+wtHs4ZXtAOJOs
SlwwDaABXYC2dt0hUS2zRAL3gBGf4udH/CKJ1vPE58sNeh0ERYLxPHgwrpqI
oNVWOWH50kUBIdqd7by8RwLOk9GyV6008iFOlOG90mfjvt2g5DsnSB4wEeMg
pVu3fXj8iAKvFxvihwv1M7gNtP14StP6CngvyGVVEHQ=
=mvcB
-----END PGP MESSAGE-----`;

const ecdh_dec_key = `-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: OpenPGP.js v4.4.6
Comment: https://openpgpjs.org

xYYEXEBTPxYJKwYBBAHaRw8BAQdAbXBY+2lpOatB+ZLokS/JIWqrVOseja9S
ewQxMKN6ueT+CQMIuUXr0XofC6VgJvFLyLwDlyyvT4I1HWGKZ6W9HUaslKvS
rw362rbMZKKfUtfjRJvpqiIU3Dr7iDkHB5vT7Tp5S7AZ2tNKoh/bwfTKdHsT
1803InFhX3Rlc3RlcjJAcHJvdG9ubWFpbC5jb20iIDxxYV90ZXN0ZXIyQHBy
b3Rvbm1haWwuY29tPsJ3BBAWCgAfBQJcQFM/BgsJBwgDAgQVCAoCAxYCAQIZ
AQIbAwIeAQAKCRClzcrGJTMHyTpjAQCJZ7p0TJBZyPQ8m64N24glaM6oM78q
2Ogpc0e9LcrPowD6AssY2YfUwJNzVFVzR+Lulzu6XVPjn0pXGMhOl03SrQ3H
iwRcQFM/EgorBgEEAZdVAQUBAQdAAgJJUhKvjGWMq1sDhrJgvqbHK1t1W5RF
Xoet5noIlAADAQgH/gkDCOFdJ7Yv2cTZYETRT5+ak/ntmslcAqtk3ebd7Ok3
tQIjO3TYUbkV1eqrpA4I42kGCUkU4Dy26wxuaLRSsO1u/RgXjExZLP9FlWFI
h6lLS1bCYQQYFggACQUCXEBTPwIbDAAKCRClzcrGJTMHyfNBAP9sdyU3GHNR
7+QdwYvQp7wN+2VUd8vIf7iwAHOK1Cj4ywD+NhzjFfGYESJ68nnkrYlYdf+u
OBqYz6mzZAWQZqsjbg4=
=zrks
-----END PGP PRIVATE KEY BLOCK-----`;

const ecdh_msg_bad_2 = `-----BEGIN PGP MESSAGE-----
Version: ProtonMail
Comment: https://protonmail.com

wV4DtM+8giJEGNISAQhA2rYu8+B41rJi6Gsr4TVeKyDtI0KjhhlLZs891rCG
6X4wxNkxCuTJZax7gQZbDKh2kETK/RH75s9g7H/WV9kZ192NTGmMFiKiautH
c5BGRGxM0sDfAQZb3ZsAUORHKPP7FczMv5aMU2Ko7O2FHc06bMdnZ/ag7GMF
Bdl4EizttNTQ5sNCAdIXUoA8BJLHPgPiglnfTqqx3ynkBNMzfH46oKf08oJ+
6CAQhJdif67/iDX8BRtaKDICBpv3b5anJht7irOBqf9XX13SGkmqKYF3T8eB
W7ZV5EdCTC9KU+1BBPfPEi93F4OHsG/Jo80e5MDN24/wNxC67h7kUQiy3H4s
al+5mSAKcIfZJA4NfPJg9zSoHgfRNGI8Q7ao+c8CLPiefGcMsakNsWUdRyBT
SSLH3z/7AH4GxBvhDEEG3cZwmXzZAJMZmzTa+SrsxZzRpGB/aawyRntOWm8w
6Lq9ntq4S8suj/YK62dJpJxFl8xs+COngpMDvCexX9lYlh/r/y4JRQl06oUK
wv7trvi89TkK3821qHxr7XwI1Ncr2qDJVNlN4W+b6WFyLXnXaJAUMyZ/6inm
RR8BoR2KkEAku3Ne/G5QI51ktNJ7cCodeVOkZj8+iip1/AGyjxZCybq/N8rc
bpOWdMhJ6Hy+JzGNY1qNXcHJPw==
=99Fs
-----END PGP MESSAGE-----`;

const ecdh_dec_key2 = `-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: OpenPGP.js v4.4.9
Comment: https://openpgpjs.org

xYYEXEg93hYJKwYBBAHaRw8BAQdAeoA+T4vr3P0hFFsbzJpgy7/ZnKCrlehr
Myk5QAsBYgf+CQMIQ76YL5sEx+Zgr7DLZ5fhQn1U9+8aLIQaIbaT51nEjEMD
7h6mrJmp7oIr4PyijsIU+0LasXh/qlNeVQVWSygDq9L4nXDEGQhlMq3oH1FN
NM07InBha292c2thdGVzdEBwcm90b25tYWlsLmNvbSIgPHBha292c2thdGVz
dEBwcm90b25tYWlsLmNvbT7CdwQQFgoAHwUCXEg93gYLCQcIAwIEFQgKAgMW
AgECGQECGwMCHgEACgkQp7+eOYEhwd6x5AD9E0LA62odFFDH76wjEYrPCvOH
cYM56/5ZqZoGPPmbE98BAKCz/SQ90tiCMmlLEDXGX+a1bi6ttozqrnSQigic
DI4Ix4sEXEg93hIKKwYBBAGXVQEFAQEHQPDXy2mDfbMKOpCBZB2Ic5bfoWGV
iXvCFMnTLRWfGHUkAwEIB/4JAwhxMnjHjyALomBWSsoYxxB6rj6JKnWeikyj
yjXZdZqdK5F+0rk4M0l7lF0wt5PhT2uMCLB7aH/mSFN1cz7sBeJl3w2soJsT
ve/fP/8NfzP0wmEEGBYIAAkFAlxIPd4CGwwACgkQp7+eOYEhwd5MWQEAp0E4
QTnEnG8lYXhOqnOw676oV2kEU6tcTj3DdM+cW/sA/jH3FQQjPf+mA/7xqKIv
EQr2Mx42THr260IFYp5E/rIA
=oA0b
-----END PGP PRIVATE KEY BLOCK-----`;

const mismatchingKeyParams = `-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: OpenPGP.js v4.7.0
Comment: https://openpgpjs.org

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
0NubenKyljKtyHyoU+S+TqUyx7gf5A==
=Lj9k
-----END PGP PRIVATE KEY BLOCK-----
`;

const rsaPrivateKeyPKCS1 = `-----BEGIN PGP PRIVATE KEY BLOCK-----

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
=dSNv
-----END PGP PRIVATE KEY BLOCK-----
`;

const gnuDummyKeySigningSubkey = `
-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: OpenPGP.js VERSION
Comment: https://openpgpjs.org

xZUEWCC+hwEEALu8GwefswqZLoiKJk1Nd1yKmVWBL1ypV35FN0gCjI1NyyJX
UfQZDdC2h0494OVAM2iqKepqht3tH2DebeFLnc2ivvIFmQJZDnH2/0nFG2gC
rSySWHUjVfbMSpmTaXpit8EX/rjNauGOdbePbezOSsAhW7R9pBdtDjPnq2Zm
vDXXABEBAAH+B2UAR05VAc0JR05VIER1bW15wrgEEwECACIFAlggvocCGwMG
CwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEJ3XHFanUJgCeMYD/2zKefpl
clQoBdDPJKCYJm8IhuWuoF8SnHAsbhD+U42Gbm+2EATTPj0jyGPkZzl7a0th
S2rSjQ4JF0Ktgdr9585haknpGwr31t486KxXOY4AEsiBmRyvTbaQegwKaQ+C
/0JQYo/XKpsaX7PMDBB9SNFSa8NkhxYseLaB7gbM8w+Lx8EYBFggvpwBBADF
YeeJwp6MAVwVwXX/eBRKBIft6LC4E9czu8N2AbOW97WjWNtXi3OuM32OwKXq
vSck8Mx8FLOAuvVq41NEboeknhptw7HzoQMB35q8NxA9lvvPd0+Ef+BvaVB6
NmweHttt45LxYxLMdXdGoIt3wn/HBY81HnMqfV/KnggZ+imJ0wARAQABAAP7
BA56WdHzb53HIzYgWZl04H3BJdB4JU6/FJo0yHpjeWRQ46Q7w2WJzjHS6eBB
G+OhGzjAGYK7AUr8wgjqMq6LQHt2f80N/nWLusZ00a4lcMd7rvoHLWwRj80a
RzviOvvhP7kZY1TrhbS+Sl+BWaNIDOxS2maEkxexztt4GEl2dWUCAMoJvyFm
qPVqVx2Yug29vuJsDcr9XwnjrYI8PtszJI8Fr+5rKgWE3GJumheaXaug60dr
mLMXdvT/0lj3sXquqR0CAPoZ1Mn7GaUKjPVJ7CiJ/UjqSurrGhruA5ikhehQ
vUB+v4uIl7ICcX8zfiP+SMhWY9qdkmOvLSSSMcTkguMfe68B/j/qf2en5OHy
6NJgMIjMrBHvrf34f6pxw5p10J6nxjooZQxV0P+9MoTHWsy0r6Er8IOSSTGc
WyWJ8wmSqiq/dZSoJcLAfQQYAQIACQUCWCC+nAIbAgCoCRCd1xxWp1CYAp0g
BBkBAgAGBQJYIL6cAAoJEOYZSGiVA/C9CT4D/2Vq2dKxHmzn/UD1MWSLXUbN
ISd8tvHjoVg52RafdgHFmg9AbE0DW8ifwaai7FkifD0IXiN04nER3MuVhAn1
gtMu03m1AQyX/X39tHz+otpwBn0g57NhFbHFmzKfr/+N+XsDRj4VXn13hhqM
qQR8i1wgiWBUFJbpP5M1BPdH4Qfkcn8D/j8A3QKYGGETa8bNOdVTRU+sThXr
imOfWu58V1yWCmLE1kK66qkqmgRVUefqacF/ieMqNmsAY+zmR9D4fg2wzu/d
nPjJXp1670Vlzg7oT5XVYnfys7x4GLHsbaOSjXToILq+3GwI9UjNjtpobcfm
mNG2ibD6lftLOtDsVSDY8a6a
=KjxQ
-----END PGP PRIVATE KEY BLOCK-----
`;

const multipleEncryptionAndSigningSubkeys = `-----BEGIN PGP PRIVATE KEY BLOCK-----

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
=PUAJ
-----END PGP PRIVATE KEY BLOCK-----`;

const twoPublicKeys = `-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v2.0.19 (GNU/Linux)

mI0EUmEvTgEEANyWtQQMOybQ9JltDqmaX0WnNPJeLILIM36sw6zL0nfTQ5zXSS3+
fIF6P29lJFxpblWk02PSID5zX/DYU9/zjM2xPO8Oa4xo0cVTOTLj++Ri5mtr//f5
GLsIXxFrBJhD/ghFsL3Op0GXOeLJ9A5bsOn8th7x6JucNKuaRB6bQbSPABEBAAG0
JFRlc3QgTWNUZXN0aW5ndG9uIDx0ZXN0QGV4YW1wbGUuY29tPoi5BBMBAgAjBQJS
YS9OAhsvBwsJCAcDAgEGFQgCCQoLBBYCAwECHgECF4AACgkQSmNhOk1uQJQwDAP6
AgrTyqkRlJVqz2pb46TfbDM2TDF7o9CBnBzIGoxBhlRwpqALz7z2kxBDmwpQa+ki
Bq3jZN/UosY9y8bhwMAlnrDY9jP1gdCo+H0sD48CdXybblNwaYpwqC8VSpDdTndf
9j2wE/weihGp/DAdy/2kyBCaiOY1sjhUfJ1GogF49rC4jQRSYS9OAQQA6R/PtBFa
JaT4jq10yqASk4sqwVMsc6HcifM5lSdxzExFP74naUMMyEsKHP53QxTF0Grqusag
Qg/ZtgT0CN1HUM152y7ACOdp1giKjpMzOTQClqCoclyvWOFB+L/SwGEIJf7LSCEr
woBuJifJc8xAVr0XX0JthoW+uP91eTQ3XpsAEQEAAYkBPQQYAQIACQUCUmEvTgIb
LgCoCRBKY2E6TW5AlJ0gBBkBAgAGBQJSYS9OAAoJEOCE90RsICyXuqIEANmmiRCA
SF7YK7PvFkieJNwzeK0V3F2lGX+uu6Y3Q/Zxdtwc4xR+me/CSBmsURyXTO29OWhP
GLszPH9zSJU9BdDi6v0yNprmFPX/1Ng0Abn/sCkwetvjxC1YIvTLFwtUL/7v6NS2
bZpsUxRTg9+cSrMWWSNjiY9qUKajm1tuzPDZXAUEAMNmAN3xXN/Kjyvj2OK2ck0X
W748sl/tc3qiKPMJ+0AkMF7Pjhmh9nxqE9+QCEl7qinFqqBLjuzgUhBU4QlwX1GD
AtNTq6ihLMD5v1d82ZC7tNatdlDMGWnIdvEMCv2GZcuIqDQ9rXWs49e7tq1NncLY
hz3tYjKhoFTKEIq3y3PpmQENBFKV0FUBCACtZliApy01KBGbGNB36YGH4lpr+5Ko
qF1I8A5IT0YeNjyGisOkWsDsUzOqaNvgzQ82I3MY/jQV5rLBhH/6LiRmCA16WkKc
qBrHfNGIxJ+Q+ofVBHUbaS9ClXYI88j747QgWzirnLuEA0GfilRZcewII1pDA/G7
+m1HwV4qHsPataYLeboqhPA3h1EVVQFMAcwlqjOuS8+weHQRfNVRGQdRMm6H7166
PseDVRUHdkJpVaKFhptgrDoNI0lO+UujdqeF1o5tVZ0j/s7RbyBvdLTXNuBbcpq9
3ceSWuJPZmi1XztQXKYey0f+ltgVtZDEc7TGV5WDX9erRECCcA3+s7J3ABEBAAG0
G0pTIENyeXB0byA8ZGlmZmllQGhvbWUub3JnPokBPwQTAQIAKQUCUpXQVQIbAwUJ
CWYBgAcLCQgHAwIBBhUIAgkKCwQWAgMBAh4BAheAAAoJENvyI+hwU030yRAIAKX/
mGEgi/miqasbbQoyK/CSa7sRxgZwOWQLdi2xxpE5V4W4HJIDNLJs5vGpRN4mmcNK
2fmJAh74w0PskmVgJEhPdFJ14UC3fFPq5nbqkBl7hU0tDP5jZxo9ruQZfDOWpHKx
OCz5guYJ0CW97bz4fChZNFDyfU7VsJQwRIoViVcMCipP0fVZQkIhhwpzQpmVmN8E
0a6jWezTZv1YpMdlzbEfH79l3StaOh9/Un9CkIyqEWdYiKvIYms9nENyehN7r/OK
YN3SW+qlt5GaL+ws+N1w6kEZjPFwnsr+Y4A3oHcAwXq7nfOz71USojSmmo8pgdN8
je16CP98vw3/k6TncLS5AQ0EUpXQVQEIAMEjHMeqg7B04FliUFWr/8C6sJDb492M
lGAWgghIbnuJfXAnUGdNoAzn0S+n93Y/qHbW6YcjHD4/G+kK3MuxthAFqcVjdHZQ
XK0rkhXO/u1co7v1cdtkOTEcyOpyLXolM/1S2UYImhrml7YulTHMnWVja7xu6QIR
so+7HBFT/u9D47L/xXrXMzXFVZfBtVY+yoeTrOY3OX9cBMOAu0kuN9eT18Yv2yi6
XMzP3iONVHtl6HfFrAA7kAtx4ne0jgAPWZ+a8hMy59on2ZFs/AvSpJtSc1kw/vMT
WkyVP1Ky20vAPHQ6Ej5q1NGJ/JbcFgolvEeI/3uDueLjj4SdSIbLOXMAEQEAAYkB
JQQYAQIADwUCUpXQVQIbDAUJCWYBgAAKCRDb8iPocFNN9NLkB/wO4iRxia0zf4Kw
2RLVZG8qcuo3Bw9UTXYYlI0AutoLNnSURMLLCq6rcJ0BCXGj/2iZ0NBxZq3t5vbR
h6uUv+hpiSxK1nF7AheN4aAAzhbWx0UDTF04ebG/neE4uDklRIJLhif6+Bwu+EUe
TlGbDj7fqGSsNe8g92w71e41rF/9CMoOswrKgIjXAou3aexogWcHvKY2D+1q9exO
Re1rIa1+sUGl5PG2wsEsznN6qtN5gMlGY1ofWDY+I02gO4qzaZ/FxRZfittCw7v5
dmQYKot9qRi2Kx3Fvw+hivFBpC4TWgppFBnJJnAsFXZJQcejMW4nEmOViRQXY8N8
PepQmgsu
=w6wd
-----END PGP PUBLIC KEY BLOCK-----`;

const twoPrivateKeys = `-----BEGIN PGP PRIVATE KEY BLOCK-----

xVgEYJQe2xYJKwYBBAHaRw8BAQdAjTDKUXTWruoPIdDA5tpTEax/nCIKgmeS
jabWRyMTWoEAAQCM8rs15ex7sQ7T4sBf8jHeKvHiUBoTkhKJVAzsnorHdhGn
zRB0ZXN0IDx0ZXN0QGEuaXQ+wowEEBYKAB0FAmCUHtsECwkHCAMVCAoEFgAC
AQIZAQIbAwIeAQAhCRAQIA5NLDEFChYhBAWs5LsefVu3mjXXaBAgDk0sMQUK
BYcBAMxy3zEZhNtw2nnB9jAlIOSeCUJq/GuarTWQkhAZLFIeAP9400rWrELS
zvNgdct9fctoM21ZByUlkmNdPgYf7fjaAMddBGCUHtsSCisGAQQBl1UBBQEB
B0DdGhv0sVHFzGvDPzTYhNKnUxd68oocIEkt5Ku6ZAD0VAMBCAcAAP9rRNBE
OumQKygox59KL7FjEYXSR8TqI4t3CFlfWW/D8A+gwngEGBYIAAkFAmCUHtsC
GwwAIQkQECAOTSwxBQoWIQQFrOS7Hn1bt5o112gQIA5NLDEFCoPdAQCTy2kg
z3F/iZApy2Sf5SIThnQMsgEr296Fgfvm8YMFCAEA82+TF79snlPbVHSIrdDg
lPMSDEkIcxzIQN0EEo1qlwzFWARglB7iFgkrBgEEAdpHDwEBB0D/kNASbsOD
S9RePgrsUDdY3plKDRLIIvpAIkbr1PoDoAABANEBtAiU2YjVOfHzDgbblSCd
+tPSDaYbAyHmCNMDqsRQD8rNEHRlc3QgPHRlc3RAYS5pdD7CjAQQFgoAHQUC
YJQe4gQLCQcIAxUICgQWAAIBAhkBAhsDAh4BACEJEIrXtvI38e+rFiEERNKb
HKnqdF8HwqMZite28jfx76trWAEA6YFR+4gMFr3xM/HReS+pYE1SSHIQjHgz
SsU0N93pk5EA/ijuLZfsRf7uD6Yb0rEDIJa3NT7KwIUIUtDpbQLtIrcFx10E
YJQe4hIKKwYBBAGXVQEFAQEHQLfK3MpbSeRa1Ko1NtNDNXOc/sqvEeIjAAKg
V0OWVpsJAwEIBwAA/3Nr3/t32OJi9GFEVEN2/VWes5825aFBPEU6UcBaSgCw
EU/CeAQYFggACQUCYJQe4gIbDAAhCRCK17byN/HvqxYhBETSmxyp6nRfB8Kj
GYrXtvI38e+rSKMBAJaIk9bLz+AN0Ho8pHGP3gEddvLwvioNhdkCJ7CfwWmI
AP9fcXZg/Eo55YB/B5XKLkuzDFwJaTlncrD5jcUgtVXFCg==
=q2yi
-----END PGP PRIVATE KEY BLOCK-----`;

const armoredDummyPrivateKey1 = `-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: GnuPG v1.4.11 (GNU/Linux)

lQGqBFERnrMRBADmM0hIfkI3yosjgbWo9v0Lnr3CCE+8KsMszgVS+hBu0XfGraKm
ivcA2aaJimHqVYOP7gEnwFAxHBBpeTJcu5wzCFyJwEYqVeS3nnaIhBPplSF14Duf
i6bB9RV7KxVAg6aunmM2tAutqC+a0y2rDaf7jkJoZ9gWJe2zI+vraD6fiwCgxvHo
3IgULB9RqIqpLoMgXfcjC+cD/1jeJlKRm+n71ryYwT/ECKsspFz7S36z6q3XyS8Q
QfrsUz2p1fbFicvJwIOJ8B20J/N2/nit4P0gBUTUxv3QEa7XCM/56/xrGkyBzscW
AzBoy/AK9K7GN6z13RozuAS60F1xO7MQc6Yi2VU3eASDQEKiyL/Ubf/s/rkZ+sGj
yJizBACtwCbQzA+z9XBZNUat5NPgcZz5Qeh1nwF9Nxnr6pyBv7tkrLh/3gxRGHqG
063dMbUk8pmUcJzBUyRsNiIPDoEUsLjY5zmZZmp/waAhpREsnK29WLCbqLdpUors
c1JJBsObkA1IM8TZY8YUmvsMEvBLCCanuKpclZZXqeRAeOHJ0v4DZQJHTlUBtBZU
ZXN0MiA8dGVzdDJAdGVzdC5jb20+iGIEExECACIFAlERnrMCGwMGCwkIBwMCBhUI
AgkKCwQWAgMBAh4BAheAAAoJEBEnlAPLFp74xc0AoLNZINHe0ytOsNtMCuLvc3Vd
vePUAJ9KX3L5IBqHarsa+aJHX7r796SokZ0BWARREZ6zEAQA2WkxmNbfeMzGUocN
3JEVe0o6rxGt5eGrTSmWisduDP3MURabhUXnf4T8oaeYcbJjkLLxMrJmNq55ln1e
4bSG5mDkh/ryKsV81m3F0DbqO/z/891nRSP5fondFVral4wsMOzBNgs4vVk7V/F2
0MPjR90CIhnVDKPAQbQA+3PjUR8AAwUEALn922AEE+0d7xSMMFpR7ic3Me5QEGnp
cT4ft6oc0UK5kAnvKoksZUc0hpBHjX1w3LTz847/5hRDuuDvwvGMWK8IfsjOF9T7
rK8QtJuBEyJxjoScA/YZP5vX4y0U1reUEa0EdwmVrnZzatMAe2FhlaR9PlHkOcm5
DZwkcExL0dbI/gMDArxZ+5N7kH4zYLtr9glJS/pJ7F0YJqJpNwCbqD8+8DqHD8Uv
MgQ/rtBxBJJOaF+1AjCd123hLgzIkkfdTh8loV9hDXMKeJgmiEkEGBECAAkFAlER
nrMCGwwACgkQESeUA8sWnvhBswCfdXjznvHCc73/6/MhWcv3dbeTT/wAoLyiZg8+
iY3UT9QkV9d0sMgyLkug
=GQsY
-----END PGP PRIVATE KEY BLOCK-----`;

const armoredPublicKey1 = `-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.11 (GNU/Linux)

mQGiBFERlw4RBAD6Bmcf2w1dtUmtCLkdxeqZLArk3vYoQAjdibxA3gXVyur7fsWb
ro0jVbBHqOCtC6jDxE2l52NP9+tTlWeVMaqqNvUE47LSaPq2DGI8Wx1Rj6bF3mTs
obYEwhGbGh/MhJnME9AHODarvk8AZbzo0+k1EwrBWF6dTUBPfqO7rGU2ewCg80WV
x5pt3evj8rRK3jQ8SMKTNRsD/1PhTdxdZTdXARAFzcW1VaaruWW0Rr1+XHKKwDCz
i7HE76SO9qjnQfZCZG75CdQxI0h8GFeN3zsDqmhob2iSz2aJ1krtjM+iZ1FBFd57
OqCV6wmk5IT0RBN12ZzMS19YvzN/ONXHrmTZlKExd9Mh9RKLeVNw+bf6JsKQEzcY
JzFkBACX9X+hDYchO/2hiTwx4iOO9Fhsuh7eIWumB3gt+aUpm1jrSbas/QLTymmk
uZuQVXI4NtnlvzlNgWv4L5s5RU5WqNGG7WSaKNdcrvJZRC2dgbUJt04J5CKrWp6R
aIYal/81Ut1778lU01PEt563TcQnUBlnjU5OR25KhfSeN5CZY7QUVGVzdCA8dGVz
dEB0ZXN0LmNvbT6IYgQTEQIAIgUCURGXDgIbAwYLCQgHAwIGFQgCCQoLBBYCAwEC
HgECF4AACgkQikDlZK/UvLSspgCfcNaOpTg1W2ucR1JwBbBGvaERfuMAnRgt3/rs
EplqEakMckCtikEnpxYe
=b2Ln
-----END PGP PUBLIC KEY BLOCK-----`;

const expiredPublicKeyThroughDirectSignature = `-----BEGIN PGP PUBLIC KEY BLOCK-----

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
Be4ubVrj5KjhX2PVNEJd3XZRzaXZE2aAMQ==
=ZeAz
-----END PGP PUBLIC KEY BLOCK-----`;

const eccPrivateKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----

xVgEYaYskRYJKwYBBAHaRw8BAQdAlHT6jzgvcng/qDvb+LH+nA4+AWrMLUYf
aNJIuJRUjXMAAP9llTr5+fNSY78FNnpx53muMtyeDINkeUGGwgqAfxj9lhEV
zRN0ZXN0IDx0ZXN0QHRlc3QuaXQ+wowEEBYKAB0FAmGmLJEECwkHCAMVCAoE
FgACAQIZAQIbAwIeAQAhCRBvJAzR+vGyExYhBCaNeWwMzRW97WhAq28kDNH6
8bITWWkA/0R3zADs94dVo+iSNzrtZaDkbHOMb/yjketYmI0XS8UpAP4hUmKN
QcohP6007t0gaQUcgdwum7PKUoM6BeBG8GaTAsddBGGmLJESCisGAQQBl1UB
BQEBB0CibQAv6tvWCWoe6xlkkZGbLpVWvHwgIPzRVdz4e79DdQMBCAcAAP9T
4SntnkgSUnM39dFoTPIoitrsOcHZbvXPCcvclKgZKBJTwngEGBYIAAkFAmGm
LJECGwwAIQkQbyQM0frxshMWIQQmjXlsDM0Vve1oQKtvJAzR+vGyE5ORAQD+
lfFvJjue+tnuIR+ZubxtpKaJpCOWkAcrkx41NtsLwgD/TAkWh1KDWg0IOcUE
MbVkSnU2Z+vhSmYubDCldNOSVwE=
=bTUQ
-----END PGP PRIVATE KEY BLOCK-----`;

function withCompression(tests) {
  const compressionTypes = Object.values(openpgp.enums.compression);

  compressionTypes.forEach(function (compression) {
    const compressionName = openpgp.enums.read(openpgp.enums.compression, compression);
    if (compressionName === 'bzip2') {
      return; // bzip2 compression is not supported.
    }
    const group = `compression - ${compressionName}`;

    describe(group, function() {
      let compressSpy;
      let decompressSpy;

      beforeEach(function () {
        compressSpy = spy(openpgp.CompressedDataPacket.prototype, 'compress');
        decompressSpy = spy(openpgp.CompressedDataPacket.prototype, 'decompress');
      });

      afterEach(function () {
        compressSpy.restore();
        decompressSpy.restore();
      });

      tests(
        function(options) {
          options.config = { preferredCompressionAlgorithm: compression };
          return options;
        },
        function() {
          if (compression === openpgp.enums.compression.uncompressed) {
            expect(compressSpy.called).to.be.false;
            expect(decompressSpy.called).to.be.false;
            return;
          }

          expect(compressSpy.called).to.be.true;
          expect(compressSpy.thisValues[0].algorithm).to.equal(compression);
          expect(decompressSpy.called).to.be.true;
          expect(decompressSpy.thisValues[0].algorithm).to.equal(compression);
        }
      );
    });
  });
}

module.exports = () => describe('OpenPGP.js public api tests', function() {
  describe('readKey(s) and readPrivateKey(s) - unit tests', function() {
    it('readKey and readPrivateKey should create equal private keys', async function() {
      const key = await openpgp.readKey({ armoredKey: priv_key });
      const privateKey = await openpgp.readPrivateKey({ armoredKey: priv_key });
      expect(key.isPrivate()).to.be.true;
      expect(privateKey.isPrivate()).to.be.true;
      expect(key.isDecrypted()).to.be.false;
      expect(privateKey.isDecrypted()).to.be.false;
      expect(key.getKeyID().equals(privateKey.getKeyID())).to.be.true;
    });

    it('readPrivateKeys and readKeys should create equal private keys', async function() {
      const keys = await openpgp.readKeys({ armoredKeys: twoPrivateKeys });
      const privateKeys = await openpgp.readPrivateKeys({ armoredKeys: twoPrivateKeys });
      // pairwise comparison
      const zip = (arr1, arr2) => arr1.map((el, i) => [el, arr2[i]]);
      zip(keys, privateKeys).forEach(([key, privateKey]) => {
        expect(key.isPrivate()).to.be.true;
        expect(privateKey.isPrivate()).to.be.true;
        expect(key.isDecrypted()).to.be.true;
        expect(privateKey.isDecrypted()).to.be.true;
        expect(key.getKeyID().equals(privateKey.getKeyID())).to.be.true;
      });
    });

    it('readPrivateKey should throw on armored public key', async function() {
      await expect(openpgp.readPrivateKey({ armoredKey: pub_key })).to.be.rejectedWith(/Armored text not of type private key/);
    });

    it('readPrivateKeys should throw on armored public keys', async function() {
      await expect(openpgp.readPrivateKeys({ armoredKeys: twoPublicKeys })).to.be.rejectedWith(/Armored text not of type private key/);
    });
  });

  describe('generateKey - validate user ids', function() {
    it('should fail for invalid user name', async function() {
      const opt = {
        userIDs: [{ name: {}, email: 'text@example.com' }]
      };
      const test = openpgp.generateKey(opt);
      await expect(test).to.eventually.be.rejectedWith(/Invalid user ID format/);
    });

    it('should fail for invalid user email address', async function() {
      const opt = {
        userIDs: [{ name: 'Test User', email: 'textexample.com' }]
      };
      const test = openpgp.generateKey(opt);
      await expect(test).to.eventually.be.rejectedWith(/Invalid user ID format/);
    });

    it('should fail for invalid user email address', async function() {
      const opt = {
        userIDs: [{ name: 'Test User', email: 'text@examplecom' }]
      };
      const test = openpgp.generateKey(opt);
      await expect(test).to.eventually.be.rejectedWith(/Invalid user ID format/);
    });

    it('should fail for string user ID', async function() {
      const opt = {
        userIDs: 'Test User <text@example.com>'
      };
      const test = openpgp.generateKey(opt);
      await expect(test).to.eventually.be.rejectedWith(/Invalid user ID format/);
    });

    it('should work for valid single user ID object', function() {
      const opt = {
        userIDs: { name: 'Test User', email: 'text@example.com' }
      };
      return openpgp.generateKey(opt);
    });

    it('should work for array of user ID objects', function() {
      const opt = {
        userIDs: [{ name: 'Test User', email: 'text@example.com' }]
      };
      return openpgp.generateKey(opt);
    });

    it('should work for undefined name', function() {
      const opt = {
        userIDs: { email: 'text@example.com' }
      };
      return openpgp.generateKey(opt);
    });

    it('should work for an undefined email address', function() {
      const opt = {
        userIDs: { name: 'Test User' }
      };
      return openpgp.generateKey(opt);
    });
  });

  describe('generateKey - unit tests', function() {
    it('should have default params set', function() {
      const now = util.normalizeDate(new Date());
      const opt = {
        userIDs: { name: 'Test User', email: 'text@example.com' },
        passphrase: 'secret',
        date: now,
        format: 'object'
      };
      return openpgp.generateKey(opt).then(async function({ privateKey, publicKey }) {
        for (const key of [publicKey, privateKey]) {
          expect(key).to.exist;
          expect(key.users.length).to.equal(1);
          expect(key.users[0].userID.name).to.equal('Test User');
          expect(key.users[0].userID.email).to.equal('text@example.com');
          expect(key.getAlgorithmInfo().rsaBits).to.equal(undefined);
          expect(key.getAlgorithmInfo().curve).to.equal('ed25519');
          expect(+key.getCreationTime()).to.equal(+now);
          expect(await key.getExpirationTime()).to.equal(Infinity);
          expect(key.subkeys.length).to.equal(1);
          expect(key.subkeys[0].getAlgorithmInfo().rsaBits).to.equal(undefined);
          expect(key.subkeys[0].getAlgorithmInfo().curve).to.equal('curve25519');
          expect(+key.subkeys[0].getCreationTime()).to.equal(+now);
          expect(await key.subkeys[0].getExpirationTime()).to.equal(Infinity);
        }
      });
    });

    it('should output keypair with expected format', async function() {
      const opt = {
        userIDs: { name: 'Test User', email: 'text@example.com' }
      };
      const armored = await openpgp.generateKey({ ...opt, format: 'armored' });
      expect((await openpgp.readKey({ armoredKey: armored.privateKey })).isPrivate()).to.be.true;
      expect((await openpgp.readKey({ armoredKey: armored.publicKey })).isPrivate()).to.be.false;

      const binary = await openpgp.generateKey({ ...opt, format: 'binary' });
      expect((await openpgp.readKey({ binaryKey: binary.privateKey })).isPrivate()).to.be.true;
      expect((await openpgp.readKey({ binaryKey: binary.publicKey })).isPrivate()).to.be.false;

      const { privateKey, publicKey } = await openpgp.generateKey({ ...opt, format: 'object' });
      expect(privateKey.isPrivate()).to.be.true;
      expect(publicKey.isPrivate()).to.be.false;
    });
  });

  describe('reformatKey - unit tests', function() {
    it('should output keypair with expected format', async function() {
      const encryptedKey = await openpgp.readKey({ armoredKey: priv_key });
      const original = await openpgp.decryptKey({
        privateKey: encryptedKey,
        passphrase: passphrase
      });

      const opt = {
        privateKey: original,
        userIDs: { name: 'Test User', email: 'text@example.com' }
      };
      const armored = await openpgp.reformatKey({ ...opt, format: 'armored' });
      expect((await openpgp.readKey({ armoredKey: armored.privateKey })).isPrivate()).to.be.true;
      expect((await openpgp.readKey({ armoredKey: armored.publicKey })).isPrivate()).to.be.false;

      const binary = await openpgp.reformatKey({ ...opt, format: 'binary' });
      expect((await openpgp.readKey({ binaryKey: binary.privateKey })).isPrivate()).to.be.true;
      expect((await openpgp.readKey({ binaryKey: binary.publicKey })).isPrivate()).to.be.false;

      const { privateKey, publicKey } = await openpgp.reformatKey({ ...opt, format: 'object' });
      expect(privateKey.isPrivate()).to.be.true;
      expect(publicKey.isPrivate()).to.be.false;
    });
  });

  describe('revokeKey - unit tests', function() {
    it('should output key with expected format', async function() {
      const encryptedKey = await openpgp.readKey({ armoredKey: priv_key });
      const key = await openpgp.decryptKey({
        privateKey: encryptedKey,
        passphrase: passphrase
      });

      const armored = await openpgp.revokeKey({ key, format: 'armored' });
      expect((await openpgp.readKey({ armoredKey: armored.privateKey })).isPrivate()).to.be.true;
      expect((await openpgp.readKey({ armoredKey: armored.publicKey })).isPrivate()).to.be.false;

      const binary = await openpgp.revokeKey({ key, format: 'binary' });
      expect((await openpgp.readKey({ binaryKey: binary.privateKey })).isPrivate()).to.be.true;
      expect((await openpgp.readKey({ binaryKey: binary.publicKey })).isPrivate()).to.be.false;

      const { privateKey, publicKey } = await openpgp.revokeKey({ key, format: 'object' });
      expect(privateKey.isPrivate()).to.be.true;
      expect(publicKey.isPrivate()).to.be.false;
    });
  });

  describe('decryptKey - unit tests', function() {
    it('should work for correct passphrase', async function() {
      const privateKey = await openpgp.readKey({ armoredKey: priv_key });
      const originalKey = await openpgp.readKey({ armoredKey: privateKey.armor() });
      return openpgp.decryptKey({
        privateKey: privateKey,
        passphrase: passphrase
      }).then(unlocked => {
        expect(unlocked.getKeyID().toHex()).to.equal(privateKey.getKeyID().toHex());
        expect(unlocked.subkeys[0].getKeyID().toHex()).to.equal(privateKey.subkeys[0].getKeyID().toHex());
        expect(unlocked.isDecrypted()).to.be.true;
        expect(unlocked.keyPacket.privateParams).to.not.be.null;
        // original key should be unchanged
        expect(privateKey.isDecrypted()).to.be.false;
        expect(privateKey.keyPacket.privateParams).to.be.null;
        expect(privateKey).to.deep.equal(originalKey);
      });
    });

    it('should work with multiple passphrases', async function() {
      const privateKey = await openpgp.readKey({ armoredKey: priv_key });
      const originalKey = await openpgp.readKey({ armoredKey: privateKey.armor() });
      return openpgp.decryptKey({
        privateKey: privateKey,
        passphrase: ['rubbish', passphrase]
      }).then(unlocked => {
        expect(unlocked.getKeyID().toHex()).to.equal(privateKey.getKeyID().toHex());
        expect(unlocked.subkeys[0].getKeyID().toHex()).to.equal(privateKey.subkeys[0].getKeyID().toHex());
        expect(unlocked.isDecrypted()).to.be.true;
        expect(unlocked.keyPacket.privateParams).to.not.be.null;
        // original key should be unchanged
        expect(privateKey.isDecrypted()).to.be.false;
        expect(privateKey.keyPacket.privateParams).to.be.null;
        expect(privateKey).to.deep.equal(originalKey);
      });
    });

    it('should fail for incorrect passphrase', async function() {
      const privateKey = await openpgp.readKey({ armoredKey: priv_key });
      const originalKey = await openpgp.readKey({ armoredKey: privateKey.armor() });
      return openpgp.decryptKey({
        privateKey: privateKey,
        passphrase: 'incorrect'
      }).then(function() {
        throw new Error('Should not decrypt with incorrect passphrase');
      }).catch(function(error){
        expect(error.message).to.match(/Incorrect key passphrase/);
        // original key should be unchanged
        expect(privateKey.isDecrypted()).to.be.false;
        expect(privateKey.keyPacket.privateParams).to.be.null;
        expect(privateKey).to.deep.equal(originalKey);
      });
    });

    it('should fail for corrupted key', async function() {
      const privateKeyMismatchingParams = await openpgp.readKey({ armoredKey: mismatchingKeyParams });
      const originalKey = await openpgp.readKey({ armoredKey: privateKeyMismatchingParams.armor() });
      return openpgp.decryptKey({
        privateKey: privateKeyMismatchingParams,
        passphrase: 'userpass'
      }).then(function() {
        throw new Error('Should not decrypt corrupted key');
      }).catch(function(error) {
        expect(error.message).to.match(/Key is invalid/);
        expect(privateKeyMismatchingParams.isDecrypted()).to.be.false;
        expect(privateKeyMismatchingParams.keyPacket.privateParams).to.be.null;
        expect(privateKeyMismatchingParams).to.deep.equal(originalKey);
      });
    });
  });

  describe('encryptKey - unit tests', function() {
    it('should not change original key', async function() {
      const { privateKey: armoredKey } = await openpgp.generateKey({ userIDs: [{ name: 'test', email: 'test@test.com' }] });
      // read both keys from armored data to make sure all fields are exactly the same
      const key = await openpgp.readKey({ armoredKey });
      const originalKey = await openpgp.readKey({ armoredKey });
      return openpgp.encryptKey({
        privateKey: key,
        passphrase: passphrase
      }).then(locked => {
        expect(locked.getKeyID().toHex()).to.equal(key.getKeyID().toHex());
        expect(locked.subkeys[0].getKeyID().toHex()).to.equal(key.subkeys[0].getKeyID().toHex());
        expect(locked.isDecrypted()).to.be.false;
        expect(locked.keyPacket.privateParams).to.be.null;
        // original key should be unchanged
        expect(key.isDecrypted()).to.be.true;
        expect(key.keyPacket.privateParams).to.not.be.null;
        expect(key).to.deep.equal(originalKey);
      });
    });

    it('encrypted key can be decrypted', async function() {
      const { privateKey } = await openpgp.generateKey({ userIDs: [{ name: 'test', email: 'test@test.com' }], format: 'object' });
      const locked = await openpgp.encryptKey({
        privateKey,
        passphrase: passphrase
      });
      expect(locked.isDecrypted()).to.be.false;
      const unlocked = await openpgp.decryptKey({
        privateKey: locked,
        passphrase: passphrase
      });
      expect(unlocked.isDecrypted()).to.be.true;
    });

    it('should support multiple passphrases', async function() {
      const { privateKey } = await openpgp.generateKey({ userIDs: [{ name: 'test', email: 'test@test.com' }], format: 'object' });
      const passphrases = ['123', '456'];
      const locked = await openpgp.encryptKey({
        privateKey,
        passphrase: passphrases
      });
      expect(locked.isDecrypted()).to.be.false;
      await expect(openpgp.decryptKey({
        privateKey: locked,
        passphrase: passphrases[0]
      })).to.eventually.be.rejectedWith(/Incorrect key passphrase/);
      const unlocked = await openpgp.decryptKey({
        privateKey: locked,
        passphrase: passphrases
      });
      expect(unlocked.isDecrypted()).to.be.true;
    });

    it('should encrypt gnu-dummy key', async function() {
      const key = await openpgp.readKey({ armoredKey: gnuDummyKeySigningSubkey });
      const locked = await openpgp.encryptKey({
        privateKey: key,
        passphrase: passphrase
      });
      expect(key.isDecrypted()).to.be.true;
      expect(locked.isDecrypted()).to.be.false;
      expect(locked.keyPacket.isDummy()).to.be.true;
      const unlocked = await openpgp.decryptKey({
        privateKey: locked,
        passphrase: passphrase
      });
      expect(key.isDecrypted()).to.be.true;
      expect(unlocked.isDecrypted()).to.be.true;
      expect(unlocked.keyPacket.isDummy()).to.be.true;
    });
  });

  describe('decrypt - unit tests', function() {
    let minRSABitsVal;

    beforeEach(async function() {
      minRSABitsVal = openpgp.config.minRSABits;
      openpgp.config.minRSABits = 512;
    });

    afterEach(function() {
      openpgp.config.minRSABits = minRSABitsVal;
    });

    it('Calling decrypt with encrypted key leads to exception', async function() {
      const publicKey = await openpgp.readKey({ armoredKey: pub_key });
      const privateKey = await openpgp.readKey({ armoredKey: priv_key });

      const encOpt = {
        message: await openpgp.createMessage({ text: plaintext }),
        encryptionKeys: publicKey
      };
      const decOpt = {
        decryptionKeys: privateKey
      };
      const encrypted = await openpgp.encrypt(encOpt);
      decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
      await expect(openpgp.decrypt(decOpt)).to.be.rejectedWith('Error decrypting message: Decryption key is not decrypted.');
    });

    it('decrypt/verify should succeed with valid signature  (expectSigned=true)', async function () {
      const publicKey = await openpgp.readKey({ armoredKey: pub_key });
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });

      const encrypted = await openpgp.encrypt({
        message: await openpgp.createMessage({ text: plaintext }),
        signingKeys: privateKey,
        encryptionKeys: publicKey
      });
      const { data, signatures } = await openpgp.decrypt({
        message: await openpgp.readMessage({ armoredMessage: encrypted }),
        decryptionKeys: privateKey,
        verificationKeys: publicKey,
        expectSigned: true
      });
      expect(data).to.equal(plaintext);
      expect(await signatures[0].verified).to.be.true;
    });

    it('decrypt/verify should throw on missing public keys (expectSigned=true)', async function () {
      const publicKey = await openpgp.readKey({ armoredKey: pub_key });
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });

      const encrypted = await openpgp.encrypt({
        message: await openpgp.createMessage({ text: plaintext }),
        encryptionKeys: publicKey,
        signingKeys: privateKey
      });
      await expect(openpgp.decrypt({
        message: await openpgp.readMessage({ armoredMessage: encrypted }),
        decryptionKeys: privateKey,
        expectSigned: true
      })).to.be.eventually.rejectedWith(/Verification keys are required/);
    });

    it('decrypt/verify should throw on missing signature (expectSigned=true)', async function () {
      const publicKey = await openpgp.readKey({ armoredKey: pub_key });
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });

      const encrypted = await openpgp.encrypt({
        message: await openpgp.createMessage({ text: plaintext }),
        encryptionKeys: publicKey
      });
      await expect(openpgp.decrypt({
        message: await openpgp.readMessage({ armoredMessage: encrypted }),
        decryptionKeys: privateKey,
        verificationKeys: publicKey,
        expectSigned: true
      })).to.be.eventually.rejectedWith(/Message is not signed/);
    });

    it('decrypt/verify should throw on invalid signature (expectSigned=true)', async function () {
      const publicKey = await openpgp.readKey({ armoredKey: pub_key });
      const wrongPublicKey = (await openpgp.readKey({ armoredKey: priv_key_2000_2008 })).toPublic();
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });

      const encrypted = await openpgp.encrypt({
        message: await openpgp.createMessage({ text: plaintext }),
        encryptionKeys: publicKey,
        signingKeys: privateKey
      });
      await expect(openpgp.decrypt({
        message: await openpgp.readMessage({ armoredMessage: encrypted }),
        decryptionKeys: privateKey,
        verificationKeys: wrongPublicKey,
        expectSigned: true
      })).to.be.eventually.rejectedWith(/Could not find signing key/);
    });

    it('decrypt/verify should succeed with valid signature (expectSigned=true, with streaming)', async function () {
      const publicKey = await openpgp.readKey({ armoredKey: pub_key });
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });

      const encrypted = await openpgp.encrypt({
        message: await openpgp.createMessage({ text: plaintext }),
        signingKeys: privateKey,
        encryptionKeys: publicKey
      });
      const { data: streamedData, signatures } = await openpgp.decrypt({
        message: await openpgp.readMessage({ armoredMessage: stream.toStream(encrypted) }),
        decryptionKeys: privateKey,
        verificationKeys: publicKey,
        expectSigned: true
      });
      const data = await stream.readToEnd(streamedData);
      expect(data).to.equal(plaintext);
      expect(await signatures[0].verified).to.be.true;
    });

    it('decrypt/verify should throw on missing public keys (expectSigned=true, with streaming)', async function () {
      const publicKey = await openpgp.readKey({ armoredKey: pub_key });
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });

      const encrypted = await openpgp.encrypt({
        message: await openpgp.createMessage({ text: plaintext }),
        encryptionKeys: publicKey,
        signingKeys: privateKey
      });
      await expect(openpgp.decrypt({
        message: await openpgp.readMessage({ armoredMessage: stream.toStream(encrypted) }),
        decryptionKeys: privateKey,
        expectSigned: true
      })).to.be.eventually.rejectedWith(/Verification keys are required/);
    });

    it('decrypt/verify should throw on missing signature (expectSigned=true, with streaming)', async function () {
      const publicKey = await openpgp.readKey({ armoredKey: pub_key });
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });

      const encrypted = await openpgp.encrypt({
        message: await openpgp.createMessage({ text: plaintext }),
        encryptionKeys: publicKey
      });
      await expect(openpgp.decrypt({
        message: await openpgp.readMessage({ armoredMessage: stream.toStream(encrypted) }),
        decryptionKeys: privateKey,
        verificationKeys: publicKey,
        expectSigned: true
      })).to.be.eventually.rejectedWith(/Message is not signed/);
    });

    it('decrypt/verify should throw on invalid signature (expectSigned=true, with streaming)', async function () {
      const publicKey = await openpgp.readKey({ armoredKey: pub_key });
      const wrongPublicKey = (await openpgp.readKey({ armoredKey: priv_key_2000_2008 })).toPublic();
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });

      const encrypted = await openpgp.encrypt({
        message: await openpgp.createMessage({ text: plaintext }),
        encryptionKeys: publicKey,
        signingKeys: privateKey
      });
      const { data: streamedData } = await openpgp.decrypt({
        message: await openpgp.readMessage({ armoredMessage: stream.toStream(encrypted) }),
        decryptionKeys: privateKey,
        verificationKeys: wrongPublicKey,
        expectSigned: true
      });
      await expect(
        stream.readToEnd(streamedData)
      ).to.be.eventually.rejectedWith(/Could not find signing key/);
    });

    it('Supports decrypting with GnuPG dummy key', async function() {
      const { rejectMessageHashAlgorithms } = openpgp.config;
      Object.assign(openpgp.config, { rejectMessageHashAlgorithms: new Set([openpgp.enums.hash.md5, openpgp.enums.hash.ripemd]) });
      try {
        const armoredMessage = `-----BEGIN PGP MESSAGE-----
Version: GnuPG v1.4.11 (GNU/Linux)

hQEOA1N4OCSSjECBEAP/diDJCQn4e88193PgqhbfAkohk9RQ0v0MPnXpJbCRTHKO
8r9nxiAr/TQv4ZOingXdAp2JZEoE9pXxZ3r1UWew04czxmgJ8FP1ztZYWVFAWFVi
Tj930TBD7L1fY/MD4fK6xjEG7z5GT8k4tn4mLm/PpWMbarIglfMopTy1M/py2cID
/2Sj7Ikh3UFiG+zm4sViYc5roNbMy8ixeoKixxi99Mx8INa2cxNfqbabjblFyc0Z
BwmbIc+ZiY2meRNI5y/tk0gRD7hT84IXGGl6/mH00bsX/kkWdKGeTvz8s5G8RDHa
Za4HgLbXItkX/QarvRS9kvkD01ujHfj+1ZvgmOBttNfP0p8BQLIICqvg1eYD9aPB
+GtOZ2F3+k5VyBL5yIn/s65SBjNO8Fqs3aL0x+p7s1cfUzx8J8a8nWpqq/qIQIqg
ZJH6MZRKuQwscwH6NWgsSVwcnVCAXnYOpbHxFQ+j7RbF/+uiuqU+DFH/Rd5pik8b
0Dqnp0yfefrkjQ0nuvubgB6Rv89mHpnvuJfFJRInpg4lrHwLvRwdpN2HDozFHcKK
aOU=
=4iGt
-----END PGP MESSAGE-----`;
        const passphrase = 'abcd';
        // exercises the GnuPG s2k type 1001 extension:
        // the secrets on the primary key have been stripped.
        const dummyKey = await openpgp.readKey({ armoredKey: armoredDummyPrivateKey1 });
        const publicKey = await openpgp.readKey({ armoredKey: armoredPublicKey1 });
        const message = await openpgp.readMessage({ armoredMessage });
        const primaryKeyPacket = dummyKey.keyPacket.write();
        expect(dummyKey.isDecrypted()).to.be.false;
        const decryptedDummyKey = await openpgp.decryptKey({ privateKey: dummyKey, passphrase });
        expect(decryptedDummyKey.isDecrypted()).to.be.true;
        // decrypting with a secret subkey works
        const msg = await openpgp.decrypt({
          message, decryptionKeys: decryptedDummyKey, verificationKeys: publicKey, config: { rejectPublicKeyAlgorithms: new Set() }
        });
        expect(msg.signatures).to.exist;
        expect(msg.signatures).to.have.length(1);
        expect(await msg.signatures[0].verified).to.be.true;
        expect((await msg.signatures[0].signature).packets.length).to.equal(1);
        // secret key operations involving the primary key should fail
        await expect(openpgp.sign({
          message: await openpgp.createMessage({ text: 'test' }), signingKeys: decryptedDummyKey, config: { rejectPublicKeyAlgorithms: new Set() }
        })).to.eventually.be.rejectedWith(/Cannot sign with a gnu-dummy key/);
        await expect(
          openpgp.reformatKey({ userIDs: { name: 'test' }, privateKey: decryptedDummyKey })
        ).to.eventually.be.rejectedWith(/Cannot reformat a gnu-dummy primary key/);

        const encryptedDummyKey = await openpgp.encryptKey({ privateKey: decryptedDummyKey, passphrase });
        expect(encryptedDummyKey.isDecrypted()).to.be.false;
        const primaryKeyPacket2 = encryptedDummyKey.keyPacket.write();
        expect(primaryKeyPacket).to.deep.equal(primaryKeyPacket2);
      } finally {
        Object.assign(openpgp.config, { rejectMessageHashAlgorithms });
      }
    });

    it('decrypt with `config.constantTimePKCS1Decryption` option should succeed', async function () {
      const publicKey = await openpgp.readKey({ armoredKey: pub_key });
      const publicKey2 = await openpgp.readKey({ armoredKey: eccPrivateKey });
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });

      const encrypted = await openpgp.encrypt({
        message: await openpgp.createMessage({ text: plaintext }),
        signingKeys: privateKey,
        encryptionKeys: [publicKey, publicKey2]
      });
      const { data } = await openpgp.decrypt({
        message: await openpgp.readMessage({ armoredMessage: encrypted }),
        decryptionKeys: privateKey,
        config: { constantTimePKCS1Decryption: true }
      });
      expect(data).to.equal(plaintext);
    });

    it('decrypt with `config.constantTimePKCS1Decryption` option should succeed (with streaming)', async function () {
      const publicKey = await openpgp.readKey({ armoredKey: pub_key });
      const publicKey2 = await openpgp.readKey({ armoredKey: eccPrivateKey });
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });

      const encrypted = await openpgp.encrypt({
        message: await openpgp.createMessage({ text: plaintext }),
        signingKeys: privateKey,
        encryptionKeys: [publicKey, publicKey2]
      });
      const { data: streamedData } = await openpgp.decrypt({
        message: await openpgp.readMessage({ armoredMessage: stream.toStream(encrypted) }),
        decryptionKeys: privateKey,
        verificationKeys: publicKey,
        expectSigned: true,
        config: { constantTimePKCS1Decryption: true }
      });
      const data = await stream.readToEnd(streamedData);
      expect(data).to.equal(plaintext);
    });

    it('decrypt with `config.constantTimePKCS1Decryption` option should fail if session key algo support is disabled', async function () {
      const publicKeyRSA = await openpgp.readKey({ armoredKey: pub_key });
      const privateKeyRSA = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });
      const privateKeyECC = await openpgp.readPrivateKey({ armoredKey: eccPrivateKey });

      const encrypted = await openpgp.encrypt({
        message: await openpgp.createMessage({ text: plaintext }),
        signingKeys: privateKeyRSA,
        encryptionKeys: [publicKeyRSA, privateKeyECC]
      });

      const config = {
        constantTimePKCS1Decryption: true,
        constantTimePKCS1DecryptionSupportedSymmetricAlgorithms: new Set()
      };
      // decryption using RSA key should fail
      await expect(openpgp.decrypt({
        message: await openpgp.readMessage({ armoredMessage: encrypted }),
        decryptionKeys: privateKeyRSA,
        config
      })).to.be.rejectedWith(/Session key decryption failed/);
      // decryption using ECC key should succeed (PKCS1 is not used, so constant time countermeasures are not applied)
      const { data } = await openpgp.decrypt({
        message: await openpgp.readMessage({ armoredMessage: encrypted }),
        decryptionKeys: privateKeyECC,
        config
      });
      expect(data).to.equal(plaintext);
    });

  });

  describe('verify - unit tests', function() {
    let minRSABitsVal;

    beforeEach(async function() {
      minRSABitsVal = openpgp.config.minRSABits;
      openpgp.config.minRSABits = 512;
    });

    afterEach(function() {
      openpgp.config.minRSABits = minRSABitsVal;
    });

    describe('message', function() {
      verifyTests(false);

      it('verify should succeed with valid signature (expectSigned=true, with streaming)', async function () {
        const publicKey = await openpgp.readKey({ armoredKey: pub_key });
        const privateKey = await openpgp.decryptKey({
          privateKey: await openpgp.readKey({ armoredKey: priv_key }),
          passphrase
        });

        const signed = await openpgp.sign({
          message: await openpgp.createMessage({ text: plaintext }),
          signingKeys: privateKey
        });
        const { data: streamedData, signatures } = await openpgp.verify({
          message: await openpgp.readMessage({ armoredMessage: stream.toStream(signed) }),
          verificationKeys: publicKey,
          expectSigned: true
        });
        const data = await stream.readToEnd(streamedData);
        expect(data).to.equal(plaintext);
        expect(await signatures[0].verified).to.be.true;
      });

      it('verify should throw on missing signature (expectSigned=true, with streaming)', async function () {

        const publicKey = await openpgp.readKey({ armoredKey: pub_key });

        await expect(openpgp.verify({
          message: await openpgp.createMessage({ text: stream.toStream(plaintext) }),
          verificationKeys: publicKey,
          expectSigned: true
        })).to.be.eventually.rejectedWith(/Message is not signed/);
      });

      it('verify should throw on invalid signature (expectSigned=true, with streaming)', async function () {
        const wrongPublicKey = (await openpgp.readKey({ armoredKey: priv_key_2000_2008 })).toPublic();
        const privateKey = await openpgp.decryptKey({
          privateKey: await openpgp.readKey({ armoredKey: priv_key }),
          passphrase
        });

        const signed = await openpgp.sign({
          message: await openpgp.createMessage({ text: plaintext }),
          signingKeys: privateKey
        });
        const { data: streamedData } = await openpgp.verify({
          message: await openpgp.readMessage({ armoredMessage: stream.toStream(signed) }),
          verificationKeys: wrongPublicKey,
          expectSigned: true
        });
        await expect(
          stream.readToEnd(streamedData)
        ).to.be.eventually.rejectedWith(/Could not find signing key/);
      });

      it('verify should fail if the signature is re-used with a different message', async function () {
        const privateKey = await openpgp.decryptKey({
          privateKey: await openpgp.readKey({ armoredKey: priv_key }),
          passphrase
        });

        const message = await openpgp.createMessage({ text: 'a message' });
        const armoredSignature = await openpgp.sign({
          message,
          signingKeys: privateKey,
          detached: true
        });
        const { signatures } = await openpgp.verify({
          message,
          signature: await openpgp.readSignature({ armoredSignature }),
          verificationKeys: privateKey.toPublic()
        });
        expect(await signatures[0].verified).to.be.true;
        // pass a different message
        await expect(openpgp.verify({
          message: await openpgp.createMessage({ text: 'a different message' }),
          signature: await openpgp.readSignature({ armoredSignature }),
          verificationKeys: privateKey.toPublic(),
          expectSigned: true
        })).to.be.rejectedWith(/digest did not match/);
      });
    });

    describe('cleartext message', function() {
      verifyTests(true);
    });

    function verifyTests(useCleartext) {
      const createMessage = useCleartext ? openpgp.createCleartextMessage : openpgp.createMessage;
      const readMessage = ({ armoredMessage }) => (
        useCleartext ?
          openpgp.readCleartextMessage({ cleartextMessage: armoredMessage }) :
          openpgp.readMessage({ armoredMessage })
      );
      const text = useCleartext ? util.removeTrailingSpaces(plaintext) : plaintext;

      it('verify should succeed with valid signature (expectSigned=true)', async function () {
        const publicKey = await openpgp.readKey({ armoredKey: pub_key });
        const privateKey = await openpgp.decryptKey({
          privateKey: await openpgp.readKey({ armoredKey: priv_key }),
          passphrase
        });

        const signed = await openpgp.sign({
          message: await createMessage({ text }),
          signingKeys: privateKey
        });
        const { data, signatures } = await openpgp.verify({
          message: await readMessage({ armoredMessage: signed }),
          verificationKeys: publicKey,
          expectSigned: true
        });
        expect(data).to.equal(text);
        expect(await signatures[0].verified).to.be.true;
      });

      it('verify should throw on missing signature (expectSigned=true)', async function () {
        const publicKey = await openpgp.readKey({ armoredKey: pub_key });

        await expect(openpgp.verify({
          message: await createMessage({ text }),
          verificationKeys: publicKey,
          expectSigned: true
        })).to.be.eventually.rejectedWith(/Message is not signed/);
      });

      it('verify should throw on invalid signature (expectSigned=true)', async function () {
        const wrongPublicKey = (await openpgp.readKey({ armoredKey: priv_key_2000_2008 })).toPublic();
        const privateKey = await openpgp.decryptKey({
          privateKey: await openpgp.readKey({ armoredKey: priv_key }),
          passphrase
        });

        const signed = await openpgp.sign({
          message: await createMessage({ text }),
          signingKeys: privateKey
        });
        await expect(openpgp.verify({
          message: await readMessage({ armoredMessage: signed }),
          verificationKeys: wrongPublicKey,
          expectSigned: true
        })).to.be.eventually.rejectedWith(/Could not find signing key/);
      });
    }
  });

  describe('sign - unit tests', function() {
    it('Supports signing with GnuPG dummy key', async function() {
      const dummyKey = await openpgp.readKey({ armoredKey: gnuDummyKeySigningSubkey });
      const sig = await openpgp.sign({
        message: await openpgp.createMessage({ text: 'test' }),
        signingKeys: dummyKey,
        date: new Date('2018-12-17T03:24:00'),
        config: { minRSABits: 1024 }
      });
      expect(sig).to.match(/-----END PGP MESSAGE-----\n$/);
    });

    it('Calling sign with no signing key leads to exception', async function() {
      await expect(openpgp.sign({
        message: await openpgp.createMessage({ text: plaintext })
      })).to.be.rejectedWith(/No signing keys provided/);
    });

    it('should output cleartext message of expected format', async function() {
      const text = 'test';
      const message = await openpgp.createCleartextMessage({ text });
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });
      const config = { minRSABits: 1024 };

      const cleartextMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'armored' });
      const parsedArmored = await openpgp.readCleartextMessage({ cleartextMessage });
      expect(parsedArmored.text).to.equal(text);
      expect(parsedArmored.signature.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);

      await expect(openpgp.sign({ message, signingKeys: privateKey, config, format: 'binary' })).to.be.rejectedWith('');

      const objectMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'object' });
      expect(objectMessage.signature.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);
      const verified = await openpgp.verify({ message: objectMessage, verificationKeys: privateKey, expectSigned: true, config });
      expect(verified.data).to.equal(text);
    });

    it('should output message of expected format', async function() {
      const text = 'test';
      const message = await openpgp.createMessage({ text });
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });
      const config = { minRSABits: 1024 };

      const armoredMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'armored' });
      const parsedArmored = await openpgp.readMessage({ armoredMessage });
      expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1);

      const binaryMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'binary' });
      const parsedBinary = await openpgp.readMessage({ binaryMessage });
      expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1);

      const objectMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'object' });
      expect(objectMessage.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1);
      const verified = await openpgp.verify({ message: objectMessage, verificationKeys: privateKey, expectSigned: true, config });
      expect(verified.data).to.equal(text);
    });

    it('should output message of expected format', async function() {
      const text = 'test';
      const message = await openpgp.createMessage({ text });
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });
      const config = { minRSABits: 1024 };

      const armoredMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'armored' });
      const parsedArmored = await openpgp.readMessage({ armoredMessage });
      expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1);

      const binaryMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'binary' });
      const parsedBinary = await openpgp.readMessage({ binaryMessage });
      expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1);

      const objectMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'object' });
      expect(objectMessage.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1);
      const verified = await openpgp.verify({ message: objectMessage, verificationKeys: privateKey, expectSigned: true, config });
      expect(verified.data).to.equal(text);
    });

    it('should output message of expected format (with streaming)', async function() {
      const text = 'test';
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });
      const config = { minRSABits: 1024 };

      const armoredMessage = await openpgp.sign({
        message: await openpgp.createMessage({ text: stream.toStream(text) }),
        signingKeys: privateKey,
        format: 'armored',
        config
      });
      const parsedArmored = await openpgp.readMessage({ armoredMessage });
      expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1);

      const binaryMessage = await openpgp.sign({
        message: await openpgp.createMessage({ text: stream.toStream(text) }),
        signingKeys: privateKey,
        format: 'binary',
        config
      });
      const parsedBinary = await openpgp.readMessage({ binaryMessage });
      expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1);

      const objectMessage = await openpgp.sign({
        message: await openpgp.createMessage({ text: stream.toStream(text) }),
        signingKeys: privateKey,
        format: 'object',
        config
      });
      expect(objectMessage.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1);
      objectMessage.packets[1].data = await stream.readToEnd(objectMessage.packets[1].data);
      objectMessage.packets[2].signedHashValue = await stream.readToEnd(objectMessage.packets[2].signedHashValue);
      const { data: streamedData } = await openpgp.verify({ message: objectMessage, verificationKeys: privateKey, expectSigned: true, config });
      expect(await stream.readToEnd(streamedData)).to.equal(text);
      expect(streamedData).to.equal(text);
    });

    it('should output message of expected format (detached)', async function() {
      const text = 'test';
      const message = await openpgp.createMessage({ text });
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });
      const config = { minRSABits: 1024 };

      const armoredSignature = await openpgp.sign({ message, signingKeys: privateKey, detached: true, config, format: 'armored' });
      const parsedArmored = await openpgp.readSignature({ armoredSignature });
      expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);

      const binarySignature = await openpgp.sign({ message, signingKeys: privateKey, detached: true, config, format: 'binary' });
      const parsedBinary = await openpgp.readSignature({ binarySignature });
      expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);

      const objectSignature = await openpgp.sign({ message, signingKeys: privateKey, detached: true, config, format: 'object' });
      expect(objectSignature.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);
      const verified = await openpgp.verify({ message, signature: objectSignature, verificationKeys: privateKey, expectSigned: true, config });
      expect(verified.data).to.equal(text);
    });

    it('should output message of expected format (detached, with streaming)', async function() {
      const text = 'test';
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });
      const config = { minRSABits: 1024 };

      const armoredSignature = await openpgp.sign({
        message: await openpgp.createMessage({ text: stream.toStream(text) }),
        signingKeys: privateKey,
        detached: true,
        format: 'armored',
        config
      });
      const parsedArmored = await openpgp.readSignature({ armoredSignature: await stream.readToEnd(armoredSignature) });
      expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);

      const binarySignature = await openpgp.sign({
        message: await openpgp.createMessage({ text: stream.toStream(text) }),
        signingKeys: privateKey,
        detached: true,
        format: 'binary',
        config
      });
      const parsedBinary = await openpgp.readSignature({ binarySignature: await stream.readToEnd(binarySignature) });
      expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);

      const streamedMessage = await openpgp.createMessage({ text: stream.toStream(text) });
      const objectSignature = await openpgp.sign({
        message: streamedMessage,
        signingKeys: privateKey,
        detached: true,
        format: 'object',
        config
      });
      expect(objectSignature.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);

      const armoredStreamedMessage = streamedMessage.armor(); // consume input message stream, to allow to read the signed hash
      objectSignature.packets[0].signedHashValue = await stream.readToEnd(objectSignature.packets[0].signedHashValue);
      const { data: streamedData } = await openpgp.verify({
        message: await openpgp.readMessage({ armoredMessage: armoredStreamedMessage }),
        signature: objectSignature,
        verificationKeys: privateKey,
        expectSigned: true,
        config
      });
      expect(await stream.readToEnd(streamedData)).to.equal(text);
    });
  });

  describe('encrypt - unit tests', function() {
    it('Does not encrypt to expired key (expiration time subpacket on a direct-key signature)', async function() {
      const expiredKey = await openpgp.readKey({ armoredKey: expiredPublicKeyThroughDirectSignature });
      await expect(
        openpgp.encrypt({ message: await openpgp.createMessage({ text: 'test' }), encryptionKeys: expiredKey })
      ).to.be.rejectedWith(/Primary key is expired/);
    });

    it('should output message of expected format', async function() {
      const passwords = 'password';
      const text = 'test';
      const message = await openpgp.createMessage({ text });
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });

      const armoredMessage = await openpgp.encrypt({ message, passwords, format: 'armored' });
      const parsedArmored = await openpgp.readMessage({ armoredMessage });
      expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1);

      const binaryMessage = await openpgp.encrypt({ message, passwords, format: 'binary' });
      const parsedBinary = await openpgp.readMessage({ binaryMessage });
      expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1);

      const config = { minRSABits: 1024 };
      const objectMessage = await openpgp.encrypt({ message, passwords, signingKeys: privateKey, config, format: 'object' });
      expect(objectMessage.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1);
      const decrypted = await openpgp.decrypt({ message: objectMessage, passwords, verificationKeys: privateKey, expectSigned: true, config });
      expect(decrypted.data).to.equal(text);
    });

    it('should output message of expected format (with streaming)', async function() {
      const passwords = 'password';
      const text = 'test';
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });

      const armoredMessage = await openpgp.encrypt({
        message: await openpgp.createMessage({ text: stream.toStream(text) }),
        passwords,
        format: 'armored'
      });
      const parsedArmored = await openpgp.readMessage({ armoredMessage });
      expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1);

      const binaryMessage = await openpgp.encrypt({
        message: await openpgp.createMessage({ text: stream.toStream(text) }),
        passwords,
        format: 'binary'
      });
      const parsedBinary = await openpgp.readMessage({ binaryMessage });
      expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1);

      const config = { minRSABits: 1024 };
      const objectMessage = await openpgp.encrypt({
        message: await openpgp.createMessage({ text: stream.toStream(text) }),
        passwords,
        signingKeys: privateKey,
        format: 'object',
        config
      });
      expect(objectMessage.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1);
      const { data: streamedData } = await openpgp.decrypt({ message: objectMessage, passwords, verificationKeys: privateKey, expectSigned: true, config });
      expect(await stream.readToEnd(streamedData)).to.equal(text);
    });
  });

  describe('encryptSessionKey - unit tests', function() {
    it('should output message of expected format', async function() {
      const passwords = 'password';
      const sessionKey = { data: new Uint8Array(16).fill(1), algorithm: 'aes128' };

      const armoredMessage = await openpgp.encryptSessionKey({ ...sessionKey, passwords, format: 'armored' });
      const parsedArmored = await openpgp.readMessage({ armoredMessage });
      expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1);

      const binaryMessage = await openpgp.encryptSessionKey({ ...sessionKey, passwords, format: 'binary' });
      const parsedBinary = await openpgp.readMessage({ binaryMessage });
      expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1);

      const objectMessage = await openpgp.encryptSessionKey({ ...sessionKey, passwords, format: 'object' });
      expect(objectMessage.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1);
      const [decryptedSessionKey] = await openpgp.decryptSessionKeys({ message: objectMessage, passwords });
      expect(decryptedSessionKey).to.deep.equal(sessionKey);
    });
  });

  describe('encrypt, decrypt, sign, verify - integration tests', function() {
    let privateKey_2000_2008;
    let publicKey_2000_2008;
    let privateKey_2038_2045;
    let publicKey_2038_2045;
    let privateKey_1337;
    let publicKey_1337;
    let privateKey;
    let publicKey;
    let publicKeyNoAEAD;

    let aeadProtectVal;
    let preferredAEADAlgorithmVal;
    let aeadChunkSizeByteVal;
    let v5KeysVal;
    let minRSABitsVal;

    beforeEach(async function() {
      publicKey = await openpgp.readKey({ armoredKey: pub_key });
      publicKeyNoAEAD = await openpgp.readKey({ armoredKey: pub_key });
      privateKey = await openpgp.readKey({ armoredKey: priv_key });
      privateKey_2000_2008 = await openpgp.readKey({ armoredKey: priv_key_2000_2008 });
      publicKey_2000_2008 = privateKey_2000_2008.toPublic();
      privateKey_2038_2045 = await openpgp.readKey({ armoredKey: priv_key_2038_2045 });
      publicKey_2038_2045 = privateKey_2038_2045.toPublic();
      privateKey_1337 = await openpgp.readKey({ armoredKey: priv_key_expires_1337 });
      publicKey_1337 = privateKey_1337.toPublic();

      aeadProtectVal = openpgp.config.aeadProtect;
      preferredAEADAlgorithmVal = openpgp.config.preferredAEADAlgorithm;
      aeadChunkSizeByteVal = openpgp.config.aeadChunkSizeByte;
      v5KeysVal = openpgp.config.v5Keys;
      minRSABitsVal = openpgp.config.minRSABits;

      openpgp.config.minRSABits = 512;
    });

    afterEach(function() {
      openpgp.config.aeadProtect = aeadProtectVal;
      openpgp.config.preferredAEADAlgorithm = preferredAEADAlgorithmVal;
      openpgp.config.aeadChunkSizeByte = aeadChunkSizeByteVal;
      openpgp.config.v5Keys = v5KeysVal;
      openpgp.config.minRSABits = minRSABitsVal;
    });

    it('Configuration', async function() {
      const showCommentVal = openpgp.config.showComment;
      const showVersionVal = openpgp.config.showVersion;
      const commentStringVal = openpgp.config.commentString;

      try {
        const encryptedDefault = await openpgp.encrypt({ encryptionKeys:publicKey, message:await openpgp.createMessage({ text: plaintext }) });
        expect(encryptedDefault).to.exist;
        expect(encryptedDefault).not.to.match(/^Version:/);
        expect(encryptedDefault).not.to.match(/^Comment:/);

        openpgp.config.showComment = true;
        openpgp.config.commentString = 'different';
        const encryptedWithComment = await openpgp.encrypt({ encryptionKeys:publicKey, message:await openpgp.createMessage({ text: plaintext }) });
        expect(encryptedWithComment).to.exist;
        expect(encryptedWithComment).not.to.match(/^Version:/);
        expect(encryptedWithComment).to.match(/Comment: different/);
      } finally {
        openpgp.config.showComment = showCommentVal;
        openpgp.config.showVersion = showVersionVal;
        openpgp.config.commentString = commentStringVal;
      }
    });

    tryTests('CFB mode (asm.js)', tests, {
      if: true,
      beforeEach: function() {
        openpgp.config.aeadProtect = false;
      }
    });

    tryTests('GCM mode (V5 keys)', tests, {
      if: true,
      beforeEach: function() {
        openpgp.config.aeadProtect = true;
        openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.experimentalGCM;
        openpgp.config.v5Keys = true;

        // Monkey-patch AEAD feature flag
        publicKey.users[0].selfCertifications[0].features = [7];
        publicKey_2000_2008.users[0].selfCertifications[0].features = [7];
        publicKey_2038_2045.users[0].selfCertifications[0].features = [7];
      }
    });

    tryTests('EAX mode (small chunk size)', tests, {
      if: true,
      beforeEach: function() {
        openpgp.config.aeadProtect = true;
        openpgp.config.aeadChunkSizeByte = 0;

        // Monkey-patch AEAD feature flag
        publicKey.users[0].selfCertifications[0].features = [7];
        publicKey_2000_2008.users[0].selfCertifications[0].features = [7];
        publicKey_2038_2045.users[0].selfCertifications[0].features = [7];
      }
    });

    tryTests('OCB mode', tests, {
      if: !openpgp.config.ci,
      beforeEach: function() {
        openpgp.config.aeadProtect = true;
        openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.ocb;

        // Monkey-patch AEAD feature flag
        publicKey.users[0].selfCertifications[0].features = [7];
        publicKey_2000_2008.users[0].selfCertifications[0].features = [7];
        publicKey_2038_2045.users[0].selfCertifications[0].features = [7];
      }
    });

    function tests() {
      describe('encryptSessionKey, decryptSessionKeys', function() {
        const sk = new Uint8Array([0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01]);

        let decryptedPrivateKey; // to avoid decrypting key before each test
        beforeEach(async function() {
          if (!decryptedPrivateKey) {
            decryptedPrivateKey = await openpgp.decryptKey({ privateKey, passphrase });
          }
          privateKey = decryptedPrivateKey;
        });

        it('should encrypt with public key', function() {
          return openpgp.encryptSessionKey({
            data: sk,
            algorithm: 'aes128',
            encryptionKeys: publicKey,
            format: 'binary'
          }).then(async function(encrypted) {
            const message = await openpgp.readMessage({ binaryMessage: encrypted });
            return openpgp.decryptSessionKeys({
              message,
              decryptionKeys: privateKey
            });
          }).then(function(decrypted) {
            expect(decrypted[0].data).to.deep.equal(sk);
          });
        });

        it('should encrypt with password', function() {
          return openpgp.encryptSessionKey({
            data: sk,
            algorithm: 'aes128',
            passwords: password1,
            format: 'binary'
          }).then(async function(encrypted) {
            const message = await openpgp.readMessage({ binaryMessage: encrypted });
            return openpgp.decryptSessionKeys({
              message,
              passwords: password1
            });
          }).then(function(decrypted) {
            expect(decrypted[0].data).to.deep.equal(sk);
          });
        });

        it('should not decrypt with a key without binding signatures', function() {
          return openpgp.encryptSessionKey({
            data: sk,
            algorithm: 'aes128',
            encryptionKeys: publicKey,
            format: 'binary'
          }).then(async function(encrypted) {
            const message = await openpgp.readMessage({ binaryMessage: encrypted });
            const invalidPrivateKey = await openpgp.readKey({ armoredKey: priv_key });
            invalidPrivateKey.subkeys[0].bindingSignatures = [];
            return openpgp.decryptSessionKeys({
              message,
              decryptionKeys: invalidPrivateKey
            }).then(() => {
              throw new Error('Should not decrypt with invalid key');
            }).catch(error => {
              expect(error.message).to.match(/Error decrypting session keys: Session key decryption failed./);
            });
          });
        });

        it('roundtrip workflow: encrypt, decryptSessionKeys, decrypt with pgp key pair', async function () {
          const encrypted = await openpgp.encrypt({
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: publicKey
          });
          const decryptedSessionKeys = await openpgp.decryptSessionKeys({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            decryptionKeys: privateKey
          });
          const decrypted = await openpgp.decrypt({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            sessionKeys: decryptedSessionKeys[0]
          });
          expect(decrypted.data).to.equal(plaintext);
        });

        it('roundtrip workflow: encrypt, decryptSessionKeys, decrypt with pgp key pair -- trailing spaces', async function () {
          const plaintext = 'space: \nspace and tab: \t\nno trailing space\n  \ntab:\t\ntab and space:\t ';
          const encrypted = await openpgp.encrypt({
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: publicKey
          });
          const decryptedSessionKeys = await openpgp.decryptSessionKeys({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            decryptionKeys: privateKey
          });
          const decrypted = await openpgp.decrypt({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            sessionKeys: decryptedSessionKeys[0]
          });
          expect(decrypted.data).to.equal(plaintext);
        });

        it('roundtrip workflow: encrypt, decryptSessionKeys, decrypt with password', async function () {
          const encrypted = await openpgp.encrypt({
            message: await openpgp.createMessage({ text: plaintext }),
            passwords: password1
          });
          const decryptedSessionKeys = await openpgp.decryptSessionKeys({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            passwords: password1
          });
          const decrypted = await openpgp.decrypt({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            sessionKeys: decryptedSessionKeys[0]
          });
          expect(decrypted.data).to.equal(plaintext);
        });

        it('roundtrip workflow: encrypt with multiple passwords, decryptSessionKeys, decrypt with multiple passwords', async function () {
          const encrypted = await openpgp.encrypt({
            message: await openpgp.createMessage({ text: plaintext }),
            passwords: [password1, password2]
          });
          const decryptedSessionKeys = await openpgp.decryptSessionKeys({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            passwords: [password1, password2]
          });
          const decrypted = await openpgp.decrypt({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            sessionKeys: decryptedSessionKeys[0]
          });
          expect(decrypted.data).to.equal(plaintext);
        });

        it('roundtrip workflow: encrypt twice with one password, decryptSessionKeys, only one session key', async function () {
          const encrypted = await openpgp.encrypt({
            message: await openpgp.createMessage({ text: plaintext }),
            passwords: [password1, password1]
          });
          const decryptedSessionKeys = await openpgp.decryptSessionKeys({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            passwords: password1
          });
          expect(decryptedSessionKeys.length).to.equal(1);
          const decrypted = await openpgp.decrypt({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            sessionKeys: decryptedSessionKeys[0]
          });
          expect(decrypted.data).to.equal(plaintext);
        });
      });

      describe('AES / RSA encrypt, decrypt, sign, verify', function() {
        const wrong_pubkey = '-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n' +
          'Version: OpenPGP.js v0.9.0\r\n' +
          'Comment: Hoodiecrow - https://hoodiecrow.com\r\n' +
          '\r\n' +
          'xk0EUlhMvAEB/2MZtCUOAYvyLFjDp3OBMGn3Ev8FwjzyPbIF0JUw+L7y2XR5\r\n' +
          'RVGvbK88unV3cU/1tOYdNsXI6pSp/Ztjyv7vbBUAEQEAAc0pV2hpdGVvdXQg\r\n' +
          'VXNlciA8d2hpdGVvdXQudGVzdEB0LW9ubGluZS5kZT7CXAQQAQgAEAUCUlhM\r\n' +
          'vQkQ9vYOm0LN/0wAAAW4Af9C+kYW1AvNWmivdtr0M0iYCUjM9DNOQH1fcvXq\r\n' +
          'IiN602mWrkd8jcEzLsW5IUNzVPLhrFIuKyBDTpLnC07Loce1\r\n' +
          '=6XMW\r\n' +
          '-----END PGP PUBLIC KEY BLOCK-----\r\n\r\n';

        let decryptedPrivateKey; // to avoid decrypting key before each test
        beforeEach(async function() {
          if (!decryptedPrivateKey) {
            decryptedPrivateKey = await openpgp.decryptKey({ privateKey, passphrase });
          }
          privateKey = decryptedPrivateKey;
        });

        it('should encrypt then decrypt', async function () {
          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: publicKey
          };
          const decOpt = {
            decryptionKeys: privateKey
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            expect(encrypted).to.match(/^-----BEGIN PGP MESSAGE/);
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            return openpgp.decrypt(decOpt);
          }).then(function (decrypted) {
            expect(decrypted.data).to.equal(plaintext);
            expect(decrypted.signatures).to.exist;
            expect(decrypted.signatures.length).to.equal(0);
          });
        });

        it('should encrypt then decrypt with multiple private keys', async function () {
          const privKeyDE = await openpgp.decryptKey({
            privateKey: await openpgp.readKey({ armoredKey: priv_key_de }),
            passphrase
          });

          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: publicKey
          };
          const decOpt = {
            decryptionKeys: [privKeyDE, privateKey]
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            expect(encrypted).to.match(/^-----BEGIN PGP MESSAGE/);
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            return openpgp.decrypt(decOpt);
          }).then(function (decrypted) {
            expect(decrypted.data).to.equal(plaintext);
            expect(decrypted.signatures).to.exist;
            expect(decrypted.signatures.length).to.equal(0);
          });
        });

        it('should encrypt then decrypt with wildcard', async function () {
          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: publicKey,
            wildcard: true
          };
          const decOpt = {
            decryptionKeys: privateKey
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            expect(encrypted).to.match(/^-----BEGIN PGP MESSAGE/);
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            return openpgp.decrypt(decOpt);
          }).then(function (decrypted) {
            expect(decrypted.data).to.equal(plaintext);
            expect(decrypted.signatures).to.exist;
            expect(decrypted.signatures.length).to.equal(0);
          });
        });

        it('should encrypt then decrypt with wildcard with multiple private keys', async function () {
          const privKeyDE = await openpgp.decryptKey({
            privateKey: await openpgp.readKey({ armoredKey: priv_key_de }),
            passphrase
          });

          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: publicKey,
            wildcard: true
          };
          const decOpt = {
            decryptionKeys: [privKeyDE, privateKey]
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            expect(encrypted).to.match(/^-----BEGIN PGP MESSAGE/);
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            return openpgp.decrypt(decOpt);
          }).then(function (decrypted) {
            expect(decrypted.data).to.equal(plaintext);
            expect(decrypted.signatures).to.exist;
            expect(decrypted.signatures.length).to.equal(0);
          });
        });

        it('should encrypt then decrypt using returned session key', async function () {
          const sessionKey = await openpgp.generateSessionKey({
            encryptionKeys: publicKey
          });
          const encrypted = await openpgp.encrypt({
            message: await openpgp.createMessage({ text: plaintext }),
            sessionKey
          });
          expect(encrypted).to.match(/^-----BEGIN PGP MESSAGE/);
          const decrypted = await openpgp.decrypt({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            sessionKeys: sessionKey
          });
          expect(decrypted.data).to.equal(plaintext);
          expect(decrypted.signatures).to.exist;
          expect(decrypted.signatures.length).to.equal(0);
        });

        it('should encrypt using custom session key and decrypt using session key', async function () {
          const sessionKey = {
            data: await crypto.generateSessionKey(openpgp.enums.symmetric.aes256),
            algorithm: 'aes256'
          };
          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            sessionKey: sessionKey,
            encryptionKeys: publicKey
          };
          const decOpt = {
            sessionKeys: sessionKey
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            expect(encrypted).to.match(/^-----BEGIN PGP MESSAGE/);
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(false);
            return openpgp.decrypt(decOpt);
          }).then(function (decrypted) {
            expect(decrypted.data).to.equal(plaintext);
          });
        });

        it('should encrypt using custom session key and decrypt using private key', async function () {
          const sessionKey = {
            data: await crypto.generateSessionKey(openpgp.enums.symmetric.aes128),
            algorithm: 'aes128'
          };
          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            sessionKey: sessionKey,
            encryptionKeys: publicKey
          };
          const decOpt = {
            decryptionKeys: privateKey
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            expect(encrypted).to.match(/^-----BEGIN PGP MESSAGE/);
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(false);
            return openpgp.decrypt(decOpt);
          }).then(function (decrypted) {
            expect(decrypted.data).to.equal(plaintext);
          });
        });

        it('should encrypt/sign and decrypt/verify', async function () {
          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: publicKey,
            signingKeys: privateKey
          };
          const decOpt = {
            decryptionKeys: privateKey,
            verificationKeys: publicKey
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(openpgp.config.aeadProtect);
            return openpgp.decrypt(decOpt);
          }).then(async function (decrypted) {
            expect(decrypted.data).to.equal(plaintext);
            expect(await decrypted.signatures[0].verified).to.be.true;
            const signingKey = await privateKey.getSigningKey();
            expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
            expect((await decrypted.signatures[0].signature).packets.length).to.equal(1);
          });
        });

        it('should encrypt/sign and decrypt/verify (expectSigned=true)', async function () {
          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: publicKey,
            signingKeys: privateKey
          };
          const decOpt = {
            decryptionKeys: privateKey,
            verificationKeys: publicKey,
            expectSigned: true
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(openpgp.config.aeadProtect);
            return openpgp.decrypt(decOpt);
          }).then(async function (decrypted) {
            expect(decrypted.data).to.equal(plaintext);
            expect(await decrypted.signatures[0].verified).to.be.true;
            const signingKey = await privateKey.getSigningKey();
            expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
            expect((await decrypted.signatures[0].signature).packets.length).to.equal(1);
          });
        });

        it('should encrypt/sign and decrypt/verify (no AEAD support)', async function () {
          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: publicKeyNoAEAD,
            signingKeys: privateKey
          };
          const decOpt = {
            decryptionKeys: privateKey,
            verificationKeys: publicKeyNoAEAD
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(false);
            return openpgp.decrypt(decOpt);
          }).then(async function (decrypted) {
            expect(decrypted.data).to.equal(plaintext);
            expect(await decrypted.signatures[0].verified).to.be.true;
            const signingKey = await privateKey.getSigningKey();
            expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
            expect((await decrypted.signatures[0].signature).packets.length).to.equal(1);
          });
        });

        it('should encrypt/sign and decrypt/verify with generated key', function () {
          const genOpt = {
            userIDs: [{ name: 'Test User', email: 'text@example.com' }]
          };

          return openpgp.generateKey(genOpt).then(async function(newKey) {
            const newPublicKey = await openpgp.readKey({ armoredKey: newKey.publicKey });
            const newPrivateKey = await openpgp.readKey({ armoredKey: newKey.privateKey });

            const encOpt = {
              message: await openpgp.createMessage({ text: plaintext }),
              encryptionKeys: newPublicKey,
              signingKeys: newPrivateKey
            };
            const decOpt = {
              decryptionKeys: newPrivateKey,
              verificationKeys: newPublicKey
            };
            return openpgp.encrypt(encOpt).then(async function (encrypted) {
              decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
              expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(openpgp.config.aeadProtect);
              return openpgp.decrypt(decOpt);
            }).then(async function (decrypted) {
              expect(decrypted.data).to.equal(plaintext);
              expect(await decrypted.signatures[0].verified).to.be.true;
              const signingKey = await newPrivateKey.getSigningKey();
              expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
              expect((await decrypted.signatures[0].signature).packets.length).to.equal(1);
            });
          });
        });

        it('should encrypt/sign and decrypt/verify with generated key and detached signatures', async function () {
          const newKey = await openpgp.generateKey({
            userIDs: [{ name: 'Test User', email: 'text@example.com' }]
          });
          const newPublicKey = await openpgp.readKey({ armoredKey: newKey.publicKey });
          const newPrivateKey = await openpgp.readKey({ armoredKey: newKey.privateKey });

          const encrypted = await openpgp.encrypt({
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: newPublicKey
          });
          const signed = await openpgp.sign({
            message: await openpgp.createMessage({ text: plaintext }),
            signingKeys: newPrivateKey,
            detached: true
          });
          const message = await openpgp.readMessage({ armoredMessage: encrypted });
          expect(!!message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(openpgp.config.aeadProtect);
          const decrypted = await openpgp.decrypt({
            message,
            signature: await openpgp.readSignature({ armoredSignature: signed }),
            decryptionKeys: newPrivateKey,
            verificationKeys: newPublicKey
          });
          expect(decrypted.data).to.equal(plaintext);
          expect(await decrypted.signatures[0].verified).to.be.true;
          const signingKey = await newPrivateKey.getSigningKey();
          expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
          expect((await decrypted.signatures[0].signature).packets.length).to.equal(1);
        });

        it('should encrypt/sign and decrypt/verify with null string input', async function () {
          const encOpt = {
            message: await openpgp.createMessage({ text: '' }),
            encryptionKeys: publicKey,
            signingKeys: privateKey
          };
          const decOpt = {
            decryptionKeys: privateKey,
            verificationKeys: publicKey
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            return openpgp.decrypt(decOpt);
          }).then(async function (decrypted) {
            expect(decrypted.data).to.equal('');
            expect(await decrypted.signatures[0].verified).to.be.true;
            const signingKey = await privateKey.getSigningKey();
            expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
            expect((await decrypted.signatures[0].signature).packets.length).to.equal(1);
          });
        });

        it('should encrypt/sign and decrypt/verify with detached signatures', async function () {
          const encrypted = await openpgp.encrypt({
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: publicKey
          });
          const signed = await openpgp.sign({
            message: await openpgp.createMessage({ text: plaintext }),
            signingKeys: privateKey,
            detached: true
          });
          const decrypted = await openpgp.decrypt({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            signature: await openpgp.readSignature({ armoredSignature: signed }),
            decryptionKeys: privateKey,
            verificationKeys: publicKey
          });
          expect(decrypted.data).to.equal(plaintext);
          expect(await decrypted.signatures[0].verified).to.be.true;
          const signingKey = await privateKey.getSigningKey();
          expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
          expect((await decrypted.signatures[0].signature).packets.length).to.equal(1);
        });

        it('should encrypt and decrypt/verify with detached signature as input for encryption', async function () {
          const { rejectPublicKeyAlgorithms } = openpgp.config;
          try {
            openpgp.config.rejectPublicKeyAlgorithms = new Set();

            const plaintext = '  \t┍ͤ޵၂༫዇◧˘˻ᙑ᎚⏴ំந⛑nٓኵΉⅶ⋋ŵ⋲΂ͽᣏ₅ᄶɼ┋⌔û᬴Ƚᔡᧅ≃ṱἆ⃷݂૿ӌ᰹෇ٹჵ⛇໶⛌  \t\n한국어/조선말';

            const privKeyDE = await openpgp.decryptKey({
              privateKey: await openpgp.readKey({ armoredKey: priv_key_de }),
              passphrase
            });

            const pubKeyDE = await openpgp.readKey({ armoredKey: pub_key_de });

            const signOpt = {
              message: await openpgp.createMessage({ text: plaintext }),
              signingKeys: privKeyDE,
              detached: true
            };

            const encOpt = {
              message: await openpgp.createMessage({ text: plaintext }),
              encryptionKeys: publicKey,
              signingKeys: privateKey
            };

            const decOpt = {
              decryptionKeys: privateKey,
              verificationKeys: [publicKey, pubKeyDE]
            };

            await openpgp.sign(signOpt).then(async function (armoredSignature) {
              encOpt.signature = await openpgp.readSignature({ armoredSignature });
              return openpgp.encrypt(encOpt);
            }).then(async function (armoredMessage) {
              decOpt.message = await openpgp.readMessage({ armoredMessage });
              return openpgp.decrypt(decOpt);
            }).then(async function (decrypted) {
              let signingKey;
              expect(decrypted.data).to.equal(plaintext);
              expect(await decrypted.signatures[0].verified).to.be.true;
              signingKey = await privateKey.getSigningKey();
              expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
              expect((await decrypted.signatures[0].signature).packets.length).to.equal(1);
              expect(await decrypted.signatures[1].verified).to.be.true;
              signingKey = await privKeyDE.getSigningKey();
              expect(decrypted.signatures[1].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
              expect((await decrypted.signatures[1].signature).packets.length).to.equal(1);
            });
          } finally {
            openpgp.config.rejectPublicKeyAlgorithms = rejectPublicKeyAlgorithms;
          }
        });

        it('should fail to encrypt and decrypt/verify with detached signature as input for encryption with wrong public key', async function () {
          const signOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            signingKeys: privateKey,
            detached: true
          };

          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: publicKey
          };

          const decOpt = {
            decryptionKeys: privateKey,
            verificationKeys: await openpgp.readKey({ armoredKey: wrong_pubkey })
          };

          return openpgp.sign(signOpt).then(async function (armoredSignature) {
            encOpt.signature = await openpgp.readSignature({ armoredSignature });
            return openpgp.encrypt(encOpt);
          }).then(async function (armoredMessage) {
            decOpt.message = await openpgp.readMessage({ armoredMessage });
            return openpgp.decrypt(decOpt);
          }).then(async function ({ signatures, data }) {
            expect(data).to.equal(plaintext);
            await expect(signatures[0].verified).to.be.rejectedWith(/Could not find signing key/);
            const signingKey = await privateKey.getSigningKey();
            expect(signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
            expect((await signatures[0].signature).packets.length).to.equal(1);
          });
        });

        it('should fail to verify decrypted data with wrong public pgp key', async function () {
          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: publicKey,
            signingKeys: privateKey
          };
          const decOpt = {
            decryptionKeys: privateKey,
            verificationKeys: await openpgp.readKey({ armoredKey: wrong_pubkey })
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            return openpgp.decrypt(decOpt);
          }).then(async function ({ signatures, data }) {
            expect(data).to.equal(plaintext);
            await expect(signatures[0].verified).to.be.rejectedWith(/Could not find signing key/);
            const signingKey = await privateKey.getSigningKey();
            expect(signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
            expect((await signatures[0].signature).packets.length).to.equal(1);
          });
        });

        it('should fail to verify decrypted null string with wrong public pgp key', async function () {
          const encOpt = {
            message: await openpgp.createMessage({ text: '' }),
            encryptionKeys: publicKey,
            signingKeys: privateKey
          };
          const decOpt = {
            decryptionKeys: privateKey,
            verificationKeys: await openpgp.readKey({ armoredKey: wrong_pubkey })
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            return openpgp.decrypt(decOpt);
          }).then(async function ({ signatures, data }) {
            expect(data).to.equal('');
            await expect(signatures[0].verified).to.be.rejectedWith(/Could not find signing key/);
            const signingKey = await privateKey.getSigningKey();
            expect(signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
            expect((await signatures[0].signature).packets.length).to.equal(1);
          });
        });

        it('should successfully decrypt signed message without public keys to verify', async function () {
          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: publicKey,
            signingKeys: privateKey
          };
          const decOpt = {
            decryptionKeys: privateKey
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            return openpgp.decrypt(decOpt);
          }).then(async function ({ signatures, data }) {
            expect(data).to.equal(plaintext);
            await expect(signatures[0].verified).to.be.rejectedWith(/Could not find signing key/);
            const signingKey = await privateKey.getSigningKey();
            expect(signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
            expect((await signatures[0].signature).packets.length).to.equal(1);
          });
        });

        it('should fail to verify decrypted data with wrong public pgp key with detached signatures', async function () {
          const encrypted = await openpgp.encrypt({
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: publicKey
          });
          const signed = await openpgp.sign({
            message: await openpgp.createMessage({ text: plaintext }),
            signingKeys: privateKey,
            detached: true
          });
          const { signatures, data } = await openpgp.decrypt({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            signature: await openpgp.readSignature({ armoredSignature: signed }),
            decryptionKeys: privateKey,
            verificationKeys: await openpgp.readKey({ armoredKey: wrong_pubkey })
          });
          expect(data).to.equal(plaintext);
          await expect(signatures[0].verified).to.be.rejectedWith(/Could not find signing key/);
          const signingKey = await privateKey.getSigningKey();
          expect(signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
          expect((await signatures[0].signature).packets.length).to.equal(1);
        });

        it('should encrypt and decrypt/verify both signatures when signed with two private keys', async function () {
          const { rejectPublicKeyAlgorithms } = openpgp.config;
          try {
            openpgp.config.rejectPublicKeyAlgorithms = new Set();

            const privKeyDE = await openpgp.decryptKey({
              privateKey: await openpgp.readKey({ armoredKey: priv_key_de }),
              passphrase
            });

            const pubKeyDE = await openpgp.readKey({ armoredKey: pub_key_de });

            const encOpt = {
              message: await openpgp.createMessage({ text: plaintext }),
              encryptionKeys: publicKey,
              signingKeys: [privateKey, privKeyDE]
            };

            const decOpt = {
              decryptionKeys: privateKey,
              verificationKeys: [publicKey, pubKeyDE]
            };

            await openpgp.encrypt(encOpt).then(async function (encrypted) {
              decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
              return openpgp.decrypt(decOpt);
            }).then(async function (decrypted) {
              let signingKey;
              expect(decrypted.data).to.equal(plaintext);
              expect(await decrypted.signatures[0].verified).to.be.true;
              signingKey = await privateKey.getSigningKey();
              expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
              expect((await decrypted.signatures[0].signature).packets.length).to.equal(1);
              expect(await decrypted.signatures[1].verified).to.be.true;
              signingKey = await privKeyDE.getSigningKey();
              expect(decrypted.signatures[1].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
              expect((await decrypted.signatures[1].signature).packets.length).to.equal(1);
            });
          } finally {
            openpgp.config.rejectPublicKeyAlgorithms = rejectPublicKeyAlgorithms;
          }
        });

        it('should fail to decrypt modified message', async function() {
          const allowUnauthenticatedStream = openpgp.config.allowUnauthenticatedStream;
          const { privateKey: key } = await openpgp.generateKey({ userIDs: [{ email: 'test@email.com' }], format: 'object' });
          expect(await isAEADSupported([key])).to.equal(openpgp.config.aeadProtect);

          const data = await openpgp.encrypt({ message: await openpgp.createMessage({ binary: new Uint8Array(500) }), encryptionKeys: [key.toPublic()] });
          let badSumEncrypted = data.replace(/\n=[a-zA-Z0-9/+]{4}/, '\n=aaaa');
          if (badSumEncrypted === data) { // checksum was already =aaaa
            badSumEncrypted = data.replace(/\n=[a-zA-Z0-9/+]{4}/, '\n=bbbb');
          }
          if (badSumEncrypted === data) {
            throw new Error('Was not able to successfully modify checksum');
          }
          const badBodyEncrypted = data.replace(/\n=([a-zA-Z0-9/+]{4})/, 'aaa\n=$1');
          await stream.loadStreamsPonyfill();
          try {
            for (const allowStreaming of [true, false]) {
              openpgp.config.allowUnauthenticatedStream = allowStreaming;
              await Promise.all([badSumEncrypted, badBodyEncrypted].map(async (encrypted, i) => {
                await Promise.all([
                  encrypted,
                  new stream.ReadableStream({
                    start(controller) {
                      controller.enqueue(encrypted);
                      controller.close();
                    }
                  }),
                  new stream.ReadableStream({
                    start() {
                      this.remaining = encrypted.split('\n');
                    },
                    async pull(controller) {
                      if (this.remaining.length) {
                        await new Promise(res => setTimeout(res));
                        controller.enqueue(this.remaining.shift() + '\n');
                      } else {
                        controller.close();
                      }
                    }
                  })
                ].map(async (encrypted, j) => {
                  let stepReached = 0;
                  try {
                    const message = await openpgp.readMessage({ armoredMessage: encrypted });
                    stepReached = 1;
                    const { data: decrypted } = await openpgp.decrypt({ message: message, decryptionKeys: [key] });
                    stepReached = 2;
                    await stream.readToEnd(decrypted);
                  } catch (e) {
                    expect(e.message).to.match(/Ascii armor integrity check failed/);
                    expect(stepReached).to.equal(
                      j === 0 ? 0 :
                        (openpgp.config.aeadChunkSizeByte === 0 && (j === 2 || util.detectNode() || util.getHardwareConcurrency() < 8)) || (!openpgp.config.aeadProtect && openpgp.config.allowUnauthenticatedStream) ? 2 :
                          1
                    );
                    return;
                  }
                  throw new Error(`Expected "Ascii armor integrity check failed" error in subtest ${i}.${j}`);
                }));
              }));
            }
          } finally {
            openpgp.config.allowUnauthenticatedStream = allowUnauthenticatedStream;
          }
        });

        it('should fail to decrypt unarmored message with garbage data appended', async function() {
          const key = privateKey;
          const message = await openpgp.encrypt({ message: await openpgp.createMessage({ text: 'test' }), encryptionKeys: key, signingKeys: key, format: 'binary' });
          const encrypted = util.concat([message, new Uint8Array([11])]);
          await expect((async () => {
            await openpgp.decrypt({ message: await openpgp.readMessage({ binaryMessage: encrypted }), decryptionKeys: key, verificationKeys: key });
          })()).to.be.rejectedWith('Error during parsing. This message / key probably does not conform to a valid OpenPGP format.');
        });
      });

      describe('ELG / DSA encrypt, decrypt, sign, verify', function() {

        it('round trip test', async function () {
          const { rejectPublicKeyAlgorithms } = openpgp.config;
          try {
            openpgp.config.rejectPublicKeyAlgorithms = new Set();

            const pubKeyDE = await openpgp.readKey({ armoredKey: pub_key_de });
            const privKeyDE = await openpgp.decryptKey({
              privateKey: await openpgp.readKey({ armoredKey: priv_key_de }),
              passphrase
            });
            pubKeyDE.users[0].selfCertifications[0].features = [7]; // Monkey-patch AEAD feature flag
            await openpgp.encrypt({
              encryptionKeys: pubKeyDE,
              signingKeys: privKeyDE,
              message: await openpgp.createMessage({ text: plaintext })
            }).then(async function (encrypted) {
              return openpgp.decrypt({
                decryptionKeys: privKeyDE,
                verificationKeys: pubKeyDE,
                message: await openpgp.readMessage({ armoredMessage: encrypted })
              });
            }).then(async function (decrypted) {
              expect(decrypted.data).to.exist;
              expect(decrypted.data).to.equal(plaintext);
              expect(await decrypted.signatures[0].verified).to.be.true;
              const signingKey = await privKeyDE.getSigningKey();
              expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
              expect((await decrypted.signatures[0].signature).packets.length).to.equal(1);
            });
          } finally {
            openpgp.config.rejectPublicKeyAlgorithms = rejectPublicKeyAlgorithms;
          }
        });
      });

      describe('3DES decrypt', function() {
        const pgp_msg = [
          '-----BEGIN PGP MESSAGE-----',
          'Version: GnuPG/MacGPG2 v2.0.19 (Darwin)',
          'Comment: GPGTools - https://gpgtools.org',
          '',
          'hIwDBU4Dycfvp2EBA/9tuhQgOrcATcm2PRmIOcs6q947YhlsBTZZdVJDfVjkKlyM',
          'M0yE+lnNplWb041Cpfkkl6IvorKQd2iPbAkOL0IXwmVN41l+PvVgMcuFvvzetehG',
          'Ca0/VEYOaTZRNqyr9FIzcnVy1I/PaWT3iqVAYa+G8TEA5Dh9RLfsx8ZA9UNIaNI+',
          'ASm9aZ3H6FerNhm8RezDY5vRn6xw3o/wH5YEBvV2BEmmFKZ2BlqFQxqChr8UNwd1',
          'Ieebnq0HtBPE8YU/L0U=',
          '=JyIa',
          '-----END PGP MESSAGE-----'
        ].join('\n');

        const priv_key = [
          '-----BEGIN PGP PRIVATE KEY BLOCK-----',
          'Version: GnuPG/MacGPG2 v2.0.19 (Darwin)',
          'Comment: GPGTools - https://gpgtools.org',
          '',
          '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',
          '22q10ausyQXoOxXfDWVwKA==',
          '=IkKW',
          '-----END PGP PRIVATE KEY BLOCK-----'
        ].join('\n');

        it('Decrypt message', async function() {
          const privKey = await openpgp.decryptKey({
            privateKey: await openpgp.readKey({ armoredKey: priv_key }),
            passphrase: '1234'
          });
          const message = await openpgp.readMessage({ armoredMessage: pgp_msg });

          return openpgp.decrypt({ decryptionKeys:privKey, message:message }).then(function(decrypted) {
            expect(decrypted.data).to.equal('hello 3des\n');
            expect(decrypted.signatures.length).to.equal(0);
          });
        });
      });

      describe('AES encrypt, decrypt', function() {

        it('should encrypt and decrypt with one password', async function () {
          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            passwords: password1
          };
          const decOpt = {
            passwords: password1
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            return openpgp.decrypt(decOpt);
          }).then(function (decrypted) {
            expect(decrypted.data).to.equal(plaintext);
            expect(decrypted.signatures.length).to.equal(0);
          });
        });

        it('should encrypt and decrypt with two passwords', async function () {
          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            passwords: [password1, password2]
          };
          const decOpt = {
            passwords: password2
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            return openpgp.decrypt(decOpt);
          }).then(function (decrypted) {
            expect(decrypted.data).to.equal(plaintext);
            expect(decrypted.signatures.length).to.equal(0);
          });
        });

        it('should encrypt and decrypt with password and not ascii armor', async function () {
          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            passwords: password1,
            format: 'binary'
          };
          const decOpt = {
            passwords: password1
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            decOpt.message = await openpgp.readMessage({ binaryMessage: encrypted });
            return openpgp.decrypt(decOpt);
          }).then(function (decrypted) {
            expect(decrypted.data).to.equal(plaintext);
            expect(decrypted.signatures.length).to.equal(0);
          });
        });

        it('should encrypt and decrypt with binary data', async function () {
          const encOpt = {
            message: await openpgp.createMessage({ binary: new Uint8Array([0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01]) }),
            passwords: password1,
            format: 'binary'
          };
          const decOpt = {
            passwords: password1,
            format: 'binary'
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            decOpt.message = await openpgp.readMessage({ binaryMessage: encrypted });
            return openpgp.decrypt(decOpt);
          }).then(function (decrypted) {
            expect(decrypted.data).to.deep.equal(new Uint8Array([0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01]));
            expect(decrypted.signatures.length).to.equal(0);
          });
        });
      });

      describe('Encrypt, decrypt with compression', function() {
        withCompression(function (modifyCompressionEncryptOptions, verifyCompressionDecrypted) {
          it('should encrypt and decrypt with one password', async function () {
            const encOpt = modifyCompressionEncryptOptions({
              message: await openpgp.createMessage({ text: plaintext }),
              passwords: password1
            });
            const decOpt = {
              passwords: password1
            };
            return openpgp.encrypt(encOpt).then(async function (encrypted) {
              decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
              return openpgp.decrypt(decOpt);
            }).then(function (decrypted) {
              expect(decrypted.data).to.equal(plaintext);
              expect(decrypted.signatures.length).to.equal(0);
              verifyCompressionDecrypted(decrypted);
            });
          });

          it('Streaming encrypt and decrypt small message roundtrip', async function() {
            const plaintext = [];
            let i = 0;
            const useNativeStream = (() => { try { new global.ReadableStream(); return true; } catch (e) { return false; } })(); // eslint-disable-line no-new
            await stream.loadStreamsPonyfill();
            const ReadableStream = useNativeStream ? global.ReadableStream : stream.ReadableStream;
            const data = new ReadableStream({
              async pull(controller) {
                if (i++ < 4) {
                  const randomBytes = await random.getRandomBytes(10);
                  controller.enqueue(randomBytes);
                  plaintext.push(randomBytes.slice());
                } else {
                  controller.close();
                }
              }
            });
            const encrypted = await openpgp.encrypt(modifyCompressionEncryptOptions({
              message: await openpgp.createMessage({ binary: data }),
              passwords: ['test']
            }));
            expect(stream.isStream(encrypted)).to.equal(useNativeStream ? 'web' : 'web-like');

            const message = await openpgp.readMessage({ armoredMessage: encrypted });
            const decrypted = await openpgp.decrypt({
              passwords: ['test'],
              message,
              format: 'binary'
            });
            expect(stream.isStream(decrypted.data)).to.equal(useNativeStream ? 'web' : 'web-like');
            expect(await stream.readToEnd(decrypted.data)).to.deep.equal(util.concatUint8Array(plaintext));
          });
        });
      });

    }

    describe('AES / RSA encrypt, decrypt, sign, verify', function() {
      const wrong_pubkey = '-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n' +
        'Version: OpenPGP.js v0.9.0\r\n' +
        'Comment: Hoodiecrow - https://hoodiecrow.com\r\n' +
        '\r\n' +
        'xk0EUlhMvAEB/2MZtCUOAYvyLFjDp3OBMGn3Ev8FwjzyPbIF0JUw+L7y2XR5\r\n' +
        'RVGvbK88unV3cU/1tOYdNsXI6pSp/Ztjyv7vbBUAEQEAAc0pV2hpdGVvdXQg\r\n' +
        'VXNlciA8d2hpdGVvdXQudGVzdEB0LW9ubGluZS5kZT7CXAQQAQgAEAUCUlhM\r\n' +
        'vQkQ9vYOm0LN/0wAAAW4Af9C+kYW1AvNWmivdtr0M0iYCUjM9DNOQH1fcvXq\r\n' +
        'IiN602mWrkd8jcEzLsW5IUNzVPLhrFIuKyBDTpLnC07Loce1\r\n' +
        '=6XMW\r\n' +
        '-----END PGP PUBLIC KEY BLOCK-----\r\n\r\n';

      let decryptedPrivateKey; // to avoid decrypting key before each test
      beforeEach(async function() {
        if (!decryptedPrivateKey) {
          decryptedPrivateKey = await openpgp.decryptKey({ privateKey, passphrase });
        }
        privateKey = decryptedPrivateKey;
      });

      it('should sign and verify cleartext message', async function () {
        const message = await openpgp.createCleartextMessage({ text: plaintext });
        const signOpt = {
          message,
          signingKeys: privateKey
        };
        const verifyOpt = {
          verificationKeys: publicKey
        };
        return openpgp.sign(signOpt).then(async function (signed) {
          expect(signed).to.match(/-----BEGIN PGP SIGNED MESSAGE-----/);
          verifyOpt.message = await openpgp.readCleartextMessage({ cleartextMessage: signed });
          return openpgp.verify(verifyOpt);
        }).then(async function (verified) {
          expect(verified.data).to.equal(plaintext.replace(/[ \t]+$/mg, ''));
          expect(await verified.signatures[0].verified).to.be.true;
          const signingKey = await privateKey.getSigningKey();
          expect(verified.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
          expect((await verified.signatures[0].signature).packets.length).to.equal(1);
        });
      });

      it('should sign and verify cleartext message with multiple private keys', async function () {
        const { rejectPublicKeyAlgorithms } = openpgp.config;
        try {
          openpgp.config.rejectPublicKeyAlgorithms = new Set();

          const privKeyDE = await openpgp.decryptKey({
            privateKey: await openpgp.readKey({ armoredKey: priv_key_de }),
            passphrase
          });

          const message = await openpgp.createCleartextMessage({ text: plaintext });
          const signOpt = {
            message,
            signingKeys: [privateKey, privKeyDE]
          };
          const verifyOpt = {
            verificationKeys: [publicKey, privKeyDE.toPublic()]
          };
          await openpgp.sign(signOpt).then(async function (signed) {
            expect(signed).to.match(/-----BEGIN PGP SIGNED MESSAGE-----/);
            verifyOpt.message = await openpgp.readCleartextMessage({ cleartextMessage: signed });
            return openpgp.verify(verifyOpt);
          }).then(async function (verified) {
            let signingKey;
            expect(verified.data).to.equal(plaintext.replace(/[ \t]+$/mg, ''));
            expect(await verified.signatures[0].verified).to.be.true;
            signingKey = await privateKey.getSigningKey();
            expect(verified.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
            expect((await verified.signatures[0].signature).packets.length).to.equal(1);
            expect(await verified.signatures[1].verified).to.be.true;
            signingKey = await privKeyDE.getSigningKey();
            expect(verified.signatures[1].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
            expect((await verified.signatures[1].signature).packets.length).to.equal(1);
          });
        } finally {
          openpgp.config.rejectPublicKeyAlgorithms = rejectPublicKeyAlgorithms;
        }
      });

      it('should sign and verify data with detached signatures', async function () {
        const message = await openpgp.createMessage({ text: plaintext });
        const signOpt = {
          message,
          signingKeys: privateKey,
          detached: true
        };
        const verifyOpt = {
          message,
          verificationKeys: publicKey
        };
        return openpgp.sign(signOpt).then(async function (armoredSignature) {
          verifyOpt.signature = await openpgp.readSignature({ armoredSignature });
          return openpgp.verify(verifyOpt);
        }).then(async function (verified) {
          expect(verified.data).to.equal(plaintext);
          expect(await verified.signatures[0].verified).to.be.true;
          const signingKey = await privateKey.getSigningKey();
          expect(verified.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
          expect((await verified.signatures[0].signature).packets.length).to.equal(1);
        });
      });

      it('should sign and fail to verify cleartext message with wrong public pgp key', async function () {
        const message = await openpgp.createCleartextMessage({ text: plaintext });
        const signOpt = {
          message,
          signingKeys: privateKey
        };
        const verifyOpt = {
          verificationKeys: await openpgp.readKey({ armoredKey: wrong_pubkey })
        };
        return openpgp.sign(signOpt).then(async function (signed) {
          verifyOpt.message = await openpgp.readCleartextMessage({ cleartextMessage: signed });
          return openpgp.verify(verifyOpt);
        }).then(async function ({ data, signatures }) {
          expect(data).to.equal(plaintext.replace(/[ \t]+$/mg, ''));
          await expect(signatures[0].verified).to.be.rejectedWith(/Could not find signing key/);
          const signingKey = await privateKey.getSigningKey();
          expect(signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
          expect((await signatures[0].signature).packets.length).to.equal(1);
        });
      });

      it('should sign and fail to verify data with wrong public pgp key with detached signature', async function () {
        const message = await openpgp.createMessage({ text: plaintext });
        const signOpt = {
          message,
          signingKeys: privateKey,
          detached: true
        };
        const verifyOpt = {
          message,
          verificationKeys: await openpgp.readKey({ armoredKey: wrong_pubkey })
        };
        return openpgp.sign(signOpt).then(async function (armoredSignature) {
          verifyOpt.signature = await openpgp.readSignature({ armoredSignature });
          return openpgp.verify(verifyOpt);
        }).then(async function ({ data, signatures }) {
          expect(data).to.equal(plaintext);
          await expect(signatures[0].verified).to.be.rejectedWith(/Could not find signing key/);
          const signingKey = await privateKey.getSigningKey();
          expect(signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
          expect((await signatures[0].signature).packets.length).to.equal(1);
        });
      });

      it('should sign and verify data and not armor', async function () {
        const message = await openpgp.createMessage({ text: plaintext });
        const signOpt = {
          message,
          signingKeys: privateKey,
          format: 'binary'
        };
        const verifyOpt = {
          verificationKeys: publicKey
        };
        return openpgp.sign(signOpt).then(async function (signed) {
          verifyOpt.message = await openpgp.readMessage({ binaryMessage: signed });
          return openpgp.verify(verifyOpt);
        }).then(async function (verified) {
          expect(verified.data).to.equal(plaintext);
          expect(await verified.signatures[0].verified).to.be.true;
          const signingKey = await privateKey.getSigningKey();
          expect(verified.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
          expect((await verified.signatures[0].signature).packets.length).to.equal(1);
        });
      });

      it('should sign and verify data and not armor with detached signatures', async function () {
        const start = util.normalizeDate();
        const message = await openpgp.createMessage({ text: plaintext });
        const signOpt = {
          message,
          signingKeys: privateKey,
          detached: true,
          format: 'binary'
        };
        const verifyOpt = {
          message,
          verificationKeys: publicKey
        };
        return openpgp.sign(signOpt).then(async function (signed) {
          verifyOpt.signature = await openpgp.readSignature({ binarySignature: signed });
          return openpgp.verify(verifyOpt);
        }).then(async function (verified) {
          expect(verified.data).to.equal(plaintext);
          expect(+(await verified.signatures[0].signature).packets[0].created).to.be.lte(+util.normalizeDate());
          expect(+(await verified.signatures[0].signature).packets[0].created).to.be.gte(+start);
          expect(await verified.signatures[0].verified).to.be.true;
          const signingKey = await privateKey.getSigningKey();
          expect(verified.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
          expect((await verified.signatures[0].signature).packets.length).to.equal(1);
        });
      });

      it('should sign and verify data with a date in the past', async function () {
        const message = await openpgp.createMessage({ text: plaintext });
        const past = new Date(2000);
        const signOpt = {
          message,
          signingKeys: privateKey_1337,
          detached: true,
          date: past,
          format: 'binary'
        };
        const verifyOpt = {
          message,
          verificationKeys: publicKey_1337,
          date: past
        };
        return openpgp.sign(signOpt).then(async function (signed) {
          verifyOpt.signature = await openpgp.readSignature({ binarySignature: signed });
          return openpgp.verify(verifyOpt).then(async function (verified) {
            expect(+(await verified.signatures[0].signature).packets[0].created).to.equal(+past);
            expect(verified.data).to.equal(plaintext);
            expect(await verified.signatures[0].verified).to.be.true;
            expect(await privateKey_1337.getSigningKey(verified.signatures[0].keyID, past))
              .to.be.not.null;
            expect((await verified.signatures[0].signature).packets.length).to.equal(1);
            // now check with expiration checking disabled
            verifyOpt.date = null;
            return openpgp.verify(verifyOpt);
          }).then(async function (verified) {
            expect(+(await verified.signatures[0].signature).packets[0].created).to.equal(+past);
            expect(verified.data).to.equal(plaintext);
            expect(await verified.signatures[0].verified).to.be.true;
            expect(await privateKey_1337.getSigningKey(verified.signatures[0].keyID, null))
              .to.be.not.null;
            expect((await verified.signatures[0].signature).packets.length).to.equal(1);
          });
        });
      });

      it('should sign and verify binary data with a date in the future', async function () {
        const future = new Date(2040, 5, 5, 5, 5, 5, 0);
        const data = new Uint8Array([3, 14, 15, 92, 65, 35, 59]);
        const signOpt = {
          message: await openpgp.createMessage({ binary: data }),
          signingKeys: privateKey_2038_2045,
          detached: true,
          date: future,
          format: 'binary'
        };
        const verifyOpt = {
          verificationKeys: publicKey_2038_2045,
          date: future,
          format: 'binary'
        };
        return openpgp.sign(signOpt).then(async function (signed) {
          verifyOpt.message = await openpgp.createMessage({ binary: data });
          verifyOpt.signature = await openpgp.readSignature({ binarySignature: signed });
          return openpgp.verify(verifyOpt);
        }).then(async function (verified) {
          expect(+(await verified.signatures[0].signature).packets[0].created).to.equal(+future);
          expect([].slice.call(verified.data)).to.deep.equal([].slice.call(data));
          expect(await verified.signatures[0].verified).to.be.true;
          expect(await privateKey_2038_2045.getSigningKey(verified.signatures[0].keyID, future))
            .to.be.not.null;
          expect((await verified.signatures[0].signature).packets.length).to.equal(1);
        });
      });

      it('should sign and verify binary data without one-pass signature', async function () {
        const data = new Uint8Array([3, 14, 15, 92, 65, 35, 59]);
        const signOpt = {
          message: await openpgp.createMessage({ binary: data }),
          signingKeys: privateKey,
          format: 'binary'
        };
        const verifyOpt = {
          verificationKeys: publicKey,
          format: 'binary'
        };
        return openpgp.sign(signOpt).then(async function (signed) {
          const message = await openpgp.readMessage({ binaryMessage: signed });
          message.packets.push(...await stream.readToEnd(message.packets.stream, _ => _));
          const packets = new openpgp.PacketList();
          packets.push(message.packets.findPacket(openpgp.enums.packet.signature));
          packets.push(message.packets.findPacket(openpgp.enums.packet.literalData));
          verifyOpt.message = new openpgp.Message(packets);
          return openpgp.verify(verifyOpt);
        }).then(async function (verified) {
          expect([].slice.call(verified.data)).to.deep.equal([].slice.call(data));
          expect(await verified.signatures[0].verified).to.be.true;
          expect(await privateKey.getSigningKey(verified.signatures[0].keyID))
            .to.be.not.null;
          expect((await verified.signatures[0].signature).packets.length).to.equal(1);
        });
      });

      it('should streaming sign and verify binary data without one-pass signature', async function () {
        const data = new Uint8Array([3, 14, 15, 92, 65, 35, 59]);
        const dataStream = global.ReadableStream ? new global.ReadableStream({
          start(controller) {
            controller.enqueue(data);
            controller.close();
          }
        }) : new (require('stream').Readable)({
          read() {
            this.push(data);
            this.push(null);
          }
        });
        const signOpt = {
          message: await openpgp.createMessage({ binary: dataStream }),
          signingKeys: privateKey,
          format: 'binary'
        };
        const verifyOpt = {
          verificationKeys: publicKey,
          format: 'binary'
        };
        return openpgp.sign(signOpt).then(async function (signed) {
          expect(stream.isStream(signed)).to.equal(global.ReadableStream ? 'web' : 'node');
          const message = await openpgp.readMessage({ binaryMessage: signed });
          message.packets.push(...await stream.readToEnd(message.packets.stream, _ => _));
          const packets = new openpgp.PacketList();
          packets.push(message.packets.findPacket(openpgp.enums.packet.signature));
          packets.push(message.packets.findPacket(openpgp.enums.packet.literalData));
          verifyOpt.message = await openpgp.readMessage({
            binaryMessage: stream[
              global.ReadableStream ? (global.ReadableStream === stream.ReadableStream ? 'toStream' : 'toNativeReadable') : 'webToNode'
            ](packets.write())
          });
          return openpgp.verify(verifyOpt);
        }).then(async function (verified) {
          expect(stream.isStream(verified.data)).to.equal(global.ReadableStream ? 'web' : 'node');
          expect([].slice.call(await stream.readToEnd(verified.data))).to.deep.equal([].slice.call(data));
          expect(await verified.signatures[0].verified).to.be.true;
          expect(await privateKey.getSigningKey(verified.signatures[0].keyID))
            .to.be.not.null;
          expect((await verified.signatures[0].signature).packets.length).to.equal(1);
        });
      });

      it('should encrypt and decrypt data with a date in the future', async function () {
        const future = new Date(2040, 5, 5, 5, 5, 5, 0);
        const encryptOpt = {
          message: await openpgp.createMessage({ text: plaintext, date: future }),
          encryptionKeys: publicKey_2038_2045,
          date: future,
          format: 'binary'
        };

        return openpgp.encrypt(encryptOpt).then(async function (encrypted) {
          const message = await openpgp.readMessage({ binaryMessage: encrypted });
          return message.decrypt([privateKey_2038_2045], undefined, undefined, undefined, openpgp.config);
        }).then(async function (packets) {
          const literals = packets.packets.filterByTag(openpgp.enums.packet.literalData);
          expect(literals.length).to.equal(1);
          expect(+literals[0].date).to.equal(+future);
          expect(await stream.readToEnd(packets.getText())).to.equal(plaintext);
        });
      });

      it('should encrypt and decrypt binary data with a date in the past', async function () {
        const past = new Date(2005, 5, 5, 5, 5, 5, 0);
        const data = new Uint8Array([3, 14, 15, 92, 65, 35, 59]);
        const encryptOpt = {
          message: await openpgp.createMessage({ binary: data, date: past }),
          encryptionKeys: publicKey_2000_2008,
          date: past,
          format: 'binary'
        };

        return openpgp.encrypt(encryptOpt).then(async function (encrypted) {
          const message = await openpgp.readMessage({ binaryMessage: encrypted });
          return message.decrypt([privateKey_2000_2008], undefined, undefined, undefined, openpgp.config);
        }).then(async function (packets) {
          const literals = packets.packets.filterByTag(openpgp.enums.packet.literalData);
          expect(literals.length).to.equal(1);
          expect(+literals[0].date).to.equal(+past);
          expect(await stream.readToEnd(packets.getLiteralData())).to.deep.equal(data);
        });
      });

      it('should sign, encrypt and decrypt, verify data with a date in the past', async function () {
        const past = new Date(2005, 5, 5, 5, 5, 5, 0);
        const encryptOpt = {
          message: await openpgp.createMessage({ text: plaintext, date: past }),
          encryptionKeys: publicKey_2000_2008,
          signingKeys: privateKey_2000_2008,
          date: past,
          format: 'binary'
        };

        return openpgp.encrypt(encryptOpt).then(async function (encrypted) {
          const message = await openpgp.readMessage({ binaryMessage: encrypted });
          return message.decrypt([privateKey_2000_2008], undefined, undefined, undefined, openpgp.config);
        }).then(async function (message) {
          const literals = message.packets.filterByTag(openpgp.enums.packet.literalData);
          expect(literals.length).to.equal(1);
          expect(+literals[0].date).to.equal(+past);
          const signatures = await message.verify([publicKey_2000_2008], past, undefined, openpgp.config);
          expect(await stream.readToEnd(message.getText())).to.equal(plaintext);
          expect(+(await signatures[0].signature).packets[0].created).to.equal(+past);
          expect(await signatures[0].verified).to.be.true;
          expect(await privateKey_2000_2008.getSigningKey(signatures[0].keyID, past))
            .to.be.not.null;
          expect((await signatures[0].signature).packets.length).to.equal(1);
        });
      });

      it('should sign, encrypt and decrypt, verify binary data with a date in the future', async function () {
        const future = new Date(2040, 5, 5, 5, 5, 5, 0);
        const data = new Uint8Array([3, 14, 15, 92, 65, 35, 59]);
        const encryptOpt = {
          message: await openpgp.createMessage({ binary: data, date: future }),
          encryptionKeys: publicKey_2038_2045,
          signingKeys: privateKey_2038_2045,
          date: future,
          format: 'binary'
        };

        return openpgp.encrypt(encryptOpt).then(async function (encrypted) {
          const message = await openpgp.readMessage({ binaryMessage: encrypted });
          return message.decrypt([privateKey_2038_2045], undefined, undefined, undefined, openpgp.config);
        }).then(async function (message) {
          const literals = message.packets.filterByTag(openpgp.enums.packet.literalData);
          expect(literals.length).to.equal(1);
          expect(literals[0].format).to.equal(openpgp.enums.literal.binary);
          expect(+literals[0].date).to.equal(+future);
          const signatures = await message.verify([publicKey_2038_2045], future, undefined, openpgp.config);
          expect(await stream.readToEnd(message.getLiteralData())).to.deep.equal(data);
          expect(+(await signatures[0].signature).packets[0].created).to.equal(+future);
          expect(await signatures[0].verified).to.be.true;
          expect(await privateKey_2038_2045.getSigningKey(signatures[0].keyID, future))
            .to.be.not.null;
          expect((await signatures[0].signature).packets.length).to.equal(1);
        });
      });

      it('should sign, encrypt and decrypt, verify mime data with a date in the future', async function () {
        const future = new Date(2040, 5, 5, 5, 5, 5, 0);
        const data = new Uint8Array([3, 14, 15, 92, 65, 35, 59]);
        const encryptOpt = {
          message: await openpgp.createMessage({ binary: data, date: future, format: 'mime' }),
          encryptionKeys: publicKey_2038_2045,
          signingKeys: privateKey_2038_2045,
          date: future,
          format: 'binary'
        };

        return openpgp.encrypt(encryptOpt).then(async function (encrypted) {
          const message = await openpgp.readMessage({ binaryMessage: encrypted });
          return message.decrypt([privateKey_2038_2045], undefined, undefined, undefined, openpgp.config);
        }).then(async function (message) {
          const literals = message.packets.filterByTag(openpgp.enums.packet.literalData);
          expect(literals.length).to.equal(1);
          expect(literals[0].format).to.equal(openpgp.enums.literal.mime);
          expect(+literals[0].date).to.equal(+future);
          const signatures = await message.verify([publicKey_2038_2045], future, undefined, openpgp.config);
          expect(await stream.readToEnd(message.getLiteralData())).to.deep.equal(data);
          expect(+(await signatures[0].signature).packets[0].created).to.equal(+future);
          expect(await signatures[0].verified).to.be.true;
          expect(await privateKey_2038_2045.getSigningKey(signatures[0].keyID, future))
            .to.be.not.null;
          expect((await signatures[0].signature).packets.length).to.equal(1);
        });
      });

      it('should fail to encrypt with revoked key', function() {
        return openpgp.revokeKey({
          key: privateKey,
          format: 'object'
        }).then(async function({ publicKey: revKey }) {
          return openpgp.encrypt({
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: revKey
          }).then(function() {
            throw new Error('Should not encrypt with revoked key');
          }).catch(function(error) {
            expect(error.message).to.match(/Error encrypting message: Primary key is revoked/);
          });
        });
      });

      it('should fail to encrypt with revoked subkey', async function() {
        const pubKeyDE = await openpgp.readKey({ armoredKey: pub_key_de });
        const privKeyDE = await openpgp.decryptKey({
          privateKey: await openpgp.readKey({ armoredKey: priv_key_de }),
          passphrase
        });
        return privKeyDE.subkeys[0].revoke(privKeyDE.keyPacket).then(async function(revSubkey) {
          pubKeyDE.subkeys[0] = revSubkey;
          return openpgp.encrypt({
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: pubKeyDE,
            config: { rejectPublicKeyAlgorithms: new Set() }
          }).then(function() {
            throw new Error('Should not encrypt with revoked subkey');
          }).catch(function(error) {
            expect(error.message).to.match(/Could not find valid encryption key packet/);
          });
        });
      });

      it('should decrypt with revoked subkey', async function() {
        const pubKeyDE = await openpgp.readKey({ armoredKey: pub_key_de });
        const privKeyDE = await openpgp.decryptKey({
          privateKey: await openpgp.readKey({ armoredKey: priv_key_de }),
          passphrase
        });
        const encrypted = await openpgp.encrypt({
          message: await openpgp.createMessage({ text: plaintext }),
          encryptionKeys: pubKeyDE,
          config: { rejectPublicKeyAlgorithms: new Set() }
        });
        privKeyDE.subkeys[0] = await privKeyDE.subkeys[0].revoke(privKeyDE.keyPacket);
        const decOpt = {
          message: await openpgp.readMessage({ armoredMessage: encrypted }),
          decryptionKeys: privKeyDE,
          config: { rejectPublicKeyAlgorithms: new Set() }
        };
        const decrypted = await openpgp.decrypt(decOpt);
        expect(decrypted.data).to.equal(plaintext);
      });

      it('should not decrypt with corrupted subkey', async function() {
        const pubKeyDE = await openpgp.readKey({ armoredKey: pub_key_de });
        const privKeyDE = await openpgp.readKey({ armoredKey: priv_key_de });
        // corrupt the public key params
        privKeyDE.subkeys[0].keyPacket.publicParams.p[0]++;
        // validation will check the primary key -- not the decryption subkey -- and will succeed (for now)
        const decryptedKeyDE = await openpgp.decryptKey({
          privateKey: privKeyDE,
          passphrase
        });
        const encrypted = await openpgp.encrypt({
          message: await openpgp.createMessage({ text: plaintext }),
          encryptionKeys: pubKeyDE,
          config: { rejectPublicKeyAlgorithms: new Set() }
        });
        const decOpt = {
          message: await openpgp.readMessage({ armoredMessage: encrypted }),
          decryptionKeys: decryptedKeyDE
        };
        // binding signature is invalid
        await expect(openpgp.decrypt(decOpt)).to.be.rejectedWith(/Session key decryption failed/);
      });

      it('RSA decryption with PKCS1 padding of wrong length should fail', async function() {
        const key = await openpgp.readKey({ armoredKey: rsaPrivateKeyPKCS1 });
        // the paddings of these messages are prefixed by 0x02 and 0x000002 instead of 0x0002
        // the code should discriminate between these cases by checking the length of the padded plaintext
        const padding02 = `-----BEGIN PGP MESSAGE-----
Version: OpenPGP.js VERSION
Comment: https://openpgpjs.org

wcBMAxbpoSTRSSl3AQf/fepDhqeam4Ecy8GUFChc47U3hbkdgINobI9TORAf
eGFZVcyTQKVIt7fB8bwQwjxRmU98xCjF7VkLhPQJkzKlkT9cIDBKswU+d3fw
lHAVYo77yUkFkVLXrQTZj/OjsA12V7lfRagO375XB3EpJUHVPvYQFFr3aSlo
FbsCrpZoS6FXxRYVjGpIeMjam3a7qDavQpKhjOQ+Sfm0tk2JZkQwpFom6x7c
9TEn3YSo6+I0ztjiuTBZDyYr8zocHW8imFzZRlcNuuuukesyFzFgHx46eVpO
6PVjmiN50agZvsV9rgPyyH84nb3zYJ63shnrQWubTOVH4daGbe8uHi+ZM3UU
J9I8AcH94nE77JUtCm7s1kOlo0EIshZsAqJwGveDGdAuabfViVwVxG4I24M6
8sqJYJd9FpNjSbYlrLT0R9zy
=+n/4
-----END PGP MESSAGE-----`;
        const padding000002 = `-----BEGIN PGP MESSAGE-----
Version: OpenPGP.js VERSION
Comment: https://openpgpjs.org

wcBMAxbpoSTRSSl3AQf/fepDhqeam4Ecy8GUFChc47U3hbkdgINobI9TORAf
eGFZVcyTQKVIt7fB8bwQwjxRmU98xCjF7VkLhPQJkzKlkT9cIDBKswU+d3fw
lHAVYo77yUkFkVLXrQTZj/OjsA12V7lfRagO375XB3EpJUHVPvYQFFr3aSlo
FbsCrpZoS6FXxRYVjGpIeMjam3a7qDavQpKhjOQ+Sfm0tk2JZkQwpFom6x7c
9TEn3YSo6+I0ztjiuTBZDyYr8zocHW8imFzZRlcNuuuukesyFzFgHx46eVpO
6PVjmiN50agZvsV9rgPyyH84nb3zYJ63shnrQWubTOVH4daGbe8uHi+ZM3UU
J9I8AcH94nE77JUtCm7s1kOlo0EIshZsAqJwGveDGdAuabfViVwVxG4I24M6
8sqJYJd9FpNjSbYlrLT0R9zy
=+n/4
-----END PGP MESSAGE-----`;

        const decOpt02 = {
          message: await openpgp.readMessage({ armoredMessage: padding02 }),
          decryptionKeys: key
        };
        await expect(openpgp.decrypt(decOpt02)).to.be.rejectedWith(/Decryption error/);

        const decOpt000002 = {
          message: await openpgp.readMessage({ armoredMessage: padding000002 }),
          decryptionKeys: key
        };
        await expect(openpgp.decrypt(decOpt000002)).to.be.rejectedWith(/Decryption error/);
      });

      it('should decrypt with two passwords message which GPG fails on', async function() {
        const decOpt = {
          message: await openpgp.readMessage({ armoredMessage: twoPasswordGPGFail }),
          passwords: password2
        };
        return openpgp.decrypt(decOpt).then(function(decrypted) {
          expect(decrypted.data).to.equal('short message\nnext line\n한국어/조선말');
          expect(decrypted.signatures.length).to.equal(0);
        });
      });

      it('should decrypt with three passwords', async function() {
        const messageBinary = util.hexToUint8Array('c32e04090308125231fe38b0255f60a7f319fc4959c147c7af33817ceb4cf159a00f2efa17b7921961f6ead025c77588d2430166fe9395cd58e9b69a67a30470e2d31bf0bbbb31c7eca31fb9015dddf70c6957036b093d104cbf0b26e218113e69c4fa89dda97a61d0cba364efa77d5144c5b9b701');
        const message = await openpgp.readMessage({ binaryMessage: messageBinary });
        const passwords = ['Test', 'Pinata', 'a'];
        const decrypted = await openpgp.decrypt({ message, passwords });
        expect(decrypted.data).to.equal('Hello world');
      });

      it('should decrypt broken ECC message from old OpenPGP.js', async function() {
        const key = await openpgp.decryptKey({
          privateKey: await openpgp.readKey({ armoredKey: ecdh_dec_key }),
          passphrase: '12345'
        });
        const message = await openpgp.readMessage({ armoredMessage: ecdh_msg_bad });
        const decrypted = await openpgp.decrypt({ message, decryptionKeys: key });
        expect(decrypted.data).to.equal('\n');
      });

      it('should decrypt broken ECC message from old go crypto', async function() {
        const key = await openpgp.decryptKey({
          privateKey: await openpgp.readKey({ armoredKey: ecdh_dec_key2 }),
          passphrase: '12345'
        });
        const message = await openpgp.readMessage({ armoredMessage: ecdh_msg_bad_2 });
        const decrypted = await openpgp.decrypt({ message, decryptionKeys: key });
        expect(decrypted.data).to.equal('Tesssst<br><br><br>Sent from ProtonMail mobile<br><br><br>');
      });

      it('should decrypt Blowfish message', async function() {
        const { data } = await openpgp.decrypt({
          passwords: 'test',
          message: await openpgp.readMessage({
            armoredMessage: `-----BEGIN PGP MESSAGE-----
Version: OpenPGP.js v4.9.0
Comment: https://openpgpjs.org

wx4EBAMI7Di70u7hoDfgBUJQ2+1ig6ym3KMjRS9kAovSPAGRQLIPv2DgkINL
3DUgMNqtQCA23xWhq7Ly6o9H1lRfoAo7V5UElVCqGEX7cgyZjI97alY6Je3o
amnR6g==
=rPIK
-----END PGP MESSAGE-----`
          })
        });
        expect(data).to.equal('Hello World!');
      });

      it('should normalize newlines in encrypted text message', async function() {
        const message = await openpgp.createMessage({ text: '"BEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VEVENT\r\nUID:123\r\nDTSTART:20191211T121212Z\r\nDTEND:20191212T121212Z\r\nEND:VEVENT\nEND:VCALENDAR"' });
        const encrypted = await openpgp.encrypt({
          passwords: 'test',
          message
        });
        const decrypted = await openpgp.decrypt({
          passwords: 'test',
          message: await openpgp.readMessage({ armoredMessage: encrypted }),
          format: 'binary'
        });
        expect(util.decodeUTF8(decrypted.data)).to.equal('"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nBEGIN:VEVENT\r\nUID:123\r\nDTSTART:20191211T121212Z\r\nDTEND:20191212T121212Z\r\nEND:VEVENT\r\nEND:VCALENDAR"');
      });
    });

    describe('Sign and verify with each curve', function() {
      const curves = ['secp256k1' , 'p256', 'p384', 'p521', 'curve25519', 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1'];
      curves.forEach(curve => {
        it(`sign/verify with ${curve}`, async function() {
          const config = { rejectCurves: new Set() };
          const plaintext = 'short message';
          const { privateKey: key } = await openpgp.generateKey({ curve, userIDs: { name: 'Alice', email: 'info@alice.com' }, format: 'object', config });
          const signed = await openpgp.sign({ signingKeys:[key], message: await openpgp.createCleartextMessage({ text: plaintext }), config });
          const verified = await openpgp.verify({ verificationKeys:[key], message: await openpgp.readCleartextMessage({ cleartextMessage: signed }), config });
          expect(await verified.signatures[0].verified).to.be.true;
        });
      });

      it('should fail to decrypt non-integrity-protected message by default', async function() {
        const key = await openpgp.readKey({
          armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xVgEYD9r8xYJKwYBBAHaRw8BAQdApTaQJ6R/uooTqAuscoxYwbLrtoKndnsX
ydhqMybJqh0AAQCxNwi9Pezy03OQE0XOooBWaHiuhBtKA1eAuqjJFuuLuQ/+
zQDCjAQQFgoAHQUCYD9r8wQLCQcIAxUICgQWAgEAAhkBAhsDAh4BACEJEIkB
BTiDwpvwFiEEvRnmOby6fJ/OxUhSiQEFOIPCm/BidgEAq05ZiPseRsMTxNm7
IFQwQjmIFiWgLeQ0gKIvfl3SjBAA/iSPyTgWxSY98utXNuq+WoxVOzx3dJwG
2cflR/UFUlEPx10EYD9r8xIKKwYBBAGXVQEFAQEHQCASw+tMPvnXi904WASv
wRDUQofh0M7CpgQFqoOXvGlLAwEIBwAA/3gEimwdIet0gXb/hRRyBqOlcq32
lNREh+n+vZKJyXWYEjrCeAQYFggACQUCYD9r8wIbDAAhCRCJAQU4g8Kb8BYh
BL0Z5jm8unyfzsVIUokBBTiDwpvwHpEBAObHllPrJu0DqYyt4FKPkijgRpXC
ESqhlK5rrbc62SmfAQDVf5l1B6IDASBCKtC0VPPpYiK6AUcEISpaSXOa+pNI
Bw==
=3Fja
-----END PGP PRIVATE KEY BLOCK-----`
        });
        const message = await openpgp.readMessage({
          armoredMessage: `-----BEGIN PGP MESSAGE-----

wV4D+3VwOibHmagSAQdATlMJlvrkaq46zMkbIuKBOJO5X3ugVwZpEyAterQC
/RUw0OPWeO+4swh/U7ZurV8cRr/fPnyGUUKI7rI+va3kWUZv4RRpUs7eYE57
OUr3yoMNyaQEBwu6VXiQrsBN8TyUbXQxb63p7EHFXIgvVDIvOG7bQptrrKlM
kKcB+fz5hb6mT/tl+cPcYHDOjocQ92pNVm+FilQhiATRxV8ah1DCOIZZ6tgq
rWwIiEQEBPt+tXOuVF4Peumovp3WgziudrJa5Jxt2Dz+8nicBglbZLXTsZNu
bsZgJWVlAa5eil6J9ePX2xbo1vVAkLQdzE9+1jL+l7PRIZuVBQ==
=T4iR
-----END PGP MESSAGE-----`
        });
        await expect(
          openpgp.decrypt({ message, decryptionKeys: key, verificationKeys: key })
        ).to.be.rejectedWith('Error decrypting message: Message is not authenticated.');
      });

      it('should allow decrypting non-integrity-protected message when enabled', async function() {
        const key = await openpgp.readKey({
          armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xVgEYD9r8xYJKwYBBAHaRw8BAQdApTaQJ6R/uooTqAuscoxYwbLrtoKndnsX
ydhqMybJqh0AAQCxNwi9Pezy03OQE0XOooBWaHiuhBtKA1eAuqjJFuuLuQ/+
zQDCjAQQFgoAHQUCYD9r8wQLCQcIAxUICgQWAgEAAhkBAhsDAh4BACEJEIkB
BTiDwpvwFiEEvRnmOby6fJ/OxUhSiQEFOIPCm/BidgEAq05ZiPseRsMTxNm7
IFQwQjmIFiWgLeQ0gKIvfl3SjBAA/iSPyTgWxSY98utXNuq+WoxVOzx3dJwG
2cflR/UFUlEPx10EYD9r8xIKKwYBBAGXVQEFAQEHQCASw+tMPvnXi904WASv
wRDUQofh0M7CpgQFqoOXvGlLAwEIBwAA/3gEimwdIet0gXb/hRRyBqOlcq32
lNREh+n+vZKJyXWYEjrCeAQYFggACQUCYD9r8wIbDAAhCRCJAQU4g8Kb8BYh
BL0Z5jm8unyfzsVIUokBBTiDwpvwHpEBAObHllPrJu0DqYyt4FKPkijgRpXC
ESqhlK5rrbc62SmfAQDVf5l1B6IDASBCKtC0VPPpYiK6AUcEISpaSXOa+pNI
Bw==
=3Fja
-----END PGP PRIVATE KEY BLOCK-----`
        });
        const message = await openpgp.readMessage({
          armoredMessage: `-----BEGIN PGP MESSAGE-----

wV4D+3VwOibHmagSAQdATlMJlvrkaq46zMkbIuKBOJO5X3ugVwZpEyAterQC
/RUw0OPWeO+4swh/U7ZurV8cRr/fPnyGUUKI7rI+va3kWUZv4RRpUs7eYE57
OUr3yoMNyaQEBwu6VXiQrsBN8TyUbXQxb63p7EHFXIgvVDIvOG7bQptrrKlM
kKcB+fz5hb6mT/tl+cPcYHDOjocQ92pNVm+FilQhiATRxV8ah1DCOIZZ6tgq
rWwIiEQEBPt+tXOuVF4Peumovp3WgziudrJa5Jxt2Dz+8nicBglbZLXTsZNu
bsZgJWVlAa5eil6J9ePX2xbo1vVAkLQdzE9+1jL+l7PRIZuVBQ==
=T4iR
-----END PGP MESSAGE-----`
        });
        const decrypted = await openpgp.decrypt({ message, decryptionKeys: key, verificationKeys: key, config: { allowUnauthenticatedMessages: true } });
        expect(decrypted.data).to.equal('test');
      });

      it('should allow stream-decrypting non-integrity-protected message when enabled', async function() {
        const key = await openpgp.readKey({
          armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xVgEYD9r8xYJKwYBBAHaRw8BAQdApTaQJ6R/uooTqAuscoxYwbLrtoKndnsX
ydhqMybJqh0AAQCxNwi9Pezy03OQE0XOooBWaHiuhBtKA1eAuqjJFuuLuQ/+
zQDCjAQQFgoAHQUCYD9r8wQLCQcIAxUICgQWAgEAAhkBAhsDAh4BACEJEIkB
BTiDwpvwFiEEvRnmOby6fJ/OxUhSiQEFOIPCm/BidgEAq05ZiPseRsMTxNm7
IFQwQjmIFiWgLeQ0gKIvfl3SjBAA/iSPyTgWxSY98utXNuq+WoxVOzx3dJwG
2cflR/UFUlEPx10EYD9r8xIKKwYBBAGXVQEFAQEHQCASw+tMPvnXi904WASv
wRDUQofh0M7CpgQFqoOXvGlLAwEIBwAA/3gEimwdIet0gXb/hRRyBqOlcq32
lNREh+n+vZKJyXWYEjrCeAQYFggACQUCYD9r8wIbDAAhCRCJAQU4g8Kb8BYh
BL0Z5jm8unyfzsVIUokBBTiDwpvwHpEBAObHllPrJu0DqYyt4FKPkijgRpXC
ESqhlK5rrbc62SmfAQDVf5l1B6IDASBCKtC0VPPpYiK6AUcEISpaSXOa+pNI
Bw==
=3Fja
-----END PGP PRIVATE KEY BLOCK-----`
        });
        const message = await openpgp.readMessage({
          armoredMessage: stream.toStream(`-----BEGIN PGP MESSAGE-----

wV4D+3VwOibHmagSAQdATlMJlvrkaq46zMkbIuKBOJO5X3ugVwZpEyAterQC
/RUw0OPWeO+4swh/U7ZurV8cRr/fPnyGUUKI7rI+va3kWUZv4RRpUs7eYE57
OUr3yoMNyaQEBwu6VXiQrsBN8TyUbXQxb63p7EHFXIgvVDIvOG7bQptrrKlM
kKcB+fz5hb6mT/tl+cPcYHDOjocQ92pNVm+FilQhiATRxV8ah1DCOIZZ6tgq
rWwIiEQEBPt+tXOuVF4Peumovp3WgziudrJa5Jxt2Dz+8nicBglbZLXTsZNu
bsZgJWVlAa5eil6J9ePX2xbo1vVAkLQdzE9+1jL+l7PRIZuVBQ==
=T4iR
-----END PGP MESSAGE-----`)
        });
        const decrypted = await openpgp.decrypt({ message, decryptionKeys: key, verificationKeys: key, config: { allowUnauthenticatedMessages: true } });
        const data = await stream.readToEnd(decrypted.data);
        expect(data).to.equal('test');
      });
    });

    describe('Errors', function() {

      it('Error message should contain the original error message', async function() {
        return openpgp.encrypt({
          message: await openpgp.createMessage({ binary: new Uint8Array([0x01, 0x01, 0x01]) }),
          passwords: null
        }).then(function() {
          throw new Error('Error expected.');
        }).catch(function(error) {
          expect(error.message).to.match(/No keys, passwords, or session key provided/);
        });
      });

    });

    describe('Specific encryption/signing key testing', async function () {
      const encryptionKeyIDs = [
        keyIDType.fromID('87EAE0977B2185EA'),
        keyIDType.fromID('F94F9B34AF93FA14'),
        keyIDType.fromID('08F7D4C7C59545C0')
      ];
      const signingKeyIDs = [
        keyIDType.fromID('663277AF60400638'),
        keyIDType.fromID('BBE14491E6EE6366'),
        keyIDType.fromID('3E0F20F1A71D6DFD')
      ];
      const getPrimaryKey = async () => openpgp.readKey({
        armoredKey: multipleEncryptionAndSigningSubkeys
      });

      it('Encrypt message with a specific encryption key id', async function () {
        const primaryKey = await getPrimaryKey();
        let m;
        let p;
        for (let i = 0; i < encryptionKeyIDs.length; i++) {
          m = await openpgp.readMessage({
            armoredMessage: await openpgp.encrypt({
              message: await openpgp.createMessage({ text: 'Hello World\n' }),
              encryptionKeys: primaryKey,
              encryptionKeyIDs: [encryptionKeyIDs[i]]
            })
          });
          p = m.packets.filterByTag(openpgp.enums.packet.publicKeyEncryptedSessionKey);
          expect(p.length).equals(1);
          expect(p[0].publicKeyID.equals(encryptionKeyIDs[i])).to.be.true;
        }
      });

      it('Sign message with a specific signing key id', async function () {
        const primaryKey = await getPrimaryKey();
        let s;
        let p;
        for (let i = 0; i < signingKeyIDs.length; i++) {
          s = await openpgp.readSignature({
            armoredSignature: await openpgp.sign({
              message: await openpgp.createMessage({ text: 'Hello World\n' }),
              signingKeys: primaryKey,
              signingKeyIDs: [signingKeyIDs[i]],
              detached: true
            })
          });
          p = s.packets.filterByTag(openpgp.enums.packet.signature);
          expect(p.length).equals(1);
          expect(p[0].issuerKeyID.equals(signingKeyIDs[i])).to.be.true;
        }
      });

      it('Encrypt and sign with specific encryption/signing key ids', async function () {
        const primaryKey = await getPrimaryKey();
        const plaintextMessage = await openpgp.createMessage({ text: 'Hello World\n' });

        const checkEncryptedPackets = (encryptionKeyIDs, pKESKList) => {
          pKESKList.forEach(({ publicKeyID }, i) => {
            expect(publicKeyID.equals(encryptionKeyIDs[i])).to.be.true;
          });
        };
        const checkSignatures = (signingKeyIDs, signatures) => {
          signatures.forEach(({ keyID }, i) => {
            expect(keyID.equals(signingKeyIDs[i])).to.be.true;
          });
        };

        const kIds = [encryptionKeyIDs[1], encryptionKeyIDs[0], encryptionKeyIDs[2]];
        const sIds = [signingKeyIDs[2], signingKeyIDs[1], signingKeyIDs[0]];
        const message = await openpgp.readMessage({
          armoredMessage: await openpgp.encrypt({
            message: plaintextMessage,
            signingKeys: [primaryKey, primaryKey, primaryKey],
            encryptionKeys: [primaryKey, primaryKey, primaryKey],
            encryptionKeyIDs: kIds,
            signingKeyIDs: sIds
          })
        });
        const pKESKList = message.packets.filterByTag(openpgp.enums.packet.publicKeyEncryptedSessionKey);
        expect(pKESKList.length).equals(3);
        checkEncryptedPackets(kIds, pKESKList);
        const { signatures } = await openpgp.decrypt({
          message,
          decryptionKeys: [primaryKey, primaryKey, primaryKey]
        });
        expect(signatures.length).equals(3);
        checkSignatures(sIds, signatures);
      });
    });
  });
});