/* 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('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 }); 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 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; }); });