fork-openpgpjs/test/general/signature.js
larabr a4e2c56c49
Use JS fallback code for RSA message decryption in Node if PKCS#1 is not supported (#1728)
Necessary as Node v18.19.1, 20.11.1 and 21.6.2 have disabled support for PKCS#1 decryption.
2024-02-19 17:14:55 +01:00

2156 lines
110 KiB
JavaScript

/* eslint-disable max-lines */
/* globals tryTests: true */
const stream = require('@openpgp/web-stream-tools');
const { use: chaiUse, expect } = require('chai');
chaiUse(require('chai-as-promised'));
const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..');
const util = require('../../src/util');
module.exports = () => describe('Signature', function() {
const priv_key_arm1 =
['-----BEGIN PGP PRIVATE KEY BLOCK-----',
'Version: GnuPG v1.4.11 (GNU/Linux)',
'',
'lQHhBFERnrMRBADmM0hIfkI3yosjgbWo9v0Lnr3CCE+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',
'c1JJBsObkA1IM8TZY8YUmvsMEvBLCCanuKpclZZXqeRAeOHJ0v4DAwK8WfuTe5B+',
'M2BOOeZbN8BpfiA1l//fMMHLRS3UvbLBv4P1+4SyvhyYTR7M76Q0xPc03MFOWHL+',
'S9VumbQWVGVzdDIgPHRlc3QyQHRlc3QuY29tPohiBBMRAgAiBQJREZ6zAhsDBgsJ',
'CAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRARJ5QDyxae+MXNAKCzWSDR3tMrTrDb',
'TAri73N1Xb3j1ACfSl9y+SAah2q7GvmiR1+6+/ekqJGdAVgEURGesxAEANlpMZjW',
'33jMxlKHDdyRFXtKOq8RreXhq00plorHbgz9zFEWm4VF53+E/KGnmHGyY5Cy8TKy',
'ZjaueZZ9XuG0huZg5If68irFfNZtxdA26jv8//PdZ0Uj+X6J3RVa2peMLDDswTYL',
'OL1ZO1fxdtDD40fdAiIZ1QyjwEG0APtz41EfAAMFBAC5/dtgBBPtHe8UjDBaUe4n',
'NzHuUBBp6XE+H7eqHNFCuZAJ7yqJLGVHNIaQR419cNy08/OO/+YUQ7rg78LxjFiv',
'CH7IzhfU+6yvELSbgRMicY6EnAP2GT+b1+MtFNa3lBGtBHcJla52c2rTAHthYZWk',
'fT5R5DnJuQ2cJHBMS9HWyP4DAwK8WfuTe5B+M2C7a/YJSUv6SexdGCaiaTcAm6g/',
'PvA6hw/FLzIEP67QcQSSTmhftQIwnddt4S4MyJJH3U4fJaFfYQ1zCniYJohJBBgR',
'AgAJBQJREZ6zAhsMAAoJEBEnlAPLFp74QbMAn3V4857xwnO9/+vzIVnL93W3k0/8',
'AKC8omYPPomN1E/UJFfXdLDIMi5LoA==',
'=LSrW',
'-----END PGP PRIVATE KEY BLOCK-----'].join('\n');
const pub_key_arm1 =
['-----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-----'].join('\n');
const msg_arm1 =
['-----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-----'].join('\n');
const priv_key_arm2 =
['-----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_arm2 =
['-----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 pub_key_arm3 =
['-----BEGIN PGP PUBLIC KEY BLOCK-----',
'Version: GnuPG v2.0.19 (GNU/Linux)',
'',
'mQENBFKV0FUBCACtZliApy01KBGbGNB36YGH4lpr+5KoqF1I8A5IT0YeNjyGisOk',
'WsDsUzOqaNvgzQ82I3MY/jQV5rLBhH/6LiRmCA16WkKcqBrHfNGIxJ+Q+ofVBHUb',
'aS9ClXYI88j747QgWzirnLuEA0GfilRZcewII1pDA/G7+m1HwV4qHsPataYLeboq',
'hPA3h1EVVQFMAcwlqjOuS8+weHQRfNVRGQdRMm6H7166PseDVRUHdkJpVaKFhptg',
'rDoNI0lO+UujdqeF1o5tVZ0j/s7RbyBvdLTXNuBbcpq93ceSWuJPZmi1XztQXKYe',
'y0f+ltgVtZDEc7TGV5WDX9erRECCcA3+s7J3ABEBAAG0G0pTIENyeXB0byA8ZGlm',
'ZmllQGhvbWUub3JnPokBPwQTAQIAKQUCUpXQVQIbAwUJCWYBgAcLCQgHAwIBBhUI',
'AgkKCwQWAgMBAh4BAheAAAoJENvyI+hwU030yRAIAKX/mGEgi/miqasbbQoyK/CS',
'a7sRxgZwOWQLdi2xxpE5V4W4HJIDNLJs5vGpRN4mmcNK2fmJAh74w0PskmVgJEhP',
'dFJ14UC3fFPq5nbqkBl7hU0tDP5jZxo9ruQZfDOWpHKxOCz5guYJ0CW97bz4fChZ',
'NFDyfU7VsJQwRIoViVcMCipP0fVZQkIhhwpzQpmVmN8E0a6jWezTZv1YpMdlzbEf',
'H79l3StaOh9/Un9CkIyqEWdYiKvIYms9nENyehN7r/OKYN3SW+qlt5GaL+ws+N1w',
'6kEZjPFwnsr+Y4A3oHcAwXq7nfOz71USojSmmo8pgdN8je16CP98vw3/k6TncLS5',
'AQ0EUpXQVQEIAMEjHMeqg7B04FliUFWr/8C6sJDb492MlGAWgghIbnuJfXAnUGdN',
'oAzn0S+n93Y/qHbW6YcjHD4/G+kK3MuxthAFqcVjdHZQXK0rkhXO/u1co7v1cdtk',
'OTEcyOpyLXolM/1S2UYImhrml7YulTHMnWVja7xu6QIRso+7HBFT/u9D47L/xXrX',
'MzXFVZfBtVY+yoeTrOY3OX9cBMOAu0kuN9eT18Yv2yi6XMzP3iONVHtl6HfFrAA7',
'kAtx4ne0jgAPWZ+a8hMy59on2ZFs/AvSpJtSc1kw/vMTWkyVP1Ky20vAPHQ6Ej5q',
'1NGJ/JbcFgolvEeI/3uDueLjj4SdSIbLOXMAEQEAAYkBJQQYAQIADwUCUpXQVQIb',
'DAUJCWYBgAAKCRDb8iPocFNN9NLkB/wO4iRxia0zf4Kw2RLVZG8qcuo3Bw9UTXYY',
'lI0AutoLNnSURMLLCq6rcJ0BCXGj/2iZ0NBxZq3t5vbRh6uUv+hpiSxK1nF7AheN',
'4aAAzhbWx0UDTF04ebG/neE4uDklRIJLhif6+Bwu+EUeTlGbDj7fqGSsNe8g92w7',
'1e41rF/9CMoOswrKgIjXAou3aexogWcHvKY2D+1q9exORe1rIa1+sUGl5PG2wsEs',
'znN6qtN5gMlGY1ofWDY+I02gO4qzaZ/FxRZfittCw7v5dmQYKot9qRi2Kx3Fvw+h',
'ivFBpC4TWgppFBnJJnAsFXZJQcejMW4nEmOViRQXY8N8PepQmgsu',
'=ummy',
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
const pub_revoked =
['-----BEGIN PGP PUBLIC KEY BLOCK-----',
'Version: GnuPG v2.0.19 (GNU/Linux)',
'',
'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',
'pMiJM+NJAQ==',
'=ok+o',
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
const pub_latin1_msg = `-----BEGIN PGP PUBLIC KEY BLOCK-----
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
eGlmAABNTQAqAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEA
AABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAAAAAAAAAEgAAAABAAAASAAAAAEA
AqACAAQAAAABAAAAfaADAAQAAAABAAAAlgAAAAD/4QkhaHR0cDovL25zLmFkb2Jl
LmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENl
aGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5z
Om1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA1LjQuMCI+IDxyZGY6UkRGIHhtbG5z
OnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5z
IyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiLz4gPC9yZGY6UkRGPiA8
L3g6eG1wbWV0YT4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8P3hw
YWNrZXQgZW5kPSJ3Ij8+AP/tADhQaG90b3Nob3AgMy4wADhCSU0EBAAAAAAAADhC
SU0EJQAAAAAAENQdjNmPALIE6YAJmOz4Qn7/4gzgSUNDX1BST0ZJTEUAAQEAAAzQ
YXBwbAIQAABtbnRyUkdCIFhZWiAH3wABAAEACwA7AC1hY3NwQVBQTAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFkZXNjAAABUAAAAGJkc2Nt
AAABtAAAAbhjcHJ0AAADbAAAACN3dHB0AAADkAAAABRyWFlaAAADpAAAABRnWFla
AAADuAAAABRiWFlaAAADzAAAABRyVFJDAAAD4AAACAxhYXJnAAAL7AAAACB2Y2d0
AAAMDAAAADBuZGluAAAMPAAAAD5jaGFkAAAMfAAAACxtbW9kAAAMqAAAAChiVFJD
AAAD4AAACAxnVFJDAAAD4AAACAxhYWJnAAAL7AAAACBhYWdnAAAL7AAAACBkZXNj
AAAAAAAAAAhEaXNwbGF5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
bWx1YwAAAAAAAAAiAAAADGhySFIAAAAQAAABqGtvS1IAAAAQAAABqG5iTk8AAAAQ
AAABqGlkAAAAAAAQAAABqGh1SFUAAAAQAAABqGNzQ1oAAAAQAAABqGRhREsAAAAQ
AAABqHVrVUEAAAAQAAABqGFyAAAAAAAQAAABqGl0SVQAAAAQAAABqHJvUk8AAAAQ
AAABqGVzRVMAAAAQAAABqGhlSUwAAAAQAAABqG5sTkwAAAAQAAABqGZpRkkAAAAQ
AAABqHpoVFcAAAAQAAABqHZpVk4AAAAQAAABqHNrU0sAAAAQAAABqHpoQ04AAAAQ
AAABqHJ1UlUAAAAQAAABqGZyRlIAAAAQAAABqG1zAAAAAAAQAAABqGNhRVMAAAAQ
AAABqHRoVEgAAAAQAAABqGVzWEwAAAAQAAABqGRlREUAAAAQAAABqGVuVVMAAAAQ
AAABqHB0QlIAAAAQAAABqHBsUEwAAAAQAAABqGVsR1IAAAAQAAABqHN2U0UAAAAQ
AAABqHRyVFIAAAAQAAABqGphSlAAAAAQAAABqHB0UFQAAAAQAAABqABTAE0AQgAx
ADkANAAwAFd0ZXh0AAAAAENvcHlyaWdodCBBcHBsZSBJbmMuLCAyMDE1AABYWVog
AAAAAAAA89gAAQAAAAEWCFhZWiAAAAAAAABwAAAAOREAAAPCWFlaIAAAAAAAAGI3
AAC3mgAAGRFYWVogAAAAAAAAJJ8AAA9VAAC2WmN1cnYAAAAAAAAEAAAAAAUACgAP
ABQAGQAeACMAKAAtADIANgA7AEAARQBKAE8AVABZAF4AYwBoAG0AcgB3AHwAgQCG
AIsAkACVAJoAnwCjAKgArQCyALcAvADBAMYAywDQANUA2wDgAOUA6wDwAPYA+wEB
AQcBDQETARkBHwElASsBMgE4AT4BRQFMAVIBWQFgAWcBbgF1AXwBgwGLAZIBmgGh
AakBsQG5AcEByQHRAdkB4QHpAfIB+gIDAgwCFAIdAiYCLwI4AkECSwJUAl0CZwJx
AnoChAKOApgCogKsArYCwQLLAtUC4ALrAvUDAAMLAxYDIQMtAzgDQwNPA1oDZgNy
A34DigOWA6IDrgO6A8cD0wPgA+wD+QQGBBMEIAQtBDsESARVBGMEcQR+BIwEmgSo
BLYExATTBOEE8AT+BQ0FHAUrBToFSQVYBWcFdwWGBZYFpgW1BcUF1QXlBfYGBgYW
BicGNwZIBlkGagZ7BowGnQavBsAG0QbjBvUHBwcZBysHPQdPB2EHdAeGB5kHrAe/
B9IH5Qf4CAsIHwgyCEYIWghuCIIIlgiqCL4I0gjnCPsJEAklCToJTwlkCXkJjwmk
CboJzwnlCfsKEQonCj0KVApqCoEKmAquCsUK3ArzCwsLIgs5C1ELaQuAC5gLsAvI
C+EL+QwSDCoMQwxcDHUMjgynDMAM2QzzDQ0NJg1ADVoNdA2ODakNww3eDfgOEw4u
DkkOZA5/DpsOtg7SDu4PCQ8lD0EPXg96D5YPsw/PD+wQCRAmEEMQYRB+EJsQuRDX
EPURExExEU8RbRGMEaoRyRHoEgcSJhJFEmQShBKjEsMS4xMDEyMTQxNjE4MTpBPF
E+UUBhQnFEkUahSLFK0UzhTwFRIVNBVWFXgVmxW9FeAWAxYmFkkWbBaPFrIW1hb6
Fx0XQRdlF4kXrhfSF/cYGxhAGGUYihivGNUY+hkgGUUZaxmRGbcZ3RoEGioaURp3
Gp4axRrsGxQbOxtjG4obshvaHAIcKhxSHHscoxzMHPUdHh1HHXAdmR3DHeweFh5A
HmoelB6+HukfEx8+H2kflB+/H+ogFSBBIGwgmCDEIPAhHCFIIXUhoSHOIfsiJyJV
IoIiryLdIwojOCNmI5QjwiPwJB8kTSR8JKsk2iUJJTglaCWXJccl9yYnJlcmhya3
JugnGCdJJ3onqyfcKA0oPyhxKKIo1CkGKTgpaymdKdAqAio1KmgqmyrPKwIrNitp
K50r0SwFLDksbiyiLNctDC1BLXYtqy3hLhYuTC6CLrcu7i8kL1ovkS/HL/4wNTBs
MKQw2zESMUoxgjG6MfIyKjJjMpsy1DMNM0YzfzO4M/E0KzRlNJ402DUTNU01hzXC
Nf02NzZyNq426TckN2A3nDfXOBQ4UDiMOMg5BTlCOX85vDn5OjY6dDqyOu87LTtr
O6o76DwnPGU8pDzjPSI9YT2hPeA+ID5gPqA+4D8hP2E/oj/iQCNAZECmQOdBKUFq
QaxB7kIwQnJCtUL3QzpDfUPARANER0SKRM5FEkVVRZpF3kYiRmdGq0bwRzVHe0fA
SAVIS0iRSNdJHUljSalJ8Eo3Sn1KxEsMS1NLmkviTCpMcky6TQJNSk2TTdxOJU5u
TrdPAE9JT5NP3VAnUHFQu1EGUVBRm1HmUjFSfFLHUxNTX1OqU/ZUQlSPVNtVKFV1
VcJWD1ZcVqlW91dEV5JX4FgvWH1Yy1kaWWlZuFoHWlZaplr1W0VblVvlXDVchlzW
XSddeF3JXhpebF69Xw9fYV+zYAVgV2CqYPxhT2GiYfViSWKcYvBjQ2OXY+tkQGSU
ZOllPWWSZedmPWaSZuhnPWeTZ+loP2iWaOxpQ2maafFqSGqfavdrT2una/9sV2yv
bQhtYG25bhJua27Ebx5veG/RcCtwhnDgcTpxlXHwcktypnMBc11zuHQUdHB0zHUo
dYV14XY+dpt2+HdWd7N4EXhueMx5KnmJeed6RnqlewR7Y3vCfCF8gXzhfUF9oX4B
fmJ+wn8jf4R/5YBHgKiBCoFrgc2CMIKSgvSDV4O6hB2EgITjhUeFq4YOhnKG14c7
h5+IBIhpiM6JM4mZif6KZIrKizCLlov8jGOMyo0xjZiN/45mjs6PNo+ekAaQbpDW
kT+RqJIRknqS45NNk7aUIJSKlPSVX5XJljSWn5cKl3WX4JhMmLiZJJmQmfyaaJrV
m0Kbr5wcnImc951kndKeQJ6unx2fi5/6oGmg2KFHobaiJqKWowajdqPmpFakx6U4
pammGqaLpv2nbqfgqFKoxKk3qamqHKqPqwKrdavprFys0K1ErbiuLa6hrxavi7AA
sHWw6rFgsdayS7LCszizrrQltJy1E7WKtgG2ebbwt2i34LhZuNG5SrnCuju6tbsu
u6e8IbybvRW9j74KvoS+/796v/XAcMDswWfB48JfwtvDWMPUxFHEzsVLxcjGRsbD
x0HHv8g9yLzJOsm5yjjKt8s2y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB
00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp22vvbgNwF3IrdEN2W3hzeot8p
36/gNuC94UThzOJT4tvjY+Pr5HPk/OWE5g3mlucf56noMui86Ubp0Opb6uXrcOv7
7IbtEe2c7ijutO9A78zwWPDl8XLx//KM8xnzp/Q09ML1UPXe9m32+/eK+Bn4qPk4
+cf6V/rn+3f8B/yY/Sn9uv5L/tz/bf//cGFyYQAAAAAAAwAAAAJmZgAA8qcAAA1Z
AAAT0AAACg52Y2d0AAAAAAAAAAEAAQAAAAAAAAABAAAAAQAAAAAAAAABAAAAAQAA
AAAAAAABAABuZGluAAAAAAAAADYAAKPAAABUQAAATMAAAJmAAAAmgAAAD0AAAFBA
AABUQAACMzMAAjMzAAIzMwAAAAAAAAAAc2YzMgAAAAAAAQu3AAAFlv//81cAAAcp
AAD91///+7f///2mAAAD2gAAwPZtbW9kAAAAAAAATC0AAAaVVjg5RchujAAAAAAA
AAAAAAAAAAAAAAAA/8AAEQgAlgB9AwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAA
AAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFB
BhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNE
RUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqi
o6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz
9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIB
AgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy
0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpz
dHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXG
x8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAgICAgICAwICAwQD
AwMEBQQEBAQFBwUFBQUFBwgHBwcHBwcICAgICAgICAoKCgoKCgsLCwsLDQ0NDQ0N
DQ0NDf/bAEMBAgICAwMDBgMDBg0JBwkNDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0N
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
Blk+CJ7ytHy6En8542bB/yC+Z9/zWbVuhg==
=jmT1
-----END PGP PUBLIC KEY BLOCK-----`;
const keyExpiredBindingSig = `-----BEGIN PGP PUBLIC KEY BLOCK-----
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
hW1Hj9AO8lzggBQ=
=Nt+N
-----END PGP PUBLIC KEY BLOCK-----`;
const sequoiaBobPublicKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: Bob's OpenPGP certificate
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
NEJd3XZRzaXZE2aAMQ==
=NXei
-----END PGP PUBLIC KEY BLOCK-----`;
const signature_with_critical_notation = `-----BEGIN PGP MESSAGE-----
owGbwMvMwMH4oOW7S46CznTG09xJDDE3Wl1KUotLuDousDAwcjBYiSmyXL+48d6x
U1PSGUxcj8IUszKBVMpMaWAAAgEGZpAeh9SKxNyCnFS95PzcytRiBi5OAZjyXXzM
f8WYLqv7TXP61Sa4rqT12CI3xaN73YS2pt089f96odCKaEPnWJ3iSGmzJaW/ug10
2Zo8Wj2k4s7t8wt4H3HtTu+y5UZfV3VOO+l//sdE/o+Lsub8FZH7/eOq7OnbNp4n
vwjE8mqJXetNMfj8r2SCyvkEnlVRYR+/mnge+ib56FdJ8uKtqSxyvgA=
=fRXs
-----END PGP MESSAGE-----`;
const signature_with_non_human_readable_notations = `-----BEGIN PGP SIGNATURE-----
wncEARYKAB8FAl2TS9MYFAAAAAAADAADdGVzdEBrZXkuY29tAQIDAAoJEGZ9
gtV/iL8hrhMBAOQ/UgqRTbx1Z8inGmRdUx1cJU1SR4Pnq/eJNH/CFk5DAP0Q
hUhMKMuiM3pRwdIyDOItkUWQmjEEw7/XmhgInkXsCw==
=ZGXr
-----END PGP SIGNATURE-----
`;
it('Retrieve the issuer Key ID of a signature', async function () {
const publicKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
const privateKey = await openpgp.decryptKey({
privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
passphrase: 'hello world'
});
const message = await openpgp.createMessage({ text: 'test' });
const armoredSignature = await openpgp.sign({
message,
signingKeys: privateKey,
signingKeyIDs: privateKey.getKeyID(),
detached: true,
config: { minRSABits: 1024 }
});
const signature = await openpgp.readSignature({ armoredSignature });
expect(signature.getSigningKeyIDs).to.exist;
expect(signature.getSigningKeyIDs().map(x => x.toHex())).to.include(publicKey.getKeyID().toHex());
});
it('Throws when reading a signature missing the creation time', async function () {
const armoredSignature = `-----BEGIN PGP SIGNATURE-----
wsDtBAABCAAXFiEE0aZuGiOxgsmYD3iM+/zIKgFeczAACgkQ+/zIKgFeczDjiwv+
LFUWJohCYtauaVDHBDHWF+tojls+ducY6uuU6iUTBb1969okh2sjUmvPwIjrVXuk
cfPl616xRqVWolEU9T5sG6MjRlAaG31Oo/FVAVFXZn30ldEtuDss12+/IhES3Wfx
M1jGdJZ1ZMZJpRsNBJXBegEBFKbPleEd66seuuFfvoIUbgsdj7IT/ZlEMlixelWW
igXPVY1C05oPbkC8oo0lVSxwdq6gDvm8a52k3GCtXJELrYGH29C+eDqmyLP1zJOt
NBoZBAqMd9XYVrJtuip436D9pdo5pbg4zCE6uPf2zzx4taK7jGkk6nn7LqVDxvQm
3dAXUnIxw4V9eL3V8SFAKwmouUmHPRbjfnQ70hxYQxDXUcIwu1aYn13QS1s/F/jf
DVRZWaAhNdL9BfHfhEsRVsmjMhe0zwRpaepvXnERbnA/lAUHEmEvgfPFz/2GsAo/
kCNcH9WI6idSzFjuYegECf+ZA1xOCjS9oLTGbSeT7jNfC8dH5+E92qlBLq4Ctt7k
=lMU7
-----END PGP SIGNATURE-----`;
await expect(openpgp.readSignature({ armoredSignature })).to.be.rejectedWith(/Missing signature creation time/);
});
it('Ignores marker packets when verifying signatures', async function () {
const signatureWithMarkerPacket = `-----BEGIN PGP SIGNATURE-----
ygNQR1DCwPMEAAEKAAYFgl8831AAIQkQ+/zIKgFeczAWIQTRpm4aI7GCyZgPeIz7
/MgqAV5zMLckDACWiDbasKMTX/+czxHXyVcFJ/+ZeYqKEjYq6LueHy11XjJ0NZAM
LG9TqsXpWOsHrwE6wUQ7RvKQYtfIAeMUZtD87/XomIj6B/rQC5dHuQTb0b8lrRJb
OuW1sz6AYwceqkSvN3T5+KKNMXkaFw/DzWGPfqQQJDOqfgKxf5uO7GPVzaIU6aXn
76iKHQ7wowT2qHoFhd+t4S11iGr6XJef6QqIW2kTetZMf2Dp/rr7228VJJ1S0RdD
xxKJEbNrmdMNgE8/U+pkWjMQyVOOxWyPKlG3kv2Cu/naj4Lg2io3RhOAuNW5xEJF
gAId3WUNl3/PCu/PcTS1yS/Nj0ptwjKHwG0Zg8Dk5Jey8lUVyVhjxrV5tb6NLoAG
RlyTajZ3Sjhsg4mXHopjSF2w30saN64L5VAfGF1afFu7yzNYC+Fn6GL5yTJfKs4j
PNo4zCwOCumsVP0jWp09yUNflE6MTd21miBgbmPxyLyuwP2YrvT4+rCl+meNZ98a
cJRRGJPL16wINuk=
=VNoM
-----END PGP SIGNATURE-----`;
const key = await openpgp.readKey({ armoredKey: sequoiaBobPublicKey });
const message = await openpgp.createMessage({ text: 'Marker + Detached signature' });
const signature = await openpgp.readSignature({ armoredSignature: signatureWithMarkerPacket });
const { signatures: [sigInfo] } = await openpgp.verify({ message, signature, verificationKeys: key });
expect(await sigInfo.verified).to.be.true;
});
it('Testing signature checking on CAST5-enciphered message', async function() {
const publicKey = await openpgp.readKey({ armoredKey: pub_key_arm1 });
const privateKey = await openpgp.decryptKey({
privateKey: await openpgp.readKey({ armoredKey: priv_key_arm1 }),
passphrase: 'abcd'
});
const message = await openpgp.readMessage({ armoredMessage: msg_arm1 });
const config = {
rejectMessageHashAlgorithms: new Set([openpgp.enums.hash.md5, openpgp.enums.hash.ripemd]),
rejectPublicKeyAlgorithms: new Set()
};
const { data, signatures } = await openpgp.decrypt({ decryptionKeys: privateKey, verificationKeys: publicKey, message, config });
expect(data).to.exist;
expect(await signatures[0].verified).to.be.true;
expect((await signatures[0].signature).packets.length).to.equal(1);
});
it('Consider signature expired at the expiration time', async function() {
const key = await openpgp.readKey({ armoredKey: keyExpiredBindingSig });
const { embeddedSignature } = key.subkeys[0].bindingSignatures[0];
expect(embeddedSignature.isExpired(embeddedSignature.created)).to.be.false;
expect(embeddedSignature.isExpired(new Date(embeddedSignature.getExpirationTime() - 1))).to.be.false;
expect(embeddedSignature.isExpired(embeddedSignature.getExpirationTime())).to.be.true;
});
it('Signing fails if primary key is expired', async function() {
const armoredExpiredKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
xVgEYKKPDRYJKwYBBAHaRw8BAQdAwJcSQMkHVnZPesPJP1JaB9ptV+wG8Io1
vxRKvXQe0wMAAP0fdn6gvpVwFUE4bIRcn9hx6eDxSxUu+tg/t959Oo+iahF1
zRB0ZXN0IDx0ZXN0QGEuaXQ+wpIEEBYKACMFAmCijw0FCQAAAAEECwkHCAMV
CAoEFgACAQIZAQIbAwIeAQAhCRD16pevybCusRYhBHjm9svlAjmgVWL4wvXq
l6/JsK6xGUQBAPzxKS2Qs+vWGpxPT2N2T+PLHIgCOxVJVngj4fzREFH1AP9t
wP+fn3eSsik+vFGy93REmlD1xdu7nW/sHuxY4roqBcddBGCijw0SCisGAQQB
l1UBBQEBB0Cl1lr+aHfy6V4ePmZUULK6VKTCTPTMaPpR2TzKNIJQBQMBCAcA
AP9DZWRqQLCIkF38Q0UC/YXLCDdBEQdnlwpHgA0W1bSWmA3uwn4EGBYIAA8F
AmCijw0FCQAAAAECGwwAIQkQ9eqXr8mwrrEWIQR45vbL5QI5oFVi+ML16pev
ybCusYE4AQCYbXw8ZWoMevbOM7lAttkwyrG3V/nTW6BVo7/M9Pr9swEA0mDI
DQmhI0SZoTKy4EGhS0bNJ+g2+dJ8Y22fKzLWXwo=
=qiIN
-----END PGP PRIVATE KEY BLOCK-----`;
const key = await openpgp.readKey({ armoredKey: armoredExpiredKey });
await expect(openpgp.sign({
signingKeys: key,
message: await openpgp.createMessage({ text: 'Hello World' })
})).to.be.rejectedWith(/key is expired/);
});
it('Signing fails if the signing date is before the key creation date', async function() {
const key = await openpgp.decryptKey({
privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
passphrase: 'hello world'
});
await expect(openpgp.sign({
signingKeys: key,
date: new Date(key.keyPacket.created - 3600),
message: await openpgp.createMessage({ text: 'Hello World' })
})).to.be.rejectedWith(/Signature creation time is in the future/);
});
it('Verification fails if primary key binding signature is expired', async function() {
const armoredSignature = `-----BEGIN PGP SIGNATURE-----
wsDzBAABCgAGBYJfLAFsACEJEHwvqk35PDeyFiEEHdzhXwkhfO4vOzdgfC+qTfk8
N7KiqwwAts4QGB7v9bABCC2qkTxJhmStC0wQMcHRcjL/qAiVnmasQWmvE9KVsdm3
AaXd8mIx4a37/RRvr9dYrY2eE4uw72cMqPxNja2tvVXkHQvk1oEUqfkvbXs4ypKI
NyeTWjXNOTZEbg0hbm3nMy+Wv7zgB1CEvAsEboLDJlhGqPcD+X8a6CJGrBGUBUrv
KVmZr3U6vEzClz3DBLpoddCQseJRhT4YM1nKmBlZ5quh2LFgTSpajv5OsZheqt9y
EZAPbqmLhDmWRQwGzkWHKceKS7nZ/ox2WK6OS7Ob8ZGZkM64iPo6/EGj5Yc19vQN
AGiIaPEGszBBWlOpHTPhNm0LB0nMWqqaT87oNYwP8CQuuxDb6rKJ2lffCmZH27Lb
UbQZcH8J+0UhpeaiadPZxH5ATJAcenmVtVVMLVOFnm+eIlxzov9ntpgGYt8hLdXB
ITEG9mMgp3TGS9ZzSifMZ8UGtHdp9QdBg8NEVPFzDOMGxpc/Bftav7RRRuPiAER+
7A5CBid5
=aQkm
-----END PGP SIGNATURE-----`;
const key = await openpgp.readKey({ armoredKey: keyExpiredBindingSig });
const signature = await openpgp.readSignature({ armoredSignature });
const { signatures: [sigInfo] } = await openpgp.verify({
verificationKeys: key,
message: await openpgp.createMessage({ text: 'Hello World :)' }),
signature
});
await expect(sigInfo.verified).to.be.rejectedWith(/Signature is expired/);
});
it('Verification fails if signing key\'s self-sig were created after the time of signing, unless config allows it', async function() {
const armoredReformattedKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
xVgEYWmlshYJKwYBBAHaRw8BAQdAAxpFNPiHxz9q4HBzWqveHdP/knjwlgv8
pEQCMHDpIZIAAP9WFlwHDuVlvNb7CyoikwmG01nmdMDe9wXQRWA5vasWKA+g
zSV0ZXN0QHJlZm9ybWF0LmNvbSA8dGVzdEByZWZvcm1hdC5jb20+wowEEBYK
AB0FAmFppjQECwkHCAMVCAoEFgACAQIZAQIbAwIeAQAhCRAOZNKOg+/XQxYh
BGqP/hIaYCSJsZ4TrQ5k0o6D79dD+c8BAIXdh2hrC+l49WPN/KZF+ZzvWCWa
W5n+ozbp/sOGXvODAP4oGEj0RUDDA33b6x7fhQysBZxdrrnHxP9AXEdOTQC3
CsddBGFppbISCisGAQQBl1UBBQEBB0Cjy8Z2K7rl6J6AK1lCfVozmyLE0Gbv
1cspce6oCF6oCwMBCAcAAP9OL5V80EaYm2ic19aM+NtTj4UNOqKqIt10AaH9
SlcdMBDgwngEGBYIAAkFAmFppjQCGwwAIQkQDmTSjoPv10MWIQRqj/4SGmAk
ibGeE60OZNKOg+/XQx/EAQCM0UYrObp60YbOCxu07Dm6XjCVylbOcsaxCnE7
2eMU4AD+OkgajZgbqSIdAR1ud76FW+W+3xlDi/SMFdU7D49SbQI=
=ASQu
-----END PGP PRIVATE KEY BLOCK-----
`;
const armoredMessage = `-----BEGIN PGP MESSAGE-----
xA0DAQoWDmTSjoPv10MByw91AGFpplFwbGFpbnRleHTCdQQBFgoABgUCYWml
sgAhCRAOZNKOg+/XQxYhBGqP/hIaYCSJsZ4TrQ5k0o6D79dDDWwBAKUnRWXj
P3HTW521iD/DngK54mYS3feQzhDokhkYjO3UAP0ZlsYShKaJvXh+JgvR5BPP
gjVcn04JVVlxqgVnMqeVBw==
=eyO7
-----END PGP MESSAGE-----`;
// the key was reformatted and the message signature date preceeds the key self-signature creation date
const key = await openpgp.readKey({ armoredKey: armoredReformattedKey });
const { signatures: [sigInfoRejected] } = await openpgp.verify({
verificationKeys: key,
message: await openpgp.readMessage({ armoredMessage })
});
await expect(sigInfoRejected.verified).to.be.rejectedWith(/Signature creation time is in the future/);
// since the key is valid at the current time, the message should be verifiable if the `config` allows it
const { signatures: [sigInfoValid] } = await openpgp.verify({
verificationKeys: key,
message: await openpgp.readMessage({ armoredMessage }),
config: { allowInsecureVerificationWithReformattedKeys: true }
});
expect(await sigInfoValid.verified).to.be.true;
});
it('Verification fails if signing key was already expired at the time of signing (one-pass signature, streamed)', async function() {
const armoredMessage = `-----BEGIN PGP MESSAGE-----
xA0DAQgB4IT3RGwgLJcByxR1AGCc8BxIZWxsbyBXb3JsZCA6KcKzBAEBCAAG
BQJgnPAcACEJEOCE90RsICyXFiEE19Gx3sbKlGcANEXF4IT3RGwgLJdssAP+
KpyVi30z5iMckULAQ3Q34IB29Gxa1/99ABSld6iIVGRCfmZZlS5UGcxJJGoL
vZ1RAL4YQx/GLV1dBcKWFwzb5G2/ip4coDMCDGTAwnwjcPwjHpfMQ9gX59mG
AkLaG/AkATpuH+DMkYDmKbDLGgD+N4yuxXBJmBfC2IBe4J1S2Gg=
=zvNP
-----END PGP MESSAGE-----`;
const key = await openpgp.decryptKey({
privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
passphrase: 'hello world'
});
const { privateKey: expiredKey } = await openpgp.reformatKey({
privateKey: key,
userIDs: key.users.map(user => user.userID),
keyExpirationTime: 1,
date: key.keyPacket.created,
format: 'object'
});
await stream.loadStreamsPonyfill();
const { signatures: [sigInfo] } = await openpgp.verify({
verificationKeys: expiredKey,
message: await openpgp.readMessage({ armoredMessage: stream.toStream(armoredMessage) }),
config: { minRSABits: 1024 }
});
await expect(sigInfo.verified).to.be.rejectedWith(/Primary key is expired/);
});
it('Verification fails if signing key was already expired at the time of signing (standard signature)', async function() {
const armoredMessage = `-----BEGIN PGP MESSAGE-----
wrMEAQEIAAYFAmCc8BwAIQkQ4IT3RGwgLJcWIQTX0bHexsqUZwA0RcXghPdE
bCAsl2ywA/4qnJWLfTPmIxyRQsBDdDfggHb0bFrX/30AFKV3qIhUZEJ+ZlmV
LlQZzEkkagu9nVEAvhhDH8YtXV0FwpYXDNvkbb+KnhygMwIMZMDCfCNw/CMe
l8xD2Bfn2YYCQtob8CQBOm4f4MyRgOYpsMsaAP43jK7FcEmYF8LYgF7gnVLY
aMsUdQBgnPAcSGVsbG8gV29ybGQgOik=
=s9xh
-----END PGP MESSAGE-----`;
const key = await openpgp.decryptKey({
privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
passphrase: 'hello world'
});
const { privateKey: expiredKey } = await openpgp.reformatKey({
privateKey: key,
userIDs: key.users.map(user => user.userID),
keyExpirationTime: 1,
date: key.keyPacket.created,
format: 'object'
});
await stream.loadStreamsPonyfill();
const { signatures: [sigInfo] } = await openpgp.verify({
verificationKeys: expiredKey,
message: await openpgp.readMessage({ armoredMessage: stream.toStream(armoredMessage) }),
config: { minRSABits: 1024 }
});
await expect(sigInfo.verified).to.be.rejectedWith(/Primary key is expired/);
});
it('Verification succeeds if an expired signing key was valid at the time of signing (with streaming)', async function() {
const armoredMessage = `-----BEGIN PGP MESSAGE-----
xA0DAQgB4IT3RGwgLJcByxF1AGCdJvJoZWxsbyB3b3JsZMKzBAEBCAAGBQJS
YS9OACEJEOCE90RsICyXFiEE19Gx3sbKlGcANEXF4IT3RGwgLJcPBQP/csZd
9AhZQ3+dPkwlqlsXqYMlVEagYDavlUDI2CEJ2cn1rqHBuMlRkmYs7UqODku4
FhJ6WvghiEKx8vqghDuaUXmcKuXhYe+PomD4XBmpbURBXCdPnojTINUj7GPK
eSvSZutLuKKbidSYMLhWROPlwKc2GU2ws6PrLZAyCAel/lU=
=xDib
-----END PGP MESSAGE-----`;
const key = await openpgp.decryptKey({
privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
passphrase: 'hello world'
});
const { privateKey: expiredKey } = await openpgp.reformatKey({
privateKey: key,
userIDs: key.users.map(user => user.userID),
keyExpirationTime: 1,
date: key.keyPacket.created,
format: 'object'
});
await stream.loadStreamsPonyfill();
const { signatures: [sigInfo] } = await openpgp.verify({
verificationKeys: expiredKey,
message: await openpgp.readMessage({ armoredMessage: stream.toStream(armoredMessage) }),
config: { minRSABits: 1024 }
});
expect(await sigInfo.verified).to.be.true;
});
it('Verification succeeds if an expired signing key was valid at the time of signing (without streaming)', async function() {
const armoredMessage = `-----BEGIN PGP MESSAGE-----
xA0DAQgB4IT3RGwgLJcByxF1AGCdJvJoZWxsbyB3b3JsZMKzBAEBCAAGBQJS
YS9OACEJEOCE90RsICyXFiEE19Gx3sbKlGcANEXF4IT3RGwgLJcPBQP/csZd
9AhZQ3+dPkwlqlsXqYMlVEagYDavlUDI2CEJ2cn1rqHBuMlRkmYs7UqODku4
FhJ6WvghiEKx8vqghDuaUXmcKuXhYe+PomD4XBmpbURBXCdPnojTINUj7GPK
eSvSZutLuKKbidSYMLhWROPlwKc2GU2ws6PrLZAyCAel/lU=
=xDib
-----END PGP MESSAGE-----`;
const key = await openpgp.decryptKey({
privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
passphrase: 'hello world'
});
const { privateKey: expiredKey } = await openpgp.reformatKey({
privateKey: key,
userIDs: key.users.map(user => user.userID),
keyExpirationTime: 1,
date: key.keyPacket.created,
format: 'object'
});
const { signatures: [sigInfo] } = await openpgp.verify({
verificationKeys: expiredKey,
message: await openpgp.readMessage({ armoredMessage }),
config: { minRSABits: 1024 }
});
expect(await sigInfo.verified).to.be.true;
});
it('Reject cleartext message with arbitrary text added around hash headers (spoofed cleartext message)', async function() {
await expect(openpgp.readCleartextMessage({ cleartextMessage: `-----BEGIN PGP SIGNED MESSAGE-----
This is not signed but you might think it is Hash: SHA512
This is signed
-----BEGIN PGP SIGNATURE-----
wnUEARYKACcFgmTsqxgJkEhlqJkkhIfRFiEEUA/OS4xZ3EwNC5l8SGWomSSE
h9EAALyPAQDDR0IYwq/5XMVSYPWojBamM4NhcP5arA656ALIq9cJYAEAlw0H
Fk7EflUZzngwY4lBzYAfnNBjEjc30xD/ddo+rwE=
=O7mt
-----END PGP SIGNATURE-----` })).to.be.rejectedWith(/Only "Hash" header allowed/);
await expect(openpgp.readCleartextMessage({ cleartextMessage: `-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512\vThis is not signed but you might think it is
This is signed
-----BEGIN PGP SIGNATURE-----
wnUEARYKACcFgmTsqxgJkEhlqJkkhIfRFiEEUA/OS4xZ3EwNC5l8SGWomSSE
h9EAALyPAQDDR0IYwq/5XMVSYPWojBamM4NhcP5arA656ALIq9cJYAEAlw0H
Fk7EflUZzngwY4lBzYAfnNBjEjc30xD/ddo+rwE=
=O7mt
-----END PGP SIGNATURE-----` })).to.be.rejectedWith(/Unknown hash algorithm in armor header/);
});
it('Supports non-human-readable notations', async function() {
const { packets: [signature] } = await openpgp.readSignature({ armoredSignature: signature_with_non_human_readable_notations });
// There are no human-readable notations so `notations` property does not
// expose the `test@key.com` notation.
expect(Object.keys(signature.notations).length).to.equal(0);
expect(signature.notations['test@key.com']).to.equal(undefined);
// The notation is readable through `rawNotations` property:
expect(signature.rawNotations.length).to.equal(1);
const notation = signature.rawNotations[0];
expect(notation.name).to.equal('test@key.com');
expect(notation.value).to.deep.equal(new Uint8Array([0x01, 0x02, 0x03]));
expect(notation.humanReadable).to.equal(false);
});
it('Verify V4 signature. Hash: SHA1. PK: RSA. Signature Type: 0x00 (binary document)', async function() {
const { rejectMessageHashAlgorithms, minRSABits } = openpgp.config;
Object.assign(openpgp.config, {
rejectMessageHashAlgorithms: new Set([openpgp.enums.hash.md5, openpgp.enums.hash.ripemd]),
minRSABits: 1024
});
try {
const signedArmor =
['-----BEGIN PGP MESSAGE-----',
'Version: GnuPG v2.0.19 (GNU/Linux)',
'',
'owGbwMvMwMT4oOW7S46CznTGNeZJLCWpFSVBU3ZGF2fkF5Uo5KYWFyemp3LlAUUV',
'cjLzUrneTp3zauvaN9O26L9ZuOFNy4LXyydwcXXMYWFgZGJgY2UCaWXg4hSAmblK',
'nPmfsXYxd58Ka9eVrEnSpzilr520fXBrJsf2P/oTqzTj3hzyLG0o3TTzxFfrtOXf',
'cw6U57n3/Z4X0pEZ68C5/o/6NpPICD7fuEOz3936raZ6wXGzueY8pfPnVjY0ajAc',
'PtJzvvqj+ubYaT1sK9wWhd9lL3/V+9Zuua9QjOWC22buchsCroh8fLoZAA==',
'=VH8F',
'-----END PGP MESSAGE-----'].join('\n');
const sMsg = await openpgp.readMessage({ armoredMessage: signedArmor });
const pub_key = await openpgp.readKey({ armoredKey: pub_key_arm2 });
const verified = await sMsg.verify([pub_key]);
stream.readToEnd(sMsg.getLiteralData());
expect(verified).to.exist;
expect(verified).to.have.length(1);
expect(await verified[0].verified).to.be.true;
expect((await verified[0].signature).packets.length).to.equal(1);
} finally {
Object.assign(openpgp.config, { rejectMessageHashAlgorithms, minRSABits });
}
});
it('Verify signature of signed and encrypted message from GPG2 with openpgp.decrypt', async function() {
const msg_armor =
['-----BEGIN PGP MESSAGE-----',
'Version: GnuPG v2.0.19 (GNU/Linux)',
'',
'hIwD4IT3RGwgLJcBBADEBdm+GEW7IV1K/Bykg0nB0WYO08ai7/8/+Y/O9xu6RiU0',
'q7/jWuKms7kSjw9gxMCjf2dGnAuT4Cg505Kj5WfeBuHh618ovO8qo4h0qHyp1/y3',
'o1P0IXPAb+LGJOeO7DyM9Xp2AOBiIKOVWzFTg+MBZOc+XZEVx3FioHfm9SSDudLA',
'WAEkDakCG6MRFj/7SmOiV8mQKH+YPMKT69eDZW7hjINabrpM2pdRU7c9lC7CMUBx',
'Vj7wZsQBMASSC8f2rhpGA2iKvYMsmW3g9R1xkvj1MXWftSPUS4jeNTAgEwvvF6Af',
'cP+OYSXKlTbwfEr73ES2O3/IFE9sHRjPqWaxWuv4DDQ5YfIxE54C1aE8Aq5/QaIH',
'v38TUSia0yEMCc/tJd58DikkT07AF162tcx9Ro0ZjhudyuvUyXIfPfxA+XWR2pdz',
'ifxyV4zia9RvaCUY8vXGM+gQJ3NNXx2LkZA3kWUEyxFVL1Vl/XUQY0M6U+uccSk4',
'eMXm6eyEWDcj0lBRckqKoKo1w/uan11jPuHsnRz6jO9DsuKEz79UDgI=',
'=cFi7',
'-----END PGP MESSAGE-----'].join('\n');
const plaintext = 'short message\nnext line\n한국어/조선말';
const message = await openpgp.readMessage({ armoredMessage: msg_armor });
const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
const privKey = await openpgp.decryptKey({
privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
passphrase: 'hello world'
});
return openpgp.decrypt({
decryptionKeys: privKey, verificationKeys: pubKey , message, config: { minRSABits: 1024 }
}).then(async ({ signatures, data }) => {
expect(data).to.exist;
expect(data).to.equal(plaintext);
expect(signatures).to.have.length(1);
expect(await signatures[0].verified).to.be.true;
expect((await signatures[0].signature).packets.length).to.equal(1);
});
});
it('Verify signed message with two one pass signatures', async function() {
const msg_armor =
['-----BEGIN PGP MESSAGE-----',
'Version: GnuPG v2.0.19 (GNU/Linux)',
'',
'owGbwMvMwMF4+5Pyi4Jg3y8ME8DcBy3fXXIUdKYzrjFNYilJrSgJmsXDXJyRX1Si',
'kJtaXJyYnsqVBxRVyMnMS+V6O3XOq61r30zbov9m4YY3LQteL5/QMYeFgZGDgY2V',
'CaSRgYtTAGZiYxYLwySbQk07ptZel6gmjrKyBWsyWdkOG3oscLBdIpXXfDdb6fNv',
'8ULN5L1ed+xNo79P2dBotWud6vn7e9dtLJ7o12PunnvEz8gyyvP4/As/los0xsnZ',
'H+8ublrhvGtLxJUZuUKZO6QdHq2Nepuw8OrfiMXPBDQXXpV2q11Ze+rD3lndgv/C',
'bJQNOhll0J0H839jFvt/16m20h/ZmDoWqJywapnypjdIjcXr+7rJFess40yenV7Q',
'2LSu/EX6Aq29x+dv+GPUMfuhTNE3viWWUR4PD6T7XfmdViUwmSf8fkRNUn/t3a2n',
'cq46Xr36seCor/OLp0atSZwHrjx2SU5zPLheZn+zw/0d1/YZnD7AEeP9s/Cuycyv',
'CZ5HZNKufvB8fsh+dfdSXW0GfqkPfxk36Vw8ufpjaoZDyt2nxxg/6D4KS3UvZzv3',
'axdLZ9yd0OJNZv4P501If24W4vTGz6nI7Ser8Yd2PiOvE5MWMT0wLZQ+zPX1sv0/',
's8PvkyWmVM0O0fB/ZSHovHNNPffDg/rWhzOmXQ9/7vTn477F+aWm5sYzJ75/BQA=',
'=+L0S',
'-----END PGP MESSAGE-----'].join('\n');
const plaintext = 'short message\nnext line\n한국어/조선말';
const sMsg = await openpgp.readMessage({ armoredMessage: msg_armor });
const pubKey2 = await openpgp.readKey({ armoredKey: pub_key_arm2 });
const pubKey3 = await openpgp.readKey({ armoredKey: pub_key_arm3 });
const keyIDs = sMsg.getSigningKeyIDs();
expect(pubKey2.getKeys(keyIDs[1])).to.not.be.empty;
expect(pubKey3.getKeys(keyIDs[0])).to.not.be.empty;
const { data, signatures } = await openpgp.verify({ message: sMsg, verificationKeys: [pubKey2, pubKey3], config: { minRSABits: 1024 } });
expect(data).to.equal(plaintext);
expect(signatures).to.exist;
expect(signatures).to.have.length(2);
expect(await signatures[0].verified).to.be.true;
expect(await signatures[1].verified).to.be.true;
expect((await signatures[0].signature).packets.length).to.equal(1);
expect((await signatures[1].signature).packets.length).to.equal(1);
});
it('Verify fails with signed message with critical notations', async function() {
const message = await openpgp.readMessage({ armoredMessage: signature_with_critical_notation });
const key = await openpgp.readKey({ armoredKey: pub_key_arm2 });
const { signatures: [sig] } = await openpgp.verify({ message, verificationKeys: key, config: { minRSABits: 1024 } });
await expect(sig.verified).to.be.rejectedWith(/Unknown critical notation: test@example.com/);
});
it('Verify succeeds with known signed message with critical notations', async function() {
const message = await openpgp.readMessage({ armoredMessage: signature_with_critical_notation });
const key = await openpgp.readKey({ armoredKey: pub_key_arm2 });
const config = { knownNotations: ['test@example.com'], minRSABits: 1024 };
const { signatures: [sig] } = await openpgp.verify({ message, verificationKeys: key, config });
expect(await sig.verified).to.be.true;
});
it('Can create notations', async function() {
const privKey = await openpgp.decryptKey({
privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
passphrase: 'hello world'
});
const config = { minRSABits: 1024 };
const message_with_notation = await openpgp.encrypt({
message: await openpgp.createMessage({ text: 'test' }),
encryptionKeys: privKey,
signingKeys: privKey,
signatureNotations: [
{
name: 'test@example.com',
value: new TextEncoder().encode('test'),
humanReadable: true,
critical: true
},
{
name: 'séparation-de-domaine@proton.ch',
value: new Uint8Array([0, 1, 2, 3]),
humanReadable: false,
critical: false
}
],
config
});
await expect(openpgp.decrypt({
message: await openpgp.readMessage({ armoredMessage: message_with_notation }),
decryptionKeys: privKey,
verificationKeys: privKey,
expectSigned: true,
config
})).to.be.rejectedWith('Unknown critical notation: test@example.com');
const { signatures: [sig] } = await openpgp.decrypt({
message: await openpgp.readMessage({ armoredMessage: message_with_notation }),
decryptionKeys: privKey,
verificationKeys: privKey,
config: {
knownNotations: ['test@example.com'],
...config
}
});
expect(await sig.verified).to.be.true;
const { packets: [{ rawNotations: notations }] } = await sig.signature;
expect(notations).to.have.length(2);
expect(notations[0].name).to.equal('test@example.com');
expect(notations[0].value).to.deep.equal(new Uint8Array([116, 101, 115, 116]));
expect(notations[0].humanReadable).to.be.true;
expect(notations[0].critical).to.be.true;
expect(notations[1].name).to.equal('séparation-de-domaine@proton.ch');
expect(notations[1].value).to.deep.equal(new Uint8Array([0, 1, 2, 3]));
expect(notations[1].humanReadable).to.be.false;
expect(notations[1].critical).to.be.false;
});
it('Verify cleartext signed message with two signatures with openpgp.verify', async function() {
const cleartextMessage =
['-----BEGIN PGP SIGNED MESSAGE-----',
'Hash: SHA256',
'',
'short message',
'next line',
'한국어/조선말',
'-----BEGIN PGP SIGNATURE-----',
'Version: GnuPG v2.0.19 (GNU/Linux)',
'',
'iJwEAQEIAAYFAlKcju8ACgkQ4IT3RGwgLJci6gP/dCmIraUa6AGpJxzGfK+jYpjl',
'G0KunFyGmyPxeJVnPi2bBp3EPIbiayQ71CcDe9DKpF046tora07AA9eo+/YbvJ9P',
'PWeScw3oj/ejsmKQoDBGzyDMFUphevnhgc5lENjovJqmiu6FKjNmADTxcZ/qFTOq',
'44EWTgdW3IqXFkNpKjeJARwEAQEIAAYFAlKcju8ACgkQ2/Ij6HBTTfQi6gf9HxhE',
'ycLDhQ8iyC090TaYwsDytScU2vOMiI5rJCy2tfDV0pfn+UekYGMnKxZTpwtmno1j',
'mVOlieENszz5IcehS5TYwk4lmRFjoba+Z8qwPEYhYxP29GMbmRIsH811sQHFTigo',
'LI2t4pSSSUpAiXd9y6KtvkWcGGn8IfkNHCEHPh1ov28QvH0+ByIiKYK5N6ZB8hEo',
'0uMYhKQPVJdPCvMkAxQCRPw84EvmxuJ0HMCeSB9tHQXpz5un2m8D9yiGpBQPnqlW',
'vCCq7fgaUz8ksxvQ9bSwv0iIIbbBdTP7Z8y2c1Oof6NDl7irH+QCeNT7IIGs8Smn',
'BEzv/FqkQAhjy3Krxg==',
'=3Pkl',
'-----END PGP SIGNATURE-----'].join('\n');
const plaintext = 'short message\nnext line\n한국어/조선말';
const message = await openpgp.readCleartextMessage({ cleartextMessage });
const pubKey2 = await openpgp.readKey({ armoredKey: pub_key_arm2 });
const pubKey3 = await openpgp.readKey({ armoredKey: pub_key_arm3 });
const keyIDs = message.getSigningKeyIDs();
expect(pubKey2.getKeys(keyIDs[0])).to.not.be.empty;
expect(pubKey3.getKeys(keyIDs[1])).to.not.be.empty;
return openpgp.verify({ verificationKeys:[pubKey2, pubKey3], message, config: { minRSABits: 1024 } }).then(async function({ signatures, data }) {
expect(data).to.equal(plaintext);
expect(signatures).to.have.length(2);
expect(await signatures[0].verified).to.be.true;
expect(await signatures[1].verified).to.be.true;
expect((await signatures[0].signature).packets.length).to.equal(1);
expect((await signatures[1].signature).packets.length).to.equal(1);
});
});
it('Verify latin-1 signed message', async function() {
const latin1Binary = util.hexToUint8Array('48e46c6cf62057e86c74');
const message = await openpgp.createMessage({ binary: latin1Binary });
message.appendSignature(`-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEET5+J9VBawdGiYGMc2xGHud1faTsFAl5lE/AACgkQ2xGHud1f
aTtIuw//YWrVaXLyP8sGBc0uUSLxQbmfQQYV8Oq8Vsg+jV4orc73wmEy8+Nj5m2g
fFEPaWy07dfDBtv874XCsZmCM+ZhwkGaT9lwcCxkxNZeywTE5JRS1/6Ky3G4gDZ/
QozTXr/ZNPXF6bBENqhfqeO2xkD577bjiPu5wLcu3/RR39YnWp5zQu9ynJbpwobz
HHQW5TgSUgi/9tInQ+cc7vMkHzfe2Zg45HkyaStBW1x7Fm9FLv8GNw1R2jVbUlsX
SEL7yPrsZAmgyu1ifMrTTY4vunuUNUFiGZN9UC75b7s4tjKkvJ0sfdQusC0vmcE0
Wq7dAoeX72B94TITuXldDGIfL01smycm+mEvf/kXxQg81wj4la72IzhdWeC6r4PF
558QDEtZy984iL8RJKpbjwgsxyvfM9zf9qtmuNdscbZaZsx9LRe21uaKhX8yJsAU
jCgyjM5e7CMdvBUyxrpGzS+mP/rh77r2TXkg6u2+8Nj4osxwkfxvDIGpJfaIWl2a
sNQ4Bsgm6rcNYaxUVbOsBFJddBb503KpTX5aK8TuC6AINg6bV6rmfj2Jc/QX4eHb
Xgm6EyJo74z5R7YvA3XdUZf1sfwfoMLbbUqpeHEZOcWy4vrS79Omx1MyPxKHPiXU
PAAeuQTUrcJdZeJ86eQ9cCUB216HCwSKOWTQRzL+hBWKXij4WD4=
=ZEFm
-----END PGP SIGNATURE-----`);
const pubKey = await openpgp.readKey({ armoredKey: pub_latin1_msg });
return message.verify([pubKey]).then(async verifiedSig => {
expect(await stream.readToEnd(message.getLiteralData())).to.equal(latin1Binary);
expect(verifiedSig).to.exist;
expect(verifiedSig).to.have.length(1);
expect(await verifiedSig[0].verified).to.be.true;
expect((await verifiedSig[0].signature).packets.length).to.equal(1);
});
});
it('Verify cleartext signed message with trailing spaces from GPG', async function() {
const cleartextMessage =
`-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
space:
space and tab: \t
no trailing space
tab:\t
tab and space:\t
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
iJwEAQECAAYFAlrZzCQACgkQ4IT3RGwgLJeWggP+Pb33ubbELIzg9/imM+zlR063
g0FbG4B+RGZNFSbaDArUgh9fdVqBy8M9vvbbDMBalSiQxY09Lrasfb+tsomrygbN
NisuPRa5phPhn1bB4hZDb2ed/iK41CNyU7QHuv4AAvLC0mMamRnEg0FW2M2jZLGh
zmuVOdNuWQqxT9Sqa84=
=bqAR
-----END PGP SIGNATURE-----`;
const plaintext = 'space: \nspace and tab: \t\nno trailing space\n \ntab:\t\ntab and space:\t ';
const message = await openpgp.readCleartextMessage({ cleartextMessage });
const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
const keyIDs = message.getSigningKeyIDs();
expect(pubKey.getKeys(keyIDs[0])).to.not.be.empty;
const { signatures, data } = await openpgp.verify({
verificationKeys:[pubKey],
message,
config: { minRSABits: 1024, rejectMessageHashAlgorithms: new Set() }
});
expect(data).to.equal(plaintext.replace(/[ \t]+$/mg, ''));
expect(signatures).to.have.length(1);
expect(await signatures[0].verified).to.be.true;
expect((await signatures[0].signature).packets.length).to.equal(1);
});
it('Verify cleartext signed message with trailing spaces incorrectly normalised (from OpenPGP.js v3.0.9-v5.3.1)', async function() {
// We used to not strip trailing whitespace with \r\n line endings when signing cleartext messages
const armoredSignature = `-----BEGIN PGP SIGNATURE-----
Version: OpenPGP.js v5.3.1
wrMEAQEIAAYFAmLjqsQAIQkQ4IT3RGwgLJcWIQTX0bHexsqUZwA0RcXghPdE
bCAsl2TvBADLOHYXevDSc3LtLRYR1HteijL/MssCCoZIfuGihd5AzJpD2h2j
L8UuxlfERJn15RlFsDzlkMNMefJp5ltC8kKcf+HTuBUi+xf2t2nvlf8CrdjY
vcEqswjkODDxxZ842h0sC0ZtbzWuMXIvODEdzZxBjhlmZmv9VKQ5uyb0oD/5
WQ==
=o2gq
-----END PGP SIGNATURE-----`;
const cleartextMessage = `
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
this text
used to be incorrectly normalised
${armoredSignature}
`;
const signedText = 'this text \r\nused to be incorrectly normalised';
const message = await openpgp.readCleartextMessage({ cleartextMessage });
const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
// Direct verification won't work since the signed data was not stripped of the trailing whitespaces,
// as required for cleartext messages. Verification would always fail also in the affected OpenPGP.js versions.
await expect(openpgp.verify({
verificationKeys:[pubKey],
message,
config: { minRSABits: 1024 },
expectSigned: true
})).to.be.rejectedWith(/Signed digest did not match/);
// The signature should be verifiable over non-normalised text
const { signatures, data } = await openpgp.verify({
verificationKeys:[pubKey],
message: await openpgp.createMessage({ text: signedText }),
signature: await openpgp.readSignature({ armoredSignature }),
config: { minRSABits: 1024 },
expectSigned: true
});
expect(data).to.equal(signedText);
expect(signatures).to.have.length(1);
expect(await signatures[0].verified).to.be.true;
});
it('Sign and verify cleartext signed message with trailing spaces correctly normalised', async function() {
const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
const privKey = await openpgp.decryptKey({
privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
passphrase: 'hello world'
});
const config = { minRSABits: 1024 };
const message = await openpgp.createCleartextMessage({
text: 'this text \r\nused to be incorrectly normalised'
});
const expectedText = 'this text\nused to be incorrectly normalised';
expect(message.getText()).to.equal(expectedText);
const cleartextMessage = await openpgp.sign({ message, signingKeys: privKey, config, format: 'armored' });
const { signatures, data } = await openpgp.verify({
message: await openpgp.readCleartextMessage({ cleartextMessage }),
verificationKeys:[pubKey],
config
});
expect(data).to.equal(expectedText);
expect(signatures).to.have.length(1);
expect(await signatures[0].verified).to.be.true;
});
function tests() {
it('Verify signed message with trailing spaces from GPG', async function() {
const armoredMessage =
`-----BEGIN PGP MESSAGE-----
Version: GnuPG v1
owGbwMvMyMT4oOW7S46CznTG01El3MUFicmpxbolqcUlUTev14K5Vgq8XGCGQmJe
ikJJYpKVAicvV16+QklRYmZOZl66AliWl0sBqBAkzQmmwKohBnAqdMxhYWRkYmBj
ZQIZy8DFKQCztusM8z+Vt/svG80IS/etn90utv/T16jquk69zPvp6t9F16ryrwpb
kfVlS5Xl38KnVYxWvIor0nao6WUczA4vvZX9TXPWnnW3tt1vbZoiqWUjYjjjhuKG
4DtmMTuL3TW6/zNzVfWp/Q11+71O8RGnXMsBvWM6mSqX75uLiPo6HRaUDHnvrfCP
yYDnCgA=
=15ki
-----END PGP MESSAGE-----`;
const plaintext = 'space: \nspace and tab: \t\nno trailing space\n \ntab:\t\ntab and space:\t ';
const message = await openpgp.readMessage({ armoredMessage });
const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
const keyIDs = message.getSigningKeyIDs();
expect(pubKey.getKeys(keyIDs[0])).to.not.be.empty;
return openpgp.verify({ verificationKeys: [pubKey], message, config: { minRSABits: 1024 } }).then(async ({ data, signatures }) => {
expect(data).to.equal(plaintext);
expect(signatures).to.have.length(1);
if (openpgp.config.rejectMessageHashAlgorithms.has(openpgp.enums.hash.sha1)) {
await expect(signatures[0].verified).to.be.rejected;
} else {
expect(await signatures[0].verified).to.be.true;
}
expect((await signatures[0].signature).packets.length).to.equal(1);
});
});
it('Streaming verify signed message with trailing spaces from GPG', async function() {
const armoredMessage =
`-----BEGIN PGP MESSAGE-----
Version: GnuPG v1
owGbwMvMyMT4oOW7S46CznTG01El3MUFicmpxbolqcUlUTev14K5Vgq8XGCGQmJe
ikJJYpKVAicvV16+QklRYmZOZl66AliWl0sBqBAkzQmmwKohBnAqdMxhYWRkYmBj
ZQIZy8DFKQCztusM8z+Vt/svG80IS/etn90utv/T16jquk69zPvp6t9F16ryrwpb
kfVlS5Xl38KnVYxWvIor0nao6WUczA4vvZX9TXPWnnW3tt1vbZoiqWUjYjjjhuKG
4DtmMTuL3TW6/zNzVfWp/Q11+71O8RGnXMsBvWM6mSqX75uLiPo6HRaUDHnvrfCP
yYDnCgA=
=15ki
-----END PGP MESSAGE-----`.split('');
const plaintext = 'space: \nspace and tab: \t\nno trailing space\n \ntab:\t\ntab and space:\t ';
await stream.loadStreamsPonyfill();
const message = await openpgp.readMessage({
armoredMessage: new stream.ReadableStream({
async pull(controller) {
await new Promise(setTimeout);
controller.enqueue(armoredMessage.shift());
if (!armoredMessage.length) controller.close();
}
})
});
const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
const keyIDs = message.getSigningKeyIDs();
expect(pubKey.getKeys(keyIDs[0])).to.not.be.empty;
return openpgp.verify({ verificationKeys: [pubKey], message, config: { minRSABits: 1024 } }).then(async function(cleartextSig) {
expect(cleartextSig).to.exist;
expect(await stream.readToEnd(cleartextSig.data)).to.equal(plaintext);
expect(cleartextSig.signatures).to.have.length(1);
if (!openpgp.config.rejectMessageHashAlgorithms.has(openpgp.enums.hash.sha1)) {
expect(await cleartextSig.signatures[0].verified).to.be.true;
} else {
await expect(cleartextSig.signatures[0].verified).to.be.rejectedWith('Insecure message hash algorithm: SHA1');
}
expect((await cleartextSig.signatures[0].signature).packets.length).to.equal(1);
});
});
it('Verify signed message with missing signature packet', async function() {
const armoredMessage =
`-----BEGIN PGP MESSAGE-----
Version: OpenPGP.js v3.1.3
Comment: https://openpgpjs.org
yFgBO8LLzMjE+KDlu0uOgs50xtNRJdzFBYnJqcW6JanFJVE3r9eCuVYKvFxg
hkJiXopCSWKSlQInL1devkJJUWJmTmZeugJYlpdLAagQJM0JpsCqIQZwKgAA
=D6TZ
-----END PGP MESSAGE-----`;
const plaintext = 'space: \nspace and tab: \t\nno trailing space\n \ntab:\t\ntab and space:\t ';
const message = await openpgp.readMessage({ armoredMessage });
const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
const keyIDs = message.getSigningKeyIDs();
expect(pubKey.getKeys(keyIDs[0])).to.not.be.empty;
return openpgp.verify({ verificationKeys: [pubKey], message, config: { minRSABits: 1024 } }).then(async ({ data, signatures }) => {
expect(data).to.equal(plaintext);
expect(signatures).to.have.length(0);
});
});
it('Streaming verify signed message with missing signature packet', async function() {
const armoredMessage =
`-----BEGIN PGP MESSAGE-----
Version: OpenPGP.js v3.1.3
Comment: https://openpgpjs.org
yFgBO8LLzMjE+KDlu0uOgs50xtNRJdzFBYnJqcW6JanFJVE3r9eCuVYKvFxg
hkJiXopCSWKSlQInL1devkJJUWJmTmZeugJYlpdLAagQJM0JpsCqIQZwKgAA
=D6TZ
-----END PGP MESSAGE-----`.split('');
const plaintext = 'space: \nspace and tab: \t\nno trailing space\n \ntab:\t\ntab and space:\t ';
await stream.loadStreamsPonyfill();
const message = await openpgp.readMessage({
armoredMessage: new stream.ReadableStream({
async pull(controller) {
await new Promise(setTimeout);
controller.enqueue(armoredMessage.shift());
if (!armoredMessage.length) controller.close();
}
})
});
const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
const keyIDs = message.getSigningKeyIDs();
expect(pubKey.getKeys(keyIDs[0])).to.not.be.empty;
return openpgp.verify({ verificationKeys: [pubKey], message, config: { minRSABits: 1024 } }).then(async ({ data, signatures }) => {
expect(await stream.readToEnd(data)).to.equal(plaintext);
expect(signatures).to.have.length(1);
await expect(signatures[0].verified).to.be.rejectedWith('Corresponding signature packet missing');
expect((await signatures[0].signature).packets.length).to.equal(0);
});
});
}
tests();
let rejectMessageHashAlgorithms;
tryTests('Accept SHA-1 signatures', tests, {
if: true,
before: function() {
({ rejectMessageHashAlgorithms } = openpgp.config);
Object.assign(openpgp.config, { rejectMessageHashAlgorithms: new Set([openpgp.enums.hash.md5, openpgp.enums.hash.ripemd]) });
},
after: function() {
Object.assign(openpgp.config, { rejectMessageHashAlgorithms });
}
});
it('Sign text with openpgp.sign and verify with openpgp.verify leads to same string cleartext and valid signatures', async function() {
const plaintext = 'short message\nnext line \n한국어/조선말';
const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
const privKey = await openpgp.decryptKey({
privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
passphrase: 'hello world'
});
const config = { minRSABits: 1024 };
return openpgp.sign({ signingKeys: privKey, message: await openpgp.createCleartextMessage({ text: plaintext }), config }).then(async signed => {
const message = await openpgp.readCleartextMessage({ cleartextMessage: signed });
return openpgp.verify({ verificationKeys:[pubKey], message, config });
}).then(async function({ data, signatures }) {
expect(data).to.equal(plaintext.replace(/[ \t\r]+$/mg, ''));
expect(signatures).to.have.length(1);
expect(await signatures[0].verified).to.be.true;
expect((await signatures[0].signature).packets.length).to.equal(1);
});
});
it('Sign text with openpgp.sign and verify with openpgp.verify leads to same string cleartext and valid signatures -- escape armored message', async function() {
const plaintext = pub_key_arm2;
const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
const privKey = await openpgp.decryptKey({
privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
passphrase: 'hello world'
});
const config = { minRSABits: 1024 };
return openpgp.sign({ signingKeys: privKey, message: await openpgp.createCleartextMessage({ text: plaintext }), config }).then(async signed => {
const message = await openpgp.readCleartextMessage({ cleartextMessage: signed });
return openpgp.verify({ verificationKeys: pubKey, message, config });
}).then(async function({ data, signatures }) {
expect(data).to.equal(plaintext);
expect(signatures).to.have.length(1);
expect(await signatures[0].verified).to.be.true;
expect((await signatures[0].signature).packets.length).to.equal(1);
});
});
it('Sign text with openpgp.sign and verify with openpgp.verify leads to same string cleartext and valid signatures -- trailing spaces', async function() {
const plaintext = 'space: \nspace and tab: \t\nno trailing space\n \ntab:\t\ntab and space:\t ';
const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
const privKey = await openpgp.decryptKey({
privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
passphrase: 'hello world'
});
const config = { minRSABits: 1024 };
return openpgp.sign({ signingKeys: privKey, message: await openpgp.createCleartextMessage({ text: plaintext }), config }).then(async signed => {
const message = await openpgp.readCleartextMessage({ cleartextMessage: signed });
return openpgp.verify({ verificationKeys: pubKey, message, config });
}).then(async function({ data, signatures }) {
expect(data).to.equal(plaintext.replace(/[ \t]+$/mg, ''));
expect(signatures).to.have.length(1);
expect(await signatures[0].verified).to.be.true;
expect((await signatures[0].signature).packets.length).to.equal(1);
});
});
it('Sign text with openpgp.sign and verify with openpgp.verify leads to same bytes cleartext and valid signatures - armored', async function() {
const plaintext = util.stringToUint8Array('short message\nnext line \n한국어/조선말');
const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
const privKey = await openpgp.decryptKey({
privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
passphrase: 'hello world'
});
const config = { minRSABits: 1024 };
return openpgp.sign({ signingKeys: privKey, message: await openpgp.createMessage({ binary: plaintext }), config }).then(async signed => {
const message = await openpgp.readMessage({ armoredMessage: signed });
return openpgp.verify({ verificationKeys: pubKey, message, format: 'binary', config });
}).then(async function({ data, signatures }) {
expect(data).to.deep.equal(plaintext);
expect(signatures).to.have.length(1);
expect(await signatures[0].verified).to.be.true;
expect((await signatures[0].signature).packets.length).to.equal(1);
});
});
it('Sign text with openpgp.sign and verify with openpgp.verify leads to same bytes cleartext and valid signatures - not armored', async function() {
const plaintext = util.stringToUint8Array('short message\nnext line \n한국어/조선말');
const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
const privKey = await openpgp.decryptKey({
privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
passphrase: 'hello world'
});
const config = { minRSABits: 1024 };
return openpgp.sign({ signingKeys: privKey, message: await openpgp.createMessage({ binary: plaintext }), format: 'binary', config }).then(async signed => {
const message = await openpgp.readMessage({ binaryMessage: signed });
return openpgp.verify({ verificationKeys: pubKey, message, format: 'binary', config });
}).then(async function({ data, signatures }) {
expect(data).to.deep.equal(plaintext);
expect(signatures).to.have.length(1);
expect(await signatures[0].verified).to.be.true;
expect((await signatures[0].signature).packets.length).to.equal(1);
});
});
it('Should verify detached signature with some unknown versions of Signature packets', async function () {
// Test from openpgp-interoperability-test-suite to ensure forward compatibility: https://tests.sequoia-pgp.org/?q=forward-compat
const plaintext = 'hello world';
// This signature includes two Signature packets: a v4 one (verifiable) and a 'dummy' v23 one.
const signatureUnknownTrailingPacketVersion = `-----BEGIN PGP SIGNATURE-----
wnUEARYKACcFgmSVpTQJkHEwNzxPuQajFiEE2KiARjeh+fU3dy+5cTA3PE+5
BqMAAKZNAP0fhECUqrE2Ts7Ho8/fuLFT+9jsGIGo0EviIEmW77vyhQEAtOBa
N77tTSawgDqnjIRH5RyI6YNC1LNz01VHCYWwegfCwTsXAAEKAG8FgmSVZN4J
EPv8yCoBXnMwRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdw
Lm9yZ8jF+epDaQ8yqg9h1mb0LcDLKC71kHyESC8fqFt9fNFsFiEE0aZuGiOx
gsmYD3iM+/zIKgFeczAAADLxDACKH0qwrZW+Eu3McHHfKojqlHoJ+Ofqotui
Gtcyx3HrE86xQHQl6346Joweomlzo2A6cjhT/nxL88sfy9yTQyUyKaON0wHz
4WI+Onu8rSaG99J/u34dDIPqFu5DzhwCrkv0IQwGYfDxG6Lrxg7gsxui2KAt
4rJqlbaeRGOTeNmew6aH74foUp86LWjdasanZ3RXxjk3yP+R/7nquQjkVGqE
jElkMwFh44TwTHlrXfI90Ki4gNrFQfbQCQm2v66rT0t3BSgVrL+FZIyXjjOh
dp83PCrkcvOcbBalvtbYPd5+23cGAylm5hkC9bxQUwUJrcJezdwSpxF5+Vgj
IkeanKfU2BhKry3Hpn3PL6vLfVkK/w0wUEbDMkFRbGAmW1sPCJWDSX6Zy75/
Li0CQ3u6tg3/m9VHUdwN5iNVk3g7AtV2eLinv4fKIuVUxUIyvacro+RBxGNc
EnZwTO2p2I0xifnoRizITFXclUc9J4vK+whpi9PHH5uoqRGcoer72rtjIIs=
=nReB
-----END PGP SIGNATURE-----`;
const publicKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
xjMEZJWk4RYJKwYBBAHaRw8BAQdA7p5RuL+Z05qld6xRz6tbJ+9pmDowaCYr
tMOW8MXHAx3NFW5hbWUgPGVtYWlsQHRlc3QuY29tPsKMBBAWCgA+BYJklaTh
BAsJBwgJkHEwNzxPuQajAxUICgQWAAIBAhkBApsDAh4BFiEE2KiARjeh+fU3
dy+5cTA3PE+5BqMAAB5pAQDUHdYs3HRK6yJZ6IrK8lfmLzeqSgW2j9wLG/zF
TXIARQEAj0PdOzSy3q75VIQraDSHWpBAue8QNEKV4Q8hlkJvmgPOOARklaTh
EgorBgEEAZdVAQUBAQdAR9bBkzKzh24TB6gJVHR49BWnhTmeF5+vA3PXtX/b
RHkDAQgHwngEGBYIACoFgmSVpOEJkHEwNzxPuQajApsMFiEE2KiARjeh+fU3
dy+5cTA3PE+5BqMAAFjVAQDKqKwFLKX+N7le3cDLHAYSqc4AWpksKS4eSBLa
uDvEBgD+LCEUOPejUTCMqPyd04ssdOq1AlMJOmUGUwLk7kFP7Aw=
=Q9Px
-----END PGP PUBLIC KEY BLOCK-----`;
const { signatures, data } = await openpgp.verify({
message: await openpgp.createMessage({ text: plaintext }),
signature: await openpgp.readSignature({ armoredSignature: signatureUnknownTrailingPacketVersion }),
verificationKeys: await openpgp.readKey({ armoredKey: publicKey })
});
expect(data).to.equal(plaintext);
expect(signatures).to.have.length(1);
expect(await signatures[0].verified).to.be.true;
expect((await signatures[0].signature).packets.length).to.equal(1);
});
it('Should verify cleartext signature with some unknown versions of Signature packets', async function () {
// Test to ensure forward compatibility:
// this signature includes two Signature packets: a v4 one (verifiable) and a 'dummy' v23 one.
const signatureUnknownTrailingPacketVersion = `-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
hello world
-----BEGIN PGP SIGNATURE-----
wnUEARYKACcFgmSVpTQJkHEwNzxPuQajFiEE2KiARjeh+fU3dy+5cTA3PE+5
BqMAAKZNAP0fhECUqrE2Ts7Ho8/fuLFT+9jsGIGo0EviIEmW77vyhQEAtOBa
N77tTSawgDqnjIRH5RyI6YNC1LNz01VHCYWwegfCwTsXAAEKAG8FgmSVZN4J
EPv8yCoBXnMwRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdw
Lm9yZ8jF+epDaQ8yqg9h1mb0LcDLKC71kHyESC8fqFt9fNFsFiEE0aZuGiOx
gsmYD3iM+/zIKgFeczAAADLxDACKH0qwrZW+Eu3McHHfKojqlHoJ+Ofqotui
Gtcyx3HrE86xQHQl6346Joweomlzo2A6cjhT/nxL88sfy9yTQyUyKaON0wHz
4WI+Onu8rSaG99J/u34dDIPqFu5DzhwCrkv0IQwGYfDxG6Lrxg7gsxui2KAt
4rJqlbaeRGOTeNmew6aH74foUp86LWjdasanZ3RXxjk3yP+R/7nquQjkVGqE
jElkMwFh44TwTHlrXfI90Ki4gNrFQfbQCQm2v66rT0t3BSgVrL+FZIyXjjOh
dp83PCrkcvOcbBalvtbYPd5+23cGAylm5hkC9bxQUwUJrcJezdwSpxF5+Vgj
IkeanKfU2BhKry3Hpn3PL6vLfVkK/w0wUEbDMkFRbGAmW1sPCJWDSX6Zy75/
Li0CQ3u6tg3/m9VHUdwN5iNVk3g7AtV2eLinv4fKIuVUxUIyvacro+RBxGNc
EnZwTO2p2I0xifnoRizITFXclUc9J4vK+whpi9PHH5uoqRGcoer72rtjIIs=
=nReB
-----END PGP SIGNATURE-----`;
const publicKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
xjMEZJWk4RYJKwYBBAHaRw8BAQdA7p5RuL+Z05qld6xRz6tbJ+9pmDowaCYr
tMOW8MXHAx3NFW5hbWUgPGVtYWlsQHRlc3QuY29tPsKMBBAWCgA+BYJklaTh
BAsJBwgJkHEwNzxPuQajAxUICgQWAAIBAhkBApsDAh4BFiEE2KiARjeh+fU3
dy+5cTA3PE+5BqMAAB5pAQDUHdYs3HRK6yJZ6IrK8lfmLzeqSgW2j9wLG/zF
TXIARQEAj0PdOzSy3q75VIQraDSHWpBAue8QNEKV4Q8hlkJvmgPOOARklaTh
EgorBgEEAZdVAQUBAQdAR9bBkzKzh24TB6gJVHR49BWnhTmeF5+vA3PXtX/b
RHkDAQgHwngEGBYIACoFgmSVpOEJkHEwNzxPuQajApsMFiEE2KiARjeh+fU3
dy+5cTA3PE+5BqMAAFjVAQDKqKwFLKX+N7le3cDLHAYSqc4AWpksKS4eSBLa
uDvEBgD+LCEUOPejUTCMqPyd04ssdOq1AlMJOmUGUwLk7kFP7Aw=
=Q9Px
-----END PGP PUBLIC KEY BLOCK-----`;
const { signatures } = await openpgp.verify({
message: await openpgp.readCleartextMessage({ cleartextMessage: signatureUnknownTrailingPacketVersion }),
verificationKeys: await openpgp.readKey({ armoredKey: publicKey })
});
expect(signatures).to.have.length(1);
expect(await signatures[0].verified).to.be.true;
expect((await signatures[0].signature).packets.length).to.equal(1);
});
it('Should verify cleartext message correctly when using a detached cleartext signature and binary literal data', async function () {
const plaintext = 'short message\nnext line \n한국어/조선말';
const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
const privKey = await openpgp.decryptKey({
privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
passphrase: 'hello world'
});
const config = { minRSABits: 1024 };
return openpgp.sign({ signingKeys: privKey, message: await openpgp.createMessage({ text: plaintext }), detached: true, config }).then(async armoredSignature => {
const signature = await openpgp.readSignature({ armoredSignature });
return openpgp.verify({ verificationKeys: pubKey, message: await openpgp.createMessage({ binary: util.encodeUTF8(plaintext) }), signature, config });
}).then(async function({ data, signatures }) {
expect(data).to.equal(plaintext);
expect(signatures).to.have.length(1);
expect(await signatures[0].verified).to.be.true;
expect((await signatures[0].signature).packets.length).to.equal(1);
});
});
it('Should verify cleartext message correctly when using a detached binary signature and text literal data', async function () {
const plaintext = 'short message\nnext line \n한국어/조선말';
const binaryPlaintext = util.encodeUTF8(plaintext);
const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
const privKey = await openpgp.decryptKey({
privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
passphrase: 'hello world'
});
const config = { minRSABits: 1024 };
return openpgp.sign({ signingKeys: privKey, message:await openpgp.createMessage({ binary: binaryPlaintext }), detached: true, config }).then(async armoredSignature => {
const signature = await openpgp.readSignature({ armoredSignature });
return openpgp.verify({ verificationKeys: pubKey, message: await openpgp.createMessage({ text: plaintext }), signature, config });
}).then(async function({ data, signatures }) {
expect(data).to.equal(plaintext);
expect(signatures).to.have.length(1);
expect(await signatures[0].verified).to.be.true;
expect((await signatures[0].signature).packets.length).to.equal(1);
});
});
it('Should verify encrypted cleartext message correctly when encrypting binary literal data with a canonical text signature', async function () {
const plaintext = 'short message\nnext line \n한국어/조선말';
const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
const privKey = await openpgp.decryptKey({
privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
passphrase: 'hello world'
});
const config = { minRSABits: 1024 };
return openpgp.sign({ signingKeys: privKey, message: await openpgp.createMessage({ text: plaintext }), detached: true, config }).then(async armoredSignature => {
const signature = await openpgp.readSignature({ armoredSignature });
return openpgp.encrypt({ message: await openpgp.createMessage({ binary: util.encodeUTF8(plaintext) }), encryptionKeys: [pubKey], signature, config });
}).then(async armoredMessage => {
const message = await openpgp.readMessage({ armoredMessage });
return openpgp.decrypt({ message, decryptionKeys: [privKey], verificationKeys: [pubKey], config });
}).then(async function({ data, signatures }) {
expect(data).to.equal(plaintext);
expect(signatures).to.have.length(1);
expect(await signatures[0].verified).to.be.true;
expect((await signatures[0].signature).packets.length).to.equal(1);
});
});
// TODO add test with multiple revocation signatures
it('Verify primary key revocation signatures', async function() {
const pubKey = await openpgp.readKey({ armoredKey: pub_revoked });
await pubKey.revocationSignatures[0].verify(
pubKey.keyPacket, openpgp.enums.signature.keyRevocation, { key: pubKey.keyPacket }
);
});
// TODO add test with multiple revocation signatures
it('Verify subkey revocation signatures', async function() {
const pubKey = await openpgp.readKey({ armoredKey: pub_revoked });
const revSig = pubKey.subkeys[0].revocationSignatures[0];
await revSig.verify(
pubKey.keyPacket, openpgp.enums.signature.subkeyRevocation, { key: pubKey.keyPacket, bind: pubKey.subkeys[0].keyPacket }
);
});
it('Verify key expiration date', async function() {
const pubKey = await openpgp.readKey({ armoredKey: pub_revoked });
expect(pubKey).to.exist;
expect(pubKey.users[0].selfCertifications[0].keyNeverExpires).to.be.false;
expect(pubKey.users[0].selfCertifications[0].keyExpirationTime).to.equal(5 * 365 * 24 * 60 * 60);
});
it('Write unhashed subpackets', async function() {
let pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
expect(pubKey.users[0].selfCertifications).to.exist;
pubKey = await openpgp.readKey({ armoredKey: pubKey.armor() });
expect(pubKey.users[0].selfCertifications).to.exist;
});
it('Write V4 signatures', async function() {
const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
const pubKey2 = await openpgp.readKey({ armoredKey: pubKey.armor() });
expect(pubKey2).to.exist;
expect(pubKey.users[0].selfCertifications).to.eql(pubKey2.users[0].selfCertifications);
});
it('Verify a detached signature using appendSignature', async function() {
const detachedSig = ['-----BEGIN PGP SIGNATURE-----',
'Version: GnuPG v1.4.13 (Darwin)',
'Comment: GPGTools - https://gpgtools.org',
'Comment: Using GnuPG with Thunderbird - https://www.enigmail.net/',
'',
'iQEcBAEBCgAGBQJTqH5OAAoJENf7k/zfv8I8oFoH/R6EFTw2CYUQoOKSAQstWIHp',
'fVVseLOkFbByUV5eLuGVBNI3DM4GQ6C7dGntKAn34a1iTGcAIZH+fIhaZ2WtNdtA',
'R+Ijn8xDjbF/BWvcTBOaRvgw9b8viPxhkVYa3PioHYz6krt/LmFqFdp/phWZcqR4',
'jzWMX55h4FOw3YBNGiz2NuIg+iGrFRWPYgd8NVUmJKReZHs8C/6HGz7F4/A24k6Y',
'7xms9D6Er+MhspSl+1dlRdHjtXiRqC5Ld1hi2KBKc6YzgOLpVw5l9sffbnH+aRG4',
'dH+2J5U3elqBDK1i3GyG8ixLSB0FGW9+lhYNosZne2xy8SbQKdgsnTBnWSGevP0=',
'=xiih',
'-----END PGP SIGNATURE-----'].join('\r\n');
const content = ['Content-Type: multipart/mixed;',
' boundary="------------070307080002050009010403"',
'',
'This is a multi-part message in MIME format.',
'--------------070307080002050009010403',
'Content-Type: text/plain; charset=ISO-8859-1',
'Content-Transfer-Encoding: quoted-printable',
'',
'test11',
'',
'--------------070307080002050009010403',
'Content-Type: application/macbinary;',
' name="test.bin"',
'Content-Transfer-Encoding: base64',
'Content-Disposition: attachment;',
' filename="test.bin"',
'',
'dGVzdGF0dGFjaG1lbnQ=',
'--------------070307080002050009010403--',
''].join('\r\n');
const publicKeyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----\r\nVersion: OpenPGP.js v.1.20131116\r\nComment: Whiteout Mail - https://whiteout.io\r\n\r\nxsBNBFKODs4BB/9iOF4THsjQMY+WEpT7ShgKxj4bHzRRaQkqczS4nZvP0U3g\r\nqeqCnbpagyeKXA+bhWFQW4GmXtgAoeD5PXs6AZYrw3tWNxLKu2Oe6Tp9K/XI\r\nxTMQ2wl4qZKDXHvuPsJ7cmgaWqpPyXtxA4zHHS3WrkI/6VzHAcI/y6x4szSB\r\nKgSuhI3hjh3s7TybUC1U6AfoQGx/S7e3WwlCOrK8GTClirN/2mCPRC5wuIft\r\nnkoMfA6jK8d2OPrJ63shy5cgwHOjQg/xuk46dNS7tkvGmbaa+X0PgqSKB+Hf\r\nYPPNS/ylg911DH9qa8BqYU2QpNh9jUKXSF+HbaOM+plWkCSAL7czV+R3ABEB\r\nAAHNLVdoaXRlb3V0IFVzZXIgPHNhZmV3aXRobWUudGVzdHVzZXJAZ21haWwu\r\nY29tPsLAXAQQAQgAEAUCUo4O2gkQ1/uT/N+/wjwAAN2cB/9gFRmAfvEQ2qz+\r\nWubmT2EsSSnjPMxzG4uyykFoa+TaZCWo2Xa2tQghmU103kEkQb1OEjRjpgwJ\r\nYX9Kghnl8DByM686L5AXnRyHP78qRJCLXSXl0AGicboUDp5sovaa4rswQceH\r\nvcdWgZ/mgHTRoiQeJddy9k+H6MPFiyFaVcFwegVsmpc+dCcC8yT+qh8ZIbyG\r\nRJU60PmKKN7LUusP+8DbSv39zCGJCBlVVKyA4MzdF5uM+sqTdXbKzOrT5DGd\r\nCZaox4s+w16Sq1rHzZKFWfQPfKLDB9pyA0ufCVRA3AF6BUi7G3ZqhZiHNhMP\r\nNvE45V/hS1PbZcfPVoUjE2qc1Ix1\r\n=7Wpe\r\n-----END PGP PUBLIC KEY BLOCK-----';
const publicKey = await openpgp.readKey({ armoredKey: publicKeyArmored });
const message = await openpgp.createMessage({ text: content });
await message.appendSignature(detachedSig);
const { data, signatures } = await openpgp.verify({ verificationKeys:[publicKey], message, config: { minRSABits: 1024 } });
expect(data).to.equal(content);
expect(signatures).to.have.length(1);
expect(await signatures[0].verified).to.be.true;
expect((await signatures[0].signature).packets.length).to.equal(1);
expect(await signatures[0].verified).to.be.true;
});
it('Detached signature signing and verification', async function() {
const message = await openpgp.createMessage({ text: 'hello' });
const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
const privKey = await openpgp.decryptKey({
privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
passphrase: 'hello world'
});
const opt = { userIDs: { name:'test', email:'a@b.com' }, format: 'object' };
const { privateKey: generatedKey } = await openpgp.generateKey(opt);
const armoredSignature = await openpgp.sign({ signingKeys: [generatedKey, privKey], message, detached: true, config: { minRSABits: 1024 } });
const signature = await openpgp.readSignature({ armoredSignature });
const { data, signatures } = await openpgp.verify({ verificationKeys: [generatedKey.toPublic(), pubKey], message, signature, config: { minRSABits: 1024 } });
expect(data).to.equal('hello');
expect(await signatures[0].verified).to.be.true;
expect(await signatures[1].verified).to.be.true;
});
it('Sign message with key without password', function() {
const opt = { userIDs: { name:'test', email:'a@b.com' }, passphrase: null, format: 'object' };
return openpgp.generateKey(opt).then(async function({ privateKey: key }) {
const message = await openpgp.createMessage({ text: 'hello world' });
return message.sign([key]);
});
});
it('Verify signed key', async function() {
const signedArmor = [
'-----BEGIN PGP PUBLIC KEY BLOCK-----',
'Version: GnuPG v1',
'',
'mI0EUmEvTgEEANyWtQQMOybQ9JltDqmaX0WnNPJeLILIM36sw6zL0nfTQ5zXSS3+',
'fIF6P29lJFxpblWk02PSID5zX/DYU9/zjM2xPO8Oa4xo0cVTOTLj++Ri5mtr//f5',
'GLsIXxFrBJhD/ghFsL3Op0GXOeLJ9A5bsOn8th7x6JucNKuaRB6bQbSPABEBAAG0',
'JFRlc3QgTWNUZXN0aW5ndG9uIDx0ZXN0QGV4YW1wbGUuY29tPoi5BBMBAgAjBQJS',
'YS9OAhsvBwsJCAcDAgEGFQgCCQoLBBYCAwECHgECF4AACgkQSmNhOk1uQJQwDAP6',
'AgrTyqkRlJVqz2pb46TfbDM2TDF7o9CBnBzIGoxBhlRwpqALz7z2kxBDmwpQa+ki',
'Bq3jZN/UosY9y8bhwMAlnrDY9jP1gdCo+H0sD48CdXybblNwaYpwqC8VSpDdTndf',
'9j2wE/weihGp/DAdy/2kyBCaiOY1sjhUfJ1GogF49rCIRgQQEQIABgUCVuXBfQAK',
'CRARJ5QDyxae+O0fAJ9hUQPejXvZv6VW1Q3/Pm3+x2wfJACgwFg9NlrPPfejoC1w',
'P+z+vE5NFA24jQRSYS9OAQQA6R/PtBFaJaT4jq10yqASk4sqwVMsc6HcifM5lSdx',
'zExFP74naUMMyEsKHP53QxTF0GrqusagQg/ZtgT0CN1HUM152y7ACOdp1giKjpMz',
'OTQClqCoclyvWOFB+L/SwGEIJf7LSCErwoBuJifJc8xAVr0XX0JthoW+uP91eTQ3',
'XpsAEQEAAYkBPQQYAQIACQUCUmEvTgIbLgCoCRBKY2E6TW5AlJ0gBBkBAgAGBQJS',
'YS9OAAoJEOCE90RsICyXuqIEANmmiRCASF7YK7PvFkieJNwzeK0V3F2lGX+uu6Y3',
'Q/Zxdtwc4xR+me/CSBmsURyXTO29OWhPGLszPH9zSJU9BdDi6v0yNprmFPX/1Ng0',
'Abn/sCkwetvjxC1YIvTLFwtUL/7v6NS2bZpsUxRTg9+cSrMWWSNjiY9qUKajm1tu',
'zPDZXAUEAMNmAN3xXN/Kjyvj2OK2ck0XW748sl/tc3qiKPMJ+0AkMF7Pjhmh9nxq',
'E9+QCEl7qinFqqBLjuzgUhBU4QlwX1GDAtNTq6ihLMD5v1d82ZC7tNatdlDMGWnI',
'dvEMCv2GZcuIqDQ9rXWs49e7tq1NncLYhz3tYjKhoFTKEIq3y3Pp',
'=fvK7',
'-----END PGP PUBLIC KEY BLOCK-----'
].join('\n');
const signedKey = await openpgp.readKey({ armoredKey: signedArmor });
const signerKey = await openpgp.readKey({ armoredKey: priv_key_arm1 });
return signedKey.verifyPrimaryUser([signerKey], undefined, undefined, { ...openpgp.config, rejectPublicKeyAlgorithms: new Set() }).then(signatures => {
expect(signatures[0].valid).to.be.null;
expect(signatures[0].keyID.toHex()).to.equal(signedKey.getKeyID().toHex());
expect(signatures[1].valid).to.be.true;
expect(signatures[1].keyID.toHex()).to.equal(signerKey.getKeyID().toHex());
});
});
it('Verify signed UserIDs and User Attributes', async function() {
const armoredKeyWithPhoto = `-----BEGIN PGP PUBLIC KEY BLOCK-----
mI0EW1CJGAEEAM+BzuFzcYk9HttmDbjGexQ8dfme074Q5PuHas3PBISPm0AwmnDM
tzjlcrrg2VGuLqHvNF600w2ZgOo2gElNYCOas1q/fVFuIgJ4SUduNOEe/JnIW4uP
iEGU9l6zOVVgTc/nGVpZdvHgvOL8nl9BKHtWEnMD3Du7UYAm+Avshu9jABEBAAG0
AViI1AQTAQoAPhYhBKcH118Rrg0wLBrTk5IyMikCym+4BQJbUIkYAhsDBQkDwmcA
BQsJCAcDBRUKCQgLBRYDAgEAAh4BAheAAAoJEJIyMikCym+4K8oEAJc7YFiNau6V
HTVK4cTvWU5MuYiejejFZai4ELUJy+WF6cZYrLuF/z/kRt8B7hpumXChPCUlT0q7
FWypQtA3leu83DGMXqhfS80h2S1+VLmDVVWKQXOwgOb44jT9F08bDU5QK08SkjF8
/EirIy8ANzdwCA4rHytIS2yx6tLlthvX0cBwwG4BEAABAQAAAAAAAAAAAAAAAP/Y
/+AAEEpGSUYAAQEAAAEAAQAA/9sAQwABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB/9sAQwEBAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEB/8AAEQgAAQABAwEiAAIRAQMRAf/EABUAAQEAAAAAAAAAAAAA
AAAAAAAK/8QAFBABAAAAAAAAAAAAAAAAAAAAAP/EABQBAQAAAAAAAAAAAAAAAAAA
AAD/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwC/gAH/2YjUBBMB
CgA+FiEEpwfXXxGuDTAsGtOTkjIyKQLKb7gFAltQimUCGwMFCQPCZwAFCwkIBwMF
FQoJCAsFFgMCAQACHgECF4AACgkQkjIyKQLKb7gm/wQAiyZF89qr8hf3XQNJ6Ir/
QtaniPcesjrYCIE47ZfeDYpBTPeiMm295o2dZXVJS4ItllYsplASw5DJiIMnQKlJ
mbXakYFzzclTa/JrKzFYCy/DPT95xK+633omgrIUgJodizoKJE7XeB2U6aRUJJ4O
iTuGu4fEU1UligAXSrZmCdE=
=VK6I
-----END PGP PUBLIC KEY BLOCK-----`;
const key = await openpgp.readKey({ armoredKey: armoredKeyWithPhoto });
await Promise.all(key.users.map(async user => {
await user.verify(undefined, openpgp.config);
}));
});
it('should verify a shorter RSA signature', async function () {
const encrypted = `-----BEGIN PGP MESSAGE-----
wYwD4IT3RGwgLJcBBACmH+a2c2yieZJ3wFchKeTVqzWkoltiidWgHHNE5v5x
8aZGNzZFBd02v80VS23P9oxeJOpqKX2IZyuD36SniNoi+eXdT3zraqIe9x5p
0RY9OrTP9pl58iogFBi1ARls41j7ui8KKDt2/iyQDCWHW1LoOVstiEb5/Xi3
EWI+34EbNNTBMgEJAQAwEXImkOPmhYhE7bB3FnXe9rb7Fo3GZYA4/8B9YVf7
GGZRLGwbICGu8E0MolmzLYW9hRThEfusAsNPGSgB+Yaqp0drsk01N4JJj3FT
RKEUvd5EcL3u+Z5EoUUW6GpUL5p8Hvy2thqQfeem7XUbDBY6V3wqydOjbN9u
c4CWB5Zu3GjDGDOvXFsy6cgdQvd/B9xbugKvUbAIsecTPlLtjZwfQklIu63T
DA/3Pz/+zTAknBCsuIM0m7U/ZP3N6AGQIp4To7RJk0I6AxthHF5LbU11MjDZ
iB7+vmhqlrPyIS11g25UNijottJm13f84glVwBdWTJCiEqjh3KbcnTQCckCY
V39DDLtbZG/XIx1ktqp765O9D/9xp2IA4zTyZzH4TuDbYs1j+JRdMsAq254k
1m+wtW5gxJGcD5nh2T2T+ABL0n3jW0G504kR0LNBAQOZhVSKnSLn+F0GkjmI
iGw8+BOy8p2pX/WCLOf776ppSL77TpzhpG6wSE2oQxDrudazmRgVkZpyGzFE
fDjspLTJHOhZ5zlLuoiKS9qEARGp39ysQnElR4dsx7tyVZz0uJvIrVzrQBlB
ekoD0DH0bhfqiwDrqeTJT2ORk8I/Q3jWnhQ3MnRN+q9d0yf1LWApMCwA7xU2
C4KUFRC/wuF2TR9NvA==
=v3WS
-----END PGP MESSAGE-----`;
const armoredKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
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
oaBUyhCKt8tz6Q==
=52k1
-----END PGP PRIVATE KEY BLOCK-----`;
const key = await openpgp.readKey({ armoredKey });
const decrypted = await openpgp.decrypt({
message: await openpgp.readMessage({ armoredMessage: encrypted }),
verificationKeys: key,
decryptionKeys: key,
config: { minRSABits: 1024 }
});
expect(await decrypted.signatures[0].verified).to.be.true;
});
it('should verify a shorter EdDSA signature', async function() {
const key = await openpgp.readKey({
armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----
xVgEX8+jfBYJKwYBBAHaRw8BAQdA9GbdDjprR0sWf0R5a5IpulUauc0FsmzJ
mOYCfoowt8EAAP9UwaqC0LWWQ5RlX7mps3728vFa/If1KBVwAjk7Uqhi2BKL
zQ90ZXN0MiA8YkBhLmNvbT7CjAQQFgoAHQUCX8+jfAQLCQcIAxUICgQWAgEA
AhkBAhsDAh4BACEJEG464aV2od77FiEEIcg441MtKnyJnPDRbjrhpXah3vuR
gQD+Il6Gw2oIok4/ANyDDLBYZtKqRrMv4NcfF9DHYuAFcP4BAPhFOffyP3qU
AEZb7QPrWdLfhn8/FeSFZxJvnmupQ9sDx10EX8+jfBIKKwYBBAGXVQEFAQEH
QOSzo9cX1U2esGFClprOt0QWXNJ97228R5tKFxo6/0NoAwEIBwAA/0n4sq2i
N6/jE+6rVO4o/7LW0xahxpV1tTA6qv1Op9TwFIDCeAQYFggACQUCX8+jfAIb
DAAhCRBuOuGldqHe+xYhBCHIOONTLSp8iZzw0W464aV2od773XcA/jlmX8/c
1/zIotEkyMZB4mI+GAg3FQ6bIACFBH1sz0MzAP9Snri0P4FRZ8D5THRCJoUm
GBgpBmrf6IVv484jBswGDA==
=8rBO
-----END PGP PRIVATE KEY BLOCK-----`
});
const encrypted = `-----BEGIN PGP MESSAGE-----
wV4DWlRRjuYiLSsSAQdAWwDKQLN4ZUS5fqiwFtAMrRfZZe9J4SgClhG6avEe
AEowkSZwWRT+8Hy8aBIb4oPehYUFXXZ7BtlJCyd7LOTUtqyc00OE0721PC3M
v0+zird60sACATlDmTwweR5GFtEAjHVheIL5rbkOBRD+oSqB8z+IovNg83Pz
FVwsFZnCLtECoYgpF2MJdopuC/bPHcrvf4ndwmD11uXtms4Rq4y25QyqApbn
Hj/hljufk0OkavUXxrNKjGQtxLHMpa3Nsi0MHWY8JguxOKFKpAIMP32CD1e+
j+GItrR+QbbN13ODlcR3hf66cwjLLsJCx5VcBaRspKF05O3ix/u9KVjJqtbi
Ie6jnY0zP2ldtS4JmhKBa43qmOHCxHc=
=7B58
-----END PGP MESSAGE-----`;
const decrypted = await openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: encrypted }), decryptionKeys: key, verificationKeys: key.toPublic() });
expect(await decrypted.signatures[0].verified).to.be.true;
});
it('should verify a shorter ECDSA signature', async function() {
const key = await openpgp.readKey({
armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----
xYAFX9JrLRMAAABMCCqGSM49AwEHAgMErtQdX4vh7ng/ut+k1mooYNh3Ywqt
wr0tSS8hxZMvQRIFQ53Weq0e97ioZKXGimprEL571yvAN7I19wtQtqi61AAA
AAAAJAEAjWdW+qlMFaKwXCls3O/X8I1rbZ0OdFgeE3TnRP3YETAP5s0KYSA8
YUBhLml0PsKSBRATCAAhBQJf0mstBAsJBwgDFQgKBBYCAQACGQECGwMCHgcD
IgECACMiIQUee6Tb+GlhTk/ozKrt7RhInCyR6w3OJb/tYAN1+qbIoYUqAP9S
XmJCmSMrq6KfAD1aWSTBhtmujh+6y/pYTaf6VJVBYQEAt18zK0tw5EihHASY
FXbfdFHBzrMmPJ4UV6UiBvH6k2zHhAVf0mstEgAAAFAIKoZIzj0DAQcCAwQx
qnVPmWex365Nx8X8BGuMNI2TITXzTh9+AuPftZjPm09dhxdT9xmrCstPu/U1
cpacIp0LIq13ngLgeZWcGFcnAwEIBwAAAAAAJAEAsTvBsKk/XoCz2mi8sz5q
EYaN9YdDOU2jF+HOaSNaJAsPF8J6BRgTCAAJBQJf0mstAhsMACMiIQUee6Tb
+GlhTk/ozKrt7RhInCyR6w3OJb/tYAN1+qbIoVutAP9GHPLn7D9Uahm81lhK
AcvDfr9a0Cp4WAVzKDKLUzrRMgEAozi0VyjiBo1U2LcwTPJkA4PEQqQRVW1D
KZTMSAH7JEo=
=tqWy
-----END PGP PRIVATE KEY BLOCK-----`
});
const signed = `-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
short message
-----BEGIN PGP SIGNATURE-----
wnYFARMIAAYFAl/Say0AIyIhBR57pNv4aWFOT+jMqu3tGEicLJHrDc4lv+1g
A3X6psihFkcA+Nuog2qpAq20Zc2lzVjDZzQosb8MLvKMg3UFCX12Oc0BAJwd
JImeZLY02MctIpGZULbqgcUGK0P/yqrPL8Pe4lQM
=Pacb
-----END PGP SIGNATURE-----`;
const message = await openpgp.readCleartextMessage({ cleartextMessage: signed });
const verified = await openpgp.verify({ verificationKeys: key, message });
expect(await verified.signatures[0].verified).to.be.true;
});
});