Skip to content

Releases: openpgpjs/openpgpjs

v5.1.0

24 Jan 18:04
Compare
Choose a tag to compare
  • Add support for constant-time decryption of PKCS#1 v1.5-encoded session keys (#1445)

    Implement optional constant-time decryption flow to hinder Bleichenbacher-like attacks against RSA- and ElGamal public-key encrypted session keys.

    Changes:

    • Add config.constantTimePKCS1Decryption to enable the constant-time processing (defaults to false). The constant-time option is off by default since it has measurable performance impact on message decryption, and it is only helpful in specific application scenarios (more info below).
    • Add config.constantTimePKCS1DecryptionSupportedSymmetricAlgorithms (defaults to the AES algorithms). The set of supported ciphers is restricted by default since the number of algorithms negatively affects performance.

    Bleichenbacher-like attacks are of concern for applications where both of the following conditions are met:

    1. New/incoming messages are automatically decrypted (without user interaction);
    2. An attacker can determine how long it takes to decrypt each message (e.g. due to decryption errors being logged remotely).
  • Check key requirements in PrivateKey.addSubkey() (#1423)

    Breaking change: when generating new subkeys through key.addSubkey(), we now check config.rejectCurves and prevent adding subkeys using the corresponding curves. By default, config.rejectCurves includes the brainpool curves (brainpoolP256r1, brainpoolP384r1, brainpoolP512r1) and the Bitcoin curve (secp256k1).

    This is a follow up to #1395, which introduced the same check to openpgp.generateKey.

  • Initial Deno support (#1448)

  • Replace strings with integer algorithm identifiers in packet classes (#1410)

    In several packet classes, we used to store string identifiers for public-key, aead, cipher or hash algorithms. To make the code consistent and to avoid having to convert to/from string values, we now always store integer values instead, e.g. enums.symmetric.aes128 is used instead of 'aes128'.

    This is not expected to be a breaking change for most library users. Note that the type of Key.getAlgorithmInfo() and of the session key objects returned and accepted by top-level functions remain unchanged.

    Affected classes (type changes for some properties and method's arguments):

    • PublicKeyPacket, PublicSubkeyPacket, SecretKeyPacket, SecretSubkeyPacket
    • SymEncryptedIntegrityProtectedDataPacket, AEADEncryptedDataPacket, SymmetricallyEncryptedDataPacket
    • LiteralDataPacket, CompressedDataPacket
    • PublicKeyEncryptedSessionKey, SymEncryptedSessionKeyPacket
    • SignaturePacket

    Other potentially breaking changes:

    • Removed property AEADEncryptedDataPacket.aeadAlgo, since it was redudant given .aeadAlgorithm.
    • Renamed AEADEncryptedDataPacket.cipherAlgo -> .cipherAlgorithm
  • CI: create annotations on performance regression warnings/errors (#1441)

  • CI: use Node v16 instead of v15

  • Update documentation link (#1455)

  • Rename master branch to main

v5.0.1

17 Nov 20:51
Compare
Choose a tag to compare
  • Remove stream from type definitions (#1413)
    It was removed in v5.0.0; the type definition was left behind.
  • Try to use process.env.NODE_ENV directly (#1402)
    Properly detect development / production mode when using tools like webpack or Vite, but don't throw an error if it's not available.
  • CI: Add performance and memory usage regression monitoring for pull requests (#1411)
  • Add config.allowInsecureVerificationWithReformattedKeys (#1231)
    Allows verifying signatures using keys that have been reformatted after the signature was created. To avoid this issue entirely, we recommend passing the key creation date to reformatKey.
  • Do not include checksum values in error messages when armor integrity check fails (#1428)
  • Update web stream tools and improve performance (#1439)
  • Remove line terminators from randomly generated test messages (#1440)

v5.0.0

02 Sep 15:20
Compare
Choose a tag to compare

This is the first stable release of OpenPGP.js v5 (no more breaking changes to the high-level API will be made). There are no changes since the last prerelease (v5.0.0-6). The changelog since OpenPGP.js v4 is:

Security improvements

  • Improve the security of newly generated keys:
    • Generate ECC keys by default (#1065)
    • RSA keys can still be generated with the new type parameter of generateKey: (#1179)
      import { generateKey } from 'openpgp';
      const { privateKey } = await generateKey({
        userIDs: [{ name: 'Test', email: 'test@email' }],
        type: 'rsa'
      });
    • Newly generated RSA keys are now 4096-bits by default
    • Remove SHA-1 from the default preferred hash algorithms (#1067)
    • Remove 3DES and CAST5 from the default preferred symmetric algorithms (#1068)
  • Reject certain insecure keys by default:
    • Add config.rejectPublicKeyAlgorithms, and default to disallowing the use of ElGamal and DSA for encrypting new messages and signing and verifying signatures, respectively (#1264)
    • Reject RSA keys with fewer than config.minRSABits bits (defaulting to 2048) when encrypting new messages and signing and verifying signatures, not just on key generation (#1264)
    • If you want to make an exception for a certain key or algorithm, rather than adjusting the global openpgp.config, you can now pass a config to a single function call, perhaps after warning the user / confirming that they want to allow this; for example:
      import { createMessage, encrypt } from 'openpgp';
      const message = await createMessage('Hello world!');
      try {
        await encrypt({
          message,
          encryptionKeys: publicKeys
        });
      } catch (err) {
        if (err.message.includes('...') && confirm('Warning: keys are insecure. Use anyway?')) {
          await encrypt({
            message,
            encryptionKeys: publicKeys,
            config: { minRSABits: 1024, rejectPublicKeyAlgorithms: new Set() }
          });
        } else {
          throw err;
        }
      }
      Of course, if at all possible, it's better to (ask users to) generate new keys, instead.
  • … and a few smaller configuration changes

Library size reductions

  • openpgp.HKP has been moved to a separate package: openpgpjs/hkp-client
  • openpgp.WKD has been moved to a separate package: openpgpjs/wkd-client
  • openpgp.Keyring and LocalStore have been removed, because keyring handling and storage should be handled in the application, as localStorage may not meet the durability requirements of the application.
  • The built-in Web Worker and openpgp.createWorker have been removed (for the rationale, please see #1072)
  • The "lightweight build" has been greatly reduced in size. The lightweight build loads certain dependencies on demand only when needed:
    • The indutny/elliptic library is loaded only when using or generating an ECC key with a brainpool or secp256k1 curve (or the NIST curves if Web Crypto is not supported). When using Curve25519/Ed25519, "elliptic" is never loaded.
    • The indutny/bn.js library is only loaded when the native BigInt API is not supported (see caniuse.com/bigint), and/or when the elliptic library is loaded (as it depends on bn.js).
    • The web-streams-polyfill and web-streams-adapter libraries are only loaded when streaming is used, and only if TransformStreams aren't natively supported (see caniuse.com/transformstream).
    • To use the lightweight build:
      import * as openpgp from 'openpgp/lightweight';
  • OpenPGP.js has been refactored to be more amenable to dead-code elimination, such that it's now more beneficial to only import the functions you need. For example, if you only import generateKey, support for encrypting and compressing messages is not needed, reducing the size of the build when using a build system such as webpack:
    import { generateKey } from 'openpgp/lightweight';

High-level API changes

(Examples below)

  • Replace openpgp.key.read/readArmored with openpgp.readKey
    • openpgp.readKey now takes an options object (either { armoredKey } or { binaryKey })
    • It now only returns a single key object, rather than a { keys: [key...], err } object
    • It now throws an error if the key failed to parse or if the key block contains multiple keys
  • Add openpgp.readKeys
    • It takes an options object (either { armoredKeys } or { binaryKeys })
    • It returns an array of key objects
    • It throws if any of the keys in the key block failed to parse
  • Add openpgp.readPrivateKey and openpgp.readPrivateKeys
    • These behave identically to readKey and readKeys, but additionally guarantee that a private key is returned
    • They throw an error if the parsed key is a public key
  • Remove Key.prototype.encrypt/decrypt in favor of openpgp.encryptKey/decryptKey
    • These functions take an option object ({ privateKey, passphrase })
    • They return a clone of the key object, rather than mutating the key
    • This eliminates the possibility of ending up in an inconsistent state where Key.prototype.decrypt would throw when decrypting one subkey after already decrypting (and mutating) others
  • Replace openpgp.message.read/readArmored with openpgp.readMessage, openpgp.signature.read/readArmored with openpgp.readSignature, and openpgp.cleartext.readArmored with openpgp.readCleartextMessage
    • openpgp.readMessage now takes an options object (either { armoredMessage } or { binaryMessage })
    • openpgp.readSignature now takes an options object (either { armoredSignature } or { binarySignature })
    • openpgp.readCleartextMessage now takes an options object ({ cleartextMessage })
  • Replace openpgp.message.fromText/fromBinary with openpgp.createMessage, and openpgp.cleartext.fromText with openpgp.createCleartextMessage
    • openpgp.createMessage now takes an options object (either { text } or { binary })
    • openpgp.createCleartextMessage now takes an options object ({ text })
    • Both of these functions are now async, and need to be awaited, like all the other top-level functions
  • In openpgp.generateKey, reformatKey, and revokeKey, return a simplified "key pair" object:
    • The returned object now contains privateKey and publicKey properties, and no longer contains key, privateKeyArmored, and publicKeyArmored
    • These functions now take the option format: 'armored' | 'binary' | 'object' to determine the format of privateKey and privateKey (defaulting to 'armored')
    • The object returned from generateKey and reformatKey still contains a revocationCertificate property, which is not affected by the format option
  • In openpgp.encrypt, sign, encryptSessionKey, encryptKey and decryptKey, return the result directly without wrapping it in a "results" object
  • In all top-level functions, rename public/privateKeys to encryption/decryption/signing/verificationKeys depending on their use
    • In openpgp.encrypt, encryptSessionKey, and generateSessionKey (see below), publicKeys is now called encryptionKeys
    • In openpgp.encrypt and sign, privateKeys is now called signingKeys
    • In openpgp.decrypt and decryptSessionKeys, privateKeys is now called decryptionKeys
    • In openpgp.decrypt and verify, publicKeys is now called verificationKeys
    • Similarly, rename toUserIDs to encryptionUserIDs and fromUserIDs to signingUserIDs
  • Remove the detached option of openpgp.encrypt. You can separately call openpgp.sign({ message, privateKeys, detached: true }) instead (don't forget to remove the privateKeys option from openpgp.encrypt as well if you do so, if you don't want the message to be signed inline). However, note that storing detached signatures of plaintext data together with the encrypted data is not secure
  • Add a new openpgp.generateSessionKey function
  • Remove the returnSessionKey option of openpgp.encrypt. You can separately call openpgp.generateSessionKey({ encryptionKeys: publicKeys }) instead and call openpgp.encrypt({ sessionKey }) with the result.
  • Remove the streaming option of openpgp.encrypt, decrypt, sign and verify. These functions now only use streaming when a ReadableStream or Node.js Readable stream is passed.
  • Replace the armor option in openpgp.encrypt and sign with a format option accepting the values 'armored' (default), 'binary' or 'object':
    • format: 'armored' is equivalent to armor: true
    • format: 'object' is equivalent to armor: false
    • format: 'binary' returns the message serialized as a Uint8Array (this was not supported before)
  • Add an expectSigned option to openpgp.encrypt and verify, which causes these functions to throw if there was no valid signature in the message.
  • Throughout the API, `user...
Read more

v5.0.0-6

20 Aug 13:40
Compare
Choose a tag to compare
v5.0.0-6 Pre-release
Pre-release

This is a prerelease of v5.0.0. The full changelog since OpenPGP.js v4 can be found here. The changelog since v5.0.0-5 is:

  • Add config.rejectCurves and prevent using and generating keys using blacklisted curves (#1395)
  • Set default config.minRSABits to 2047 (#1392)
  • Docs: clarify content of returned signatures value in openpgp.verify and decrypt (#1404)

v5.0.0-5

23 Jul 17:19
Compare
Choose a tag to compare
v5.0.0-5 Pre-release
Pre-release

This is a prerelease of v5.0.0. The full changelog since OpenPGP.js v4 can be found here. The changelog since v5.0.0-4 is:

  • Add security policy (#1388)
  • Throw on unrecognized configuration parameters in top-level functions (#1387)
  • Rename config.tolerant to config.ignoreUnsupportedPackets, add config.ignoreMalformedPackets (#1386)
  • TypeScript: add missing config options to the type definitions (#1385)
  • Update dependencies (#1383)

v5.0.0-4

20 Jul 18:37
Compare
Choose a tag to compare
v5.0.0-4 Pre-release
Pre-release

This is a prerelease of v5.0.0. The full changelog since OpenPGP.js v4 can be found here. The changelog since v5.0.0-3 is:

  • Fix various signature verification issues (#1302)
  • Uniform casing of subkey(s): rename the SubKey class to Subkey, and key.subKeys to key.subkeys (#1310)
  • CI: Check that JSDoc comments are valid (#1328)
  • CI: Detect unhandled rejections in browser tests (#1333) except in Safari 14.1 (#1371)
  • Require keys in openpgp.sign and make all top-level functions fully async (#1318)
  • Remove primaryKey argument from User methods, and rename User.sign to User.certify (#1329)
  • Always generate RSA keys of exact bit length (#1336)
  • Drop capabilities, keyID args in key.getExpirationTime() and consider direct-key signatures (#1319)
  • Add Signature#getSigningKeyIDs method to get Issuer Key IDs from a Signature (#1331)
  • Always throw on unexpected packets, but ignore Trust and Marker packets on parsing (#1340)
  • TypeScript: make the packets, keyPacket and mainKey properties readonly (#1337)
  • Simplify return value of generateKey, reformatKey and revokeKey and add support for binary output (#1345)
  • Support passing a non-array value to encryption/signingKeyIDs in top-level functions (#1342)
  • Support using key.isPrivate() for type inference, remove key.isPublic() (#1347)
  • Lint: enforce single quotes and do not error on class methods without this (#1341)
  • Remove valid and error from the verification result of openpgp.verify and decrypt, in favor of await verified (#1348)
  • Update README to use openpgp.readPrivateKey() where applicable (#1362)
  • Extend BaseStream<> from AsyncIterable<>, to enable the use of for await (chunk of result) on returned streams in TypeScript (#1373)
  • Replace armor option with format in openpgp.encrypt, sign and encryptSessionKey (#1354, #1377)
    • If format: 'armor' is passed (the default), an armored signed/encrypted message is returned (same as armor: true)
    • If format: 'binary' is passed, a binary signed/encrypted message is returned (same as armor: false in previous v5 prereleases)
    • If format: 'object' is passed, a Message or Signature object is returned (same as armor: false in v4)
  • Github: add issue templates (#1369)
  • Export Subkey class (#1381)
  • Update web-stream-tools, web-streams-polyfill and web-streams-adapter dependencies

Examples

Generate an armored key pair

v5.0.0-3:

import { generateKey } from 'openpgp';
const { privateKeyArmored, publicKeyArmored, revocationCertificate } = await generateKey({ userIDs: [{ name: 'Test', email: 'test@email' }] });

v5.0.0-4:

import { generateKey } from 'openpgp';
const { privateKey, publicKey, revocationCertificate } = await generateKey({ userIDs: [{ name: 'Test', email: 'test@email' }] });
Generate a private key object

v5.0.0-3:

import { generateKey } from 'openpgp';
const { key } = await generateKey({ userIDs: [{ name: 'Test', email: 'test@email' }] });

v5.0.0-4:

import { generateKey } from 'openpgp';
const { key: privateKey } = await generateKey({ userIDs: [{ name: 'Test', email: 'test@email' }], format: 'object' });
Encrypt and sign binary message (binary output)

v5.0.0-3:

import { createMessage, encrypt } from 'openpgp';
const message = await createMessage({ binary: data });
const encrypted = await encrypt({ encryptionKeys: publicKeys, signingKeys: privateKeys, message, armor: false });
console.log(encrypted); // Uint8Array

v5.0.0-4:

import { createMessage, encrypt } from 'openpgp';
const message = await createMessage({ binary: data });
const encrypted = await encrypt({ encryptionKeys: publicKeys, signingKeys: privateKeys, message, format: 'binary' });
console.log(encrypted); // Uint8Array

v5.0.0-3

28 May 16:02
Compare
Choose a tag to compare
v5.0.0-3 Pre-release
Pre-release

This is a prerelease of v5.0.0. The full changelog since OpenPGP.js v4 can be found here. The changelog since v5.0.0-2 is:

  • Switch from Uint8Array.from to new Uint8Array (fixes #1076)
  • Update pako (fixes #1301)
  • Replace Key with PrivateKey and PublicKey classes (#1300)
    • Add PrivateKey and PublicKey classes. A PrivateKey can always be passed where a PublicKey key is expected, but not vice versa.
    • Unexport Key, and export PrivateKey and PublicKey.
    • Rename Key.packetlist2structure to Key.packetListToStructure.
    • Change Key.update to return a new updated key, rather than modifying the destination one in place.
    • Add openpgp.readPrivateKey and openpgp.readPrivateKeys to avoid having to downcast the result of readKey(s) in TypeScript.
  • Fix link in README's table of contents (#1307)
  • Rename public/privateKeys to encryption/decryption/signing/verificationKeys (#1299)
    • Rename publicKeys to encryptionKeys or verificationKeys depending on their use
    • Rename privateKeys to decryptionKeys or signingKeys depending on their use
    • Similarly, rename toUserIDs to encryptionUserIDs and fromUserIDs to signingUserIDs
  • Unexport openpgp.stream (#1291)
    This change allows us to only load the ReadableStream polyfill when needed without behaving inconsistently in the external API.
    Users of the library should use the global ReadableStream or Node.js stream.Readable instead, or import a polyfill if needed. This version also loosens the detection criteria such that polyfilled streams are better detected.
  • Only ignore unsupported packets when config.tolerant is set (#1298)
    Don't ignore parse errors if config.tolerant is enabled. This leads to more useful error messages in most cases, as ignoring these errors will most likely still lead to an error down the line (e.g. if a key binding signature is missing). Unsupported and unknown packets and packets with an unsupported or unknown version are still ignored, for forward compatibility.
  • Make key fingerprint computation async (#1297)
  • Make PacketList a valid subtype of Array and update Packet.tag types (#1289)
  • Add PacketList.fromBinary and add missing config param in some functions (#1294)

v5.0.0-2

27 Apr 12:24
Compare
Choose a tag to compare
v5.0.0-2 Pre-release
Pre-release

This is a prerelease of v5.0.0. The full changelog since OpenPGP.js v4 can be found here. The changelog since v5.0.0-1 is:

  • Remove Key.prototype.encrypt and Key.prototype.decrypt in favor of openpgp.encryptKey and openpgp.decryptKey (#1285)
  • Fix encoded length in unencrypted v5 secret key packets (#1278)
  • Add expectSigned option to openpgp.decrypt and openpgp.verify (#1275)
  • Fix streaming verification example in README (#1269)
  • Fix createMessage return type in type definitions (#1276)
  • Test type definitions in Continuous Integration
  • Disallow passing streams to readKey[s], readSignature and readCleartextMessage
  • Remove top-level streaming options, and only return a stream if a stream was passed
  • Replace Message.fromText and Message.fromBinary with createMessage, and replace CleartextMessage.fromText with createCleartextMessage
  • Fetch Streams ponyfill on demand in lightweight build
  • Internally use ArrayStreams instead of real streams when not streaming
  • Use consistent name casing (#1268)
    • Use PascalCase for classes (with uppercase acronyms)
    • Use camelCase for function and variables (first word/acronym is always lowercase, otherwise acronyms are uppercase)
  • Make the packet classes' tag properties static
  • Add config.rejectPublicKeyAlgorithms (#1264)
    • Default to disallowing the use of ElGamal and DSA for verifying and signing or encrypting new messages
    • When verifying a message, if the verification key is not found or too weak, the corresponding signature will have signature.valid=false (instead of signature.valid=null); signature.error will explain whether the key was missing/too weak/etc
  • Take config.minRsaBits into account when signing, verifying and encrypting messages, not just on key generation
  • Drop support for verification of detached cleartext signatures (#1265)
  • Add key.write() and update type definitions (#1267)
  • Simplify algorithm preference selection and normalize config names (#1262)
    • Rename config.compression to config.preferredCompressionAlgorithm
    • Rename config.encryptionCipher to config.preferredSymmetricAlgorithm
    • Rename config.preferHashAlgorithm to config.preferredHashAlgorithm
    • Rename config.aeadMode to config.preferredAeadAlgorithm
    • When encrypting to public keys, the compression/aead/symmetric algorithm is selected by:
      • taking the preferred algorithm specified in config, if it is supported by all recipients
      • otherwise, taking the "MUST implement" algorithm specified by rfc4880bis
    • When encrypting to passphrases only (no public keys), the preferred algorithms from config are always used
    • EdDSA signing with a hash algorithm weaker than sha256 is explicitly disallowed (https://tools.ietf.org/id/draft-ietf-openpgp-rfc4880bis-10.html#section-15-7.2)

v5.0.0-1

03 Mar 19:33
Compare
Choose a tag to compare
v5.0.0-1 Pre-release
Pre-release

This is a prerelease of v5.0.0. The full changelog since OpenPGP.js v4 can be found here. The changelog since v5.0.0-0 is:

  • Fix unhandled promise rejection when stream-decrypting non-MDC messages (#1260)
  • Rename config.ignoreMdcError to config.allowUnauthenticatedMessages (#1261)
  • Do not support creating symmetrically encrypted message packets without integrity protection
  • Use config.aeadProtect to determine private key encryption mode
  • Check critical notations during signature verification instead of during parsing (#1259)
  • Remove KeyRing class (keyring handling and storage should be handled in the application, as localStorage may not meet the durability requirements of the application, for example)
  • Move WKD client to openpgpjs/wkd-client
  • Move HKP client to openpgpjs/hkp-client
  • Switch packages installed from git to @openpgp npm scope
  • Update web-stream-tools
  • Default to empty filename when creating messages
  • Update documentation
  • Update TypeScript definitions

v5.0.0-0

27 Feb 00:31
Compare
Choose a tag to compare
v5.0.0-0 Pre-release
Pre-release

This is a prerelease of v5.0.0. The changelog can be found here.