/* eslint-disable max-lines */ /* globals tryTests: true */ const spy = require('sinon/lib/sinon/spy'); 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 crypto = require('../../src/crypto'); const random = require('../../src/crypto/random'); const util = require('../../src/util'); const keyIDType = require('../../src/type/keyid'); const { isAEADSupported } = require('../../src/key'); const input = require('./testInputs'); const detectNode = () => typeof globalThis.process === 'object' && typeof globalThis.process.versions === 'object'; const pub_key = [ '-----BEGIN PGP PUBLIC KEY BLOCK-----', 'Version: GnuPG v2.0.19 (GNU/Linux)', '', 'mI0EUmEvTgEEANyWtQQMOybQ9JltDqmaX0WnNPJeLILIM36sw6zL0nfTQ5zXSS3+', 'fIF6P29lJFxpblWk02PSID5zX/DYU9/zjM2xPO8Oa4xo0cVTOTLj++Ri5mtr//f5', 'GLsIXxFrBJhD/ghFsL3Op0GXOeLJ9A5bsOn8th7x6JucNKuaRB6bQbSPABEBAAG0', 'JFRlc3QgTWNUZXN0aW5ndG9uIDx0ZXN0QGV4YW1wbGUuY29tPoi5BBMBAgAjBQJS', 'YS9OAhsvBwsJCAcDAgEGFQgCCQoLBBYCAwECHgECF4AACgkQSmNhOk1uQJQwDAP6', 'AgrTyqkRlJVqz2pb46TfbDM2TDF7o9CBnBzIGoxBhlRwpqALz7z2kxBDmwpQa+ki', 'Bq3jZN/UosY9y8bhwMAlnrDY9jP1gdCo+H0sD48CdXybblNwaYpwqC8VSpDdTndf', '9j2wE/weihGp/DAdy/2kyBCaiOY1sjhUfJ1GogF49rC4jQRSYS9OAQQA6R/PtBFa', 'JaT4jq10yqASk4sqwVMsc6HcifM5lSdxzExFP74naUMMyEsKHP53QxTF0Grqusag', 'Qg/ZtgT0CN1HUM152y7ACOdp1giKjpMzOTQClqCoclyvWOFB+L/SwGEIJf7LSCEr', 'woBuJifJc8xAVr0XX0JthoW+uP91eTQ3XpsAEQEAAYkBPQQYAQIACQUCUmEvTgIb', 'LgCoCRBKY2E6TW5AlJ0gBBkBAgAGBQJSYS9OAAoJEOCE90RsICyXuqIEANmmiRCA', 'SF7YK7PvFkieJNwzeK0V3F2lGX+uu6Y3Q/Zxdtwc4xR+me/CSBmsURyXTO29OWhP', 'GLszPH9zSJU9BdDi6v0yNprmFPX/1Ng0Abn/sCkwetvjxC1YIvTLFwtUL/7v6NS2', 'bZpsUxRTg9+cSrMWWSNjiY9qUKajm1tuzPDZXAUEAMNmAN3xXN/Kjyvj2OK2ck0X', 'W748sl/tc3qiKPMJ+0AkMF7Pjhmh9nxqE9+QCEl7qinFqqBLjuzgUhBU4QlwX1GD', 'AtNTq6ihLMD5v1d82ZC7tNatdlDMGWnIdvEMCv2GZcuIqDQ9rXWs49e7tq1NncLY', 'hz3tYjKhoFTKEIq3y3Pp', '=h/aX', '-----END PGP PUBLIC KEY BLOCK-----' ].join('\n'); const priv_key = [ '-----BEGIN PGP PRIVATE KEY BLOCK-----', 'Version: GnuPG v2.0.19 (GNU/Linux)', '', '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', '=lw5e', '-----END PGP PRIVATE KEY BLOCK-----' ].join('\n'); const pub_key_de = [ '-----BEGIN PGP PUBLIC KEY BLOCK-----', 'Version: GnuPG v2.0.22 (GNU/Linux)', '', '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', 'og2umGfGng==', '=v3+L', '-----END PGP PUBLIC KEY BLOCK-----' ].join('\n'); const priv_key_de = [ '-----BEGIN PGP PRIVATE KEY BLOCK-----', 'Version: GnuPG v2.0.22 (GNU/Linux)', '', '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', 'DHTORVqAOdoOcu5Qh7AQg1GdSmfFAsx2', '=kyeP', '-----END PGP PRIVATE KEY BLOCK-----' ].join('\n'); const priv_key_2000_2008 = `-----BEGIN PGP PRIVATE KEY BLOCK----- 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 t/ia1kMpSEiOVLlX5dfHZzhR3WNtBqU= =C0fJ -----END PGP PRIVATE KEY BLOCK-----`; const priv_key_2038_2045 = `-----BEGIN PGP PRIVATE KEY BLOCK----- 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 0uyeibPdgJTjARMuQzDFA8bdbM540vBf5Q== =WLIN -----END PGP PRIVATE KEY BLOCK-----`; const priv_key_expires_1337 = `-----BEGIN PGP PRIVATE KEY BLOCK----- xcA4BAAAAAEBAgCgONc0J8rfO6cJw5YTP38x1ze2tAYIO7EcmRCNYwMkXngb 0Qdzg34Q5RW0rNiR56VB6KElPUhePRPVklLFiIvHABEBAAEAAf9qabYMzsz/ /LeRVZSsTgTljmJTdzd2ambUbpi+vt8MXJsbaWh71vjoLMWSXajaKSPDjVU5 waFNt9kLqwGGGLqpAQD5ZdMH2XzTq6GU9Ka69iZs6Pbnzwdz59Vc3i8hXlUj zQEApHargCTsrtvSrm+hK/pN51/BHAy9lxCAw9f2etx+AeMA/RGrijkFZtYt jeWdv/usXL3mgHvEcJv63N5zcEvDX5X4W1bND3Rlc3QxIDxhQGIuY29tPsJ7 BBABCAAvBQIAAAABBQMAAAU5BgsJBwgDAgkQzcF99nGrkAkEFQgKAgMWAgEC GQECGwMCHgEAABAlAfwPehmLZs+gOhOTTaSslqQ50bl/REjmv42Nyr1ZBlQS DECl1Qu4QyeXin29uEXWiekMpNlZVsEuc8icCw6ABhIZ =/7PI -----END PGP PRIVATE KEY BLOCK-----`; const passphrase = 'hello world'; const plaintext = input.createSomeMessage(); const password1 = 'I am a password'; const password2 = 'I am another password'; const twoPasswordGPGFail = [ '-----BEGIN PGP MESSAGE-----', 'Version: OpenPGP.js v3.0.0', 'Comment: https://openpgpjs.org', '', 'wy4ECQMIWjj3WEfWxGpgrfb3vXu0TS9L8UNTBvNZFIjltGjMVkLFD+/afgs5', 'aXt0wy4ECQMIrFo3TFN5xqtgtB+AaAjBcWJrA4bvIPBpJ38PbMWeF0JQgrqg', 'j3uehxXy0mUB5i7B61g0ho+YplyFGM0s9XayJCnu40tWmr5LqqsRxuwrhJKR', 'migslOF/l6Y9F0F9xGIZWGhxp3ugQPjVKjj8fOH7ap14mLm60C8q8AOxiSmL', 'ubsd/hL7FPZatUYAAZVA0a6hmQ==', '=cHCV', '-----END PGP MESSAGE-----' ].join('\n'); const ecdh_msg_bad = `-----BEGIN PGP MESSAGE----- Version: ProtonMail Comment: https://protonmail.com wV4DlF328rtCW+wSAQdA9FsAz4rCdoxY/oZaa68WMPMXbO+wtHs4ZXtAOJOs SlwwDaABXYC2dt0hUS2zRAL3gBGf4udH/CKJ1vPE58sNeh0ERYLxPHgwrpqI oNVWOWH50kUBIdqd7by8RwLOk9GyV6008iFOlOG90mfjvt2g5DsnSB4wEeMg pVu3fXj8iAKvFxvihwv1M7gNtP14StP6CngvyGVVEHQ= =mvcB -----END PGP MESSAGE-----`; const ecdh_dec_key = `-----BEGIN PGP PRIVATE KEY BLOCK----- Version: OpenPGP.js v4.4.6 Comment: https://openpgpjs.org xYYEXEBTPxYJKwYBBAHaRw8BAQdAbXBY+2lpOatB+ZLokS/JIWqrVOseja9S ewQxMKN6ueT+CQMIuUXr0XofC6VgJvFLyLwDlyyvT4I1HWGKZ6W9HUaslKvS rw362rbMZKKfUtfjRJvpqiIU3Dr7iDkHB5vT7Tp5S7AZ2tNKoh/bwfTKdHsT 1803InFhX3Rlc3RlcjJAcHJvdG9ubWFpbC5jb20iIDxxYV90ZXN0ZXIyQHBy b3Rvbm1haWwuY29tPsJ3BBAWCgAfBQJcQFM/BgsJBwgDAgQVCAoCAxYCAQIZ AQIbAwIeAQAKCRClzcrGJTMHyTpjAQCJZ7p0TJBZyPQ8m64N24glaM6oM78q 2Ogpc0e9LcrPowD6AssY2YfUwJNzVFVzR+Lulzu6XVPjn0pXGMhOl03SrQ3H iwRcQFM/EgorBgEEAZdVAQUBAQdAAgJJUhKvjGWMq1sDhrJgvqbHK1t1W5RF Xoet5noIlAADAQgH/gkDCOFdJ7Yv2cTZYETRT5+ak/ntmslcAqtk3ebd7Ok3 tQIjO3TYUbkV1eqrpA4I42kGCUkU4Dy26wxuaLRSsO1u/RgXjExZLP9FlWFI h6lLS1bCYQQYFggACQUCXEBTPwIbDAAKCRClzcrGJTMHyfNBAP9sdyU3GHNR 7+QdwYvQp7wN+2VUd8vIf7iwAHOK1Cj4ywD+NhzjFfGYESJ68nnkrYlYdf+u OBqYz6mzZAWQZqsjbg4= =zrks -----END PGP PRIVATE KEY BLOCK-----`; const ecdh_msg_bad_2 = `-----BEGIN PGP MESSAGE----- Version: ProtonMail Comment: https://protonmail.com wV4DtM+8giJEGNISAQhA2rYu8+B41rJi6Gsr4TVeKyDtI0KjhhlLZs891rCG 6X4wxNkxCuTJZax7gQZbDKh2kETK/RH75s9g7H/WV9kZ192NTGmMFiKiautH c5BGRGxM0sDfAQZb3ZsAUORHKPP7FczMv5aMU2Ko7O2FHc06bMdnZ/ag7GMF Bdl4EizttNTQ5sNCAdIXUoA8BJLHPgPiglnfTqqx3ynkBNMzfH46oKf08oJ+ 6CAQhJdif67/iDX8BRtaKDICBpv3b5anJht7irOBqf9XX13SGkmqKYF3T8eB W7ZV5EdCTC9KU+1BBPfPEi93F4OHsG/Jo80e5MDN24/wNxC67h7kUQiy3H4s al+5mSAKcIfZJA4NfPJg9zSoHgfRNGI8Q7ao+c8CLPiefGcMsakNsWUdRyBT SSLH3z/7AH4GxBvhDEEG3cZwmXzZAJMZmzTa+SrsxZzRpGB/aawyRntOWm8w 6Lq9ntq4S8suj/YK62dJpJxFl8xs+COngpMDvCexX9lYlh/r/y4JRQl06oUK wv7trvi89TkK3821qHxr7XwI1Ncr2qDJVNlN4W+b6WFyLXnXaJAUMyZ/6inm RR8BoR2KkEAku3Ne/G5QI51ktNJ7cCodeVOkZj8+iip1/AGyjxZCybq/N8rc bpOWdMhJ6Hy+JzGNY1qNXcHJPw== =99Fs -----END PGP MESSAGE-----`; const ecdh_dec_key2 = `-----BEGIN PGP PRIVATE KEY BLOCK----- Version: OpenPGP.js v4.4.9 Comment: https://openpgpjs.org xYYEXEg93hYJKwYBBAHaRw8BAQdAeoA+T4vr3P0hFFsbzJpgy7/ZnKCrlehr Myk5QAsBYgf+CQMIQ76YL5sEx+Zgr7DLZ5fhQn1U9+8aLIQaIbaT51nEjEMD 7h6mrJmp7oIr4PyijsIU+0LasXh/qlNeVQVWSygDq9L4nXDEGQhlMq3oH1FN NM07InBha292c2thdGVzdEBwcm90b25tYWlsLmNvbSIgPHBha292c2thdGVz dEBwcm90b25tYWlsLmNvbT7CdwQQFgoAHwUCXEg93gYLCQcIAwIEFQgKAgMW AgECGQECGwMCHgEACgkQp7+eOYEhwd6x5AD9E0LA62odFFDH76wjEYrPCvOH cYM56/5ZqZoGPPmbE98BAKCz/SQ90tiCMmlLEDXGX+a1bi6ttozqrnSQigic DI4Ix4sEXEg93hIKKwYBBAGXVQEFAQEHQPDXy2mDfbMKOpCBZB2Ic5bfoWGV iXvCFMnTLRWfGHUkAwEIB/4JAwhxMnjHjyALomBWSsoYxxB6rj6JKnWeikyj yjXZdZqdK5F+0rk4M0l7lF0wt5PhT2uMCLB7aH/mSFN1cz7sBeJl3w2soJsT ve/fP/8NfzP0wmEEGBYIAAkFAlxIPd4CGwwACgkQp7+eOYEhwd5MWQEAp0E4 QTnEnG8lYXhOqnOw676oV2kEU6tcTj3DdM+cW/sA/jH3FQQjPf+mA/7xqKIv EQr2Mx42THr260IFYp5E/rIA =oA0b -----END PGP PRIVATE KEY BLOCK-----`; const mismatchingKeyParams = `-----BEGIN PGP PRIVATE KEY BLOCK----- Version: OpenPGP.js v4.7.0 Comment: https://openpgpjs.org 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 0NubenKyljKtyHyoU+S+TqUyx7gf5A== =Lj9k -----END PGP PRIVATE KEY BLOCK----- `; const rsaPrivateKeyPKCS1 = `-----BEGIN PGP PRIVATE KEY BLOCK----- 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 =dSNv -----END PGP PRIVATE KEY BLOCK----- `; const gnuDummyKeySigningSubkey = ` -----BEGIN PGP PRIVATE KEY BLOCK----- Version: OpenPGP.js VERSION Comment: https://openpgpjs.org xZUEWCC+hwEEALu8GwefswqZLoiKJk1Nd1yKmVWBL1ypV35FN0gCjI1NyyJX UfQZDdC2h0494OVAM2iqKepqht3tH2DebeFLnc2ivvIFmQJZDnH2/0nFG2gC rSySWHUjVfbMSpmTaXpit8EX/rjNauGOdbePbezOSsAhW7R9pBdtDjPnq2Zm vDXXABEBAAH+B2UAR05VAc0JR05VIER1bW15wrgEEwECACIFAlggvocCGwMG CwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEJ3XHFanUJgCeMYD/2zKefpl clQoBdDPJKCYJm8IhuWuoF8SnHAsbhD+U42Gbm+2EATTPj0jyGPkZzl7a0th S2rSjQ4JF0Ktgdr9585haknpGwr31t486KxXOY4AEsiBmRyvTbaQegwKaQ+C /0JQYo/XKpsaX7PMDBB9SNFSa8NkhxYseLaB7gbM8w+Lx8EYBFggvpwBBADF YeeJwp6MAVwVwXX/eBRKBIft6LC4E9czu8N2AbOW97WjWNtXi3OuM32OwKXq vSck8Mx8FLOAuvVq41NEboeknhptw7HzoQMB35q8NxA9lvvPd0+Ef+BvaVB6 NmweHttt45LxYxLMdXdGoIt3wn/HBY81HnMqfV/KnggZ+imJ0wARAQABAAP7 BA56WdHzb53HIzYgWZl04H3BJdB4JU6/FJo0yHpjeWRQ46Q7w2WJzjHS6eBB G+OhGzjAGYK7AUr8wgjqMq6LQHt2f80N/nWLusZ00a4lcMd7rvoHLWwRj80a RzviOvvhP7kZY1TrhbS+Sl+BWaNIDOxS2maEkxexztt4GEl2dWUCAMoJvyFm qPVqVx2Yug29vuJsDcr9XwnjrYI8PtszJI8Fr+5rKgWE3GJumheaXaug60dr mLMXdvT/0lj3sXquqR0CAPoZ1Mn7GaUKjPVJ7CiJ/UjqSurrGhruA5ikhehQ vUB+v4uIl7ICcX8zfiP+SMhWY9qdkmOvLSSSMcTkguMfe68B/j/qf2en5OHy 6NJgMIjMrBHvrf34f6pxw5p10J6nxjooZQxV0P+9MoTHWsy0r6Er8IOSSTGc WyWJ8wmSqiq/dZSoJcLAfQQYAQIACQUCWCC+nAIbAgCoCRCd1xxWp1CYAp0g BBkBAgAGBQJYIL6cAAoJEOYZSGiVA/C9CT4D/2Vq2dKxHmzn/UD1MWSLXUbN ISd8tvHjoVg52RafdgHFmg9AbE0DW8ifwaai7FkifD0IXiN04nER3MuVhAn1 gtMu03m1AQyX/X39tHz+otpwBn0g57NhFbHFmzKfr/+N+XsDRj4VXn13hhqM qQR8i1wgiWBUFJbpP5M1BPdH4Qfkcn8D/j8A3QKYGGETa8bNOdVTRU+sThXr imOfWu58V1yWCmLE1kK66qkqmgRVUefqacF/ieMqNmsAY+zmR9D4fg2wzu/d nPjJXp1670Vlzg7oT5XVYnfys7x4GLHsbaOSjXToILq+3GwI9UjNjtpobcfm mNG2ibD6lftLOtDsVSDY8a6a =KjxQ -----END PGP PRIVATE KEY BLOCK----- `; const multipleEncryptionAndSigningSubkeys = `-----BEGIN PGP PRIVATE KEY BLOCK----- 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 =PUAJ -----END PGP PRIVATE KEY BLOCK-----`; const twoPublicKeys = `-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v2.0.19 (GNU/Linux) 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 PepQmgsu =w6wd -----END PGP PUBLIC KEY BLOCK-----`; const twoPrivateKeys = `-----BEGIN PGP PRIVATE KEY BLOCK----- xVgEYJQe2xYJKwYBBAHaRw8BAQdAjTDKUXTWruoPIdDA5tpTEax/nCIKgmeS jabWRyMTWoEAAQCM8rs15ex7sQ7T4sBf8jHeKvHiUBoTkhKJVAzsnorHdhGn zRB0ZXN0IDx0ZXN0QGEuaXQ+wowEEBYKAB0FAmCUHtsECwkHCAMVCAoEFgAC AQIZAQIbAwIeAQAhCRAQIA5NLDEFChYhBAWs5LsefVu3mjXXaBAgDk0sMQUK BYcBAMxy3zEZhNtw2nnB9jAlIOSeCUJq/GuarTWQkhAZLFIeAP9400rWrELS zvNgdct9fctoM21ZByUlkmNdPgYf7fjaAMddBGCUHtsSCisGAQQBl1UBBQEB B0DdGhv0sVHFzGvDPzTYhNKnUxd68oocIEkt5Ku6ZAD0VAMBCAcAAP9rRNBE OumQKygox59KL7FjEYXSR8TqI4t3CFlfWW/D8A+gwngEGBYIAAkFAmCUHtsC GwwAIQkQECAOTSwxBQoWIQQFrOS7Hn1bt5o112gQIA5NLDEFCoPdAQCTy2kg z3F/iZApy2Sf5SIThnQMsgEr296Fgfvm8YMFCAEA82+TF79snlPbVHSIrdDg lPMSDEkIcxzIQN0EEo1qlwzFWARglB7iFgkrBgEEAdpHDwEBB0D/kNASbsOD S9RePgrsUDdY3plKDRLIIvpAIkbr1PoDoAABANEBtAiU2YjVOfHzDgbblSCd +tPSDaYbAyHmCNMDqsRQD8rNEHRlc3QgPHRlc3RAYS5pdD7CjAQQFgoAHQUC YJQe4gQLCQcIAxUICgQWAAIBAhkBAhsDAh4BACEJEIrXtvI38e+rFiEERNKb HKnqdF8HwqMZite28jfx76trWAEA6YFR+4gMFr3xM/HReS+pYE1SSHIQjHgz SsU0N93pk5EA/ijuLZfsRf7uD6Yb0rEDIJa3NT7KwIUIUtDpbQLtIrcFx10E YJQe4hIKKwYBBAGXVQEFAQEHQLfK3MpbSeRa1Ko1NtNDNXOc/sqvEeIjAAKg V0OWVpsJAwEIBwAA/3Nr3/t32OJi9GFEVEN2/VWes5825aFBPEU6UcBaSgCw EU/CeAQYFggACQUCYJQe4gIbDAAhCRCK17byN/HvqxYhBETSmxyp6nRfB8Kj GYrXtvI38e+rSKMBAJaIk9bLz+AN0Ho8pHGP3gEddvLwvioNhdkCJ7CfwWmI AP9fcXZg/Eo55YB/B5XKLkuzDFwJaTlncrD5jcUgtVXFCg== =q2yi -----END PGP PRIVATE KEY BLOCK-----`; const armoredDummyPrivateKey1 = `-----BEGIN PGP PRIVATE KEY BLOCK----- Version: GnuPG v1.4.11 (GNU/Linux) lQGqBFERnrMRBADmM0hIfkI3yosjgbWo9v0Lnr3CCE+8KsMszgVS+hBu0XfGraKm ivcA2aaJimHqVYOP7gEnwFAxHBBpeTJcu5wzCFyJwEYqVeS3nnaIhBPplSF14Duf i6bB9RV7KxVAg6aunmM2tAutqC+a0y2rDaf7jkJoZ9gWJe2zI+vraD6fiwCgxvHo 3IgULB9RqIqpLoMgXfcjC+cD/1jeJlKRm+n71ryYwT/ECKsspFz7S36z6q3XyS8Q QfrsUz2p1fbFicvJwIOJ8B20J/N2/nit4P0gBUTUxv3QEa7XCM/56/xrGkyBzscW AzBoy/AK9K7GN6z13RozuAS60F1xO7MQc6Yi2VU3eASDQEKiyL/Ubf/s/rkZ+sGj yJizBACtwCbQzA+z9XBZNUat5NPgcZz5Qeh1nwF9Nxnr6pyBv7tkrLh/3gxRGHqG 063dMbUk8pmUcJzBUyRsNiIPDoEUsLjY5zmZZmp/waAhpREsnK29WLCbqLdpUors c1JJBsObkA1IM8TZY8YUmvsMEvBLCCanuKpclZZXqeRAeOHJ0v4DZQJHTlUBtBZU ZXN0MiA8dGVzdDJAdGVzdC5jb20+iGIEExECACIFAlERnrMCGwMGCwkIBwMCBhUI AgkKCwQWAgMBAh4BAheAAAoJEBEnlAPLFp74xc0AoLNZINHe0ytOsNtMCuLvc3Vd vePUAJ9KX3L5IBqHarsa+aJHX7r796SokZ0BWARREZ6zEAQA2WkxmNbfeMzGUocN 3JEVe0o6rxGt5eGrTSmWisduDP3MURabhUXnf4T8oaeYcbJjkLLxMrJmNq55ln1e 4bSG5mDkh/ryKsV81m3F0DbqO/z/891nRSP5fondFVral4wsMOzBNgs4vVk7V/F2 0MPjR90CIhnVDKPAQbQA+3PjUR8AAwUEALn922AEE+0d7xSMMFpR7ic3Me5QEGnp cT4ft6oc0UK5kAnvKoksZUc0hpBHjX1w3LTz847/5hRDuuDvwvGMWK8IfsjOF9T7 rK8QtJuBEyJxjoScA/YZP5vX4y0U1reUEa0EdwmVrnZzatMAe2FhlaR9PlHkOcm5 DZwkcExL0dbI/gMDArxZ+5N7kH4zYLtr9glJS/pJ7F0YJqJpNwCbqD8+8DqHD8Uv MgQ/rtBxBJJOaF+1AjCd123hLgzIkkfdTh8loV9hDXMKeJgmiEkEGBECAAkFAlER nrMCGwwACgkQESeUA8sWnvhBswCfdXjznvHCc73/6/MhWcv3dbeTT/wAoLyiZg8+ iY3UT9QkV9d0sMgyLkug =GQsY -----END PGP PRIVATE KEY BLOCK-----`; const armoredPublicKey1 = `-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1.4.11 (GNU/Linux) mQGiBFERlw4RBAD6Bmcf2w1dtUmtCLkdxeqZLArk3vYoQAjdibxA3gXVyur7fsWb ro0jVbBHqOCtC6jDxE2l52NP9+tTlWeVMaqqNvUE47LSaPq2DGI8Wx1Rj6bF3mTs obYEwhGbGh/MhJnME9AHODarvk8AZbzo0+k1EwrBWF6dTUBPfqO7rGU2ewCg80WV x5pt3evj8rRK3jQ8SMKTNRsD/1PhTdxdZTdXARAFzcW1VaaruWW0Rr1+XHKKwDCz i7HE76SO9qjnQfZCZG75CdQxI0h8GFeN3zsDqmhob2iSz2aJ1krtjM+iZ1FBFd57 OqCV6wmk5IT0RBN12ZzMS19YvzN/ONXHrmTZlKExd9Mh9RKLeVNw+bf6JsKQEzcY JzFkBACX9X+hDYchO/2hiTwx4iOO9Fhsuh7eIWumB3gt+aUpm1jrSbas/QLTymmk uZuQVXI4NtnlvzlNgWv4L5s5RU5WqNGG7WSaKNdcrvJZRC2dgbUJt04J5CKrWp6R aIYal/81Ut1778lU01PEt563TcQnUBlnjU5OR25KhfSeN5CZY7QUVGVzdCA8dGVz dEB0ZXN0LmNvbT6IYgQTEQIAIgUCURGXDgIbAwYLCQgHAwIGFQgCCQoLBBYCAwEC HgECF4AACgkQikDlZK/UvLSspgCfcNaOpTg1W2ucR1JwBbBGvaERfuMAnRgt3/rs EplqEakMckCtikEnpxYe =b2Ln -----END PGP PUBLIC KEY BLOCK-----`; const expiredPublicKeyThroughDirectSignature = `-----BEGIN PGP PUBLIC KEY BLOCK----- xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv /seOXpgecTdOcVttfzC8ycIKrt3aQTiwOG/ctaR4Bk/t6ayNFfdUNxHWk4WCKzdz /56fW2O0F23qIRd8UUJp5IIlN4RDdRCtdhVQIAuzvp2oVy/LaS2kxQoKvph/5pQ/ 5whqsyroEWDJoSV0yOb25B/iwk/pLUFoyhDG9bj0kIzDxrEqW+7Ba8nocQlecMF3 X5KMN5kp2zraLv9dlBBpWW43XktjcCZgMy20SouraVma8Je/ECwUWYUiAZxLIlMv 9CurEOtxUw6N3RdOtLmYZS9uEnn5y1UkF88o8Nku890uk6BrewFzJyLAx5wRZ4F0 qV/yq36UWQ0JB/AUGhHVPdFf6pl6eaxBwT5GXvbBUibtf8YI2og5RsgTWtXfU7eb SGXrl5ZMpbA6mbfhd0R8aPxWfmDWiIOhBufhMCvUHh1sApMKVZnvIff9/0Dca3wb vLIwa3T4CyshfT0AEQEAAcLA+QQfAQoADAWCX2i/SgWJAT9MWAAhCRD7/MgqAV5z MBYhBNGmbhojsYLJmA94jPv8yCoBXnMwZNYL/RmU7kIYsi7w8d7sPLiqb5C9fs9k TJuxLREYpKE7zWz9z16+c9ketkoLpoMSDaZL+4+QEfyAJA+q8c8ZFHJ8E60cPNwe jN/ZI+vJRloDAfxMkH+BdKshMtvcmlLq2+AbQWzT0kAUkiiKiUiUsQwrTfenjkT5 FCsZyKviLsarzdIhpwEdd6zCxWQDap55njXfpUh/vQFZo4aHHtWPwXXRjLZRlKA+ gI8LQyYuIFOCFQMrhZVEwaLJQa6IbauL4B/qD4y5AMenNumW5M06p0G8yj1L22b6 R2hWS7Ueo0iu9J4abTEDo1gGxeLwCiMRUGpN7L+4J3yrzGNcjjtXz1/FT6/YSvT2 bnPraOOGaEO5tflQZ6plEOIc9bKnb2vySlwpxnWgJ7CQdAT+lGVT5xRZ//we5yja vsb4pdo0xIW32YDzFQ36HgAO8XUXnz0NkgVDHLujWsyhjq9xkfMOhSmGSeXxvsXa 1O9uC2n+qX8hV7whWf20UPHKatYbBV0HHJeA280hQm9iIEJhYmJhZ2UgPGJvYkBv cGVucGdwLmV4YW1wbGU+wsEOBBMBCgA4AhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4B AheAFiEE0aZuGiOxgsmYD3iM+/zIKgFeczAFAl2lnvoACgkQ+/zIKgFeczBvbAv/ VNk90a6hG8Od9xTzXxH5YRFUSGfIA1yjPIVOnKqhMwps2U+sWE3urL+MvjyQRlyR V8oY9IOhQ5Esm6DOZYrTnE7qVETm1ajIAP2OFChEc55uH88x/anpPOXOJY7S8jbn 3naC9qad75BrZ+3g9EBUWiy5p8TykP05WSnSxNRt7vFKLfEB4nGkehpwHXOVF0CR NwYle42bg8lpmdXFDcCZCi+qEbafmTQzkAqyzS3nCh3IAqq6Y0kBuaKLm2tSNUOl ZbD+OHYQNZ5Jix7cZUzs6Xh4+I55NRWl5smrLq66yOQoFPy9jot/Qxikx/wP3MsA zeGaZSEPc0fHp5G16rlGbxQ3vl8/usUV7W+TMEMljgwd5x8POR6HC8EaCDfVnUBC Pi/Gv+egLjsIbPJZZEroiE40e6/UoCiQtlpQB5exPJYSd1Q1txCwueih99PHepsD hmUQKiACszNU+RRozAYau2VdHqnRJ7QYdxHDiH49jPK4NTMyb/tJh2TiIwcmsIpG zsDNBF2lnPIBDADWML9cbGMrp12CtF9b2P6z9TTT74S8iyBOzaSvdGDQY/sUtZXR g21HWamXnn9sSXvIDEINOQ6A9QxdxoqWdCHrOuW3ofneYXoG+zeKc4dC86wa1TR2 q9vW+RMXSO4uImA+Uzula/6k1DogDf28qhCxMwG/i/m9g1c/0aApuDyKdQ1PXsHH Nlgd/Dn6rrd5y2AObaifV7wIhEJnvqgFXDN2RXGjLeCOHV4Q2WTYPg/S4k1nMXVD wZXrvIsA0YwIMgIT86Rafp1qKlgPNbiIlC1g9RY/iFaGN2b4Ir6GDohBQSfZW2+L XoPZuVE/wGlQ01rh827KVZW4lXvqsge+wtnWlszcselGATyzqOK9LdHPdZGzROZY I2e8c+paLNDdVPL6vdRBUnkCaEkOtl1mr2JpQi5nTU+gTX4IeInC7E+1a9UDF/Y8 5ybUz8XV8rUnR76UqVC7KidNepdHbZjjXCt8/Zo+Tec9JNbYNQB/e9ExmDntmlHE sSEQzFwzj8sxH48AEQEAAcLA9gQYAQoAIBYhBNGmbhojsYLJmA94jPv8yCoBXnMw BQJdpZzyAhsMAAoJEPv8yCoBXnMw6f8L/26C34dkjBffTzMj5Bdzm8MtF67OYneJ 4TQMw7+41IL4rVcSKhIhk/3Ud5knaRtP2ef1+5F66h9/RPQOJ5+tvBwhBAcUWSup KnUrdVaZQanYmtSxcVV2PL9+QEiNN3tzluhaWO//rACxJ+K/ZXQlIzwQVTpNhfGz AaMVV9zpf3u0k14itcv6alKY8+rLZvO1wIIeRZLmU0tZDD5HtWDvUV7rIFI1WuoL b+KZgbYn3OWjCPHVdTrdZ2CqnZbG3SXw6awH9bzRLV9EXkbhIMez0deCVdeo+wFF klh8/5VK2b0vk/+wqMJxfpa1lHvJLobzOP9fvrswsr92MA2+k901WeISR7qEzcI0 Fdg8AyFAExaEK6VyjP7SXGLwvfisw34OxuZr3qmx1Sufu4toH3XrB7QJN8Xyqqbs GxUCBqWif9RSK4xjzRTe56iPeiSJJOIciMP9i2ldI+KgLycyeDvGoBj0HCLO3gVa Be4ubVrj5KjhX2PVNEJd3XZRzaXZE2aAMQ== =ZeAz -----END PGP PUBLIC KEY BLOCK-----`; const eccPrivateKey = `-----BEGIN PGP PRIVATE KEY BLOCK----- xVgEYaYskRYJKwYBBAHaRw8BAQdAlHT6jzgvcng/qDvb+LH+nA4+AWrMLUYf aNJIuJRUjXMAAP9llTr5+fNSY78FNnpx53muMtyeDINkeUGGwgqAfxj9lhEV zRN0ZXN0IDx0ZXN0QHRlc3QuaXQ+wowEEBYKAB0FAmGmLJEECwkHCAMVCAoE FgACAQIZAQIbAwIeAQAhCRBvJAzR+vGyExYhBCaNeWwMzRW97WhAq28kDNH6 8bITWWkA/0R3zADs94dVo+iSNzrtZaDkbHOMb/yjketYmI0XS8UpAP4hUmKN QcohP6007t0gaQUcgdwum7PKUoM6BeBG8GaTAsddBGGmLJESCisGAQQBl1UB BQEBB0CibQAv6tvWCWoe6xlkkZGbLpVWvHwgIPzRVdz4e79DdQMBCAcAAP9T 4SntnkgSUnM39dFoTPIoitrsOcHZbvXPCcvclKgZKBJTwngEGBYIAAkFAmGm LJECGwwAIQkQbyQM0frxshMWIQQmjXlsDM0Vve1oQKtvJAzR+vGyE5ORAQD+ lfFvJjue+tnuIR+ZubxtpKaJpCOWkAcrkx41NtsLwgD/TAkWh1KDWg0IOcUE MbVkSnU2Z+vhSmYubDCldNOSVwE= =bTUQ -----END PGP PRIVATE KEY BLOCK-----`; function withCompression(tests) { const compressionTypes = Object.values(openpgp.enums.compression); compressionTypes.forEach(function (compression) { const compressionName = openpgp.enums.read(openpgp.enums.compression, compression); if (compressionName === 'bzip2') { return; // bzip2 compression is not supported. } const group = `compression - ${compressionName}`; describe(group, function() { let compressSpy; let decompressSpy; beforeEach(function () { compressSpy = spy(openpgp.CompressedDataPacket.prototype, 'compress'); decompressSpy = spy(openpgp.CompressedDataPacket.prototype, 'decompress'); }); afterEach(function () { compressSpy.restore(); decompressSpy.restore(); }); tests( function(options) { options.config = { preferredCompressionAlgorithm: compression }; return options; }, function() { if (compression === openpgp.enums.compression.uncompressed) { expect(compressSpy.called).to.be.false; expect(decompressSpy.called).to.be.false; return; } expect(compressSpy.called).to.be.true; expect(compressSpy.thisValues[0].algorithm).to.equal(compression); expect(decompressSpy.called).to.be.true; expect(decompressSpy.thisValues[0].algorithm).to.equal(compression); } ); }); }); } module.exports = () => describe('OpenPGP.js public api tests', function() { describe('readKey(s) and readPrivateKey(s) - unit tests', function() { it('readKey and readPrivateKey should create equal private keys', async function() { const key = await openpgp.readKey({ armoredKey: priv_key }); const privateKey = await openpgp.readPrivateKey({ armoredKey: priv_key }); expect(key.isPrivate()).to.be.true; expect(privateKey.isPrivate()).to.be.true; expect(key.isDecrypted()).to.be.false; expect(privateKey.isDecrypted()).to.be.false; expect(key.getKeyID().equals(privateKey.getKeyID())).to.be.true; }); it('readPrivateKeys and readKeys should create equal private keys', async function() { const keys = await openpgp.readKeys({ armoredKeys: twoPrivateKeys }); const privateKeys = await openpgp.readPrivateKeys({ armoredKeys: twoPrivateKeys }); // pairwise comparison const zip = (arr1, arr2) => arr1.map((el, i) => [el, arr2[i]]); zip(keys, privateKeys).forEach(([key, privateKey]) => { expect(key.isPrivate()).to.be.true; expect(privateKey.isPrivate()).to.be.true; expect(key.isDecrypted()).to.be.true; expect(privateKey.isDecrypted()).to.be.true; expect(key.getKeyID().equals(privateKey.getKeyID())).to.be.true; }); }); it('readPrivateKey should throw on armored public key', async function() { await expect(openpgp.readPrivateKey({ armoredKey: pub_key })).to.be.rejectedWith(/Armored text not of type private key/); }); it('readPrivateKeys should throw on armored public keys', async function() { await expect(openpgp.readPrivateKeys({ armoredKeys: twoPublicKeys })).to.be.rejectedWith(/Armored text not of type private key/); }); }); describe('generateKey - validate user ids', function() { it('should fail for invalid user name', async function() { const opt = { userIDs: [{ name: {}, email: 'text@example.com' }] }; const test = openpgp.generateKey(opt); await expect(test).to.eventually.be.rejectedWith(/Invalid user ID format/); }); it('should fail for invalid user email address', async function() { const opt = { userIDs: [{ name: 'Test User', email: 'textexample.com' }] }; const test = openpgp.generateKey(opt); await expect(test).to.eventually.be.rejectedWith(/Invalid user ID format/); }); it('should fail for invalid user email address', async function() { const opt = { userIDs: [{ name: 'Test User', email: 'text@examplecom' }] }; const test = openpgp.generateKey(opt); await expect(test).to.eventually.be.rejectedWith(/Invalid user ID format/); }); it('should fail for string user ID', async function() { const opt = { userIDs: 'Test User ' }; const test = openpgp.generateKey(opt); await expect(test).to.eventually.be.rejectedWith(/Invalid user ID format/); }); it('should work for valid single user ID object', function() { const opt = { userIDs: { name: 'Test User', email: 'text@example.com' } }; return openpgp.generateKey(opt); }); it('should work for array of user ID objects', function() { const opt = { userIDs: [{ name: 'Test User', email: 'text@example.com' }] }; return openpgp.generateKey(opt); }); it('should work for undefined name', function() { const opt = { userIDs: { email: 'text@example.com' } }; return openpgp.generateKey(opt); }); it('should work for an undefined email address', function() { const opt = { userIDs: { name: 'Test User' } }; return openpgp.generateKey(opt); }); }); describe('generateKey - unit tests', function() { it('should have default params set', function() { const now = util.normalizeDate(new Date()); const opt = { userIDs: { name: 'Test User', email: 'text@example.com' }, passphrase: 'secret', date: now, format: 'object' }; return openpgp.generateKey(opt).then(async function({ privateKey, publicKey }) { for (const key of [publicKey, privateKey]) { expect(key).to.exist; expect(key.users.length).to.equal(1); expect(key.users[0].userID.name).to.equal('Test User'); expect(key.users[0].userID.email).to.equal('text@example.com'); expect(key.getAlgorithmInfo().rsaBits).to.equal(undefined); expect(key.getAlgorithmInfo().curve).to.equal('ed25519'); expect(+key.getCreationTime()).to.equal(+now); expect(await key.getExpirationTime()).to.equal(Infinity); expect(key.subkeys.length).to.equal(1); expect(key.subkeys[0].getAlgorithmInfo().rsaBits).to.equal(undefined); expect(key.subkeys[0].getAlgorithmInfo().curve).to.equal('curve25519'); expect(+key.subkeys[0].getCreationTime()).to.equal(+now); expect(await key.subkeys[0].getExpirationTime()).to.equal(Infinity); } }); }); it('should output keypair with expected format', async function() { const opt = { userIDs: { name: 'Test User', email: 'text@example.com' } }; const armored = await openpgp.generateKey({ ...opt, format: 'armored' }); expect((await openpgp.readKey({ armoredKey: armored.privateKey })).isPrivate()).to.be.true; expect((await openpgp.readKey({ armoredKey: armored.publicKey })).isPrivate()).to.be.false; const binary = await openpgp.generateKey({ ...opt, format: 'binary' }); expect((await openpgp.readKey({ binaryKey: binary.privateKey })).isPrivate()).to.be.true; expect((await openpgp.readKey({ binaryKey: binary.publicKey })).isPrivate()).to.be.false; const { privateKey, publicKey } = await openpgp.generateKey({ ...opt, format: 'object' }); expect(privateKey.isPrivate()).to.be.true; expect(publicKey.isPrivate()).to.be.false; }); }); describe('reformatKey - unit tests', function() { it('should output keypair with expected format', async function() { const encryptedKey = await openpgp.readKey({ armoredKey: priv_key }); const original = await openpgp.decryptKey({ privateKey: encryptedKey, passphrase: passphrase }); const opt = { privateKey: original, userIDs: { name: 'Test User', email: 'text@example.com' } }; const armored = await openpgp.reformatKey({ ...opt, format: 'armored' }); expect((await openpgp.readKey({ armoredKey: armored.privateKey })).isPrivate()).to.be.true; expect((await openpgp.readKey({ armoredKey: armored.publicKey })).isPrivate()).to.be.false; const binary = await openpgp.reformatKey({ ...opt, format: 'binary' }); expect((await openpgp.readKey({ binaryKey: binary.privateKey })).isPrivate()).to.be.true; expect((await openpgp.readKey({ binaryKey: binary.publicKey })).isPrivate()).to.be.false; const { privateKey, publicKey } = await openpgp.reformatKey({ ...opt, format: 'object' }); expect(privateKey.isPrivate()).to.be.true; expect(publicKey.isPrivate()).to.be.false; }); }); describe('revokeKey - unit tests', function() { it('should output key with expected format', async function() { const encryptedKey = await openpgp.readKey({ armoredKey: priv_key }); const key = await openpgp.decryptKey({ privateKey: encryptedKey, passphrase: passphrase }); const armored = await openpgp.revokeKey({ key, format: 'armored' }); expect((await openpgp.readKey({ armoredKey: armored.privateKey })).isPrivate()).to.be.true; expect((await openpgp.readKey({ armoredKey: armored.publicKey })).isPrivate()).to.be.false; const binary = await openpgp.revokeKey({ key, format: 'binary' }); expect((await openpgp.readKey({ binaryKey: binary.privateKey })).isPrivate()).to.be.true; expect((await openpgp.readKey({ binaryKey: binary.publicKey })).isPrivate()).to.be.false; const { privateKey, publicKey } = await openpgp.revokeKey({ key, format: 'object' }); expect(privateKey.isPrivate()).to.be.true; expect(publicKey.isPrivate()).to.be.false; }); }); describe('decryptKey - unit tests', function() { it('should work for correct passphrase', async function() { const privateKey = await openpgp.readKey({ armoredKey: priv_key }); const originalKey = await openpgp.readKey({ armoredKey: privateKey.armor() }); return openpgp.decryptKey({ privateKey: privateKey, passphrase: passphrase }).then(unlocked => { expect(unlocked.getKeyID().toHex()).to.equal(privateKey.getKeyID().toHex()); expect(unlocked.subkeys[0].getKeyID().toHex()).to.equal(privateKey.subkeys[0].getKeyID().toHex()); expect(unlocked.isDecrypted()).to.be.true; expect(unlocked.keyPacket.privateParams).to.not.be.null; // original key should be unchanged expect(privateKey.isDecrypted()).to.be.false; expect(privateKey.keyPacket.privateParams).to.be.null; expect(privateKey).to.deep.equal(originalKey); }); }); it('should work with multiple passphrases', async function() { const privateKey = await openpgp.readKey({ armoredKey: priv_key }); const originalKey = await openpgp.readKey({ armoredKey: privateKey.armor() }); return openpgp.decryptKey({ privateKey: privateKey, passphrase: ['rubbish', passphrase] }).then(unlocked => { expect(unlocked.getKeyID().toHex()).to.equal(privateKey.getKeyID().toHex()); expect(unlocked.subkeys[0].getKeyID().toHex()).to.equal(privateKey.subkeys[0].getKeyID().toHex()); expect(unlocked.isDecrypted()).to.be.true; expect(unlocked.keyPacket.privateParams).to.not.be.null; // original key should be unchanged expect(privateKey.isDecrypted()).to.be.false; expect(privateKey.keyPacket.privateParams).to.be.null; expect(privateKey).to.deep.equal(originalKey); }); }); it('should fail for incorrect passphrase', async function() { const privateKey = await openpgp.readKey({ armoredKey: priv_key }); const originalKey = await openpgp.readKey({ armoredKey: privateKey.armor() }); return openpgp.decryptKey({ privateKey: privateKey, passphrase: 'incorrect' }).then(function() { throw new Error('Should not decrypt with incorrect passphrase'); }).catch(function(error) { expect(error.message).to.match(/Incorrect key passphrase/); // original key should be unchanged expect(privateKey.isDecrypted()).to.be.false; expect(privateKey.keyPacket.privateParams).to.be.null; expect(privateKey).to.deep.equal(originalKey); }); }); it('should fail for corrupted key', async function() { const privateKeyMismatchingParams = await openpgp.readKey({ armoredKey: mismatchingKeyParams }); const originalKey = await openpgp.readKey({ armoredKey: privateKeyMismatchingParams.armor() }); return openpgp.decryptKey({ privateKey: privateKeyMismatchingParams, passphrase: 'userpass' }).then(function() { throw new Error('Should not decrypt corrupted key'); }).catch(function(error) { expect(error.message).to.match(/Key is invalid/); expect(privateKeyMismatchingParams.isDecrypted()).to.be.false; expect(privateKeyMismatchingParams.keyPacket.privateParams).to.be.null; expect(privateKeyMismatchingParams).to.deep.equal(originalKey); }); }); }); describe('encryptKey - unit tests', function() { it('should not change original key', async function() { const { privateKey: armoredKey } = await openpgp.generateKey({ userIDs: [{ name: 'test', email: 'test@test.com' }] }); // read both keys from armored data to make sure all fields are exactly the same const key = await openpgp.readKey({ armoredKey }); const originalKey = await openpgp.readKey({ armoredKey }); return openpgp.encryptKey({ privateKey: key, passphrase: passphrase }).then(locked => { expect(locked.getKeyID().toHex()).to.equal(key.getKeyID().toHex()); expect(locked.subkeys[0].getKeyID().toHex()).to.equal(key.subkeys[0].getKeyID().toHex()); expect(locked.isDecrypted()).to.be.false; expect(locked.keyPacket.privateParams).to.be.null; // original key should be unchanged expect(key.isDecrypted()).to.be.true; expect(key.keyPacket.privateParams).to.not.be.null; expect(key).to.deep.equal(originalKey); }); }); it('encrypted key can be decrypted', async function() { const { privateKey } = await openpgp.generateKey({ userIDs: [{ name: 'test', email: 'test@test.com' }], format: 'object' }); const locked = await openpgp.encryptKey({ privateKey, passphrase: passphrase }); expect(locked.isDecrypted()).to.be.false; const unlocked = await openpgp.decryptKey({ privateKey: locked, passphrase: passphrase }); expect(unlocked.isDecrypted()).to.be.true; }); it('should throw on empty passphrase', async function() { const { privateKey } = await openpgp.generateKey({ userIDs: [{ name: 'test', email: 'test@test.com' }], format: 'object' }); await expect(openpgp.encryptKey({ privateKey, passphrase: '' })).to.be.rejectedWith(/passphrase is required for key encryption/); }); it('should support multiple passphrases', async function() { const { privateKey } = await openpgp.generateKey({ userIDs: [{ name: 'test', email: 'test@test.com' }], format: 'object' }); const passphrases = ['123', '456']; const locked = await openpgp.encryptKey({ privateKey, passphrase: passphrases }); expect(locked.isDecrypted()).to.be.false; await expect(openpgp.decryptKey({ privateKey: locked, passphrase: passphrases[0] })).to.eventually.be.rejectedWith(/Incorrect key passphrase/); const unlocked = await openpgp.decryptKey({ privateKey: locked, passphrase: passphrases }); expect(unlocked.isDecrypted()).to.be.true; }); it('should encrypt gnu-dummy key', async function() { const key = await openpgp.readKey({ armoredKey: gnuDummyKeySigningSubkey }); const locked = await openpgp.encryptKey({ privateKey: key, passphrase: passphrase }); expect(key.isDecrypted()).to.be.true; expect(locked.isDecrypted()).to.be.false; expect(locked.keyPacket.isDummy()).to.be.true; const unlocked = await openpgp.decryptKey({ privateKey: locked, passphrase: passphrase }); expect(key.isDecrypted()).to.be.true; expect(unlocked.isDecrypted()).to.be.true; expect(unlocked.keyPacket.isDummy()).to.be.true; }); }); describe('decrypt - unit tests', function() { let minRSABitsVal; beforeEach(async function() { minRSABitsVal = openpgp.config.minRSABits; openpgp.config.minRSABits = 512; }); afterEach(function() { openpgp.config.minRSABits = minRSABitsVal; }); it('Calling decrypt with encrypted key leads to exception', async function() { const publicKey = await openpgp.readKey({ armoredKey: pub_key }); const privateKey = await openpgp.readKey({ armoredKey: priv_key }); const encOpt = { message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: publicKey }; const decOpt = { decryptionKeys: privateKey }; const encrypted = await openpgp.encrypt(encOpt); decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); await expect(openpgp.decrypt(decOpt)).to.be.rejectedWith('Error decrypting message: Decryption key is not decrypted.'); }); it('decrypt/verify should succeed with valid signature (expectSigned=true)', async function () { const publicKey = await openpgp.readKey({ armoredKey: pub_key }); const privateKey = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key }), passphrase }); const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: plaintext }), signingKeys: privateKey, encryptionKeys: publicKey }); const { data, signatures } = await openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: encrypted }), decryptionKeys: privateKey, verificationKeys: publicKey, expectSigned: true }); expect(data).to.equal(plaintext); expect(await signatures[0].verified).to.be.true; }); it('decrypt/verify should throw on missing public keys (expectSigned=true)', async function () { const publicKey = await openpgp.readKey({ armoredKey: pub_key }); const privateKey = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key }), passphrase }); const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: publicKey, signingKeys: privateKey }); await expect(openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: encrypted }), decryptionKeys: privateKey, expectSigned: true })).to.be.eventually.rejectedWith(/Verification keys are required/); }); it('decrypt/verify should throw on missing signature (expectSigned=true)', async function () { const publicKey = await openpgp.readKey({ armoredKey: pub_key }); const privateKey = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key }), passphrase }); const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: publicKey }); await expect(openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: encrypted }), decryptionKeys: privateKey, verificationKeys: publicKey, expectSigned: true })).to.be.eventually.rejectedWith(/Message is not signed/); }); it('decrypt/verify should throw on invalid signature (expectSigned=true)', async function () { const publicKey = await openpgp.readKey({ armoredKey: pub_key }); const wrongPublicKey = (await openpgp.readKey({ armoredKey: priv_key_2000_2008 })).toPublic(); const privateKey = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key }), passphrase }); const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: publicKey, signingKeys: privateKey }); await expect(openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: encrypted }), decryptionKeys: privateKey, verificationKeys: wrongPublicKey, expectSigned: true })).to.be.eventually.rejectedWith(/Could not find signing key/); }); it('decrypt/verify should succeed with valid signature (expectSigned=true, with streaming)', async function () { const publicKey = await openpgp.readKey({ armoredKey: pub_key }); const privateKey = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key }), passphrase }); const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: plaintext }), signingKeys: privateKey, encryptionKeys: publicKey }); const { data: streamedData, signatures } = await openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: stream.toStream(encrypted) }), decryptionKeys: privateKey, verificationKeys: publicKey, expectSigned: true }); const data = await stream.readToEnd(streamedData); expect(data).to.equal(plaintext); expect(await signatures[0].verified).to.be.true; }); it('decrypt/verify should throw on missing public keys (expectSigned=true, with streaming)', async function () { const publicKey = await openpgp.readKey({ armoredKey: pub_key }); const privateKey = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key }), passphrase }); const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: publicKey, signingKeys: privateKey }); await expect(openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: stream.toStream(encrypted) }), decryptionKeys: privateKey, expectSigned: true })).to.be.eventually.rejectedWith(/Verification keys are required/); }); it('decrypt/verify should throw on missing signature (expectSigned=true, with streaming)', async function () { const publicKey = await openpgp.readKey({ armoredKey: pub_key }); const privateKey = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key }), passphrase }); const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: publicKey }); await expect(openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: stream.toStream(encrypted) }), decryptionKeys: privateKey, verificationKeys: publicKey, expectSigned: true })).to.be.eventually.rejectedWith(/Message is not signed/); }); it('decrypt/verify should throw on invalid signature (expectSigned=true, with streaming)', async function () { const publicKey = await openpgp.readKey({ armoredKey: pub_key }); const wrongPublicKey = (await openpgp.readKey({ armoredKey: priv_key_2000_2008 })).toPublic(); const privateKey = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key }), passphrase }); const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: publicKey, signingKeys: privateKey }); const { data: streamedData } = await openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: stream.toStream(encrypted) }), decryptionKeys: privateKey, verificationKeys: wrongPublicKey, expectSigned: true }); await expect( stream.readToEnd(streamedData) ).to.be.eventually.rejectedWith(/Could not find signing key/); }); it('Supports decrypting with GnuPG dummy key', async function() { const { rejectMessageHashAlgorithms } = openpgp.config; Object.assign(openpgp.config, { rejectMessageHashAlgorithms: new Set([openpgp.enums.hash.md5, openpgp.enums.hash.ripemd]) }); try { const armoredMessage = `-----BEGIN PGP MESSAGE----- Version: GnuPG v1.4.11 (GNU/Linux) hQEOA1N4OCSSjECBEAP/diDJCQn4e88193PgqhbfAkohk9RQ0v0MPnXpJbCRTHKO 8r9nxiAr/TQv4ZOingXdAp2JZEoE9pXxZ3r1UWew04czxmgJ8FP1ztZYWVFAWFVi Tj930TBD7L1fY/MD4fK6xjEG7z5GT8k4tn4mLm/PpWMbarIglfMopTy1M/py2cID /2Sj7Ikh3UFiG+zm4sViYc5roNbMy8ixeoKixxi99Mx8INa2cxNfqbabjblFyc0Z BwmbIc+ZiY2meRNI5y/tk0gRD7hT84IXGGl6/mH00bsX/kkWdKGeTvz8s5G8RDHa Za4HgLbXItkX/QarvRS9kvkD01ujHfj+1ZvgmOBttNfP0p8BQLIICqvg1eYD9aPB +GtOZ2F3+k5VyBL5yIn/s65SBjNO8Fqs3aL0x+p7s1cfUzx8J8a8nWpqq/qIQIqg ZJH6MZRKuQwscwH6NWgsSVwcnVCAXnYOpbHxFQ+j7RbF/+uiuqU+DFH/Rd5pik8b 0Dqnp0yfefrkjQ0nuvubgB6Rv89mHpnvuJfFJRInpg4lrHwLvRwdpN2HDozFHcKK aOU= =4iGt -----END PGP MESSAGE-----`; const passphrase = 'abcd'; // exercises the GnuPG s2k type 1001 extension: // the secrets on the primary key have been stripped. const dummyKey = await openpgp.readKey({ armoredKey: armoredDummyPrivateKey1 }); const publicKey = await openpgp.readKey({ armoredKey: armoredPublicKey1 }); const message = await openpgp.readMessage({ armoredMessage }); const primaryKeyPacket = dummyKey.keyPacket.write(); expect(dummyKey.isDecrypted()).to.be.false; const decryptedDummyKey = await openpgp.decryptKey({ privateKey: dummyKey, passphrase }); expect(decryptedDummyKey.isDecrypted()).to.be.true; // decrypting with a secret subkey works const msg = await openpgp.decrypt({ message, decryptionKeys: decryptedDummyKey, verificationKeys: publicKey, config: { rejectPublicKeyAlgorithms: new Set() } }); expect(msg.signatures).to.exist; expect(msg.signatures).to.have.length(1); expect(await msg.signatures[0].verified).to.be.true; expect((await msg.signatures[0].signature).packets.length).to.equal(1); // secret key operations involving the primary key should fail await expect(openpgp.sign({ message: await openpgp.createMessage({ text: 'test' }), signingKeys: decryptedDummyKey, config: { rejectPublicKeyAlgorithms: new Set() } })).to.eventually.be.rejectedWith(/Cannot sign with a gnu-dummy key/); await expect( openpgp.reformatKey({ userIDs: { name: 'test' }, privateKey: decryptedDummyKey }) ).to.eventually.be.rejectedWith(/Cannot reformat a gnu-dummy primary key/); const encryptedDummyKey = await openpgp.encryptKey({ privateKey: decryptedDummyKey, passphrase }); expect(encryptedDummyKey.isDecrypted()).to.be.false; const primaryKeyPacket2 = encryptedDummyKey.keyPacket.write(); expect(primaryKeyPacket).to.deep.equal(primaryKeyPacket2); } finally { Object.assign(openpgp.config, { rejectMessageHashAlgorithms }); } }); it('decrypt with `config.constantTimePKCS1Decryption` option should succeed', async function () { const publicKey = await openpgp.readKey({ armoredKey: pub_key }); const publicKey2 = await openpgp.readKey({ armoredKey: eccPrivateKey }); const privateKey = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key }), passphrase }); const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: plaintext }), signingKeys: privateKey, encryptionKeys: [publicKey, publicKey2] }); const { data } = await openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: encrypted }), decryptionKeys: privateKey, config: { constantTimePKCS1Decryption: true } }); expect(data).to.equal(plaintext); }); it('decrypt with `config.constantTimePKCS1Decryption` option should succeed (with streaming)', async function () { const publicKey = await openpgp.readKey({ armoredKey: pub_key }); const publicKey2 = await openpgp.readKey({ armoredKey: eccPrivateKey }); const privateKey = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key }), passphrase }); const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: plaintext }), signingKeys: privateKey, encryptionKeys: [publicKey, publicKey2] }); const { data: streamedData } = await openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: stream.toStream(encrypted) }), decryptionKeys: privateKey, verificationKeys: publicKey, expectSigned: true, config: { constantTimePKCS1Decryption: true } }); const data = await stream.readToEnd(streamedData); expect(data).to.equal(plaintext); }); it('decrypt with `config.constantTimePKCS1Decryption` option should fail if session key algo support is disabled', async function () { const publicKeyRSA = await openpgp.readKey({ armoredKey: pub_key }); const privateKeyRSA = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key }), passphrase }); const privateKeyECC = await openpgp.readPrivateKey({ armoredKey: eccPrivateKey }); const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: plaintext }), signingKeys: privateKeyRSA, encryptionKeys: [publicKeyRSA, privateKeyECC] }); const config = { constantTimePKCS1Decryption: true, constantTimePKCS1DecryptionSupportedSymmetricAlgorithms: new Set() }; // decryption using RSA key should fail await expect(openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: encrypted }), decryptionKeys: privateKeyRSA, config })).to.be.rejectedWith(/Session key decryption failed/); // decryption using ECC key should succeed (PKCS1 is not used, so constant time countermeasures are not applied) const { data } = await openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: encrypted }), decryptionKeys: privateKeyECC, config }); expect(data).to.equal(plaintext); }); }); describe('verify - unit tests', function() { let minRSABitsVal; beforeEach(async function() { minRSABitsVal = openpgp.config.minRSABits; openpgp.config.minRSABits = 512; }); afterEach(function() { openpgp.config.minRSABits = minRSABitsVal; }); describe('message', function() { verifyTests(false); it('verify should succeed with valid signature (expectSigned=true, with streaming)', async function () { const publicKey = await openpgp.readKey({ armoredKey: pub_key }); const privateKey = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key }), passphrase }); const signed = await openpgp.sign({ message: await openpgp.createMessage({ text: plaintext }), signingKeys: privateKey }); const { data: streamedData, signatures } = await openpgp.verify({ message: await openpgp.readMessage({ armoredMessage: stream.toStream(signed) }), verificationKeys: publicKey, expectSigned: true }); const data = await stream.readToEnd(streamedData); expect(data).to.equal(plaintext); expect(await signatures[0].verified).to.be.true; }); it('verify should throw on missing signature (expectSigned=true, with streaming)', async function () { const publicKey = await openpgp.readKey({ armoredKey: pub_key }); await expect(openpgp.verify({ message: await openpgp.createMessage({ text: stream.toStream(plaintext) }), verificationKeys: publicKey, expectSigned: true })).to.be.eventually.rejectedWith(/Message is not signed/); }); it('verify should throw on invalid signature (expectSigned=true, with streaming)', async function () { const wrongPublicKey = (await openpgp.readKey({ armoredKey: priv_key_2000_2008 })).toPublic(); const privateKey = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key }), passphrase }); const signed = await openpgp.sign({ message: await openpgp.createMessage({ text: plaintext }), signingKeys: privateKey }); const { data: streamedData } = await openpgp.verify({ message: await openpgp.readMessage({ armoredMessage: stream.toStream(signed) }), verificationKeys: wrongPublicKey, expectSigned: true }); await expect( stream.readToEnd(streamedData) ).to.be.eventually.rejectedWith(/Could not find signing key/); }); it('verify should fail if the signature is re-used with a different message', async function () { const privateKey = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key }), passphrase }); const message = await openpgp.createMessage({ text: 'a message' }); const armoredSignature = await openpgp.sign({ message, signingKeys: privateKey, detached: true }); const { signatures } = await openpgp.verify({ message, signature: await openpgp.readSignature({ armoredSignature }), verificationKeys: privateKey.toPublic() }); expect(await signatures[0].verified).to.be.true; // pass a different message await expect(openpgp.verify({ message: await openpgp.createMessage({ text: 'a different message' }), signature: await openpgp.readSignature({ armoredSignature }), verificationKeys: privateKey.toPublic(), expectSigned: true })).to.be.rejectedWith(/digest did not match/); }); }); describe('cleartext message', function() { verifyTests(true); }); function verifyTests(useCleartext) { const createMessage = useCleartext ? openpgp.createCleartextMessage : openpgp.createMessage; const readMessage = ({ armoredMessage }) => ( useCleartext ? openpgp.readCleartextMessage({ cleartextMessage: armoredMessage }) : openpgp.readMessage({ armoredMessage }) ); const text = useCleartext ? util.removeTrailingSpaces(plaintext) : plaintext; it('verify should succeed with valid signature (expectSigned=true)', async function () { const publicKey = await openpgp.readKey({ armoredKey: pub_key }); const privateKey = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key }), passphrase }); const signed = await openpgp.sign({ message: await createMessage({ text }), signingKeys: privateKey }); const { data, signatures } = await openpgp.verify({ message: await readMessage({ armoredMessage: signed }), verificationKeys: publicKey, expectSigned: true }); expect(data).to.equal(text); expect(await signatures[0].verified).to.be.true; }); it('verify should throw on missing signature (expectSigned=true)', async function () { const publicKey = await openpgp.readKey({ armoredKey: pub_key }); await expect(openpgp.verify({ message: await createMessage({ text }), verificationKeys: publicKey, expectSigned: true })).to.be.eventually.rejectedWith(/Message is not signed/); }); it('verify should throw on invalid signature (expectSigned=true)', async function () { const wrongPublicKey = (await openpgp.readKey({ armoredKey: priv_key_2000_2008 })).toPublic(); const privateKey = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key }), passphrase }); const signed = await openpgp.sign({ message: await createMessage({ text }), signingKeys: privateKey }); await expect(openpgp.verify({ message: await readMessage({ armoredMessage: signed }), verificationKeys: wrongPublicKey, expectSigned: true })).to.be.eventually.rejectedWith(/Could not find signing key/); }); } }); describe('sign - unit tests', function() { it('Supports signing with GnuPG dummy key', async function() { const dummyKey = await openpgp.readKey({ armoredKey: gnuDummyKeySigningSubkey }); const sig = await openpgp.sign({ message: await openpgp.createMessage({ text: 'test' }), signingKeys: dummyKey, date: new Date('2018-12-17T03:24:00'), config: { minRSABits: 1024 } }); expect(sig).to.match(/-----END PGP MESSAGE-----\n$/); }); it('Calling sign with no signing key leads to exception', async function() { await expect(openpgp.sign({ message: await openpgp.createMessage({ text: plaintext }) })).to.be.rejectedWith(/No signing keys provided/); }); it('should output cleartext message of expected format', async function() { const text = 'test'; const message = await openpgp.createCleartextMessage({ text }); const privateKey = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key }), passphrase }); const config = { minRSABits: 1024 }; const cleartextMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'armored' }); const parsedArmored = await openpgp.readCleartextMessage({ cleartextMessage }); expect(parsedArmored.text).to.equal(text); expect(parsedArmored.signature.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1); await expect(openpgp.sign({ message, signingKeys: privateKey, config, format: 'binary' })).to.be.rejectedWith(''); const objectMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'object' }); expect(objectMessage.signature.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1); const verified = await openpgp.verify({ message: objectMessage, verificationKeys: privateKey, expectSigned: true, config }); expect(verified.data).to.equal(text); }); it('should output message of expected format', async function() { const text = 'test'; const message = await openpgp.createMessage({ text }); const privateKey = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key }), passphrase }); const config = { minRSABits: 1024 }; const armoredMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'armored' }); const parsedArmored = await openpgp.readMessage({ armoredMessage }); expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1); const binaryMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'binary' }); const parsedBinary = await openpgp.readMessage({ binaryMessage }); expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1); const objectMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'object' }); expect(objectMessage.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1); const verified = await openpgp.verify({ message: objectMessage, verificationKeys: privateKey, expectSigned: true, config }); expect(verified.data).to.equal(text); }); it('should output message of expected format', async function() { const text = 'test'; const message = await openpgp.createMessage({ text }); const privateKey = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key }), passphrase }); const config = { minRSABits: 1024 }; const armoredMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'armored' }); const parsedArmored = await openpgp.readMessage({ armoredMessage }); expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1); const binaryMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'binary' }); const parsedBinary = await openpgp.readMessage({ binaryMessage }); expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1); const objectMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'object' }); expect(objectMessage.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1); const verified = await openpgp.verify({ message: objectMessage, verificationKeys: privateKey, expectSigned: true, config }); expect(verified.data).to.equal(text); }); it('should output message of expected format (with streaming)', async function() { const text = 'test'; const privateKey = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key }), passphrase }); const config = { minRSABits: 1024 }; const armoredMessage = await openpgp.sign({ message: await openpgp.createMessage({ text: stream.toStream(text) }), signingKeys: privateKey, format: 'armored', config }); const parsedArmored = await openpgp.readMessage({ armoredMessage }); expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1); const binaryMessage = await openpgp.sign({ message: await openpgp.createMessage({ text: stream.toStream(text) }), signingKeys: privateKey, format: 'binary', config }); const parsedBinary = await openpgp.readMessage({ binaryMessage }); expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1); const objectMessage = await openpgp.sign({ message: await openpgp.createMessage({ text: stream.toStream(text) }), signingKeys: privateKey, format: 'object', config }); expect(objectMessage.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1); objectMessage.packets[1].data = await stream.readToEnd(objectMessage.packets[1].data); objectMessage.packets[2].signedHashValue = await stream.readToEnd(objectMessage.packets[2].signedHashValue); const { data: streamedData } = await openpgp.verify({ message: objectMessage, verificationKeys: privateKey, expectSigned: true, config }); expect(await stream.readToEnd(streamedData)).to.equal(text); expect(streamedData).to.equal(text); }); it('should output message of expected format (detached)', async function() { const text = 'test'; const message = await openpgp.createMessage({ text }); const privateKey = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key }), passphrase }); const config = { minRSABits: 1024 }; const armoredSignature = await openpgp.sign({ message, signingKeys: privateKey, detached: true, config, format: 'armored' }); const parsedArmored = await openpgp.readSignature({ armoredSignature }); expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1); const binarySignature = await openpgp.sign({ message, signingKeys: privateKey, detached: true, config, format: 'binary' }); const parsedBinary = await openpgp.readSignature({ binarySignature }); expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1); const objectSignature = await openpgp.sign({ message, signingKeys: privateKey, detached: true, config, format: 'object' }); expect(objectSignature.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1); const verified = await openpgp.verify({ message, signature: objectSignature, verificationKeys: privateKey, expectSigned: true, config }); expect(verified.data).to.equal(text); }); it('should output message of expected format (detached, with streaming)', async function() { const text = 'test'; const privateKey = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key }), passphrase }); const config = { minRSABits: 1024 }; const armoredSignature = await openpgp.sign({ message: await openpgp.createMessage({ text: stream.toStream(text) }), signingKeys: privateKey, detached: true, format: 'armored', config }); const parsedArmored = await openpgp.readSignature({ armoredSignature: await stream.readToEnd(armoredSignature) }); expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1); const binarySignature = await openpgp.sign({ message: await openpgp.createMessage({ text: stream.toStream(text) }), signingKeys: privateKey, detached: true, format: 'binary', config }); const parsedBinary = await openpgp.readSignature({ binarySignature: await stream.readToEnd(binarySignature) }); expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1); const streamedMessage = await openpgp.createMessage({ text: stream.toStream(text) }); const objectSignature = await openpgp.sign({ message: streamedMessage, signingKeys: privateKey, detached: true, format: 'object', config }); expect(objectSignature.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1); const armoredStreamedMessage = streamedMessage.armor(); // consume input message stream, to allow to read the signed hash objectSignature.packets[0].signedHashValue = await stream.readToEnd(objectSignature.packets[0].signedHashValue); const { data: streamedData } = await openpgp.verify({ message: await openpgp.readMessage({ armoredMessage: armoredStreamedMessage }), signature: objectSignature, verificationKeys: privateKey, expectSigned: true, config }); expect(await stream.readToEnd(streamedData)).to.equal(text); }); }); describe('encrypt - unit tests', function() { it('Does not encrypt to expired key (expiration time subpacket on a direct-key signature)', async function() { const expiredKey = await openpgp.readKey({ armoredKey: expiredPublicKeyThroughDirectSignature }); await expect( openpgp.encrypt({ message: await openpgp.createMessage({ text: 'test' }), encryptionKeys: expiredKey }) ).to.be.rejectedWith(/Primary key is expired/); }); it('should output message of expected format', async function() { const passwords = 'password'; const text = 'test'; const message = await openpgp.createMessage({ text }); const privateKey = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key }), passphrase }); const armoredMessage = await openpgp.encrypt({ message, passwords, format: 'armored' }); const parsedArmored = await openpgp.readMessage({ armoredMessage }); expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1); const binaryMessage = await openpgp.encrypt({ message, passwords, format: 'binary' }); const parsedBinary = await openpgp.readMessage({ binaryMessage }); expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1); const config = { minRSABits: 1024 }; const objectMessage = await openpgp.encrypt({ message, passwords, signingKeys: privateKey, config, format: 'object' }); expect(objectMessage.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1); const decrypted = await openpgp.decrypt({ message: objectMessage, passwords, verificationKeys: privateKey, expectSigned: true, config }); expect(decrypted.data).to.equal(text); }); it('should output message of expected format (with streaming)', async function() { const passwords = 'password'; const text = 'test'; const privateKey = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key }), passphrase }); const armoredMessage = await openpgp.encrypt({ message: await openpgp.createMessage({ text: stream.toStream(text) }), passwords, format: 'armored' }); const parsedArmored = await openpgp.readMessage({ armoredMessage }); expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1); const binaryMessage = await openpgp.encrypt({ message: await openpgp.createMessage({ text: stream.toStream(text) }), passwords, format: 'binary' }); const parsedBinary = await openpgp.readMessage({ binaryMessage }); expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1); const config = { minRSABits: 1024 }; const objectMessage = await openpgp.encrypt({ message: await openpgp.createMessage({ text: stream.toStream(text) }), passwords, signingKeys: privateKey, format: 'object', config }); expect(objectMessage.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1); const { data: streamedData } = await openpgp.decrypt({ message: objectMessage, passwords, verificationKeys: privateKey, expectSigned: true, config }); expect(await stream.readToEnd(streamedData)).to.equal(text); }); }); describe('encryptSessionKey - unit tests', function() { it('should output message of expected format', async function() { const passwords = 'password'; const sessionKey = { data: new Uint8Array(16).fill(1), algorithm: 'aes128' }; const armoredMessage = await openpgp.encryptSessionKey({ ...sessionKey, passwords, format: 'armored' }); const parsedArmored = await openpgp.readMessage({ armoredMessage }); expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1); const binaryMessage = await openpgp.encryptSessionKey({ ...sessionKey, passwords, format: 'binary' }); const parsedBinary = await openpgp.readMessage({ binaryMessage }); expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1); const objectMessage = await openpgp.encryptSessionKey({ ...sessionKey, passwords, format: 'object' }); expect(objectMessage.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1); const [decryptedSessionKey] = await openpgp.decryptSessionKeys({ message: objectMessage, passwords }); expect(decryptedSessionKey).to.deep.equal(sessionKey); }); it('passing no encryption keys or passwords leads to exception', async function() { await expect(openpgp.encryptSessionKey({ algorithm: 'aes256', data: util.hexToUint8Array('3e99c1bb485e70a1fcef09a7ad8d38d171015243bbdd853e1a2b0e334d122ff3') })).to.be.rejectedWith(/No encryption keys or passwords provided/); }); }); describe('encrypt, decrypt, sign, verify - integration tests', function() { let privateKey_2000_2008; let publicKey_2000_2008; let privateKey_2038_2045; let publicKey_2038_2045; let privateKey_1337; let publicKey_1337; let privateKey; let publicKey; let publicKeyNoAEAD; let aeadProtectVal; let preferredAEADAlgorithmVal; let aeadChunkSizeByteVal; let v5KeysVal; let minRSABitsVal; beforeEach(async function() { publicKey = await openpgp.readKey({ armoredKey: pub_key }); publicKeyNoAEAD = await openpgp.readKey({ armoredKey: pub_key }); privateKey = await openpgp.readKey({ armoredKey: priv_key }); privateKey_2000_2008 = await openpgp.readKey({ armoredKey: priv_key_2000_2008 }); publicKey_2000_2008 = privateKey_2000_2008.toPublic(); privateKey_2038_2045 = await openpgp.readKey({ armoredKey: priv_key_2038_2045 }); publicKey_2038_2045 = privateKey_2038_2045.toPublic(); privateKey_1337 = await openpgp.readKey({ armoredKey: priv_key_expires_1337 }); publicKey_1337 = privateKey_1337.toPublic(); aeadProtectVal = openpgp.config.aeadProtect; preferredAEADAlgorithmVal = openpgp.config.preferredAEADAlgorithm; aeadChunkSizeByteVal = openpgp.config.aeadChunkSizeByte; v5KeysVal = openpgp.config.v5Keys; minRSABitsVal = openpgp.config.minRSABits; openpgp.config.minRSABits = 512; }); afterEach(function() { openpgp.config.aeadProtect = aeadProtectVal; openpgp.config.preferredAEADAlgorithm = preferredAEADAlgorithmVal; openpgp.config.aeadChunkSizeByte = aeadChunkSizeByteVal; openpgp.config.v5Keys = v5KeysVal; openpgp.config.minRSABits = minRSABitsVal; }); it('Configuration', async function() { const showCommentVal = openpgp.config.showComment; const showVersionVal = openpgp.config.showVersion; const commentStringVal = openpgp.config.commentString; try { const encryptedDefault = await openpgp.encrypt({ encryptionKeys:publicKey, message:await openpgp.createMessage({ text: plaintext }) }); expect(encryptedDefault).to.exist; expect(encryptedDefault).not.to.match(/^Version:/); expect(encryptedDefault).not.to.match(/^Comment:/); openpgp.config.showComment = true; openpgp.config.commentString = 'different'; const encryptedWithComment = await openpgp.encrypt({ encryptionKeys:publicKey, message:await openpgp.createMessage({ text: plaintext }) }); expect(encryptedWithComment).to.exist; expect(encryptedWithComment).not.to.match(/^Version:/); expect(encryptedWithComment).to.match(/Comment: different/); } finally { openpgp.config.showComment = showCommentVal; openpgp.config.showVersion = showVersionVal; openpgp.config.commentString = commentStringVal; } }); tryTests('CFB mode (asm.js)', tests, { if: true, beforeEach: function() { openpgp.config.aeadProtect = false; } }); tryTests('GCM mode (V5 keys)', tests, { if: true, beforeEach: function() { openpgp.config.aeadProtect = true; openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.experimentalGCM; openpgp.config.v5Keys = true; // Monkey-patch AEAD feature flag publicKey.users[0].selfCertifications[0].features = [7]; publicKey_2000_2008.users[0].selfCertifications[0].features = [7]; publicKey_2038_2045.users[0].selfCertifications[0].features = [7]; } }); tryTests('EAX mode (small chunk size)', tests, { if: true, beforeEach: function() { openpgp.config.aeadProtect = true; openpgp.config.aeadChunkSizeByte = 0; // Monkey-patch AEAD feature flag publicKey.users[0].selfCertifications[0].features = [7]; publicKey_2000_2008.users[0].selfCertifications[0].features = [7]; publicKey_2038_2045.users[0].selfCertifications[0].features = [7]; } }); tryTests('OCB mode', tests, { if: !openpgp.config.ci, beforeEach: function() { openpgp.config.aeadProtect = true; openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.ocb; // Monkey-patch AEAD feature flag publicKey.users[0].selfCertifications[0].features = [7]; publicKey_2000_2008.users[0].selfCertifications[0].features = [7]; publicKey_2038_2045.users[0].selfCertifications[0].features = [7]; } }); function tests() { describe('encryptSessionKey, decryptSessionKeys', function() { const sk = new Uint8Array([0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01]); let decryptedPrivateKey; // to avoid decrypting key before each test beforeEach(async function() { if (!decryptedPrivateKey) { decryptedPrivateKey = await openpgp.decryptKey({ privateKey, passphrase }); } privateKey = decryptedPrivateKey; }); it('should encrypt with public key', function() { return openpgp.encryptSessionKey({ data: sk, algorithm: 'aes128', encryptionKeys: publicKey, format: 'binary' }).then(async function(encrypted) { const message = await openpgp.readMessage({ binaryMessage: encrypted }); return openpgp.decryptSessionKeys({ message, decryptionKeys: privateKey }); }).then(function(decrypted) { expect(decrypted[0].data).to.deep.equal(sk); }); }); it('should encrypt with password', function() { return openpgp.encryptSessionKey({ data: sk, algorithm: 'aes128', passwords: password1, format: 'binary' }).then(async function(encrypted) { const message = await openpgp.readMessage({ binaryMessage: encrypted }); return openpgp.decryptSessionKeys({ message, passwords: password1 }); }).then(function(decrypted) { expect(decrypted[0].data).to.deep.equal(sk); }); }); it('should not decrypt with a key without binding signatures', function() { return openpgp.encryptSessionKey({ data: sk, algorithm: 'aes128', encryptionKeys: publicKey, format: 'binary' }).then(async function(encrypted) { const message = await openpgp.readMessage({ binaryMessage: encrypted }); const invalidPrivateKey = await openpgp.readKey({ armoredKey: priv_key }); invalidPrivateKey.subkeys[0].bindingSignatures = []; return openpgp.decryptSessionKeys({ message, decryptionKeys: invalidPrivateKey }).then(() => { throw new Error('Should not decrypt with invalid key'); }).catch(error => { expect(error.message).to.match(/Error decrypting session keys: Session key decryption failed./); }); }); }); it('roundtrip workflow: encrypt, decryptSessionKeys, decrypt with pgp key pair', async function () { const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: publicKey }); const decryptedSessionKeys = await openpgp.decryptSessionKeys({ message: await openpgp.readMessage({ armoredMessage: encrypted }), decryptionKeys: privateKey }); const decrypted = await openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: encrypted }), sessionKeys: decryptedSessionKeys[0] }); expect(decrypted.data).to.equal(plaintext); }); it('roundtrip workflow: encrypt, decryptSessionKeys, decrypt with pgp key pair -- trailing spaces', async function () { const plaintext = 'space: \nspace and tab: \t\nno trailing space\n \ntab:\t\ntab and space:\t '; const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: publicKey }); const decryptedSessionKeys = await openpgp.decryptSessionKeys({ message: await openpgp.readMessage({ armoredMessage: encrypted }), decryptionKeys: privateKey }); const decrypted = await openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: encrypted }), sessionKeys: decryptedSessionKeys[0] }); expect(decrypted.data).to.equal(plaintext); }); it('roundtrip workflow: encrypt, decryptSessionKeys, decrypt with password', async function () { const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: plaintext }), passwords: password1 }); const decryptedSessionKeys = await openpgp.decryptSessionKeys({ message: await openpgp.readMessage({ armoredMessage: encrypted }), passwords: password1 }); const decrypted = await openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: encrypted }), sessionKeys: decryptedSessionKeys[0] }); expect(decrypted.data).to.equal(plaintext); }); it('roundtrip workflow: encrypt with multiple passwords, decryptSessionKeys, decrypt with multiple passwords', async function () { const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: plaintext }), passwords: [password1, password2] }); const decryptedSessionKeys = await openpgp.decryptSessionKeys({ message: await openpgp.readMessage({ armoredMessage: encrypted }), passwords: [password1, password2] }); const decrypted = await openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: encrypted }), sessionKeys: decryptedSessionKeys[0] }); expect(decrypted.data).to.equal(plaintext); }); it('roundtrip workflow: encrypt twice with one password, decryptSessionKeys, only one session key', async function () { const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: plaintext }), passwords: [password1, password1] }); const decryptedSessionKeys = await openpgp.decryptSessionKeys({ message: await openpgp.readMessage({ armoredMessage: encrypted }), passwords: password1 }); expect(decryptedSessionKeys.length).to.equal(1); const decrypted = await openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: encrypted }), sessionKeys: decryptedSessionKeys[0] }); expect(decrypted.data).to.equal(plaintext); }); }); describe('AES / RSA encrypt, decrypt, sign, verify', function() { const wrong_pubkey = '-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n' + 'Version: OpenPGP.js v0.9.0\r\n' + 'Comment: Hoodiecrow - https://hoodiecrow.com\r\n' + '\r\n' + 'xk0EUlhMvAEB/2MZtCUOAYvyLFjDp3OBMGn3Ev8FwjzyPbIF0JUw+L7y2XR5\r\n' + 'RVGvbK88unV3cU/1tOYdNsXI6pSp/Ztjyv7vbBUAEQEAAc0pV2hpdGVvdXQg\r\n' + 'VXNlciA8d2hpdGVvdXQudGVzdEB0LW9ubGluZS5kZT7CXAQQAQgAEAUCUlhM\r\n' + 'vQkQ9vYOm0LN/0wAAAW4Af9C+kYW1AvNWmivdtr0M0iYCUjM9DNOQH1fcvXq\r\n' + 'IiN602mWrkd8jcEzLsW5IUNzVPLhrFIuKyBDTpLnC07Loce1\r\n' + '=6XMW\r\n' + '-----END PGP PUBLIC KEY BLOCK-----\r\n\r\n'; let decryptedPrivateKey; // to avoid decrypting key before each test beforeEach(async function() { if (!decryptedPrivateKey) { decryptedPrivateKey = await openpgp.decryptKey({ privateKey, passphrase }); } privateKey = decryptedPrivateKey; }); it('should encrypt then decrypt', async function () { const encOpt = { message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: publicKey }; const decOpt = { decryptionKeys: privateKey }; return openpgp.encrypt(encOpt).then(async function (encrypted) { expect(encrypted).to.match(/^-----BEGIN PGP MESSAGE/); decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); return openpgp.decrypt(decOpt); }).then(function (decrypted) { expect(decrypted.data).to.equal(plaintext); expect(decrypted.signatures).to.exist; expect(decrypted.signatures.length).to.equal(0); }); }); it('should encrypt then decrypt with multiple private keys', async function () { const privKeyDE = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key_de }), passphrase }); const encOpt = { message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: publicKey }; const decOpt = { decryptionKeys: [privKeyDE, privateKey] }; return openpgp.encrypt(encOpt).then(async function (encrypted) { expect(encrypted).to.match(/^-----BEGIN PGP MESSAGE/); decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); return openpgp.decrypt(decOpt); }).then(function (decrypted) { expect(decrypted.data).to.equal(plaintext); expect(decrypted.signatures).to.exist; expect(decrypted.signatures.length).to.equal(0); }); }); it('should encrypt then decrypt with wildcard', async function () { const encOpt = { message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: publicKey, wildcard: true }; const decOpt = { decryptionKeys: privateKey }; return openpgp.encrypt(encOpt).then(async function (encrypted) { expect(encrypted).to.match(/^-----BEGIN PGP MESSAGE/); decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); return openpgp.decrypt(decOpt); }).then(function (decrypted) { expect(decrypted.data).to.equal(plaintext); expect(decrypted.signatures).to.exist; expect(decrypted.signatures.length).to.equal(0); }); }); it('should encrypt then decrypt with wildcard with multiple private keys', async function () { const privKeyDE = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key_de }), passphrase }); const encOpt = { message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: publicKey, wildcard: true }; const decOpt = { decryptionKeys: [privKeyDE, privateKey] }; return openpgp.encrypt(encOpt).then(async function (encrypted) { expect(encrypted).to.match(/^-----BEGIN PGP MESSAGE/); decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); return openpgp.decrypt(decOpt); }).then(function (decrypted) { expect(decrypted.data).to.equal(plaintext); expect(decrypted.signatures).to.exist; expect(decrypted.signatures.length).to.equal(0); }); }); it('should encrypt then decrypt using returned session key', async function () { const sessionKey = await openpgp.generateSessionKey({ encryptionKeys: publicKey }); const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: plaintext }), sessionKey }); expect(encrypted).to.match(/^-----BEGIN PGP MESSAGE/); const decrypted = await openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: encrypted }), sessionKeys: sessionKey }); expect(decrypted.data).to.equal(plaintext); expect(decrypted.signatures).to.exist; expect(decrypted.signatures.length).to.equal(0); }); it('should encrypt using custom session key and decrypt using session key', async function () { const sessionKey = { data: crypto.generateSessionKey(openpgp.enums.symmetric.aes256), algorithm: 'aes256' }; const encOpt = { message: await openpgp.createMessage({ text: plaintext }), sessionKey: sessionKey, encryptionKeys: publicKey }; const decOpt = { sessionKeys: sessionKey }; return openpgp.encrypt(encOpt).then(async function (encrypted) { expect(encrypted).to.match(/^-----BEGIN PGP MESSAGE/); decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(false); return openpgp.decrypt(decOpt); }).then(function (decrypted) { expect(decrypted.data).to.equal(plaintext); }); }); it('should encrypt using custom session key and decrypt using private key', async function () { const sessionKey = { data: crypto.generateSessionKey(openpgp.enums.symmetric.aes128), algorithm: 'aes128' }; const encOpt = { message: await openpgp.createMessage({ text: plaintext }), sessionKey: sessionKey, encryptionKeys: publicKey }; const decOpt = { decryptionKeys: privateKey }; return openpgp.encrypt(encOpt).then(async function (encrypted) { expect(encrypted).to.match(/^-----BEGIN PGP MESSAGE/); decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(false); return openpgp.decrypt(decOpt); }).then(function (decrypted) { expect(decrypted.data).to.equal(plaintext); }); }); it('should encrypt/sign and decrypt/verify', async function () { const encOpt = { message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: publicKey, signingKeys: privateKey }; const decOpt = { decryptionKeys: privateKey, verificationKeys: publicKey }; return openpgp.encrypt(encOpt).then(async function (encrypted) { decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(openpgp.config.aeadProtect); return openpgp.decrypt(decOpt); }).then(async function (decrypted) { expect(decrypted.data).to.equal(plaintext); expect(await decrypted.signatures[0].verified).to.be.true; const signingKey = await privateKey.getSigningKey(); expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex()); expect((await decrypted.signatures[0].signature).packets.length).to.equal(1); }); }); it('should encrypt/sign and decrypt/verify (expectSigned=true)', async function () { const encOpt = { message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: publicKey, signingKeys: privateKey }; const decOpt = { decryptionKeys: privateKey, verificationKeys: publicKey, expectSigned: true }; return openpgp.encrypt(encOpt).then(async function (encrypted) { decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(openpgp.config.aeadProtect); return openpgp.decrypt(decOpt); }).then(async function (decrypted) { expect(decrypted.data).to.equal(plaintext); expect(await decrypted.signatures[0].verified).to.be.true; const signingKey = await privateKey.getSigningKey(); expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex()); expect((await decrypted.signatures[0].signature).packets.length).to.equal(1); }); }); it('should encrypt/sign and decrypt/verify (no AEAD support)', async function () { const encOpt = { message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: publicKeyNoAEAD, signingKeys: privateKey }; const decOpt = { decryptionKeys: privateKey, verificationKeys: publicKeyNoAEAD }; return openpgp.encrypt(encOpt).then(async function (encrypted) { decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(false); return openpgp.decrypt(decOpt); }).then(async function (decrypted) { expect(decrypted.data).to.equal(plaintext); expect(await decrypted.signatures[0].verified).to.be.true; const signingKey = await privateKey.getSigningKey(); expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex()); expect((await decrypted.signatures[0].signature).packets.length).to.equal(1); }); }); it('should encrypt/sign and decrypt/verify with generated key', function () { const genOpt = { userIDs: [{ name: 'Test User', email: 'text@example.com' }] }; return openpgp.generateKey(genOpt).then(async function(newKey) { const newPublicKey = await openpgp.readKey({ armoredKey: newKey.publicKey }); const newPrivateKey = await openpgp.readKey({ armoredKey: newKey.privateKey }); const encOpt = { message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: newPublicKey, signingKeys: newPrivateKey }; const decOpt = { decryptionKeys: newPrivateKey, verificationKeys: newPublicKey }; return openpgp.encrypt(encOpt).then(async function (encrypted) { decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(openpgp.config.aeadProtect); return openpgp.decrypt(decOpt); }).then(async function (decrypted) { expect(decrypted.data).to.equal(plaintext); expect(await decrypted.signatures[0].verified).to.be.true; const signingKey = await newPrivateKey.getSigningKey(); expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex()); expect((await decrypted.signatures[0].signature).packets.length).to.equal(1); }); }); }); it('should encrypt/sign and decrypt/verify with generated key and detached signatures', async function () { const newKey = await openpgp.generateKey({ userIDs: [{ name: 'Test User', email: 'text@example.com' }] }); const newPublicKey = await openpgp.readKey({ armoredKey: newKey.publicKey }); const newPrivateKey = await openpgp.readKey({ armoredKey: newKey.privateKey }); const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: newPublicKey }); const signed = await openpgp.sign({ message: await openpgp.createMessage({ text: plaintext }), signingKeys: newPrivateKey, detached: true }); const message = await openpgp.readMessage({ armoredMessage: encrypted }); expect(!!message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(openpgp.config.aeadProtect); const decrypted = await openpgp.decrypt({ message, signature: await openpgp.readSignature({ armoredSignature: signed }), decryptionKeys: newPrivateKey, verificationKeys: newPublicKey }); expect(decrypted.data).to.equal(plaintext); expect(await decrypted.signatures[0].verified).to.be.true; const signingKey = await newPrivateKey.getSigningKey(); expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex()); expect((await decrypted.signatures[0].signature).packets.length).to.equal(1); }); it('should encrypt/sign and decrypt/verify with null string input', async function () { const encOpt = { message: await openpgp.createMessage({ text: '' }), encryptionKeys: publicKey, signingKeys: privateKey }; const decOpt = { decryptionKeys: privateKey, verificationKeys: publicKey }; return openpgp.encrypt(encOpt).then(async function (encrypted) { decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); return openpgp.decrypt(decOpt); }).then(async function (decrypted) { expect(decrypted.data).to.equal(''); expect(await decrypted.signatures[0].verified).to.be.true; const signingKey = await privateKey.getSigningKey(); expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex()); expect((await decrypted.signatures[0].signature).packets.length).to.equal(1); }); }); it('should encrypt/sign and decrypt/verify with detached signatures', async function () { const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: publicKey }); const signed = await openpgp.sign({ message: await openpgp.createMessage({ text: plaintext }), signingKeys: privateKey, detached: true }); const decrypted = await openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: encrypted }), signature: await openpgp.readSignature({ armoredSignature: signed }), decryptionKeys: privateKey, verificationKeys: publicKey }); expect(decrypted.data).to.equal(plaintext); expect(await decrypted.signatures[0].verified).to.be.true; const signingKey = await privateKey.getSigningKey(); expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex()); expect((await decrypted.signatures[0].signature).packets.length).to.equal(1); }); it('should encrypt and decrypt/verify with detached signature as input for encryption', async function () { const { rejectPublicKeyAlgorithms } = openpgp.config; try { openpgp.config.rejectPublicKeyAlgorithms = new Set(); const plaintext = '  \t┍ͤ޵၂༫዇◧˘˻ᙑ᎚⏴ំந⛑nٓኵΉⅶ⋋ŵ⋲΂ͽᣏ₅ᄶɼ┋⌔û᬴Ƚᔡᧅ≃ṱἆ⃷݂૿ӌ᰹෇ٹჵ⛇໶⛌  \t\n한국어/조선말'; const privKeyDE = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key_de }), passphrase }); const pubKeyDE = await openpgp.readKey({ armoredKey: pub_key_de }); const signOpt = { message: await openpgp.createMessage({ text: plaintext }), signingKeys: privKeyDE, detached: true }; const encOpt = { message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: publicKey, signingKeys: privateKey }; const decOpt = { decryptionKeys: privateKey, verificationKeys: [publicKey, pubKeyDE] }; await openpgp.sign(signOpt).then(async function (armoredSignature) { encOpt.signature = await openpgp.readSignature({ armoredSignature }); return openpgp.encrypt(encOpt); }).then(async function (armoredMessage) { decOpt.message = await openpgp.readMessage({ armoredMessage }); return openpgp.decrypt(decOpt); }).then(async function (decrypted) { let signingKey; expect(decrypted.data).to.equal(plaintext); expect(await decrypted.signatures[0].verified).to.be.true; signingKey = await privateKey.getSigningKey(); expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex()); expect((await decrypted.signatures[0].signature).packets.length).to.equal(1); expect(await decrypted.signatures[1].verified).to.be.true; signingKey = await privKeyDE.getSigningKey(); expect(decrypted.signatures[1].keyID.toHex()).to.equal(signingKey.getKeyID().toHex()); expect((await decrypted.signatures[1].signature).packets.length).to.equal(1); }); } finally { openpgp.config.rejectPublicKeyAlgorithms = rejectPublicKeyAlgorithms; } }); it('should fail to encrypt and decrypt/verify with detached signature as input for encryption with wrong public key', async function () { const signOpt = { message: await openpgp.createMessage({ text: plaintext }), signingKeys: privateKey, detached: true }; const encOpt = { message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: publicKey }; const decOpt = { decryptionKeys: privateKey, verificationKeys: await openpgp.readKey({ armoredKey: wrong_pubkey }) }; return openpgp.sign(signOpt).then(async function (armoredSignature) { encOpt.signature = await openpgp.readSignature({ armoredSignature }); return openpgp.encrypt(encOpt); }).then(async function (armoredMessage) { decOpt.message = await openpgp.readMessage({ armoredMessage }); return openpgp.decrypt(decOpt); }).then(async function ({ signatures, data }) { expect(data).to.equal(plaintext); await expect(signatures[0].verified).to.be.rejectedWith(/Could not find signing key/); const signingKey = await privateKey.getSigningKey(); expect(signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex()); expect((await signatures[0].signature).packets.length).to.equal(1); }); }); it('should fail to verify decrypted data with wrong public pgp key', async function () { const encOpt = { message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: publicKey, signingKeys: privateKey }; const decOpt = { decryptionKeys: privateKey, verificationKeys: await openpgp.readKey({ armoredKey: wrong_pubkey }) }; return openpgp.encrypt(encOpt).then(async function (encrypted) { decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); return openpgp.decrypt(decOpt); }).then(async function ({ signatures, data }) { expect(data).to.equal(plaintext); await expect(signatures[0].verified).to.be.rejectedWith(/Could not find signing key/); const signingKey = await privateKey.getSigningKey(); expect(signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex()); expect((await signatures[0].signature).packets.length).to.equal(1); }); }); it('should fail to verify decrypted null string with wrong public pgp key', async function () { const encOpt = { message: await openpgp.createMessage({ text: '' }), encryptionKeys: publicKey, signingKeys: privateKey }; const decOpt = { decryptionKeys: privateKey, verificationKeys: await openpgp.readKey({ armoredKey: wrong_pubkey }) }; return openpgp.encrypt(encOpt).then(async function (encrypted) { decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); return openpgp.decrypt(decOpt); }).then(async function ({ signatures, data }) { expect(data).to.equal(''); await expect(signatures[0].verified).to.be.rejectedWith(/Could not find signing key/); const signingKey = await privateKey.getSigningKey(); expect(signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex()); expect((await signatures[0].signature).packets.length).to.equal(1); }); }); it('should successfully decrypt signed message without public keys to verify', async function () { const encOpt = { message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: publicKey, signingKeys: privateKey }; const decOpt = { decryptionKeys: privateKey }; return openpgp.encrypt(encOpt).then(async function (encrypted) { decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); return openpgp.decrypt(decOpt); }).then(async function ({ signatures, data }) { expect(data).to.equal(plaintext); await expect(signatures[0].verified).to.be.rejectedWith(/Could not find signing key/); const signingKey = await privateKey.getSigningKey(); expect(signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex()); expect((await signatures[0].signature).packets.length).to.equal(1); }); }); it('should fail to verify decrypted data with wrong public pgp key with detached signatures', async function () { const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: publicKey }); const signed = await openpgp.sign({ message: await openpgp.createMessage({ text: plaintext }), signingKeys: privateKey, detached: true }); const { signatures, data } = await openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: encrypted }), signature: await openpgp.readSignature({ armoredSignature: signed }), decryptionKeys: privateKey, verificationKeys: await openpgp.readKey({ armoredKey: wrong_pubkey }) }); expect(data).to.equal(plaintext); await expect(signatures[0].verified).to.be.rejectedWith(/Could not find signing key/); const signingKey = await privateKey.getSigningKey(); expect(signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex()); expect((await signatures[0].signature).packets.length).to.equal(1); }); it('should encrypt and decrypt/verify both signatures when signed with two private keys', async function () { const { rejectPublicKeyAlgorithms } = openpgp.config; try { openpgp.config.rejectPublicKeyAlgorithms = new Set(); const privKeyDE = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key_de }), passphrase }); const pubKeyDE = await openpgp.readKey({ armoredKey: pub_key_de }); const encOpt = { message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: publicKey, signingKeys: [privateKey, privKeyDE] }; const decOpt = { decryptionKeys: privateKey, verificationKeys: [publicKey, pubKeyDE] }; await openpgp.encrypt(encOpt).then(async function (encrypted) { decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); return openpgp.decrypt(decOpt); }).then(async function (decrypted) { let signingKey; expect(decrypted.data).to.equal(plaintext); expect(await decrypted.signatures[0].verified).to.be.true; signingKey = await privateKey.getSigningKey(); expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex()); expect((await decrypted.signatures[0].signature).packets.length).to.equal(1); expect(await decrypted.signatures[1].verified).to.be.true; signingKey = await privKeyDE.getSigningKey(); expect(decrypted.signatures[1].keyID.toHex()).to.equal(signingKey.getKeyID().toHex()); expect((await decrypted.signatures[1].signature).packets.length).to.equal(1); }); } finally { openpgp.config.rejectPublicKeyAlgorithms = rejectPublicKeyAlgorithms; } }); it('should fail to decrypt modified message', async function() { const allowUnauthenticatedStream = openpgp.config.allowUnauthenticatedStream; const { privateKey: key } = await openpgp.generateKey({ userIDs: [{ email: 'test@email.com' }], format: 'object' }); expect(await isAEADSupported([key])).to.equal(openpgp.config.aeadProtect); const data = await openpgp.encrypt({ message: await openpgp.createMessage({ binary: new Uint8Array(500) }), encryptionKeys: [key.toPublic()] }); let badSumEncrypted = data.replace(/\n=[a-zA-Z0-9/+]{4}/, '\n=aaaa'); if (badSumEncrypted === data) { // checksum was already =aaaa badSumEncrypted = data.replace(/\n=[a-zA-Z0-9/+]{4}/, '\n=bbbb'); } if (badSumEncrypted === data) { throw new Error('Was not able to successfully modify checksum'); } const badBodyEncrypted = data.replace(/\n=([a-zA-Z0-9/+]{4})/, 'aaa\n=$1'); await stream.loadStreamsPonyfill(); try { for (const allowStreaming of [true, false]) { openpgp.config.allowUnauthenticatedStream = allowStreaming; await Promise.all([badSumEncrypted, badBodyEncrypted].map(async (encrypted, i) => { await Promise.all([ encrypted, new stream.ReadableStream({ start(controller) { controller.enqueue(encrypted); controller.close(); } }), new stream.ReadableStream({ start() { this.remaining = encrypted.split('\n'); }, async pull(controller) { if (this.remaining.length) { await new Promise(res => { setTimeout(res); }); controller.enqueue(this.remaining.shift() + '\n'); } else { controller.close(); } } }) ].map(async (encrypted, j) => { let stepReached = 0; try { const message = await openpgp.readMessage({ armoredMessage: encrypted }); stepReached = 1; const { data: decrypted } = await openpgp.decrypt({ message: message, decryptionKeys: [key] }); stepReached = 2; await stream.readToEnd(decrypted); } catch (e) { expect(e.message).to.match(/Ascii armor integrity check failed/); expect(stepReached).to.equal( j === 0 ? 0 : (openpgp.config.aeadChunkSizeByte === 0 && (j === 2 || detectNode() || util.getHardwareConcurrency() < 8)) || (!openpgp.config.aeadProtect && openpgp.config.allowUnauthenticatedStream) ? 2 : 1 ); return; } throw new Error(`Expected "Ascii armor integrity check failed" error in subtest ${i}.${j}`); })); })); } } finally { openpgp.config.allowUnauthenticatedStream = allowUnauthenticatedStream; } }); it('should fail to decrypt unarmored message with garbage data appended', async function() { const key = privateKey; const message = await openpgp.encrypt({ message: await openpgp.createMessage({ text: 'test' }), encryptionKeys: key, signingKeys: key, format: 'binary' }); const encrypted = util.concat([message, new Uint8Array([11])]); await expect((async () => { await openpgp.decrypt({ message: await openpgp.readMessage({ binaryMessage: encrypted }), decryptionKeys: key, verificationKeys: key }); })()).to.be.rejectedWith('Error during parsing. This message / key probably does not conform to a valid OpenPGP format.'); }); }); describe('ELG / DSA encrypt, decrypt, sign, verify', function() { it('round trip test', async function () { const { rejectPublicKeyAlgorithms } = openpgp.config; try { openpgp.config.rejectPublicKeyAlgorithms = new Set(); const pubKeyDE = await openpgp.readKey({ armoredKey: pub_key_de }); const privKeyDE = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key_de }), passphrase }); pubKeyDE.users[0].selfCertifications[0].features = [7]; // Monkey-patch AEAD feature flag await openpgp.encrypt({ encryptionKeys: pubKeyDE, signingKeys: privKeyDE, message: await openpgp.createMessage({ text: plaintext }) }).then(async function (encrypted) { return openpgp.decrypt({ decryptionKeys: privKeyDE, verificationKeys: pubKeyDE, message: await openpgp.readMessage({ armoredMessage: encrypted }) }); }).then(async function (decrypted) { expect(decrypted.data).to.exist; expect(decrypted.data).to.equal(plaintext); expect(await decrypted.signatures[0].verified).to.be.true; const signingKey = await privKeyDE.getSigningKey(); expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex()); expect((await decrypted.signatures[0].signature).packets.length).to.equal(1); }); } finally { openpgp.config.rejectPublicKeyAlgorithms = rejectPublicKeyAlgorithms; } }); }); describe('3DES decrypt', function() { const pgp_msg = [ '-----BEGIN PGP MESSAGE-----', 'Version: GnuPG/MacGPG2 v2.0.19 (Darwin)', 'Comment: GPGTools - https://gpgtools.org', '', 'hIwDBU4Dycfvp2EBA/9tuhQgOrcATcm2PRmIOcs6q947YhlsBTZZdVJDfVjkKlyM', 'M0yE+lnNplWb041Cpfkkl6IvorKQd2iPbAkOL0IXwmVN41l+PvVgMcuFvvzetehG', 'Ca0/VEYOaTZRNqyr9FIzcnVy1I/PaWT3iqVAYa+G8TEA5Dh9RLfsx8ZA9UNIaNI+', 'ASm9aZ3H6FerNhm8RezDY5vRn6xw3o/wH5YEBvV2BEmmFKZ2BlqFQxqChr8UNwd1', 'Ieebnq0HtBPE8YU/L0U=', '=JyIa', '-----END PGP MESSAGE-----' ].join('\n'); const priv_key = [ '-----BEGIN PGP PRIVATE KEY BLOCK-----', 'Version: GnuPG/MacGPG2 v2.0.19 (Darwin)', 'Comment: GPGTools - https://gpgtools.org', '', 'lQH+BFLqLegBBAC/rN3g30Jrcpx5lTb7Kxe+ZfS7ppOIoBjjN+qcOh81cJJVS5dT', 'UGcDsm2tCLVS3P2dGaYhfU9fsoSq/wK/tXsdoWXvXdjHbbueyi1kTZqlnyT190UE', 'vmDxH0yqquvUaf7+CNXC0T6l9gGS9p0x7xNydWRb7zeK1wIsYI+dRGQmzQARAQAB', '/gMDArgQHMknurQXy0Pho3Nsdu6zCUNXuplvaSXruefKsQn6eexGPnecNTT2iy5N', '70EK371D7GcNhhLsn8roUcj1Hi3kR14wXW7lcQBy9RRbbglIJXIqKJ8ywBEO8BaQ', 'b0plL+w5A9EvX0BQc4d53MTqySh6POsEDOxPzH4D/JWbaozfmc4LfGDqH1gl7ebY', 'iu81vnBuuskjpz8rxRI81MldJEIObrTE2x46DF7AmS6L6u/Qz3AAmZd89p5INCdx', 'DemxzuMKpC3wSgdgSSKHHTKiNOMxiRd5mFH5v1KVcEG/TyXFlmah7RwA4rA4fjeo', 'OpnbVWp6ciUniRvgLaCMMbmolAoho9zaLbPzCQVQ8F7gkrjnnPm4MKA+AUXmjt7t', 'VrrYkyTp1pxLZyUWX9+aKoxEO9OIDz7p9Mh02BZ/tznQ7U+IV2bcNhwrL6LPk4Mb', 'J4YF/cLVxFVVma88GSFikSjPf30AUty5nBQFtbFGqnPctCF0aHJvd2F3YXkgPHRo', 'cm93YXdheUBleGFtcGxlLmNvbT6IuAQTAQIAIgUCUuot6AIbAwYLCQgHAwIGFQgC', 'CQoLBBYCAwECHgECF4AACgkQkk2hoj5duD/HZQP/ZXJ8PSlA1oj1NW97ccT0LiNH', 'WzxPPoH9a/qGQYg61jp+aTa0C5hlYY/GgeFpiZlpwVUtlkZYfslXJqbCcp3os4xt', 'kiukDbPnq2Y41wNVxXrDw6KbOjohbhzeRUh8txbkiXGiwHtHBSJsPMntN6cB3vn3', '08eE69vOiHPQfowa2CmdAf4EUuot6AEEAOQpNjkcTUo14JQ2o+mrpxj5yXbGtZKh', 'D8Ll+aZZrIDIa44p9KlQ3aFzPxdmFBiBX57m1nQukr58FQ5Y/FuQ1dKYc3M8QdZL', 'vCKDC8D9ZJf13iwUjYkfn/e/bDqCS2piyd63zI0xDJo+s2bXCIJxgrhbOqFDeFd6', '4W8PfBOvUuRjABEBAAH+AwMCuBAcySe6tBfLV0P5MbBesR3Ifu/ppjzLoXKhwkqm', 'PXf09taLcRfUHeMbPjboj2P2m2UOnSrbXK9qsDQ8XOMtdsEWGLWpmiqnMlkiOchv', 'MsNRYpZ67iX3JVdxNuhs5+g5bdP1PNVbKiTzx73u1h0SS93IJp1jFj50/kyGl1Eq', 'tkr0TWe5uXCh6cSZDPwhto0a12GeDHehdTw6Yq4KoZHccneHhN9ySFy0DZOeULIi', 'Y61qtR0io52T7w69fBe9Q5/d5SwpwWKMpCTOqvvzdHX7JmeFtV+2vRVilIif7AfP', 'AD+OjQ/OhMu3jYO+XNhm3raPT2tIBsBdl2UiHOnj4AUNuLuUJeVghtz4Qt6dvjyz', 'PlBvSF+ESqALjM8IqnG15FX4LmEDFrFcfNCsnmeyZ2nr1h2mV5jOON0EmBtCyhCt', 'D/Ivi4/SZk+tBVhsBI+7ZECZYDJzZQnyPDsUv31MU4OwdWi7FhzHvDj/0bhYY7+I', 'nwQYAQIACQUCUuot6AIbDAAKCRCSTaGiPl24PwYAA/sGIHvCKWP5+4ZlBHuOdbP9', '9v3PXFCm61qFEL0DTSq7NgBcuf0ASRElRI3wIKlfkwaiSzVPfNLiMTexdc7XaiTz', 'CHaOn1Xl2gmYTq2KiJkgtLuwptYU1iSj7vvSHKy0+nYIckOZB4pRCOjknT08O4ZJ', '22q10ausyQXoOxXfDWVwKA==', '=IkKW', '-----END PGP PRIVATE KEY BLOCK-----' ].join('\n'); it('Decrypt message', async function() { const privKey = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key }), passphrase: '1234' }); const message = await openpgp.readMessage({ armoredMessage: pgp_msg }); return openpgp.decrypt({ decryptionKeys:privKey, message:message }).then(function(decrypted) { expect(decrypted.data).to.equal('hello 3des\n'); expect(decrypted.signatures.length).to.equal(0); }); }); }); describe('AES encrypt, decrypt', function() { it('should encrypt and decrypt with one password', async function () { const encOpt = { message: await openpgp.createMessage({ text: plaintext }), passwords: password1 }; const decOpt = { passwords: password1 }; return openpgp.encrypt(encOpt).then(async function (encrypted) { decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); return openpgp.decrypt(decOpt); }).then(function (decrypted) { expect(decrypted.data).to.equal(plaintext); expect(decrypted.signatures.length).to.equal(0); }); }); it('should encrypt and decrypt with two passwords', async function () { const encOpt = { message: await openpgp.createMessage({ text: plaintext }), passwords: [password1, password2] }; const decOpt = { passwords: password2 }; return openpgp.encrypt(encOpt).then(async function (encrypted) { decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); return openpgp.decrypt(decOpt); }).then(function (decrypted) { expect(decrypted.data).to.equal(plaintext); expect(decrypted.signatures.length).to.equal(0); }); }); it('should encrypt and decrypt with password and not ascii armor', async function () { const encOpt = { message: await openpgp.createMessage({ text: plaintext }), passwords: password1, format: 'binary' }; const decOpt = { passwords: password1 }; return openpgp.encrypt(encOpt).then(async function (encrypted) { decOpt.message = await openpgp.readMessage({ binaryMessage: encrypted }); return openpgp.decrypt(decOpt); }).then(function (decrypted) { expect(decrypted.data).to.equal(plaintext); expect(decrypted.signatures.length).to.equal(0); }); }); it('should encrypt and decrypt with binary data', async function () { const encOpt = { message: await openpgp.createMessage({ binary: new Uint8Array([0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01]) }), passwords: password1, format: 'binary' }; const decOpt = { passwords: password1, format: 'binary' }; return openpgp.encrypt(encOpt).then(async function (encrypted) { decOpt.message = await openpgp.readMessage({ binaryMessage: encrypted }); return openpgp.decrypt(decOpt); }).then(function (decrypted) { expect(decrypted.data).to.deep.equal(new Uint8Array([0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01])); expect(decrypted.signatures.length).to.equal(0); }); }); }); describe('Encrypt, decrypt with compression', function() { withCompression(function (modifyCompressionEncryptOptions, verifyCompressionDecrypted) { it('should encrypt and decrypt with one password', async function () { const encOpt = modifyCompressionEncryptOptions({ message: await openpgp.createMessage({ text: plaintext }), passwords: password1 }); const decOpt = { passwords: password1 }; return openpgp.encrypt(encOpt).then(async function (encrypted) { decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); return openpgp.decrypt(decOpt); }).then(function (decrypted) { expect(decrypted.data).to.equal(plaintext); expect(decrypted.signatures.length).to.equal(0); verifyCompressionDecrypted(decrypted); }); }); it('Streaming encrypt and decrypt small message roundtrip', async function() { const plaintext = []; let i = 0; const useNativeStream = (() => { try { new global.ReadableStream(); return true; } catch (e) { return false; } })(); // eslint-disable-line no-new await stream.loadStreamsPonyfill(); const ReadableStream = useNativeStream ? global.ReadableStream : stream.ReadableStream; const data = new ReadableStream({ pull(controller) { if (i++ < 4) { const randomBytes = random.getRandomBytes(10); controller.enqueue(randomBytes); plaintext.push(randomBytes.slice()); } else { controller.close(); } } }); const encrypted = await openpgp.encrypt(modifyCompressionEncryptOptions({ message: await openpgp.createMessage({ binary: data }), passwords: ['test'] })); expect(stream.isStream(encrypted)).to.equal(useNativeStream ? 'web' : 'web-like'); const message = await openpgp.readMessage({ armoredMessage: encrypted }); const decrypted = await openpgp.decrypt({ passwords: ['test'], message, format: 'binary' }); expect(stream.isStream(decrypted.data)).to.equal(useNativeStream ? 'web' : 'web-like'); expect(await stream.readToEnd(decrypted.data)).to.deep.equal(util.concatUint8Array(plaintext)); }); }); }); } describe('AES / RSA encrypt, decrypt, sign, verify', function() { const wrong_pubkey = '-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n' + 'Version: OpenPGP.js v0.9.0\r\n' + 'Comment: Hoodiecrow - https://hoodiecrow.com\r\n' + '\r\n' + 'xk0EUlhMvAEB/2MZtCUOAYvyLFjDp3OBMGn3Ev8FwjzyPbIF0JUw+L7y2XR5\r\n' + 'RVGvbK88unV3cU/1tOYdNsXI6pSp/Ztjyv7vbBUAEQEAAc0pV2hpdGVvdXQg\r\n' + 'VXNlciA8d2hpdGVvdXQudGVzdEB0LW9ubGluZS5kZT7CXAQQAQgAEAUCUlhM\r\n' + 'vQkQ9vYOm0LN/0wAAAW4Af9C+kYW1AvNWmivdtr0M0iYCUjM9DNOQH1fcvXq\r\n' + 'IiN602mWrkd8jcEzLsW5IUNzVPLhrFIuKyBDTpLnC07Loce1\r\n' + '=6XMW\r\n' + '-----END PGP PUBLIC KEY BLOCK-----\r\n\r\n'; let decryptedPrivateKey; // to avoid decrypting key before each test beforeEach(async function() { if (!decryptedPrivateKey) { decryptedPrivateKey = await openpgp.decryptKey({ privateKey, passphrase }); } privateKey = decryptedPrivateKey; }); it('should sign and verify cleartext message', async function () { const message = await openpgp.createCleartextMessage({ text: plaintext }); const signOpt = { message, signingKeys: privateKey }; const verifyOpt = { verificationKeys: publicKey }; return openpgp.sign(signOpt).then(async function (signed) { expect(signed).to.match(/-----BEGIN PGP SIGNED MESSAGE-----/); verifyOpt.message = await openpgp.readCleartextMessage({ cleartextMessage: signed }); return openpgp.verify(verifyOpt); }).then(async function (verified) { expect(verified.data).to.equal(plaintext.replace(/[ \t]+$/mg, '')); expect(await verified.signatures[0].verified).to.be.true; const signingKey = await privateKey.getSigningKey(); expect(verified.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex()); expect((await verified.signatures[0].signature).packets.length).to.equal(1); }); }); it('should sign and verify cleartext message with multiple private keys', async function () { const { rejectPublicKeyAlgorithms } = openpgp.config; try { openpgp.config.rejectPublicKeyAlgorithms = new Set(); const privKeyDE = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key_de }), passphrase }); const message = await openpgp.createCleartextMessage({ text: plaintext }); const signOpt = { message, signingKeys: [privateKey, privKeyDE] }; const verifyOpt = { verificationKeys: [publicKey, privKeyDE.toPublic()] }; await openpgp.sign(signOpt).then(async function (signed) { expect(signed).to.match(/-----BEGIN PGP SIGNED MESSAGE-----/); verifyOpt.message = await openpgp.readCleartextMessage({ cleartextMessage: signed }); return openpgp.verify(verifyOpt); }).then(async function (verified) { let signingKey; expect(verified.data).to.equal(plaintext.replace(/[ \t]+$/mg, '')); expect(await verified.signatures[0].verified).to.be.true; signingKey = await privateKey.getSigningKey(); expect(verified.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex()); expect((await verified.signatures[0].signature).packets.length).to.equal(1); expect(await verified.signatures[1].verified).to.be.true; signingKey = await privKeyDE.getSigningKey(); expect(verified.signatures[1].keyID.toHex()).to.equal(signingKey.getKeyID().toHex()); expect((await verified.signatures[1].signature).packets.length).to.equal(1); }); } finally { openpgp.config.rejectPublicKeyAlgorithms = rejectPublicKeyAlgorithms; } }); it('should sign and verify data with detached signatures', async function () { const message = await openpgp.createMessage({ text: plaintext }); const signOpt = { message, signingKeys: privateKey, detached: true }; const verifyOpt = { message, verificationKeys: publicKey }; return openpgp.sign(signOpt).then(async function (armoredSignature) { verifyOpt.signature = await openpgp.readSignature({ armoredSignature }); return openpgp.verify(verifyOpt); }).then(async function (verified) { expect(verified.data).to.equal(plaintext); expect(await verified.signatures[0].verified).to.be.true; const signingKey = await privateKey.getSigningKey(); expect(verified.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex()); expect((await verified.signatures[0].signature).packets.length).to.equal(1); }); }); it('should sign and fail to verify cleartext message with wrong public pgp key', async function () { const message = await openpgp.createCleartextMessage({ text: plaintext }); const signOpt = { message, signingKeys: privateKey }; const verifyOpt = { verificationKeys: await openpgp.readKey({ armoredKey: wrong_pubkey }) }; return openpgp.sign(signOpt).then(async function (signed) { verifyOpt.message = await openpgp.readCleartextMessage({ cleartextMessage: signed }); return openpgp.verify(verifyOpt); }).then(async function ({ data, signatures }) { expect(data).to.equal(plaintext.replace(/[ \t]+$/mg, '')); await expect(signatures[0].verified).to.be.rejectedWith(/Could not find signing key/); const signingKey = await privateKey.getSigningKey(); expect(signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex()); expect((await signatures[0].signature).packets.length).to.equal(1); }); }); it('should sign and fail to verify data with wrong public pgp key with detached signature', async function () { const message = await openpgp.createMessage({ text: plaintext }); const signOpt = { message, signingKeys: privateKey, detached: true }; const verifyOpt = { message, verificationKeys: await openpgp.readKey({ armoredKey: wrong_pubkey }) }; return openpgp.sign(signOpt).then(async function (armoredSignature) { verifyOpt.signature = await openpgp.readSignature({ armoredSignature }); return openpgp.verify(verifyOpt); }).then(async function ({ data, signatures }) { expect(data).to.equal(plaintext); await expect(signatures[0].verified).to.be.rejectedWith(/Could not find signing key/); const signingKey = await privateKey.getSigningKey(); expect(signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex()); expect((await signatures[0].signature).packets.length).to.equal(1); }); }); it('should sign and verify data and not armor', async function () { const message = await openpgp.createMessage({ text: plaintext }); const signOpt = { message, signingKeys: privateKey, format: 'binary' }; const verifyOpt = { verificationKeys: publicKey }; return openpgp.sign(signOpt).then(async function (signed) { verifyOpt.message = await openpgp.readMessage({ binaryMessage: signed }); return openpgp.verify(verifyOpt); }).then(async function (verified) { expect(verified.data).to.equal(plaintext); expect(await verified.signatures[0].verified).to.be.true; const signingKey = await privateKey.getSigningKey(); expect(verified.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex()); expect((await verified.signatures[0].signature).packets.length).to.equal(1); }); }); it('should sign and verify data and not armor with detached signatures', async function () { const start = util.normalizeDate(); const message = await openpgp.createMessage({ text: plaintext }); const signOpt = { message, signingKeys: privateKey, detached: true, format: 'binary' }; const verifyOpt = { message, verificationKeys: publicKey }; return openpgp.sign(signOpt).then(async function (signed) { verifyOpt.signature = await openpgp.readSignature({ binarySignature: signed }); return openpgp.verify(verifyOpt); }).then(async function (verified) { expect(verified.data).to.equal(plaintext); expect(+(await verified.signatures[0].signature).packets[0].created).to.be.lte(+util.normalizeDate()); expect(+(await verified.signatures[0].signature).packets[0].created).to.be.gte(+start); expect(await verified.signatures[0].verified).to.be.true; const signingKey = await privateKey.getSigningKey(); expect(verified.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex()); expect((await verified.signatures[0].signature).packets.length).to.equal(1); }); }); it('should sign and verify data with a date in the past', async function () { const message = await openpgp.createMessage({ text: plaintext }); const past = new Date(2000); const signOpt = { message, signingKeys: privateKey_1337, detached: true, date: past, format: 'binary' }; const verifyOpt = { message, verificationKeys: publicKey_1337, date: past }; return openpgp.sign(signOpt).then(async function (signed) { verifyOpt.signature = await openpgp.readSignature({ binarySignature: signed }); return openpgp.verify(verifyOpt).then(async function (verified) { expect(+(await verified.signatures[0].signature).packets[0].created).to.equal(+past); expect(verified.data).to.equal(plaintext); expect(await verified.signatures[0].verified).to.be.true; expect(await privateKey_1337.getSigningKey(verified.signatures[0].keyID, past)) .to.be.not.null; expect((await verified.signatures[0].signature).packets.length).to.equal(1); // now check with expiration checking disabled verifyOpt.date = null; return openpgp.verify(verifyOpt); }).then(async function (verified) { expect(+(await verified.signatures[0].signature).packets[0].created).to.equal(+past); expect(verified.data).to.equal(plaintext); expect(await verified.signatures[0].verified).to.be.true; expect(await privateKey_1337.getSigningKey(verified.signatures[0].keyID, null)) .to.be.not.null; expect((await verified.signatures[0].signature).packets.length).to.equal(1); }); }); }); it('should sign and verify binary data with a date in the future', async function () { const future = new Date(2040, 5, 5, 5, 5, 5, 0); const data = new Uint8Array([3, 14, 15, 92, 65, 35, 59]); const signOpt = { message: await openpgp.createMessage({ binary: data }), signingKeys: privateKey_2038_2045, detached: true, date: future, format: 'binary' }; const verifyOpt = { verificationKeys: publicKey_2038_2045, date: future, format: 'binary' }; return openpgp.sign(signOpt).then(async function (signed) { verifyOpt.message = await openpgp.createMessage({ binary: data }); verifyOpt.signature = await openpgp.readSignature({ binarySignature: signed }); return openpgp.verify(verifyOpt); }).then(async function (verified) { expect(+(await verified.signatures[0].signature).packets[0].created).to.equal(+future); expect([].slice.call(verified.data)).to.deep.equal([].slice.call(data)); expect(await verified.signatures[0].verified).to.be.true; expect(await privateKey_2038_2045.getSigningKey(verified.signatures[0].keyID, future)) .to.be.not.null; expect((await verified.signatures[0].signature).packets.length).to.equal(1); }); }); it('should sign and verify binary data without one-pass signature', async function () { const data = new Uint8Array([3, 14, 15, 92, 65, 35, 59]); const signOpt = { message: await openpgp.createMessage({ binary: data }), signingKeys: privateKey, format: 'binary' }; const verifyOpt = { verificationKeys: publicKey, format: 'binary' }; return openpgp.sign(signOpt).then(async function (signed) { const message = await openpgp.readMessage({ binaryMessage: signed }); message.packets.push(...await stream.readToEnd(message.packets.stream, _ => _)); const packets = new openpgp.PacketList(); packets.push(message.packets.findPacket(openpgp.enums.packet.signature)); packets.push(message.packets.findPacket(openpgp.enums.packet.literalData)); verifyOpt.message = new openpgp.Message(packets); return openpgp.verify(verifyOpt); }).then(async function (verified) { expect([].slice.call(verified.data)).to.deep.equal([].slice.call(data)); expect(await verified.signatures[0].verified).to.be.true; expect(await privateKey.getSigningKey(verified.signatures[0].keyID)) .to.be.not.null; expect((await verified.signatures[0].signature).packets.length).to.equal(1); }); }); it('should streaming sign and verify binary data without one-pass signature', async function () { const data = new Uint8Array([3, 14, 15, 92, 65, 35, 59]); const dataStream = global.ReadableStream ? new global.ReadableStream({ start(controller) { controller.enqueue(data); controller.close(); } }) : new (require('stream').Readable)({ read() { this.push(data); this.push(null); } }); const signOpt = { message: await openpgp.createMessage({ binary: dataStream }), signingKeys: privateKey, format: 'binary' }; const verifyOpt = { verificationKeys: publicKey, format: 'binary' }; return openpgp.sign(signOpt).then(async function (signed) { expect(stream.isStream(signed)).to.equal(global.ReadableStream ? 'web' : 'node'); const message = await openpgp.readMessage({ binaryMessage: signed }); message.packets.push(...await stream.readToEnd(message.packets.stream, _ => _)); const packets = new openpgp.PacketList(); packets.push(message.packets.findPacket(openpgp.enums.packet.signature)); packets.push(message.packets.findPacket(openpgp.enums.packet.literalData)); verifyOpt.message = await openpgp.readMessage({ binaryMessage: stream[ global.ReadableStream ? (global.ReadableStream === stream.ReadableStream ? 'toStream' : 'toNativeReadable') : 'webToNode' ](packets.write()) }); return openpgp.verify(verifyOpt); }).then(async function (verified) { expect(stream.isStream(verified.data)).to.equal(global.ReadableStream ? 'web' : 'node'); expect([].slice.call(await stream.readToEnd(verified.data))).to.deep.equal([].slice.call(data)); expect(await verified.signatures[0].verified).to.be.true; expect(await privateKey.getSigningKey(verified.signatures[0].keyID)) .to.be.not.null; expect((await verified.signatures[0].signature).packets.length).to.equal(1); }); }); it('should encrypt and decrypt data with a date in the future', async function () { const future = new Date(2040, 5, 5, 5, 5, 5, 0); const encryptOpt = { message: await openpgp.createMessage({ text: plaintext, date: future }), encryptionKeys: publicKey_2038_2045, date: future, format: 'binary' }; return openpgp.encrypt(encryptOpt).then(async function (encrypted) { const message = await openpgp.readMessage({ binaryMessage: encrypted }); return message.decrypt([privateKey_2038_2045], undefined, undefined, undefined, openpgp.config); }).then(async function (packets) { const literals = packets.packets.filterByTag(openpgp.enums.packet.literalData); expect(literals.length).to.equal(1); expect(+literals[0].date).to.equal(+future); expect(await stream.readToEnd(packets.getText())).to.equal(plaintext); }); }); it('should encrypt and decrypt binary data with a date in the past', async function () { const past = new Date(2005, 5, 5, 5, 5, 5, 0); const data = new Uint8Array([3, 14, 15, 92, 65, 35, 59]); const encryptOpt = { message: await openpgp.createMessage({ binary: data, date: past }), encryptionKeys: publicKey_2000_2008, date: past, format: 'binary' }; return openpgp.encrypt(encryptOpt).then(async function (encrypted) { const message = await openpgp.readMessage({ binaryMessage: encrypted }); return message.decrypt([privateKey_2000_2008], undefined, undefined, undefined, openpgp.config); }).then(async function (packets) { const literals = packets.packets.filterByTag(openpgp.enums.packet.literalData); expect(literals.length).to.equal(1); expect(+literals[0].date).to.equal(+past); expect(await stream.readToEnd(packets.getLiteralData())).to.deep.equal(data); }); }); it('should sign, encrypt and decrypt, verify data with a date in the past', async function () { const past = new Date(2005, 5, 5, 5, 5, 5, 0); const encryptOpt = { message: await openpgp.createMessage({ text: plaintext, date: past }), encryptionKeys: publicKey_2000_2008, signingKeys: privateKey_2000_2008, date: past, format: 'binary' }; return openpgp.encrypt(encryptOpt).then(async function (encrypted) { const message = await openpgp.readMessage({ binaryMessage: encrypted }); return message.decrypt([privateKey_2000_2008], undefined, undefined, undefined, openpgp.config); }).then(async function (message) { const literals = message.packets.filterByTag(openpgp.enums.packet.literalData); expect(literals.length).to.equal(1); expect(+literals[0].date).to.equal(+past); const signatures = await message.verify([publicKey_2000_2008], past, undefined, openpgp.config); expect(await stream.readToEnd(message.getText())).to.equal(plaintext); expect(+(await signatures[0].signature).packets[0].created).to.equal(+past); expect(await signatures[0].verified).to.be.true; expect(await privateKey_2000_2008.getSigningKey(signatures[0].keyID, past)) .to.be.not.null; expect((await signatures[0].signature).packets.length).to.equal(1); }); }); it('should sign, encrypt and decrypt, verify binary data with a date in the future', async function () { const future = new Date(2040, 5, 5, 5, 5, 5, 0); const data = new Uint8Array([3, 14, 15, 92, 65, 35, 59]); const encryptOpt = { message: await openpgp.createMessage({ binary: data, date: future }), encryptionKeys: publicKey_2038_2045, signingKeys: privateKey_2038_2045, date: future, format: 'binary' }; return openpgp.encrypt(encryptOpt).then(async function (encrypted) { const message = await openpgp.readMessage({ binaryMessage: encrypted }); return message.decrypt([privateKey_2038_2045], undefined, undefined, undefined, openpgp.config); }).then(async function (message) { const literals = message.packets.filterByTag(openpgp.enums.packet.literalData); expect(literals.length).to.equal(1); expect(literals[0].format).to.equal(openpgp.enums.literal.binary); expect(+literals[0].date).to.equal(+future); const signatures = await message.verify([publicKey_2038_2045], future, undefined, openpgp.config); expect(await stream.readToEnd(message.getLiteralData())).to.deep.equal(data); expect(+(await signatures[0].signature).packets[0].created).to.equal(+future); expect(await signatures[0].verified).to.be.true; expect(await privateKey_2038_2045.getSigningKey(signatures[0].keyID, future)) .to.be.not.null; expect((await signatures[0].signature).packets.length).to.equal(1); }); }); it('should sign, encrypt and decrypt, verify mime data with a date in the future', async function () { const future = new Date(2040, 5, 5, 5, 5, 5, 0); const data = new Uint8Array([3, 14, 15, 92, 65, 35, 59]); const encryptOpt = { message: await openpgp.createMessage({ binary: data, date: future, format: 'mime' }), encryptionKeys: publicKey_2038_2045, signingKeys: privateKey_2038_2045, date: future, format: 'binary' }; return openpgp.encrypt(encryptOpt).then(async function (encrypted) { const message = await openpgp.readMessage({ binaryMessage: encrypted }); return message.decrypt([privateKey_2038_2045], undefined, undefined, undefined, openpgp.config); }).then(async function (message) { const literals = message.packets.filterByTag(openpgp.enums.packet.literalData); expect(literals.length).to.equal(1); expect(literals[0].format).to.equal(openpgp.enums.literal.mime); expect(+literals[0].date).to.equal(+future); const signatures = await message.verify([publicKey_2038_2045], future, undefined, openpgp.config); expect(await stream.readToEnd(message.getLiteralData())).to.deep.equal(data); expect(+(await signatures[0].signature).packets[0].created).to.equal(+future); expect(await signatures[0].verified).to.be.true; expect(await privateKey_2038_2045.getSigningKey(signatures[0].keyID, future)) .to.be.not.null; expect((await signatures[0].signature).packets.length).to.equal(1); }); }); it('should fail to encrypt with revoked key', function() { return openpgp.revokeKey({ key: privateKey, format: 'object' }).then(async function({ publicKey: revKey }) { return openpgp.encrypt({ message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: revKey }).then(function() { throw new Error('Should not encrypt with revoked key'); }).catch(function(error) { expect(error.message).to.match(/Error encrypting message: Primary key is revoked/); }); }); }); it('should fail to encrypt with revoked subkey', async function() { const pubKeyDE = await openpgp.readKey({ armoredKey: pub_key_de }); const privKeyDE = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key_de }), passphrase }); return privKeyDE.subkeys[0].revoke(privKeyDE.keyPacket).then(async function(revSubkey) { pubKeyDE.subkeys[0] = revSubkey; return openpgp.encrypt({ message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: pubKeyDE, config: { rejectPublicKeyAlgorithms: new Set() } }).then(function() { throw new Error('Should not encrypt with revoked subkey'); }).catch(function(error) { expect(error.message).to.match(/Could not find valid encryption key packet/); }); }); }); it('should decrypt with revoked subkey', async function() { const pubKeyDE = await openpgp.readKey({ armoredKey: pub_key_de }); const privKeyDE = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key_de }), passphrase }); const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: pubKeyDE, config: { rejectPublicKeyAlgorithms: new Set() } }); privKeyDE.subkeys[0] = await privKeyDE.subkeys[0].revoke(privKeyDE.keyPacket); const decOpt = { message: await openpgp.readMessage({ armoredMessage: encrypted }), decryptionKeys: privKeyDE, config: { rejectPublicKeyAlgorithms: new Set() } }; const decrypted = await openpgp.decrypt(decOpt); expect(decrypted.data).to.equal(plaintext); }); it('should not decrypt with corrupted subkey', async function() { const pubKeyDE = await openpgp.readKey({ armoredKey: pub_key_de }); const privKeyDE = await openpgp.readKey({ armoredKey: priv_key_de }); // corrupt the public key params privKeyDE.subkeys[0].keyPacket.publicParams.p[0]++; // validation will check the primary key -- not the decryption subkey -- and will succeed (for now) const decryptedKeyDE = await openpgp.decryptKey({ privateKey: privKeyDE, passphrase }); const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: plaintext }), encryptionKeys: pubKeyDE, config: { rejectPublicKeyAlgorithms: new Set() } }); const decOpt = { message: await openpgp.readMessage({ armoredMessage: encrypted }), decryptionKeys: decryptedKeyDE }; // binding signature is invalid await expect(openpgp.decrypt(decOpt)).to.be.rejectedWith(/Session key decryption failed/); }); it('RSA decryption with PKCS1 padding of wrong length should fail', async function() { const key = await openpgp.readKey({ armoredKey: rsaPrivateKeyPKCS1 }); // the paddings of these messages are prefixed by 0x02 and 0x000002 instead of 0x0002 // the code should discriminate between these cases by checking the length of the padded plaintext const padding02 = `-----BEGIN PGP MESSAGE----- Version: OpenPGP.js VERSION Comment: https://openpgpjs.org wcBMAxbpoSTRSSl3AQf/fepDhqeam4Ecy8GUFChc47U3hbkdgINobI9TORAf eGFZVcyTQKVIt7fB8bwQwjxRmU98xCjF7VkLhPQJkzKlkT9cIDBKswU+d3fw lHAVYo77yUkFkVLXrQTZj/OjsA12V7lfRagO375XB3EpJUHVPvYQFFr3aSlo FbsCrpZoS6FXxRYVjGpIeMjam3a7qDavQpKhjOQ+Sfm0tk2JZkQwpFom6x7c 9TEn3YSo6+I0ztjiuTBZDyYr8zocHW8imFzZRlcNuuuukesyFzFgHx46eVpO 6PVjmiN50agZvsV9rgPyyH84nb3zYJ63shnrQWubTOVH4daGbe8uHi+ZM3UU J9I8AcH94nE77JUtCm7s1kOlo0EIshZsAqJwGveDGdAuabfViVwVxG4I24M6 8sqJYJd9FpNjSbYlrLT0R9zy =+n/4 -----END PGP MESSAGE-----`; const padding000002 = `-----BEGIN PGP MESSAGE----- Version: OpenPGP.js VERSION Comment: https://openpgpjs.org wcBMAxbpoSTRSSl3AQf/fepDhqeam4Ecy8GUFChc47U3hbkdgINobI9TORAf eGFZVcyTQKVIt7fB8bwQwjxRmU98xCjF7VkLhPQJkzKlkT9cIDBKswU+d3fw lHAVYo77yUkFkVLXrQTZj/OjsA12V7lfRagO375XB3EpJUHVPvYQFFr3aSlo FbsCrpZoS6FXxRYVjGpIeMjam3a7qDavQpKhjOQ+Sfm0tk2JZkQwpFom6x7c 9TEn3YSo6+I0ztjiuTBZDyYr8zocHW8imFzZRlcNuuuukesyFzFgHx46eVpO 6PVjmiN50agZvsV9rgPyyH84nb3zYJ63shnrQWubTOVH4daGbe8uHi+ZM3UU J9I8AcH94nE77JUtCm7s1kOlo0EIshZsAqJwGveDGdAuabfViVwVxG4I24M6 8sqJYJd9FpNjSbYlrLT0R9zy =+n/4 -----END PGP MESSAGE-----`; const decOpt02 = { message: await openpgp.readMessage({ armoredMessage: padding02 }), decryptionKeys: key }; await expect(openpgp.decrypt(decOpt02)).to.be.rejectedWith(/Decryption error/); const decOpt000002 = { message: await openpgp.readMessage({ armoredMessage: padding000002 }), decryptionKeys: key }; await expect(openpgp.decrypt(decOpt000002)).to.be.rejectedWith(/Decryption error/); }); it('should decrypt with two passwords message which GPG fails on', async function() { const decOpt = { message: await openpgp.readMessage({ armoredMessage: twoPasswordGPGFail }), passwords: password2 }; return openpgp.decrypt(decOpt).then(function(decrypted) { expect(decrypted.data).to.equal('short message\nnext line\n한국어/조선말'); expect(decrypted.signatures.length).to.equal(0); }); }); it('should decrypt with three passwords', async function() { const messageBinary = util.hexToUint8Array('c32e04090308125231fe38b0255f60a7f319fc4959c147c7af33817ceb4cf159a00f2efa17b7921961f6ead025c77588d2430166fe9395cd58e9b69a67a30470e2d31bf0bbbb31c7eca31fb9015dddf70c6957036b093d104cbf0b26e218113e69c4fa89dda97a61d0cba364efa77d5144c5b9b701'); const message = await openpgp.readMessage({ binaryMessage: messageBinary }); const passwords = ['Test', 'Pinata', 'a']; const decrypted = await openpgp.decrypt({ message, passwords }); expect(decrypted.data).to.equal('Hello world'); }); it('should decrypt broken ECC message from old OpenPGP.js', async function() { const key = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: ecdh_dec_key }), passphrase: '12345' }); const message = await openpgp.readMessage({ armoredMessage: ecdh_msg_bad }); const decrypted = await openpgp.decrypt({ message, decryptionKeys: key }); expect(decrypted.data).to.equal('\n'); }); it('should decrypt broken ECC message from old go crypto', async function() { const key = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: ecdh_dec_key2 }), passphrase: '12345' }); const message = await openpgp.readMessage({ armoredMessage: ecdh_msg_bad_2 }); const decrypted = await openpgp.decrypt({ message, decryptionKeys: key }); expect(decrypted.data).to.equal('Tesssst


Sent from ProtonMail mobile


'); }); it('should decrypt Blowfish message', async function() { const { data } = await openpgp.decrypt({ passwords: 'test', message: await openpgp.readMessage({ armoredMessage: `-----BEGIN PGP MESSAGE----- Version: OpenPGP.js v4.9.0 Comment: https://openpgpjs.org wx4EBAMI7Di70u7hoDfgBUJQ2+1ig6ym3KMjRS9kAovSPAGRQLIPv2DgkINL 3DUgMNqtQCA23xWhq7Ly6o9H1lRfoAo7V5UElVCqGEX7cgyZjI97alY6Je3o amnR6g== =rPIK -----END PGP MESSAGE-----` }) }); expect(data).to.equal('Hello World!'); }); it('should normalize newlines in encrypted text message', async function() { const message = await openpgp.createMessage({ text: '"BEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VEVENT\r\nUID:123\r\nDTSTART:20191211T121212Z\r\nDTEND:20191212T121212Z\r\nEND:VEVENT\nEND:VCALENDAR"' }); const encrypted = await openpgp.encrypt({ passwords: 'test', message }); const decrypted = await openpgp.decrypt({ passwords: 'test', message: await openpgp.readMessage({ armoredMessage: encrypted }), format: 'binary' }); expect(util.decodeUTF8(decrypted.data)).to.equal('"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nBEGIN:VEVENT\r\nUID:123\r\nDTSTART:20191211T121212Z\r\nDTEND:20191212T121212Z\r\nEND:VEVENT\r\nEND:VCALENDAR"'); }); }); it('should fail to decrypt a message containing a literal packet (and no session key)', async function() { const message = await openpgp.createMessage({ text: 'plaintext' }); await expect(openpgp.decrypt({ message, passwords: 'password' })).to.be.rejectedWith(/Error decrypting message/); }); it('should fail to decrypt a message containing a literal packet (and a session key)', async function() { const skeskPlusLiteralData = `-----BEGIN PGP MESSAGE----- wy4ECQMIjvrInhvTxJwAbkqXp+KWFdBcjoPn03jCdyspVi9qXBDbyGaP1lrM habAyxd1AGKaNp1wbGFpbnRleHQgbWVzc2FnZQ== =XoUx -----END PGP MESSAGE----- `; const message = await openpgp.readMessage({ armoredMessage: skeskPlusLiteralData }); await expect(openpgp.decrypt({ message, passwords: 'password' })).to.be.rejectedWith(/No encrypted data found/); }); it('should fail to decrypt non-integrity-protected message by default', async function() { const key = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- xVgEYD9r8xYJKwYBBAHaRw8BAQdApTaQJ6R/uooTqAuscoxYwbLrtoKndnsX ydhqMybJqh0AAQCxNwi9Pezy03OQE0XOooBWaHiuhBtKA1eAuqjJFuuLuQ/+ zQDCjAQQFgoAHQUCYD9r8wQLCQcIAxUICgQWAgEAAhkBAhsDAh4BACEJEIkB BTiDwpvwFiEEvRnmOby6fJ/OxUhSiQEFOIPCm/BidgEAq05ZiPseRsMTxNm7 IFQwQjmIFiWgLeQ0gKIvfl3SjBAA/iSPyTgWxSY98utXNuq+WoxVOzx3dJwG 2cflR/UFUlEPx10EYD9r8xIKKwYBBAGXVQEFAQEHQCASw+tMPvnXi904WASv wRDUQofh0M7CpgQFqoOXvGlLAwEIBwAA/3gEimwdIet0gXb/hRRyBqOlcq32 lNREh+n+vZKJyXWYEjrCeAQYFggACQUCYD9r8wIbDAAhCRCJAQU4g8Kb8BYh BL0Z5jm8unyfzsVIUokBBTiDwpvwHpEBAObHllPrJu0DqYyt4FKPkijgRpXC ESqhlK5rrbc62SmfAQDVf5l1B6IDASBCKtC0VPPpYiK6AUcEISpaSXOa+pNI Bw== =3Fja -----END PGP PRIVATE KEY BLOCK-----` }); const message = await openpgp.readMessage({ armoredMessage: `-----BEGIN PGP MESSAGE----- wV4D+3VwOibHmagSAQdATlMJlvrkaq46zMkbIuKBOJO5X3ugVwZpEyAterQC /RUw0OPWeO+4swh/U7ZurV8cRr/fPnyGUUKI7rI+va3kWUZv4RRpUs7eYE57 OUr3yoMNyaQEBwu6VXiQrsBN8TyUbXQxb63p7EHFXIgvVDIvOG7bQptrrKlM kKcB+fz5hb6mT/tl+cPcYHDOjocQ92pNVm+FilQhiATRxV8ah1DCOIZZ6tgq rWwIiEQEBPt+tXOuVF4Peumovp3WgziudrJa5Jxt2Dz+8nicBglbZLXTsZNu bsZgJWVlAa5eil6J9ePX2xbo1vVAkLQdzE9+1jL+l7PRIZuVBQ== =T4iR -----END PGP MESSAGE-----` }); await expect( openpgp.decrypt({ message, decryptionKeys: key, verificationKeys: key }) ).to.be.rejectedWith('Error decrypting message: Message is not authenticated.'); }); it('should allow decrypting non-integrity-protected message when enabled', async function() { const key = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- xVgEYD9r8xYJKwYBBAHaRw8BAQdApTaQJ6R/uooTqAuscoxYwbLrtoKndnsX ydhqMybJqh0AAQCxNwi9Pezy03OQE0XOooBWaHiuhBtKA1eAuqjJFuuLuQ/+ zQDCjAQQFgoAHQUCYD9r8wQLCQcIAxUICgQWAgEAAhkBAhsDAh4BACEJEIkB BTiDwpvwFiEEvRnmOby6fJ/OxUhSiQEFOIPCm/BidgEAq05ZiPseRsMTxNm7 IFQwQjmIFiWgLeQ0gKIvfl3SjBAA/iSPyTgWxSY98utXNuq+WoxVOzx3dJwG 2cflR/UFUlEPx10EYD9r8xIKKwYBBAGXVQEFAQEHQCASw+tMPvnXi904WASv wRDUQofh0M7CpgQFqoOXvGlLAwEIBwAA/3gEimwdIet0gXb/hRRyBqOlcq32 lNREh+n+vZKJyXWYEjrCeAQYFggACQUCYD9r8wIbDAAhCRCJAQU4g8Kb8BYh BL0Z5jm8unyfzsVIUokBBTiDwpvwHpEBAObHllPrJu0DqYyt4FKPkijgRpXC ESqhlK5rrbc62SmfAQDVf5l1B6IDASBCKtC0VPPpYiK6AUcEISpaSXOa+pNI Bw== =3Fja -----END PGP PRIVATE KEY BLOCK-----` }); const message = await openpgp.readMessage({ armoredMessage: `-----BEGIN PGP MESSAGE----- wV4D+3VwOibHmagSAQdATlMJlvrkaq46zMkbIuKBOJO5X3ugVwZpEyAterQC /RUw0OPWeO+4swh/U7ZurV8cRr/fPnyGUUKI7rI+va3kWUZv4RRpUs7eYE57 OUr3yoMNyaQEBwu6VXiQrsBN8TyUbXQxb63p7EHFXIgvVDIvOG7bQptrrKlM kKcB+fz5hb6mT/tl+cPcYHDOjocQ92pNVm+FilQhiATRxV8ah1DCOIZZ6tgq rWwIiEQEBPt+tXOuVF4Peumovp3WgziudrJa5Jxt2Dz+8nicBglbZLXTsZNu bsZgJWVlAa5eil6J9ePX2xbo1vVAkLQdzE9+1jL+l7PRIZuVBQ== =T4iR -----END PGP MESSAGE-----` }); const decrypted = await openpgp.decrypt({ message, decryptionKeys: key, verificationKeys: key, config: { allowUnauthenticatedMessages: true } }); expect(decrypted.data).to.equal('test'); }); it('should allow stream-decrypting non-integrity-protected message when enabled', async function() { const key = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- xVgEYD9r8xYJKwYBBAHaRw8BAQdApTaQJ6R/uooTqAuscoxYwbLrtoKndnsX ydhqMybJqh0AAQCxNwi9Pezy03OQE0XOooBWaHiuhBtKA1eAuqjJFuuLuQ/+ zQDCjAQQFgoAHQUCYD9r8wQLCQcIAxUICgQWAgEAAhkBAhsDAh4BACEJEIkB BTiDwpvwFiEEvRnmOby6fJ/OxUhSiQEFOIPCm/BidgEAq05ZiPseRsMTxNm7 IFQwQjmIFiWgLeQ0gKIvfl3SjBAA/iSPyTgWxSY98utXNuq+WoxVOzx3dJwG 2cflR/UFUlEPx10EYD9r8xIKKwYBBAGXVQEFAQEHQCASw+tMPvnXi904WASv wRDUQofh0M7CpgQFqoOXvGlLAwEIBwAA/3gEimwdIet0gXb/hRRyBqOlcq32 lNREh+n+vZKJyXWYEjrCeAQYFggACQUCYD9r8wIbDAAhCRCJAQU4g8Kb8BYh BL0Z5jm8unyfzsVIUokBBTiDwpvwHpEBAObHllPrJu0DqYyt4FKPkijgRpXC ESqhlK5rrbc62SmfAQDVf5l1B6IDASBCKtC0VPPpYiK6AUcEISpaSXOa+pNI Bw== =3Fja -----END PGP PRIVATE KEY BLOCK-----` }); const message = await openpgp.readMessage({ armoredMessage: stream.toStream(`-----BEGIN PGP MESSAGE----- wV4D+3VwOibHmagSAQdATlMJlvrkaq46zMkbIuKBOJO5X3ugVwZpEyAterQC /RUw0OPWeO+4swh/U7ZurV8cRr/fPnyGUUKI7rI+va3kWUZv4RRpUs7eYE57 OUr3yoMNyaQEBwu6VXiQrsBN8TyUbXQxb63p7EHFXIgvVDIvOG7bQptrrKlM kKcB+fz5hb6mT/tl+cPcYHDOjocQ92pNVm+FilQhiATRxV8ah1DCOIZZ6tgq rWwIiEQEBPt+tXOuVF4Peumovp3WgziudrJa5Jxt2Dz+8nicBglbZLXTsZNu bsZgJWVlAa5eil6J9ePX2xbo1vVAkLQdzE9+1jL+l7PRIZuVBQ== =T4iR -----END PGP MESSAGE-----`) }); const decrypted = await openpgp.decrypt({ message, decryptionKeys: key, verificationKeys: key, config: { allowUnauthenticatedMessages: true } }); const data = await stream.readToEnd(decrypted.data); expect(data).to.equal('test'); }); describe('Sign and verify with each curve', function() { const curves = ['secp256k1' , 'p256', 'p384', 'p521', 'curve25519', 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1']; curves.forEach(curve => { it(`sign/verify with ${curve}`, async function() { const config = { rejectCurves: new Set() }; const plaintext = 'short message'; const { privateKey: key } = await openpgp.generateKey({ curve, userIDs: { name: 'Alice', email: 'info@alice.com' }, format: 'object', config }); const signed = await openpgp.sign({ signingKeys:[key], message: await openpgp.createCleartextMessage({ text: plaintext }), config }); const verified = await openpgp.verify({ verificationKeys:[key], message: await openpgp.readCleartextMessage({ cleartextMessage: signed }), config }); expect(await verified.signatures[0].verified).to.be.true; }); }); }); describe('Errors', function() { it('Error message should contain the original error message', async function() { return openpgp.encrypt({ message: await openpgp.createMessage({ binary: new Uint8Array([0x01, 0x01, 0x01]) }), passwords: null }).then(function() { throw new Error('Error expected.'); }).catch(function(error) { expect(error.message).to.match(/No keys, passwords, or session key provided/); }); }); }); describe('Specific encryption/signing key testing', async function () { const encryptionKeyIDs = [ keyIDType.fromID('87EAE0977B2185EA'), keyIDType.fromID('F94F9B34AF93FA14'), keyIDType.fromID('08F7D4C7C59545C0') ]; const signingKeyIDs = [ keyIDType.fromID('663277AF60400638'), keyIDType.fromID('BBE14491E6EE6366'), keyIDType.fromID('3E0F20F1A71D6DFD') ]; const getPrimaryKey = async () => openpgp.readKey({ armoredKey: multipleEncryptionAndSigningSubkeys }); it('Encrypt message with a specific encryption key id', async function () { const primaryKey = await getPrimaryKey(); let m; let p; for (let i = 0; i < encryptionKeyIDs.length; i++) { m = await openpgp.readMessage({ armoredMessage: await openpgp.encrypt({ message: await openpgp.createMessage({ text: 'Hello World\n' }), encryptionKeys: primaryKey, encryptionKeyIDs: [encryptionKeyIDs[i]] }) }); p = m.packets.filterByTag(openpgp.enums.packet.publicKeyEncryptedSessionKey); expect(p.length).equals(1); expect(p[0].publicKeyID.equals(encryptionKeyIDs[i])).to.be.true; } }); it('Sign message with a specific signing key id', async function () { const primaryKey = await getPrimaryKey(); let s; let p; for (let i = 0; i < signingKeyIDs.length; i++) { s = await openpgp.readSignature({ armoredSignature: await openpgp.sign({ message: await openpgp.createMessage({ text: 'Hello World\n' }), signingKeys: primaryKey, signingKeyIDs: [signingKeyIDs[i]], detached: true }) }); p = s.packets.filterByTag(openpgp.enums.packet.signature); expect(p.length).equals(1); expect(p[0].issuerKeyID.equals(signingKeyIDs[i])).to.be.true; } }); it('Encrypt and sign with specific encryption/signing key ids', async function () { const primaryKey = await getPrimaryKey(); const plaintextMessage = await openpgp.createMessage({ text: 'Hello World\n' }); const checkEncryptedPackets = (encryptionKeyIDs, pKESKList) => { pKESKList.forEach(({ publicKeyID }, i) => { expect(publicKeyID.equals(encryptionKeyIDs[i])).to.be.true; }); }; const checkSignatures = (signingKeyIDs, signatures) => { signatures.forEach(({ keyID }, i) => { expect(keyID.equals(signingKeyIDs[i])).to.be.true; }); }; const kIds = [encryptionKeyIDs[1], encryptionKeyIDs[0], encryptionKeyIDs[2]]; const sIds = [signingKeyIDs[2], signingKeyIDs[1], signingKeyIDs[0]]; const message = await openpgp.readMessage({ armoredMessage: await openpgp.encrypt({ message: plaintextMessage, signingKeys: [primaryKey, primaryKey, primaryKey], encryptionKeys: [primaryKey, primaryKey, primaryKey], encryptionKeyIDs: kIds, signingKeyIDs: sIds }) }); const pKESKList = message.packets.filterByTag(openpgp.enums.packet.publicKeyEncryptedSessionKey); expect(pKESKList.length).equals(3); checkEncryptedPackets(kIds, pKESKList); const { signatures } = await openpgp.decrypt({ message, decryptionKeys: [primaryKey, primaryKey, primaryKey] }); expect(signatures.length).equals(3); checkSignatures(sIds, signatures); }); }); }); });