openpgpjs/test/general/openpgp.js
larabr 727c7cad37 read[Private]Key: support parsing key blocks (return first parsable key)
Previously, `readKey` and `readPrivateKey` would throw when given a block
of keys as input.
With this change, the first parsable key is returned by both functions:
the behaviour is equivalent to calling `readKeys` (resp. `readPrivateKeys`)
and taking the first array entry.
2024-05-16 14:07:39 +02:00

5068 lines
242 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* eslint-disable max-lines */
/* globals tryTests, loadStreamsPolyfill */
import sinon from 'sinon';
import * as stream from '@openpgp/web-stream-tools';
import { use as chaiUse, expect } from 'chai';
import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import
chaiUse(chaiAsPromised);
import openpgp from '../initOpenpgp.js';
import crypto from '../../src/crypto';
import * as random from '../../src/crypto/random.js';
import util from '../../src/util.js';
import keyIDType from '../../src/type/keyid.js';
import { getPreferredCipherSuite } from '../../src/key';
import * as input from './testInputs.js';
const detectBrowser = () => typeof navigator === 'object';
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 priv_key_sha3 = `-----BEGIN PGP PRIVATE KEY BLOCK-----
xUsGZN8edBsAAAAgdUMlFMFCVKNo7sdUd6FVBos6NNjpUpSdrodk6BfPb/kA+3bu
A2+WY2LwyxlX5o07WR2VSn+wuegC3v28yO0tClHCtwYfGw4AAABIBYJk3x50BAsJ
CAcHFQ4MCgkICwIWAAIXgAKbAwIeCSIhBpbSe0QWuaCNSSLaePhXEP3BxQ2VHX3W
pW1U6svHvCUiBScJAgcCAAAAACMZIP8aHixoyC9wS3q/TNV/IfOQa81f+U5Ucz6H
4I+c5bWRYUzH/piBB4n5FoYlld+/SViCQIBCQ+fynLmaj5wlf22+mISTt/9je1Zf
YWlJ+WSJyi5gY5EH9DubfuIU3VaqCM0aQmVybmFkZXR0ZSA8YkBleGFtcGxlLm9y
Zz7CugYTGw4AAABLBYJk3x50BAsJCAcHFQ4MCgkICwIWAAIXgAIZAQKbAwIeCSIh
BpbSe0QWuaCNSSLaePhXEP3BxQ2VHX3WpW1U6svHvCUiBScJAgcCAAAAAMMGIJEi
9+yqkFKsNwX1H5II0riPudFpwBx2ypVjNk4aNb7Exl56Aac4tXEhz4fH41q0dAzF
ww2erZaiUqmohQ4AFSw1jN/WOiDfb1DkjT/HJ8vXMGpwWdgFPoqsWzTNhd5VCcdL
BmTfHnQZAAAAIAGMcsqVCXLclRhVamWciSxmnYF1FFs80W7dNUH07HUOAHh/S601
If+/eZKDIj3jq7oOe2PzHSYEK+mpQD1hBpF2wpsGGBsOAAAALAWCZN8edAKbDCIh
BpbSe0QWuaCNSSLaePhXEP3BxQ2VHX3WpW1U6svHvCUiAAAAANj3IBknZTPsMpWA
we0Jl5gw/Dj4lWAGoJfWfk+6s3Q86Hag3Hu8VBsapzmul+vzy0KJa+ZRcZz2n8aj
0vTl4sOZ0EcCdFDfkh/tR//gKkT6BiSBG86WoFq3f6U/RC+z0Ym7Dw==
-----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)
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
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-----`;
const rsaKeyWithNoFeaturesFromRNP = `-----BEGIN PGP PRIVATE KEY BLOCK-----
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
8wENR68u88WrKOGN359vq/DKwd3A
=c2mQ
-----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 = sinon.spy(openpgp.CompressedDataPacket.prototype, 'compress');
decompressSpy = sinon.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);
}
);
});
});
}
export default () => 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 and readPrivateKeys should have consistent results', async function() {
const privateAndPublicKeyBlock = `-----BEGIN PGP PRIVATE KEY BLOCK-----
xVgEZkI3VBYJKwYBBAHaRw8BAQdA7nW1t5qRdtIYzEVEEhRSDgTgbk2JHofY
Ph8FuGsDqgwAAQDYVpZt1w6a+vxgb4M351aCpA2sCfx8kbFg23h8Irtm1xFY
zQ88dGVzdEB0ZXN0LmNvbT7CwBMEEBYKAIUFgmZCN1QDCwkHCZDuyoEpQ/KE
H0UUAAAAAAAcACBzYWx0QG5vdGF0aW9ucy5vcGVucGdwanMub3JnzKJ8mXOC
wnEp6lVJ/5+rRzR4UcwlL8EjhOS+rV0T8pAFFQgKDA4EFgACAQIZAQKbAwIe
ARYhBECs3D9sMFn6oOxAqu7KgSlD8oQfAAAPbwD+NjyEHt1CRI+0XmgHdiwZ
JN115IO+M37bOxgBnTbVoF0BAMGECXVQoSRVNy0TYf+AUUPQ6tSZ1zLXszwe
FK3w+CoGxjMEZkI3VBYJKwYBBAHaRw8BAQdA7nW1t5qRdtIYzEVEEhRSDgTg
bk2JHofYPh8FuGsDqgzNDzx0ZXN0QHRlc3QuY29tPsLAEwQQFgoAhQWCZkI3
VAMLCQcJkO7KgSlD8oQfRRQAAAAAABwAIHNhbHRAbm90YXRpb25zLm9wZW5w
Z3Bqcy5vcmfMonyZc4LCcSnqVUn/n6tHNHhRzCUvwSOE5L6tXRPykAUVCAoM
DgQWAAIBAhkBApsDAh4BFiEEQKzcP2wwWfqg7ECq7sqBKUPyhB8AAA9vAP42
PIQe3UJEj7ReaAd2LBkk3XXkg74zfts7GAGdNtWgXQEAwYQJdVChJFU3LRNh
/4BRQ9Dq1JnXMtezPB4UrfD4KgY=
-----END PGP PRIVATE KEY BLOCK-----`;
// readPrivateKey should read the first private key encountered in a key block
const key = await openpgp.readPrivateKey({ armoredKey: privateAndPublicKeyBlock });
const privateKeys = await openpgp.readPrivateKeys({ armoredKeys: privateAndPublicKeyBlock });
expect(privateKeys.length).to.equal(1);
expect(key.isPrivate()).to.be.true;
expect(privateKeys[0].isPrivate()).to.be.true;
expect(key.isDecrypted()).to.be.true;
expect(privateKeys[0].isDecrypted()).to.be.true;
expect(key.getKeyID().equals(privateKeys[0].getKeyID())).to.be.true;
});
it('readKey and readKeys should have consistent results', async function() {
const publicAndPrivateKeyBlock = `-----BEGIN PGP PRIVATE KEY BLOCK-----
xjMEZkI3VBYJKwYBBAHaRw8BAQdA7nW1t5qRdtIYzEVEEhRSDgTgbk2JHofY
Ph8FuGsDqgzNDzx0ZXN0QHRlc3QuY29tPsLAEwQQFgoAhQWCZkI3VAMLCQcJ
kO7KgSlD8oQfRRQAAAAAABwAIHNhbHRAbm90YXRpb25zLm9wZW5wZ3Bqcy5v
cmfMonyZc4LCcSnqVUn/n6tHNHhRzCUvwSOE5L6tXRPykAUVCAoMDgQWAAIB
AhkBApsDAh4BFiEEQKzcP2wwWfqg7ECq7sqBKUPyhB8AAA9vAP42PIQe3UJE
j7ReaAd2LBkk3XXkg74zfts7GAGdNtWgXQEAwYQJdVChJFU3LRNh/4BRQ9Dq
1JnXMtezPB4UrfD4KgbFWARmQjdUFgkrBgEEAdpHDwEBB0DudbW3mpF20hjM
RUQSFFIOBOBuTYkeh9g+HwW4awOqDAABANhWlm3XDpr6/GBvgzfnVoKkDawJ
/HyRsWDbeHwiu2bXEVjNDzx0ZXN0QHRlc3QuY29tPsLAEwQQFgoAhQWCZkI3
VAMLCQcJkO7KgSlD8oQfRRQAAAAAABwAIHNhbHRAbm90YXRpb25zLm9wZW5w
Z3Bqcy5vcmfMonyZc4LCcSnqVUn/n6tHNHhRzCUvwSOE5L6tXRPykAUVCAoM
DgQWAAIBAhkBApsDAh4BFiEEQKzcP2wwWfqg7ECq7sqBKUPyhB8AAA9vAP42
PIQe3UJEj7ReaAd2LBkk3XXkg74zfts7GAGdNtWgXQEAwYQJdVChJFU3LRNh
/4BRQ9Dq1JnXMtezPB4UrfD4KgY=
-----END PGP PRIVATE KEY BLOCK-----`;
// readKey should read the first key encountered in a key block
const key = await openpgp.readKey({ armoredKey: publicAndPrivateKeyBlock });
const keys = await openpgp.readKeys({ armoredKeys: publicAndPrivateKeyBlock });
expect(keys.length).to.equal(2);
expect(key.isPrivate()).to.be.false;
expect(keys[0].isPrivate()).to.be.false;
expect(keys[1].isPrivate()).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 (missing @)', 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 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 still support curve="curve25519" for ECC key type (v4 key)', function() {
const opt = {
userIDs: { name: 'Test User', email: 'text@example.com' },
type: 'ecc',
curve: 'curve25519',
format: 'object'
};
return openpgp.generateKey(opt).then(async function({ privateKey: key }) {
expect(key).to.exist;
expect(key.getAlgorithmInfo().rsaBits).to.equal(undefined);
expect(key.getAlgorithmInfo().algorithm).to.equal('eddsaLegacy');
expect(key.getAlgorithmInfo().curve).to.equal('ed25519Legacy');
expect(key.subkeys[0].getAlgorithmInfo().rsaBits).to.equal(undefined);
expect(key.subkeys[0].getAlgorithmInfo().algorithm).to.equal('ecdh');
expect(key.subkeys[0].getAlgorithmInfo().curve).to.equal('curve25519Legacy');
});
});
it('should have default params set (v4 key)', 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.keyPacket.version).to.equal(4);
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('ed25519Legacy');
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('curve25519Legacy');
expect(+key.subkeys[0].getCreationTime()).to.equal(+now);
expect(await key.subkeys[0].getExpirationTime()).to.equal(Infinity);
}
});
});
it('should have default params set (v6 key)', function() {
const now = util.normalizeDate(new Date());
const opt = {
userIDs: { name: 'Test User', email: 'text@example.com' },
passphrase: 'secret',
date: now,
format: 'object',
config: { v6Keys: true }
};
return openpgp.generateKey(opt).then(async function({ privateKey, publicKey }) {
for (const key of [publicKey, privateKey]) {
expect(key).to.exist;
expect(key.keyPacket.version).to.equal(6);
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().algorithm).to.equal('ed25519');
expect(key.getAlgorithmInfo().rsaBits).to.equal(undefined);
expect(key.getAlgorithmInfo().curve).to.equal(undefined);
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().algorithm).to.equal('x25519');
expect(key.subkeys[0].getAlgorithmInfo().rsaBits).to.equal(undefined);
expect(key.subkeys[0].getAlgorithmInfo().curve).to.equal(undefined);
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);
});
});
it('should fail for encrypted key with unknown s2k (unparseableKeyMaterial)', async function() {
// key encrypted with invalid s2kType = 23, to test that it can still be used for encryption/verification
const encryptedKeyUnknownS2K = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----
xYYEZJ2H3RYJKwYBBAHaRw8BAQdA3V39Xv0+436Rpn/2UlcnOC1BGprmAlWY
RBKjAq0hAtD+CRcIdHzwqoLa54cAbBOEIgBh7Xa1Qh5wCGAmEVWnAldaqvk+
NcvUL2bR6AQsGIT6YEihOS3xLKobMOd2XlO5ItQoWnONzkWgzjFvctgnlhmq
I80AwowEEBYKAD4FgmSdh90ECwkHCAmQaBT7gxSTsXwDFQgKBBYAAgECGQEC
mwMCHgEWIQSvRnJTQT6TtdZFk0NoFPuDFJOxfAAAT7kBALmmUEJt5HMAOWiW
7/8y4wllm8zNQ9vbl5Q0cWbeWj/8AP9HDa2rRxHY/37g5zXdmL9f/qNWr9Fk
EBRhLLwusumuDMeLBGSdh90SCisGAQQBl1UBBQEBB0Am2yjjialeIVXHJJ2P
b7KiapCC0mD95F0EFz6zz0l4DgMBCAf+CRcISMdt0OUFCNUABB/OD0UW7MPK
Y3t8RrUTYoiCuhuPRDLOJ5NnMNagVQLt3jQsI8JRjzmYbiTrA/V3iJIEDu5C
NWbnvCM7Hs7+OqPzJPJ2w8J4BBgWCAAqBYJknYfdCZBoFPuDFJOxfAKbDBYh
BK9GclNBPpO11kWTQ2gU+4MUk7F8AADwfwD8CsOVw/3zm1UwUbGUi+fuf6Pr
VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu
0pYO
=rWL8
-----END PGP PRIVATE KEY BLOCK-----` });
await expect(openpgp.decryptKey({
privateKey: encryptedKeyUnknownS2K,
passphrase: 'test'
})).to.be.rejectedWith(/Key packet cannot be decrypted: unsupported S2K or cipher algo/);
});
});
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 throw on empty passphrase', async function() {
const { privateKey } = await openpgp.generateKey({ userIDs: [{ name: 'test', email: 'test@test.com' }], format: 'object' });
await expect(openpgp.encryptKey({
privateKey,
passphrase: ''
})).to.be.rejectedWith(/passphrase is required for key encryption/);
});
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 support encrypting with argon2 s2k', async function() {
const key = await openpgp.readKey({ armoredKey: gnuDummyKeySigningSubkey });
const locked = await openpgp.encryptKey({
privateKey: key,
passphrase: passphrase,
config: { s2kType: openpgp.enums.s2k.argon2 }
});
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;
});
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('Signing with key which uses sha3 should generate a valid sha3 signature', async function() {
const privKey = await openpgp.readKey({ armoredKey: priv_key_sha3 });
const pubKey = privKey.toPublic();
const text = 'Hello, world.';
const message = await openpgp.createCleartextMessage({ text });
const cleartextMessage = await openpgp.sign({ message, signingKeys: privKey, format: 'armored' });
const parsedArmored = await openpgp.readCleartextMessage({ cleartextMessage });
expect(parsedArmored.signature.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);
expect(
parsedArmored.signature.packets.filterByTag(openpgp.enums.packet.signature)[0].hashAlgorithm
).to.equal(openpgp.enums.hash.sha3_512);
const verified = await openpgp.verify({ message: parsedArmored, verificationKeys: pubKey, expectSigned: true });
expect(verified.data).to.equal(text);
});
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('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);
});
it('should support encrypting with encrypted key with unknown s2k (unparseableKeyMaterial)', async function() {
const originalDecryptedKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----
xVgEZJ2H3RYJKwYBBAHaRw8BAQdA3V39Xv0+436Rpn/2UlcnOC1BGprmAlWY
RBKjAq0hAtAAAQCykslk/kEP7+O9sOsuvgX2Xfz4peQuNo2vD/w4dMZpchKj
zQDCjAQQFgoAPgWCZJ2H3QQLCQcICZBoFPuDFJOxfAMVCAoEFgACAQIZAQKb
AwIeARYhBK9GclNBPpO11kWTQ2gU+4MUk7F8AABPuQEAuaZQQm3kcwA5aJbv
/zLjCWWbzM1D29uXlDRxZt5aP/wA/0cNratHEdj/fuDnNd2Yv1/+o1av0WQQ
FGEsvC6y6a4Mx10EZJ2H3RIKKwYBBAGXVQEFAQEHQCbbKOOJqV4hVccknY9v
sqJqkILSYP3kXQQXPrPPSXgOAwEIBwAA/34s+u8hyLdzdLxjrEEN9zNb+C8d
EyBNxMpyZ/NJsUxoEIPCeAQYFggAKgWCZJ2H3QmQaBT7gxSTsXwCmwwWIQSv
RnJTQT6TtdZFk0NoFPuDFJOxfAAA8H8A/ArDlcP985tVMFGxlIvn7n+j61RQ
SxvLnPSImimp5/w2AQCXGTWR0Un6Q6FdqVAORABItjHY8ZEcrBUJ2D0QLtKW
Dg==
=wiwz
-----END PGP PRIVATE KEY BLOCK-----` });
// `originalDecryptedKey` encrypted with invalid s2kType = 23, to test that it can still be used for encryption/verification
const encryptedKeyUnknownS2K = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----
xYYEZJ2H3RYJKwYBBAHaRw8BAQdA3V39Xv0+436Rpn/2UlcnOC1BGprmAlWY
RBKjAq0hAtD+CRcIdHzwqoLa54cAbBOEIgBh7Xa1Qh5wCGAmEVWnAldaqvk+
NcvUL2bR6AQsGIT6YEihOS3xLKobMOd2XlO5ItQoWnONzkWgzjFvctgnlhmq
I80AwowEEBYKAD4FgmSdh90ECwkHCAmQaBT7gxSTsXwDFQgKBBYAAgECGQEC
mwMCHgEWIQSvRnJTQT6TtdZFk0NoFPuDFJOxfAAAT7kBALmmUEJt5HMAOWiW
7/8y4wllm8zNQ9vbl5Q0cWbeWj/8AP9HDa2rRxHY/37g5zXdmL9f/qNWr9Fk
EBRhLLwusumuDMeLBGSdh90SCisGAQQBl1UBBQEBB0Am2yjjialeIVXHJJ2P
b7KiapCC0mD95F0EFz6zz0l4DgMBCAf+CRcISMdt0OUFCNUABB/OD0UW7MPK
Y3t8RrUTYoiCuhuPRDLOJ5NnMNagVQLt3jQsI8JRjzmYbiTrA/V3iJIEDu5C
NWbnvCM7Hs7+OqPzJPJ2w8J4BBgWCAAqBYJknYfdCZBoFPuDFJOxfAKbDBYh
BK9GclNBPpO11kWTQ2gU+4MUk7F8AADwfwD8CsOVw/3zm1UwUbGUi+fuf6Pr
VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu
0pYO
=rWL8
-----END PGP PRIVATE KEY BLOCK-----` });
const encrypted = await openpgp.encrypt({
message: await openpgp.createMessage({ text: 'test' }),
encryptionKeys: encryptedKeyUnknownS2K
});
// decrypt with original key
const decrypted = await openpgp.decrypt({
message: await openpgp.readMessage({ armoredMessage: encrypted }),
decryptionKeys: originalDecryptedKey
});
expect(decrypted.data).to.equal('test');
});
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('uses AEAD when the encryption key prefs support it (SEIPDv2', async function() {
const v4PrivateKeyWithOCBPref = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----
xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB
exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ
BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh
RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe
7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/
LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG
GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE
M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr
k0mXubZvyl4GBg==
-----END PGP PRIVATE KEY BLOCK-----` });
const v6PrivateKeyWithOCBPref = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----
xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB
exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ
BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh
RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe
7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/
LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG
GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE
M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr
k0mXubZvyl4GBg==
-----END PGP PRIVATE KEY BLOCK-----` });
const encrypted = await openpgp.encrypt({
message: await openpgp.createMessage({ text: 'test' }),
encryptionKeys: [v4PrivateKeyWithOCBPref, v6PrivateKeyWithOCBPref],
format: 'object'
});
const seipd = encrypted.packets[2];
expect(seipd).to.be.instanceOf(openpgp.SymEncryptedIntegrityProtectedDataPacket);
expect(seipd.version).to.equal(2);
expect(seipd.aeadAlgorithm).to.equal(openpgp.enums.aead.ocb);
});
it('should support encrypting to a key without features (missing SEIPDv1 feature)', async function () {
const key = await openpgp.readKey({ armoredKey: rsaKeyWithNoFeaturesFromRNP });
const encrypted = await openpgp.encrypt({
message: await openpgp.createMessage({ text: 'test' }),
encryptionKeys: key
});
const decrypted = await openpgp.decrypt({
message: await openpgp.readMessage({ armoredMessage: encrypted }),
decryptionKeys: key
});
expect(decrypted.data).to.equal('test');
});
});
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);
});
it('passing no encryption keys or passwords leads to exception', async function() {
await expect(openpgp.encryptSessionKey({
algorithm: 'aes256',
data: util.hexToUint8Array('3e99c1bb485e70a1fcef09a7ad8d38d171015243bbdd853e1a2b0e334d122ff3')
})).to.be.rejectedWith(/No encryption keys or passwords provided/);
});
// keep this after the 'memory-heavy' test to confirm that the Wasm module was successfully reloaded
it('supports encrypting with argon2 s2k', async function() {
const config = { s2kType: openpgp.enums.s2k.argon2 };
const passwords = 'password';
const sessionKey = {
algorithm: 'aes128',
data: util.hexToUint8Array('01FE16BBACFD1E7B78EF3B865187374F')
};
const encrypted = await openpgp.encryptSessionKey({ ...sessionKey, passwords, config, format: 'object' });
expect(encrypted.packets).to.have.length(1);
const skesk = encrypted.packets[0];
expect(skesk.s2k.type).to.equal('argon2');
const [decryptedSessionKey] = await openpgp.decryptSessionKeys({ message: encrypted, passwords });
expect(decryptedSessionKey).to.deep.equal(sessionKey);
});
});
describe('decryptSessionKeys - unit tests', function() {
it('should decrypt message with two SKESKs where the wrong password returns a symmetric algo equal to 0', async function () {
// SKESK packets do not have an intrisic integrity check, and the session key must be used to check its validity.
// This message is such that when the password is used to try and decrypt the first (mismatching) SKESK, the result returns a '0' byte for
// the session key algo. This corresponds to the 'plaintext' algo identifier (https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#section-12.2.1),
// now removed from OpenPGP.js .
// This test guards against regressions caused by mishandling this value on SKESK decryption.
const message = await openpgp.readMessage({ armoredMessage:`-----BEGIN PGP MESSAGE-----
wy4ECQMIf6HA/a6XkOAAlaU1z+uVfU5kmHmqNqahQH859nAMresQ6w1uIjsL
ZE5bwy4ECQMIKVfCf+GWBesA5KFPuIDa6kLLn/AvEgbCi5DOg2xdIf73SNSN
Tqy7nfex0sANAVtHHRkHVTRVTVa3MFjjiWeBEDtnfyVMntWJ21ihrIU9eb9p
qS7UljZZ0u++xSWclU2IGBXCIdO0wLuS6hYk3q5OFexWj8OIoYJX88nkA2iW
5xyGd9EFRWVsR4CREt8lwrIE2t/h8XpRlhJmY6Iefg8+2DeN8vDdhNs/B02o
0zAE0hF+3xvwZTLi4hDrhBZEgBedPaeX4jlmDc3qzh2wlgV/Mq/FUakYfSLl
bc1PCpfqkLZSuCfv4eoTrWohWi9lS/pRXY9hzzHtlnjo6w==
-----END PGP MESSAGE-----` });
const sessionKeys = await openpgp.decryptSessionKeys({ message, passwords: 'I am another password' });
expect(sessionKeys).to.have.length(1); // first SKESK dropped due to '0' being treated as invalid algo identifier
expect(sessionKeys[0].algorithm).to.equal('aes256');
// decrypt() used to fail on this type of input
const decrypted = await openpgp.decrypt({ message, passwords: 'I am another password', format: 'binary' });
expect(decrypted.data).to.deep.equal(
util.hexToUint8Array('e280872009d699e0b5bae18ba1e28c86d280d184e1b888e1a8b3e28ab7e0afa8e0b98be283bde28db5e1b1afd48de1b5b2e280a7e199b0e1a487e185afd3a1e18ca5e0bfb4e28892e19e98e29bb8e29594e0baaae0b681e1929af09f9882f09f9897f09f9882f09f98abf09f988ff09f98b6f09f98bcf09f98bcf09f9981f09f9982e2808720090d0aed959ceab5adec96b42feca1b0ec84a0eba790')
);
expect(decrypted.signatures.length).to.equal(0);
});
it('should decrypt PKESK v6 and return a null symmetric algorithm', async function() {
// test vector https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#appendix-A.8
const armoredMessage = `-----BEGIN PGP MESSAGE-----
wV0GIQYSyD8ecG9jCP4VGkF3Q6HwM3kOk+mXhIjR2zeNqZMIhRmHzxjV8bU/gXzO
WgBM85PMiVi93AZfJfhK9QmxfdNnZBjeo1VDeVZheQHgaVf7yopqR6W1FT6NOrfS
aQIHAgZhZBZTW+CwcW1g4FKlbExAf56zaw76/prQoN+bAzxpohup69LA7JW/Vp0l
yZnuSj3hcFj0DfqLTGgr4/u717J+sPWbtQBfgMfG9AOIwwrUBqsFE9zW+f1zdlYo
bhF30A+IitsxxA==
-----END PGP MESSAGE-----`;
const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----
xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB
exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ
BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh
RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe
7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/
LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG
GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE
M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr
k0mXubZvyl4GBg==
-----END PGP PRIVATE KEY BLOCK-----` });
const sessionKeys = await openpgp.decryptSessionKeys({
message: await openpgp.readMessage({ armoredMessage }),
decryptionKeys: privateKey
});
expect(sessionKeys).to.have.length(1);
expect(sessionKeys[0].algorithm).to.equal(null); // PKESK v6 does not include the algo info
});
it('supports decrypting with argon2 s2k (memory-heavy params)', async function() {
const passwords = 'password';
// Test vector from https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#appendix-A.8.1
const armoredMessage = `-----BEGIN PGP MESSAGE-----
Comment: Encrypted using AES with 128-bit key
Comment: Session key: 01FE16BBACFD1E7B78EF3B865187374F
wycEBwScUvg8J/leUNU1RA7N/zE2AQQVnlL8rSLPP5VlQsunlO+ECxHSPgGYGKY+
YJz4u6F+DDlDBOr5NRQXt/KJIf4m4mOlKyC/uqLbpnLJZMnTq3o79GxBTdIdOzhH
XfA3pqV4mTzF
-----END PGP MESSAGE-----`;
const expectedSessionKey = util.hexToUint8Array('01FE16BBACFD1E7B78EF3B865187374F');
try {
const [decryptedSessionKey] = await openpgp.decryptSessionKeys({
message: await openpgp.readMessage({ armoredMessage }),
passwords
});
expect(decryptedSessionKey.data).to.deep.equal(expectedSessionKey);
expect(decryptedSessionKey.algorithm).to.equal('aes128');
} catch (err) {
if (detectBrowser()) { // Expected to fail in the CI, especially in Browserstack
expect(err.message).to.match(/Could not allocate required memory/);
}
}
});
});
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 v6KeysVal;
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;
v6KeysVal = openpgp.config.v6Keys;
minRSABitsVal = openpgp.config.minRSABits;
openpgp.config.minRSABits = 512;
});
afterEach(function() {
openpgp.config.aeadProtect = aeadProtectVal;
openpgp.config.preferredAEADAlgorithm = preferredAEADAlgorithmVal;
openpgp.config.aeadChunkSizeByte = aeadChunkSizeByteVal;
openpgp.config.v6Keys = v6KeysVal;
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 (V6 keys)', tests, {
if: true,
beforeEach: function() {
openpgp.config.aeadProtect = true;
openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.gcm;
openpgp.config.v6Keys = true;
// Monkey-patch SEIPD V2 feature flag
publicKey.users[0].selfCertifications[0].features = [9];
publicKey_2000_2008.users[0].selfCertifications[0].features = [9];
publicKey_2038_2045.users[0].selfCertifications[0].features = [9];
}
});
tryTests('EAX mode (small chunk size)', tests, {
if: true,
beforeEach: function() {
openpgp.config.aeadProtect = true;
openpgp.config.aeadChunkSizeByte = 0;
openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.eax;
// Monkey-patch SEIPD V2 feature flag
publicKey.users[0].selfCertifications[0].features = [9];
publicKey_2000_2008.users[0].selfCertifications[0].features = [9];
publicKey_2038_2045.users[0].selfCertifications[0].features = [9];
}
});
tryTests('OCB mode', tests, {
if: true,
beforeEach: function() {
openpgp.config.aeadProtect = true;
openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.ocb;
// Monkey-patch SEIPD V2 feature flag
publicKey.users[0].selfCertifications[0].features = [9];
publicKey_2000_2008.users[0].selfCertifications[0].features = [9];
publicKey_2038_2045.users[0].selfCertifications[0].features = [9];
}
});
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 (anonymous recipient)', async function () {
const { privateKey: privateKeyV4orV6 } = await openpgp.generateKey({ userIDs: { email: 'test@test.it' }, format: 'object' });
const plaintext = 'hello world';
const encryptedMessage = await openpgp.encrypt({
message: await openpgp.createMessage({ text: plaintext }),
encryptionKeys: privateKeyV4orV6,
wildcard: true,
format: 'object'
});
expect(encryptedMessage.getEncryptionKeyIDs().every(keyID => keyID.isWildcard())).to.be.true;
const armoredMessage = encryptedMessage.armor();
const parsedEncryptedMessage = await openpgp.readMessage({ armoredMessage });
expect(parsedEncryptedMessage.getEncryptionKeyIDs().every(keyID => keyID.isWildcard())).to.be.true;
const decrypted = await openpgp.decrypt({ message: parsedEncryptedMessage, decryptionKeys: privateKeyV4orV6 });
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 (anonymous recipient)', 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: 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.symEncryptedIntegrityProtectedData).version === 2).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: 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.symEncryptedIntegrityProtectedData).version === 2).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 });
const supportsSEIPDv2 = !!(publicKey.users[0].selfCertifications[0].features?.[0] & openpgp.enums.features.seipdv2);
expect(decOpt.message.packets.findPacket(openpgp.enums.packet.symEncryptedIntegrityProtectedData).version).to.equal(supportsSEIPDv2 ? 2 : 1);
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 });
const supportsSEIPDv2 = !!(publicKey.users[0].selfCertifications[0].features?.[0] & openpgp.enums.features.seipdv2);
expect(decOpt.message.packets.findPacket(openpgp.enums.packet.symEncryptedIntegrityProtectedData).version).to.equal(supportsSEIPDv2 ? 2 : 1);
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.symEncryptedIntegrityProtectedData).version).to.equal(1);
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 supportsSEIPDv2 = openpgp.config.aeadProtect;
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.symEncryptedIntegrityProtectedData).version).to.equal(supportsSEIPDv2 ? 2 : 1);
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 supportsSEIPDv2 = openpgp.config.aeadProtect;
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.symEncryptedIntegrityProtectedData).version).to.equal(supportsSEIPDv2 ? 2 : 1);
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() {
await loadStreamsPolyfill();
// need to generate new key with AEAD support
const { privateKey } = await openpgp.generateKey({ userIDs: [{ email: 'test@email.com' }], type: 'rsa', format: 'object' });
const { aeadAlgo } = await getPreferredCipherSuite([privateKey], undefined, undefined, openpgp.config);
// sanity check
expect(aeadAlgo).to.equal(openpgp.config.aeadProtect ? openpgp.config.preferredAEADAlgorithm : undefined);
const encrypted = await openpgp.encrypt({
message: await openpgp.createMessage({ binary: new Uint8Array(500) }),
encryptionKeys: privateKey
});
// corrupt the SEIPD packet
const encryptedCorrupted = encrypted.substr(0, 1000) + (encrypted[1000] === 'a' ? 'b' : 'a') + encrypted.substr(1001);
const generateSingleChunkStream = () => (
new ReadableStream({
start(controller) {
controller.enqueue(encryptedCorrupted);
controller.close();
}
})
);
const generateMultiChunkStream = () => (
new ReadableStream({
start() {
this.remaining = encryptedCorrupted.split('\n');
},
async pull(controller) {
if (this.remaining.length) {
// sleep to slow down enqeueing
await new Promise(resolve => { setTimeout(resolve); });
controller.enqueue(this.remaining.shift() + '\n');
} else {
controller.close();
}
}
})
);
if (openpgp.config.aeadProtect) {
const expectedError = /Authentication tag mismatch|Unsupported state or unable to authenticate data/;
// AEAD fails either on AEAD chunk decryption or when reading the decrypted stream:
// if the corruption is in the first AEAD chunk, then `openpgp.decrypt` will throw
// when reading the decrypted stream to parse the packet list.
await Promise.all([
testStreamingDecryption(encryptedCorrupted, true, expectedError, true),
testStreamingDecryption(encryptedCorrupted, false, expectedError, true),
// `config.allowUnauthenticatedStream` does not apply to AEAD
testStreamingDecryption(generateSingleChunkStream(), true, expectedError, openpgp.config.aeadChunkSizeByte > 0),
testStreamingDecryption(generateSingleChunkStream(), false, expectedError, openpgp.config.aeadChunkSizeByte > 0),
// Increasing number of streaming chunks should not affect the result
testStreamingDecryption(generateMultiChunkStream(), true, expectedError, openpgp.config.aeadChunkSizeByte > 0),
testStreamingDecryption(generateMultiChunkStream(), false, expectedError, openpgp.config.aeadChunkSizeByte > 0)
]);
} else {
const expectedError = /Modification detected/;
await Promise.all([
testStreamingDecryption(encryptedCorrupted, true, expectedError, true),
testStreamingDecryption(encryptedCorrupted, false, expectedError, true),
testStreamingDecryption(generateSingleChunkStream(), true, expectedError, false),
testStreamingDecryption(generateSingleChunkStream(), false, expectedError, true),
// Increasing number of streaming chunks should not affect the result
testStreamingDecryption(generateMultiChunkStream(), true, expectedError, false),
testStreamingDecryption(generateMultiChunkStream(), false, expectedError, true)
]);
}
async function testStreamingDecryption(encryptedDataOrStream, allowUnauthenticatedStream, expectedErrorMessage, expectedFailureOnDecrypt = null) {
// parsing the message won't fail since armor checksum is ignored
const message = await openpgp.readMessage({ armoredMessage: encryptedDataOrStream });
let didFailOnDecrypt = true;
try {
const { data: decrypted } = await openpgp.decrypt({
message,
decryptionKeys: [privateKey],
config: { allowUnauthenticatedStream }
});
didFailOnDecrypt = false;
await stream.readToEnd(decrypted);
// expected to have thrown
throw new Error(`Expected decryption to fail with error ${expectedErrorMessage}`);
} catch (e) {
expect(e.message).to.match(expectedErrorMessage);
expect(didFailOnDecrypt).to.equal(expectedFailureOnDecrypt);
}
}
});
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',
'',
'lQH+BFLqLegBBAC/rN3g30Jrcpx5lTb7Kxe+ZfS7ppOIoBjjN+qcOh81cJJVS5dT',
'UGcDsm2tCLVS3P2dGaYhfU9fsoSq/wK/tXsdoWXvXdjHbbueyi1kTZqlnyT190UE',
'vmDxH0yqquvUaf7+CNXC0T6l9gGS9p0x7xNydWRb7zeK1wIsYI+dRGQmzQARAQAB',
'/gMDArgQHMknurQXy0Pho3Nsdu6zCUNXuplvaSXruefKsQn6eexGPnecNTT2iy5N',
'70EK371D7GcNhhLsn8roUcj1Hi3kR14wXW7lcQBy9RRbbglIJXIqKJ8ywBEO8BaQ',
'b0plL+w5A9EvX0BQc4d53MTqySh6POsEDOxPzH4D/JWbaozfmc4LfGDqH1gl7ebY',
'iu81vnBuuskjpz8rxRI81MldJEIObrTE2x46DF7AmS6L6u/Qz3AAmZd89p5INCdx',
'DemxzuMKpC3wSgdgSSKHHTKiNOMxiRd5mFH5v1KVcEG/TyXFlmah7RwA4rA4fjeo',
'OpnbVWp6ciUniRvgLaCMMbmolAoho9zaLbPzCQVQ8F7gkrjnnPm4MKA+AUXmjt7t',
'VrrYkyTp1pxLZyUWX9+aKoxEO9OIDz7p9Mh02BZ/tznQ7U+IV2bcNhwrL6LPk4Mb',
'J4YF/cLVxFVVma88GSFikSjPf30AUty5nBQFtbFGqnPctCF0aHJvd2F3YXkgPHRo',
'cm93YXdheUBleGFtcGxlLmNvbT6IuAQTAQIAIgUCUuot6AIbAwYLCQgHAwIGFQgC',
'CQoLBBYCAwECHgECF4AACgkQkk2hoj5duD/HZQP/ZXJ8PSlA1oj1NW97ccT0LiNH',
'WzxPPoH9a/qGQYg61jp+aTa0C5hlYY/GgeFpiZlpwVUtlkZYfslXJqbCcp3os4xt',
'kiukDbPnq2Y41wNVxXrDw6KbOjohbhzeRUh8txbkiXGiwHtHBSJsPMntN6cB3vn3',
'08eE69vOiHPQfowa2CmdAf4EUuot6AEEAOQpNjkcTUo14JQ2o+mrpxj5yXbGtZKh',
'D8Ll+aZZrIDIa44p9KlQ3aFzPxdmFBiBX57m1nQukr58FQ5Y/FuQ1dKYc3M8QdZL',
'vCKDC8D9ZJf13iwUjYkfn/e/bDqCS2piyd63zI0xDJo+s2bXCIJxgrhbOqFDeFd6',
'4W8PfBOvUuRjABEBAAH+AwMCuBAcySe6tBfLV0P5MbBesR3Ifu/ppjzLoXKhwkqm',
'PXf09taLcRfUHeMbPjboj2P2m2UOnSrbXK9qsDQ8XOMtdsEWGLWpmiqnMlkiOchv',
'MsNRYpZ67iX3JVdxNuhs5+g5bdP1PNVbKiTzx73u1h0SS93IJp1jFj50/kyGl1Eq',
'tkr0TWe5uXCh6cSZDPwhto0a12GeDHehdTw6Yq4KoZHccneHhN9ySFy0DZOeULIi',
'Y61qtR0io52T7w69fBe9Q5/d5SwpwWKMpCTOqvvzdHX7JmeFtV+2vRVilIif7AfP',
'AD+OjQ/OhMu3jYO+XNhm3raPT2tIBsBdl2UiHOnj4AUNuLuUJeVghtz4Qt6dvjyz',
'PlBvSF+ESqALjM8IqnG15FX4LmEDFrFcfNCsnmeyZ2nr1h2mV5jOON0EmBtCyhCt',
'D/Ivi4/SZk+tBVhsBI+7ZECZYDJzZQnyPDsUv31MU4OwdWi7FhzHvDj/0bhYY7+I',
'nwQYAQIACQUCUuot6AIbDAAKCRCSTaGiPl24PwYAA/sGIHvCKWP5+4ZlBHuOdbP9',
'9v3PXFCm61qFEL0DTSq7NgBcuf0ASRElRI3wIKlfkwaiSzVPfNLiMTexdc7XaiTz',
'CHaOn1Xl2gmYTq2KiJkgtLuwptYU1iSj7vvSHKy0+nYIckOZB4pRCOjknT08O4ZJ',
'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;
await loadStreamsPolyfill();
const data = new globalThis.ReadableStream({
pull(controller) {
if (i++ < 4) {
const randomBytes = 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('web');
const message = await openpgp.readMessage({ armoredMessage: encrypted });
const decrypted = await openpgp.decrypt({
passwords: ['test'],
message,
format: 'binary'
});
expect(stream.isStream(decrypted.data)).to.equal('web');
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 = new globalThis.ReadableStream({
start(controller) {
controller.enqueue(data);
controller.close();
}
});
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('web');
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.toStream(packets.write())
});
return openpgp.verify(verifyOpt);
}).then(async function (verified) {
expect(stream.isStream(verified.data)).to.equal('web');
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(/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"');
});
});
it('should fail to decrypt a message containing a literal packet (and no session key)', async function() {
const message = await openpgp.createMessage({ text: 'plaintext' });
await expect(openpgp.decrypt({ message, passwords: 'password' })).to.be.rejectedWith(/Error decrypting message/);
});
it('should fail to decrypt a message containing a literal packet (and a session key)', async function() {
const skeskPlusLiteralData = `-----BEGIN PGP MESSAGE-----
wy4ECQMIjvrInhvTxJwAbkqXp+KWFdBcjoPn03jCdyspVi9qXBDbyGaP1lrM
habAyxd1AGKaNp1wbGFpbnRleHQgbWVzc2FnZQ==
=XoUx
-----END PGP MESSAGE-----
`;
const message = await openpgp.readMessage({ armoredMessage: skeskPlusLiteralData });
await expect(openpgp.decrypt({ message, passwords: 'password' })).to.be.rejectedWith(/No encrypted data found/);
});
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('X25519/Ed25519 (new format)', async function () {
it('should enforce using AES session keys with x25519 keys (v4 key)', async function () {
// x25519 key (v4) with cast5 as preferred cipher
const privateKeyCast5 = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----
xUkEZK8BixuMghYwdEgHl+3ASI4VZkn048KG4DVuugT1bMe4QTtFtQCoKBOG
JxrZh8E+7I5nK7McXP2U9gyC0+RFcD46AxSmRA46zQDCiAQQGwgAPgWCZK8B
iwQLAwcICZCaWrTxMIPhVwMVCAoEFgACAQIZAQKbAwIeARYhBDFBS8Xnfotk
Oun5WZpatPEwg+FXAABwwuNWCdr1WahiGrLupYaOYQO4S9y+FYTxqEV/gsOP
TKwmNIcIJPROV2LgyxvzQo79//0CocEYojEeUhGn7BH5lwvHSQRkrwGLGbVM
1JxFUJeQ253sHMko73uPkyyb9DvaeyWHPwgF2k9GACA9caoO8GsZI7KMnVGP
c4EpytBwVIsr4ck3QaEV/UxvDpnCdAQYGwgAKgWCZK8BiwmQmlq08TCD4VcC
mwwWIQQxQUvF536LZDrp+VmaWrTxMIPhVwAAXycLtMyiv0lon4qU5/rKWjrq
MIxMchUbHvktvUqomU0pDDLMPqLFtzBbtHqODPVbLTOygJRVLeHyWTOEfmOD
kl0L
=SYJZ
-----END PGP PRIVATE KEY BLOCK-----` });
await expect(openpgp.generateSessionKey({
encryptionKeys: privateKeyCast5,
config: { preferredSymmetricAlgorithm: openpgp.enums.symmetric.cast5 }
})).to.be.rejectedWith(/Could not generate a session key compatible with the given `encryptionKeys`/);
await expect(openpgp.encrypt({
message: await openpgp.createMessage({ text: plaintext }),
encryptionKeys: privateKeyCast5,
sessionKey: { data: new Uint8Array(16).fill(1), algorithm: 'cast5' }
})).to.be.rejectedWith(/X25519 and X448 keys can only encrypt AES session keys/);
await expect(openpgp.decryptSessionKeys({
message: await openpgp.readMessage({ armoredMessage: `-----BEGIN PGP MESSAGE-----
wUQD66NYAXF0vfYZNWpc7s9eihtgj7EhHBeLOq2Ktw79artbhN5JMs+9aCIZ
A7sB7uYCTVCLIMfPFwVZH+c29gpCzPxSXQ==
=Dr02
-----END PGP MESSAGE-----` }),
decryptionKeys: privateKeyCast5
})).to.be.rejectedWith(/AES session key expected/);
});
it('supports decrypting new x25519 format (v4 key)', async function () {
// v4 key
const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----
xUkEZIbSkxsHknQrXGfb+kM2iOsOvin8yE05ff5hF8KE6k+saspAZQCy/kfFUYc2
GkpOHc42BI+MsysKzk4ofjBAfqM+bb7goQ3hzRV1c2VyIDx1c2VyQHRlc3QudGVz
dD7ChwQTGwgAPQUCZIbSkwmQQezK2iB2tIkWIQRqZza9wQZcwxpjGYNB7MraIHa0
iQIbAwIeAQIZAQILBwIVCAIWAAMnBwIAAFOeZ7jrKZsCzRfu1ffFa77074st0zRo
BTJXoXBQ1ZzLjsh+ZO6fB2odnYJtQYstv45H/3JyLVogcMnFeYmHeSP3AMdJBGSG
0pMZfpd7TiOQv7uKSK+k4HT9lKr5+dmvb7vox/8ids6unEkAF1v8fCKogIrtBWVT
nVbwnovjM3LLexpXFZSgTKRcNMgPRMJ0BBgbCAAqBQJkhtKTCZBB7MraIHa0iRYh
BGpnNr3BBlzDGmMZg0HsytogdrSJAhsMAADCYs2I9wBakIu9Hhxs4R3Jq9F8J7AH
yxsNL0GomZ+hxiE0MOZwRr10DxfVaRabF1fcf9PHSHX2SwEFXUKMIHgbMQs=
=bJqd
-----END PGP PRIVATE KEY BLOCK-----` });
const messageToDecrypt = `-----BEGIN PGP MESSAGE-----
wUQDYc6clYlCdtoZ3rAsvBDIwvoLmvM0zwViG8Ec0PgFfN5R6C4BqEZD53UZB1WM
J68hXSj1Sa235XAUYE1pZerTKhglvdI9Aeve8+L0w5RDMjmBBA50Yv/YT8liqhNi
mNwbfFbSNhZYWjFada77EKBn60j8QT/xCQzLR1clci7ieW2knw==
=NKye
-----END PGP MESSAGE-----`;
const { data } = await openpgp.decrypt({
message: await openpgp.readMessage({ armoredMessage: messageToDecrypt }),
decryptionKeys: privateKey
});
expect(data).to.equal('Hello World!');
});
it('supports encrypting/decrypting new x25519 format (v4 key)', async function () {
// v4 key
const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----
xUkEZIbSkxsHknQrXGfb+kM2iOsOvin8yE05ff5hF8KE6k+saspAZQCy/kfFUYc2
GkpOHc42BI+MsysKzk4ofjBAfqM+bb7goQ3hzRV1c2VyIDx1c2VyQHRlc3QudGVz
dD7ChwQTGwgAPQUCZIbSkwmQQezK2iB2tIkWIQRqZza9wQZcwxpjGYNB7MraIHa0
iQIbAwIeAQIZAQILBwIVCAIWAAMnBwIAAFOeZ7jrKZsCzRfu1ffFa77074st0zRo
BTJXoXBQ1ZzLjsh+ZO6fB2odnYJtQYstv45H/3JyLVogcMnFeYmHeSP3AMdJBGSG
0pMZfpd7TiOQv7uKSK+k4HT9lKr5+dmvb7vox/8ids6unEkAF1v8fCKogIrtBWVT
nVbwnovjM3LLexpXFZSgTKRcNMgPRMJ0BBgbCAAqBQJkhtKTCZBB7MraIHa0iRYh
BGpnNr3BBlzDGmMZg0HsytogdrSJAhsMAADCYs2I9wBakIu9Hhxs4R3Jq9F8J7AH
yxsNL0GomZ+hxiE0MOZwRr10DxfVaRabF1fcf9PHSHX2SwEFXUKMIHgbMQs=
=bJqd
-----END PGP PRIVATE KEY BLOCK-----` });
const plaintext = 'plaintext';
const signed = await openpgp.encrypt({
message: await openpgp.createMessage({ text: plaintext }),
encryptionKeys: privateKey
});
const { data } = await openpgp.decrypt({
message: await openpgp.readMessage({ armoredMessage: signed }),
decryptionKeys: privateKey
});
expect(data).to.equal(plaintext);
});
it('should decrypt test vector X25519-AEAD-OCB (PKESK v6, SEIPD v2)', async function() {
// test vector https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#appendix-A.8
const armoredMessage = `-----BEGIN PGP MESSAGE-----
wV0GIQYSyD8ecG9jCP4VGkF3Q6HwM3kOk+mXhIjR2zeNqZMIhRmHzxjV8bU/gXzO
WgBM85PMiVi93AZfJfhK9QmxfdNnZBjeo1VDeVZheQHgaVf7yopqR6W1FT6NOrfS
aQIHAgZhZBZTW+CwcW1g4FKlbExAf56zaw76/prQoN+bAzxpohup69LA7JW/Vp0l
yZnuSj3hcFj0DfqLTGgr4/u717J+sPWbtQBfgMfG9AOIwwrUBqsFE9zW+f1zdlYo
bhF30A+IitsxxA==
-----END PGP MESSAGE-----`;
const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----
xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB
exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ
BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh
RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe
7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/
LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG
GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE
M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr
k0mXubZvyl4GBg==
-----END PGP PRIVATE KEY BLOCK-----` });
const { data: decryptedData } = await openpgp.decrypt({
message: await openpgp.readMessage({ armoredMessage }),
decryptionKeys: privateKey
});
expect(decryptedData).to.equal('Hello, world!');
});
});
describe('X448/Ed448', async function () {
it('should enforce using AES session keys with x448 keys (v4 key)', async function () {
// X448 key (v4) with cast5 as preferred cipher
const privateKeyCast5 = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----
xXsEZRrtaRyScvyNjK0o5ccICztnWhA1MSij7WdzPfuNy7ryUzB+kqzpziBR
IIKp5PN0NW3mOYRDnUyo7QHBl4AA30tR5ED8u5v/rNIzKz/mKsD6XeYy+d0Q
5utwuR8BUxx9mcIUGdS65z9H6PUMGnfCwqAGVCTzBrSCHgTNAMK3BBAcCgA7
BYJlGu1pAwsDBwmQkFi4G9HqQDwDFQgKAhYAAhkBApsDAh4BFiEE7kZAI1Dd
SVlLtf4QkFi4G9HqQDwAAPA7E+p0vwVLtUCfT0aBFzapFn8xjoow6jrUNTo3
8EtaN0fqP2vaeQwW/vv26wobD+hbL2RwyFtAEV6AeeDsPVhbx7WA7yKHPzvl
GOYEGw0h57DuhvSxGciuyt0Y5PR2Vrz/2/wHGcEHzsrhTNysUetluxEAx3kE
ZRrtaRrySCLAqKQSATJOXdoRoNKVasJHlKrG3qgMbt1U6uSdctHBitTiHHTf
GU/Jg0ADA3Eg0bCyDupWNACmHJGu7q0o7O7BTAm0AsMbHxoIkNN9JsijwAp5
FLtdXK9cAOkNaXPMkEGQkt1hmoW50lUq0iWcGBpzwqYEGBwKACoFgmUa7WkJ
kJBYuBvR6kA8ApsMFiEE7kZAI1DdSVlLtf4QkFi4G9HqQDwAAD3uf3qdwHY8
65W22GR17PbqF+9uvkPpXLBi32FVPFkxJqYvIN5/LAQ33xdEE0mzO4As4+Oi
x8fsFb2AEXLEwlSnL+Eo0O+iUQd3/94yMbMFRlNxrdaqZ3+7CehbnieI/vby
LIEnN38XBi0HE70uoU5prxUA
-----END PGP PRIVATE KEY BLOCK-----` });
await expect(openpgp.generateSessionKey({
encryptionKeys: privateKeyCast5,
config: { preferredSymmetricAlgorithm: openpgp.enums.symmetric.cast5 }
})).to.be.rejectedWith(/Could not generate a session key compatible with the given `encryptionKeys`/);
await expect(openpgp.encrypt({
message: await openpgp.createMessage({ text: plaintext }),
encryptionKeys: privateKeyCast5,
sessionKey: { data: new Uint8Array(16).fill(1), algorithm: 'cast5' }
})).to.be.rejectedWith(/X25519 and X448 keys can only encrypt AES session keys/);
await expect(openpgp.decryptSessionKeys({
message: await openpgp.readMessage({ armoredMessage: `-----BEGIN PGP MESSAGE-----
wVwD2k7TUuqJwZkaXvEGk7B3pklJ5uRcRdKwwDJ40yKT0m5ic1e/2F+Se3xQ
zDE+N2DZ0B37pu4NUzTGBRo0oLD9EwwZA9+oJpBBOOry3cGmBYWvQHbvBpNE
5X5l8A==
-----END PGP MESSAGE-----` }),
decryptionKeys: privateKeyCast5
})).to.be.rejectedWith(/AES session key expected/);
});
it('should enforce using 512-bit signature digest', async function () {
// X448 key using sha256 for self signatures
const privateKeySHA256 = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----
xXsEZCWHXBwwtqciq6ZFU13s+dyhkWR5tOEmF1oX8OiP1B5ypfqyGVM8DkQh
5eTIMwB1oqJCROANoyA0q2dSigAAbDA5xr74DeClPPXC4ZXJ9uzuJWKvQvE8
x3EflhgoQCGBM7JfvH5zwdrJvPt8RKDvm0QkZzhPvnFoHnzNBHRlc3TCugQQ
HAgAPgWCZCWHXAQLCQcICZDsN6h/ys3ppwMVCAoEFgACAQIZAQKbAwIeARYh
BOJyE9P2eIcU2N2Ne+w3qH/KzemnAAAh1hTFCcEU77bU3YelrJTCNIOQnvt7
Hs6yZz2053CQTOC+wHkUQLaYYBEXSNyLZxoyv+NuGTiwbuYtAOlbE2erM7Cx
8B2Qz7M29UkFLMBUfb+yi+gTYYUWCXVQ7Um7MGjjgUG8+9p452i6f28mhRD8
tTgNAMd5BGQlh1wavTIFgILtbzrqQCiwDGx0YcFNzu9+FZ8vK5Mmm7UEZj0a
y7FWQtZw8tTaU6mY+RrSa52RjzkGLtQAQO++tgYqc+BnCFdCZ3ZYPRvD3mof
ffoo3l4xmto+iyvJZbQ4wQPXttg7VjCpEfOsL9TW9Xs09aIbysKmBBgcCAAq
BYJkJYdcCZDsN6h/ys3ppwKbDBYhBOJyE9P2eIcU2N2Ne+w3qH/KzemnAAC0
6/eZhh/Oj2gRdab2JeFGWACGIRDKxPXsWRCXR4YrSxcvCKK6rOvsyxQsgIsJ
JyPYkRPfmbKcseUDAEkSBLAfeizDGh7ea0GOdIMhwE/CW4f/H8ULbwi36y13
x3oMNVaYsI9dZ588Gpi8XYy2jOtqIPQ1AA==
-----END PGP PRIVATE KEY BLOCK-----` });
await expect(privateKeySHA256.getSigningKey()).to.be.rejectedWith(/Hash algorithm too weak for EdDSA/);
});
it('supports encrypting/decrypting with x448 (v4 key)', async function () {
// v4 key
const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----
xXsEZRqJ5BwHESfKnw5YJly5WobjigVm0kKY84NxrP6JKeIvIWiFqqSlozGpKZyR
50YbVTHmxpUCuJ7YNwX0UoAAoSO8IXmMM/XMd4ph00ju+fbSHdtQfyNhfFTi3UoM
V5DiFT+uOYDP+zwAwLWCR86csxmCWn6O10DNHcDNF1VzZXJBIDxVc2VyQUB0ZXN0
LnRlc3Q+wroEExwKAD4FAmUaieQJEC8lwIrxSM+5FiEEGR2s5Bj5WVDN0Px6LyXA
ivFIz7kCGwMCHgkCGQECCwcDFQoIAhYAAycHAgAA21/PqAuGDL5+3qrf3YoVOP+5
0BoJ+ZMhzcgax+cQTyndmOZYBfOqV/SJ8mf6CRhbB76JhGIvmRMtyYDQgDMVvcoA
yojVNs6e/Jco16bVJxM85wKDXJuq6AhtPQ8w/0WaCJtEf1uxqeQPEbOM+KtT/xY2
KgDHeQRlGonkGuOtAhogSIU3z/+gFzF8U7JQe7QDRYr9VWfi2WXFFarzg/3DMRur
oIB7mqkaaSatrvVuud1ZmRCWAMM4f57dvSdCKsVqSe+tlS225OmdWmnGLqyErBb6
44E2oENhDUom9OUGUPm8dXUjQbrmw6ec9hNLHWXCpgQYHAoAKgUCZRqJ5AkQLyXA
ivFIz7kWIQQZHazkGPlZUM3Q/HovJcCK8UjPuQIbDAAAZka10c8KlmwftJuboIV5
DalGWrZhbywJpEZRfoikcebSYi5++w1SbpXZGu27sl+BznGyyyqAfxyJjoCZaqCs
ewbKh04DNAg4v4v0W0a8UvD3j/CuciEMXjK9nUErt91zEwxNZy43yrQY2aAayDs8
94FqMAA=
=GBh1
-----END PGP PRIVATE KEY BLOCK-----` });
const plaintext = 'plaintext';
const signed = await openpgp.encrypt({
message: await openpgp.createMessage({ text: plaintext }),
encryptionKeys: privateKey
});
const { data } = await openpgp.decrypt({
message: await openpgp.readMessage({ armoredMessage: signed }),
decryptionKeys: privateKey
});
expect(data).to.equal(plaintext);
});
it('supports encrypting/decrypting with x448 (v6 key)', async function () {
const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----
xX0GZRqLYhwAAAA52IEq/TpKiPp6RofQaq4uhCruTtiG+qiVFnwsQgeh0ui34kHD
Y1E04mBai0pCoDiFVokwsKt3F5sAAC8lDYfVP/p3atbXJDTJB2W9WmZxIS7pUGhS
bjlWpZB/OVTBsoIfP/2J+Hi4ESwBRfDUDgwK4aJVKsLAIAYfHAoAAAA/BQJlGoti
IqEGobsxt8WKsMuJWANyTXpWMdC1QN/7EyJClfcs+nBgqdUCGwMCHgkCCwcDFQoI
AhYABScHAwcCAAAAAPiGIG2qmhCULQ/+H4rKV0XEM1x0uVY3l878Pa6ijZLouZU/
VRd5PnbGyLPL++q3LDViUUdZ1uusRc01f677Q6wpUU90k8MH/oULwI0+KPtqe1N4
6nr1NTERsAmAaPjUdf4ZUXX/GWiTd/AlsS5JqGnAQxKRJkzCJacOTOElRMjzGUX7
CGaAnhSC86YRZ68ocTPfZysAzRdVc2VyQiA8VXNlckJAdGVzdC50ZXN0PsLADQYT
HAoAAAAsBQJlGotiIqEGobsxt8WKsMuJWANyTXpWMdC1QN/7EyJClfcs+nBgqdUC
GQEAAAAASKwgVzMoPb2Hbr3lbNI1CRWECokYLokL7F8MbYiMnlg+v6QXLdStvT13
ZjxdrWQAx3MbihSOUSXbdAys90yMOAdtognj+x418J/TaYFMtIGBHwoHv8gQVnx9
9ICv8ezx1T5VvGBYNuKZ5Ww0WPEpYMf1VA+Y9JxpohdcRenNBdSug4tLWla2y8NH
aO28Fltpb4AuGQDHewZlGotiGgAAADjdabr1ohAOnbSUUkVhtUM/LVdnYgDLhmaj
YZ1N7TWY0fqEpMk2LLo2165HOmhddRPeTB1TWbuwBwB8lKc3czFUzYcAgvZ08T5S
UUHjfIhjeJeY4yd0OZDfzPw1vbegCc7t94bT+XGoIQbC/Bl7HCyAiMLADQYYHAoA
AAAsBQJlGotiIqEGobsxt8WKsMuJWANyTXpWMdC1QN/7EyJClfcs+nBgqdUCGwwA
AAAAHh0gf2kdqLoXdFX+aNVORr5VCVgcm2gBw8l68lDJ1ftA71bMllFi6Q5sLPUr
6UCpJYYk3o3hYUYzIHmCIZGVYe1pxRgIUNqxydXEfzylJmN5NbiJNkjJizrI7oAR
1mIcEEb/hmRMOUs1V2mcGuoeALBI/r/SyqDE2GRjH6d6g1RS7ZARPPHlZlY4CTqC
4a7L+8odDwA=
=chx0
-----END PGP PRIVATE KEY BLOCK-----` });
const plaintext = 'plaintext';
const signed = await openpgp.encrypt({
message: await openpgp.createMessage({ text: plaintext }),
encryptionKeys: privateKey
});
const { data } = await openpgp.decrypt({
message: await openpgp.readMessage({ armoredMessage: signed }),
decryptionKeys: privateKey
});
expect(data).to.equal(plaintext);
});
it('decrypt/verify should succeed using X448/Ed448 (PKESK v4, SEIPD v2, GCM)', async function() {
// data generated by gopenpgp
const armoredMessage = `-----BEGIN PGP MESSAGE-----
wWkGFQQ70agVm6o5r3tEzY5mrYaOV8yHChpUetZ33zrKGtw1F4PeFrE4bYkcTMQM
IcGoXcZj/0GJDCkOLLleSwrvuAuUwZV11bHBZ6eNTyj+XxhLdVflV/zqPmBhTHY9
SMn0YYHwgiQFk6PSwGICBwMMRYkTpsy0dE4YKasf6b4Oh9cn6HYY5rjnrtvrwD+F
LrsELfudYwpwHBA5jnO11Hl5mUyXhhWSdPoLGdeiYP5R/vZjqoZr3P6FL4dCdVni
fGChUUSYmpO4HIFrRBt2gAxl+f0Q8GCOG8c7EQ7c5600kJOlHM7SuoLqsxd482V1
H/1Rxd3cPwTDfOjH26KuDv60p0XjdCGyQXcDQMCPV+ZTs0TQl4wTFogZGaRMd9GC
5D5t/guKzR+H1ipXSFjFdWWTEehx8m0RKKKT3Bl81awKZb8ulR6YKI5x39nwOySN
azDRR3gn9xlKjcpa83k5sSZbUTxC8lzTeuMP0PkDrU2IpZUZOlzOhGYOGrtTFATK
PSoZU33h2h3hJqiX9aKrnw==
=Is5l
-----END PGP MESSAGE-----`;
const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----
xXsEZRqJ5BxV/xdxR4KvPofk3EzKaIZqM0Wlw904q+2S6Z84OmXN6Q1xCYurcwN1
wLOlGJ/CO2QhByEdGlUldwAAIQSMnMITcmXQU3EWK5S81FS+u1IZFP55j51bA5mS
HZ/A7MOpyN40ybW8mhMIXXUYB7kC/bOTmwVHGt3NF1VzZXJCIDxVc2VyQkB0ZXN0
LnRlc3Q+wrwEExwKAEAFAmUaieQJEP3WralmOT0PFiEEq4zuQzIBSh+Nk6G9/dat
qWY5PQ8CGwMCHgkCGQECCwcDFQoIAhYABScHAwcCAADAvnnhLp0DJYk7E0GfksCg
pUnnCjEePMVvRPVY3dwr9wLpdL/7T70fz541XVE8giYiZD7eiKvfc/nMgOhu1eqK
uXGUtDGBeabitJcrbquy2Tp/ENuW6rRHP7sAbu0mj6XxYEeCzKjGRT5Iq25AMevm
yeoIAMd5BGUaieQaqj/dF+uZGt9QLuji2eOlDC0/quq/sAtdJTbI1xj04aF0X7kJ
lVhEKeWZeAEpD4rVOCsrhMvr21gAu/BaFVKGUOuf0+ZE5jGcFcBvEP7OGyO296ry
zV7ONWS/FuoZ/NZmgWo9m9ftPtwqKDsgOWxiIj4cesKmBBgcCgAqBQJlGonkCRD9
1q2pZjk9DxYhBKuM7kMyAUofjZOhvf3WralmOT0PAhsMAADWb+0aY+NblShwsym/
2geh6XaqQUCJgdRfEl8xYLau/o8QQAzRp0ZBA+KeK3uwhRW3RizuqIw5iribAK3+
30Si5nvv0TivalPK2C9yAqzh9rkNNUQa9b17IHYs/WwQrvP3F5EZ3V+StqdveAEo
FecSL/wTAA==
=FANS
-----END PGP PRIVATE KEY BLOCK-----` });
const senderKey = await openpgp.readKey({
armoredKey: `-----BEGIN PGP PUBLIC KEY BLOCK-----
xj8EZRqJ5BwHESfKnw5YJly5WobjigVm0kKY84NxrP6JKeIvIWiFqqSlozGpKZyR
50YbVTHmxpUCuJ7YNwX0UoDNF1VzZXJBIDxVc2VyQUB0ZXN0LnRlc3Q+wroEExwK
AD4FAmUaieQJEC8lwIrxSM+5FiEEGR2s5Bj5WVDN0Px6LyXAivFIz7kCGwMCHgkC
GQECCwcDFQoIAhYAAycHAgAA21/PqAuGDL5+3qrf3YoVOP+50BoJ+ZMhzcgax+cQ
TyndmOZYBfOqV/SJ8mf6CRhbB76JhGIvmRMtyYDQgDMVvcoAyojVNs6e/Jco16bV
JxM85wKDXJuq6AhtPQ8w/0WaCJtEf1uxqeQPEbOM+KtT/xY2KgDOPgRlGonkGuOt
AhogSIU3z/+gFzF8U7JQe7QDRYr9VWfi2WXFFarzg/3DMRuroIB7mqkaaSatrvVu
ud1ZmRCWwqYEGBwKACoFAmUaieQJEC8lwIrxSM+5FiEEGR2s5Bj5WVDN0Px6LyXA
ivFIz7kCGwwAAGZGtdHPCpZsH7Sbm6CFeQ2pRlq2YW8sCaRGUX6IpHHm0mIufvsN
Um6V2Rrtu7Jfgc5xsssqgH8ciY6AmWqgrHsGyodOAzQIOL+L9FtGvFLw94/wrnIh
DF4yvZ1BK7fdcxMMTWcuN8q0GNmgGsg7PPeBajAA
=VA/P
-----END PGP PUBLIC KEY BLOCK-----` });
const { data: decryptedData, signatures } = await openpgp.decrypt({
message: await openpgp.readMessage({ armoredMessage }),
decryptionKeys: privateKey,
verificationKeys: senderKey
});
expect(decryptedData).to.equal('Hello there');
expect(signatures).to.have.length(1);
expect(await signatures[0].verified).to.be.true;
});
it('decrypt/verify should succeed using X448/Ed448 (PKESK v6, SEIPD v2, GCM)', async function() {
// data generated by gopenpgp
const armoredMessage = `-----BEGIN PGP MESSAGE-----
wXUGIQZh4qTsn8glFgGNbIdCTl8gH2OtkI/PAGCQ0gi9s9k/rhrDhXo7kUKDJ39F
fNp3kmAaM24Ce3bcYXwLy0gF2i6rxfL20D+g3cxv0i3CuXQCgcbojTN/8KY8ExiV
Xdfo+OWIZ5XndtyMpJW28BiLHru+n9bSwM8CBwMMENh7cT8lILXteh885FrUUD1Q
JMtD7xJUn2y78cVGgFSIkLbvFPDerB37xuhtMRkykuWgbUoJH/kcgBPdeCoYzJmf
LV9FyATv0/AYq0yWpQ0VUfNLTFyeHIGxz7NHvrzJSrOy1Gm31PXqWvb4sBROjnOX
oAk12JdPudz3l1QZT/DX947f4h6hwkVv7RRT0oOS2pMaz/mekRuD6utUcpsjFQ/M
EDphnhOsB4RH0il8YPVc9DCnf3GhSs66h+Z699MXHBaUmdtiN1IgoEgLfb/900U2
TfI6dvrvC56WIMA8EA1COvLGc9Ge4owW0UE8jIuqWLzA2nVg5belbzhNnOEh9b1c
OcDUh8CfBuXqHEi/ANMUOMmaIGfcHfQFVu5v/UMcLxcH/fSVF6DvtOxEoUxASWBS
mp6yC4A778BFuDFXb+/T8FjuJBaUj9rCSkYqt1TYVKG1XZPI4OdIvGtneo+vH/Cq
F6bxlLWU6oskZ5SE+xJblmmO01ObM9JRi9D8jZnXedTHExAnXHXIb8I=
=5RQw
-----END PGP MESSAGE-----`;
const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----
xX0GZRqLYhwAAAA52IEq/TpKiPp6RofQaq4uhCruTtiG+qiVFnwsQgeh0ui34kHD
Y1E04mBai0pCoDiFVokwsKt3F5sAAC8lDYfVP/p3atbXJDTJB2W9WmZxIS7pUGhS
bjlWpZB/OVTBsoIfP/2J+Hi4ESwBRfDUDgwK4aJVKsLAIAYfHAoAAAA/BQJlGoti
IqEGobsxt8WKsMuJWANyTXpWMdC1QN/7EyJClfcs+nBgqdUCGwMCHgkCCwcDFQoI
AhYABScHAwcCAAAAAPiGIG2qmhCULQ/+H4rKV0XEM1x0uVY3l878Pa6ijZLouZU/
VRd5PnbGyLPL++q3LDViUUdZ1uusRc01f677Q6wpUU90k8MH/oULwI0+KPtqe1N4
6nr1NTERsAmAaPjUdf4ZUXX/GWiTd/AlsS5JqGnAQxKRJkzCJacOTOElRMjzGUX7
CGaAnhSC86YRZ68ocTPfZysAzRdVc2VyQiA8VXNlckJAdGVzdC50ZXN0PsLADQYT
HAoAAAAsBQJlGotiIqEGobsxt8WKsMuJWANyTXpWMdC1QN/7EyJClfcs+nBgqdUC
GQEAAAAASKwgVzMoPb2Hbr3lbNI1CRWECokYLokL7F8MbYiMnlg+v6QXLdStvT13
ZjxdrWQAx3MbihSOUSXbdAys90yMOAdtognj+x418J/TaYFMtIGBHwoHv8gQVnx9
9ICv8ezx1T5VvGBYNuKZ5Ww0WPEpYMf1VA+Y9JxpohdcRenNBdSug4tLWla2y8NH
aO28Fltpb4AuGQDHewZlGotiGgAAADjdabr1ohAOnbSUUkVhtUM/LVdnYgDLhmaj
YZ1N7TWY0fqEpMk2LLo2165HOmhddRPeTB1TWbuwBwB8lKc3czFUzYcAgvZ08T5S
UUHjfIhjeJeY4yd0OZDfzPw1vbegCc7t94bT+XGoIQbC/Bl7HCyAiMLADQYYHAoA
AAAsBQJlGotiIqEGobsxt8WKsMuJWANyTXpWMdC1QN/7EyJClfcs+nBgqdUCGwwA
AAAAHh0gf2kdqLoXdFX+aNVORr5VCVgcm2gBw8l68lDJ1ftA71bMllFi6Q5sLPUr
6UCpJYYk3o3hYUYzIHmCIZGVYe1pxRgIUNqxydXEfzylJmN5NbiJNkjJizrI7oAR
1mIcEEb/hmRMOUs1V2mcGuoeALBI/r/SyqDE2GRjH6d6g1RS7ZARPPHlZlY4CTqC
4a7L+8odDwA=
=chx0
-----END PGP PRIVATE KEY BLOCK-----` });
const senderKey = await openpgp.readKey({
armoredKey: `-----BEGIN PGP PUBLIC KEY BLOCK-----
xkMGZRqLYhwAAAA5U/IaIOge/FoLzCetXKx029bdJHCz2hMFBRMuzq4msjaT+hLe
V6puyC/PeSEfaanqTuo31vvsti2AwsAeBh8cCgAAAD0FAmUai2IioQYDGJ2wdEcO
zIVPDVDs6gYQASdGfG2EozBUGqEgvaj4dQIbAwIeCQILBwMVCggCFgADJwcCAAAA
ADYqIL5j5+FD/jwKRP1atdNf1IKfe8fPjdZv74CSalYvUdCaskTdLiAaW17NkrYT
2i9qDPErFWsvXi4LqGzqQnQkiJZBJ4x57EJPL4Z2vqPTBvgWEU2egi7fK7YAGZmk
Vf/n/X3Vh5ZSvIoUMChRmYqBBNI7MkS/I7QAJHkvi9XcANx44B0bz+yqETz2tNJ6
8VeeDgkAzRdVc2VyQSA8VXNlckFAdGVzdC50ZXN0PsLADQYTHAoAAAAsBQJlGoti
IqEGAxidsHRHDsyFTw1Q7OoGEAEnRnxthKMwVBqhIL2o+HUCGQEAAAAAXD8gfvYz
WLLMxaFuC3C/RJH9fG84hb9mtPgjH3bfqW+g4Ti1ov8PjoJtk6ObtUB45J9J3G3X
FIqegAtGwI1Dy1U+M9dyXOqvpHwxs8iAFbEpwxLZ5K1ikFsbmoCZz4rmN0DbFyX2
JbltaV5nUtNqHiUXqoKIPvch98ANe3PDyIAxNf7TAzk3W0lQQa+Cp7TSiFEqJADO
QgZlGotiGgAAADjKb5lwMEt0ubSvwydaAF89wsn6H8NJO7kox5ioWW2Grn88CUZD
YaRBZj3ZH8HMdaih5kN4hJAeCMLADQYYHAoAAAAsBQJlGotiIqEGAxidsHRHDsyF
Tw1Q7OoGEAEnRnxthKMwVBqhIL2o+HUCGwwAAAAAFIQgGYYweuBej4XHAgZrcez8
8VoTbIZDjMv6Qbj9g6jjW16Fyp10DKda10FFmbY+YjbNvQNYksF9bN/KFSS/PTYt
AVaOZDfW4fiN5s1QaYmA/xCT/zLHEYGryYCJLoLd7KLw28LS1KAWrC9h5cY6+fZE
05cavO/D/WqBLVPuA+5bftXnDvGcVS1p7buaMtQjKz4hAwA=
=GUIG
-----END PGP PUBLIC KEY BLOCK-----` });
const { data: decryptedData, signatures } = await openpgp.decrypt({
message: await openpgp.readMessage({ armoredMessage }),
decryptionKeys: privateKey,
verificationKeys: senderKey
});
expect(decryptedData).to.equal('Hello there');
expect(signatures).to.have.length(1);
expect(await signatures[0].verified).to.be.true;
});
it('decrypt/verify should succeed using X448/Ed448 (PKESK v6, SEIPD v2, OCB)', async function() {
// data generated by gopenpgp
const armoredMessage = `-----BEGIN PGP MESSAGE-----
wXUGIQaYemlYu2ObOZ2IjFbL77NygqexwaCgtb0COZ0EnXfXlBri0wADNxbvwCnJ
GDlRX9VhIy46oPAvVJjm2d7ZC6wqxNfFuzQEB8KzwYBkExmZuAfO5KJ8la6+DRhc
OUH3A9cBGzq0eiKaKRqjHkiLHY5pFNPSwNoCBwIM98RL63I8iMyxcXpXQlBrYlBx
5uegrENlleNg6UJFr7rBT4eJH+Qeksb//V87eZymzqXZBsrTYmUjsFgYd5kL8NlU
wovy+qQnZmEaUKieDx3w+orR8b32ub5CNjHJa5lCdNWsIK825S5JUifZDd3hR6lC
EgtZRwxY/1CyQU94LR9j6w/YVF0W31+LxGGkL+uJEx0khJUzpxUM9QSEREOY7Frs
EegHNwDvxvxEwWpfkJOPIDME6Y7UcpsNp6xgiZ/XF06IRsliCRbeYaH1IWW+y0OS
CmPvvTFUzjwTxWogDccHz8YLHU0y6TKxf14YMvVLg2tf2P/BVVZSg0ejz6pfDKA5
AP+Q/eXBAH272SpBjKo7YcVpTsz0KpWyhB6Jra4xaUFkt6pg39ydR3RJMvxbQVlR
aZqV/+1rwIiIauyHKiJFdCiXYPDU3xibVkFIFhuk5JwHm29XvOV1r8FFx7d78X5P
yJnXcXsl+GxwOojcLXSL0CEIU/iRqyAIyyhvUyyss3glehhgx0fENV2P/Ygi/naN
nJUJgg==
=m19C
-----END PGP MESSAGE-----`;
const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----
xX0GZRqLYhwAAAA5U/IaIOge/FoLzCetXKx029bdJHCz2hMFBRMuzq4msjaT+hLe
V6puyC/PeSEfaanqTuo31vvsti2AAIttr4GDGXF4vfPzbzkWV9dT4VVsIU7QqLv1
hzwZ+k7pHroRyXnUiYxRYHuzlg7Vw4CrAtN/8T65OMLAHgYfHAoAAAA9BQJlGoti
IqEGAxidsHRHDsyFTw1Q7OoGEAEnRnxthKMwVBqhIL2o+HUCGwMCHgkCCwcDFQoI
AhYAAycHAgAAAAA2KiC+Y+fhQ/48CkT9WrXTX9SCn3vHz43Wb++AkmpWL1HQmrJE
3S4gGltezZK2E9ovagzxKxVrL14uC6hs6kJ0JIiWQSeMeexCTy+Gdr6j0wb4FhFN
noIu3yu2ABmZpFX/5/191YeWUryKFDAoUZmKgQTSOzJEvyO0ACR5L4vV3ADceOAd
G8/sqhE89rTSevFXng4JAM0XVXNlckEgPFVzZXJBQHRlc3QudGVzdD7CwA0GExwK
AAAALAUCZRqLYiKhBgMYnbB0Rw7MhU8NUOzqBhABJ0Z8bYSjMFQaoSC9qPh1AhkB
AAAAAFw/IH72M1iyzMWhbgtwv0SR/XxvOIW/ZrT4Ix9236lvoOE4taL/D46CbZOj
m7VAeOSfSdxt1xSKnoALRsCNQ8tVPjPXclzqr6R8MbPIgBWxKcMS2eStYpBbG5qA
mc+K5jdA2xcl9iW5bWleZ1LTah4lF6qCiD73IffADXtzw8iAMTX+0wM5N1tJUEGv
gqe00ohRKiQAx3sGZRqLYhoAAAA4ym+ZcDBLdLm0r8MnWgBfPcLJ+h/DSTu5KMeY
qFlthq5/PAlGQ2GkQWY92R/BzHWooeZDeISQHggAuraV/u+CE642fcbcq90OY+qg
n739wkHcBps/s/MgMI+Q2H13vEsFpYZ/kuBIIYP39xkdU48/1GbCwA0GGBwKAAAA
LAUCZRqLYiKhBgMYnbB0Rw7MhU8NUOzqBhABJ0Z8bYSjMFQaoSC9qPh1AhsMAAAA
ABSEIBmGMHrgXo+FxwIGa3Hs/PFaE2yGQ4zL+kG4/YOo41tehcqddAynWtdBRZm2
PmI2zb0DWJLBfWzfyhUkvz02LQFWjmQ31uH4jebNUGmJgP8Qk/8yxxGBq8mAiS6C
3eyi8NvC0tSgFqwvYeXGOvn2RNOXGrzvw/1qgS1T7gPuW37V5w7xnFUtae27mjLU
Iys+IQMA
=iwhO
-----END PGP PRIVATE KEY BLOCK-----` });
const senderKey = await openpgp.readKey({
armoredKey: `-----BEGIN PGP PUBLIC KEY BLOCK-----
xkMGZRqLYhwAAAA52IEq/TpKiPp6RofQaq4uhCruTtiG+qiVFnwsQgeh0ui34kHD
Y1E04mBai0pCoDiFVokwsKt3F5sAwsAgBh8cCgAAAD8FAmUai2IioQahuzG3xYqw
y4lYA3JNelYx0LVA3/sTIkKV9yz6cGCp1QIbAwIeCQILBwMVCggCFgAFJwcDBwIA
AAAA+IYgbaqaEJQtD/4fispXRcQzXHS5VjeXzvw9rqKNkui5lT9VF3k+dsbIs8v7
6rcsNWJRR1nW66xFzTV/rvtDrClRT3STwwf+hQvAjT4o+2p7U3jqevU1MRGwCYBo
+NR1/hlRdf8ZaJN38CWxLkmoacBDEpEmTMIlpw5M4SVEyPMZRfsIZoCeFILzphFn
ryhxM99nKwDNF1VzZXJCIDxVc2VyQkB0ZXN0LnRlc3Q+wsANBhMcCgAAACwFAmUa
i2IioQahuzG3xYqwy4lYA3JNelYx0LVA3/sTIkKV9yz6cGCp1QIZAQAAAABIrCBX
Myg9vYduveVs0jUJFYQKiRguiQvsXwxtiIyeWD6/pBct1K29PXdmPF2tZADHcxuK
FI5RJdt0DKz3TIw4B22iCeP7HjXwn9NpgUy0gYEfCge/yBBWfH30gK/x7PHVPlW8
YFg24pnlbDRY8Slgx/VUD5j0nGmiF1xF6c0F1K6Di0taVrbLw0do7bwWW2lvgC4Z
AM5CBmUai2IaAAAAON1puvWiEA6dtJRSRWG1Qz8tV2diAMuGZqNhnU3tNZjR+oSk
yTYsujbXrkc6aF11E95MHVNZu7AHwsANBhgcCgAAACwFAmUai2IioQahuzG3xYqw
y4lYA3JNelYx0LVA3/sTIkKV9yz6cGCp1QIbDAAAAAAeHSB/aR2ouhd0Vf5o1U5G
vlUJWBybaAHDyXryUMnV+0DvVsyWUWLpDmws9SvpQKklhiTejeFhRjMgeYIhkZVh
7WnFGAhQ2rHJ1cR/PKUmY3k1uIk2SMmLOsjugBHWYhwQRv+GZEw5SzVXaZwa6h4A
sEj+v9LKoMTYZGMfp3qDVFLtkBE88eVmVjgJOoLhrsv7yh0PAA==
=2Usy
-----END PGP PUBLIC KEY BLOCK-----` });
const { data: decryptedData, signatures } = await openpgp.decrypt({
message: await openpgp.readMessage({ armoredMessage }),
decryptionKeys: privateKey,
verificationKeys: senderKey
});
expect(decryptedData).to.equal('Hello nice to meet you');
expect(signatures).to.have.length(1);
expect(await signatures[0].verified).to.be.true;
});
});
describe('Sign and verify with each curve', function() {
const curves = ['secp256k1' , 'nistP256', 'nistP384', 'nistP521', 'curve25519Legacy', '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('sign/verify with new Ed25519 format', async function () {
const userIDs = { name: 'Alice', email: 'info@alice.com' };
const { privateKey } = await openpgp.generateKey({ type: 'curve25519', userIDs, format: 'object' });
const plaintext = 'plaintext';
const signed = await openpgp.sign({
message: await openpgp.createMessage({ text: plaintext }),
signingKeys: privateKey
});
const { signatures, data } = await openpgp.verify({
message: await openpgp.readMessage({ armoredMessage: signed }),
verificationKeys: privateKey
});
expect(data).to.equal(plaintext);
expect(signatures).to.have.length(1);
expect(await signatures[0].verified).to.be.true;
});
it('sign/verify with Ed448', async function () {
const userIDs = { name: 'Alice', email: 'info@alice.com' };
const { privateKey } = await openpgp.generateKey({ type: 'curve448', userIDs, format: 'object' });
const plaintext = 'plaintext';
const signed = await openpgp.sign({
message: await openpgp.createMessage({ text: plaintext }),
signingKeys: privateKey
});
const { signatures, data } = await openpgp.verify({
message: await openpgp.readMessage({ armoredMessage: signed }),
verificationKeys: privateKey
});
expect(data).to.equal(plaintext);
expect(signatures).to.have.length(1);
expect(await signatures[0].verified).to.be.true;
});
});
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);
});
});
});
});