Releases: openpgpjs/openpgpjs
v5.1.0
-
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 tofalse
). 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:
- New/incoming messages are automatically decrypted (without user interaction);
- An attacker can determine how long it takes to decrypt each message (e.g. due to decryption errors being logged remotely).
- Add
-
Check key requirements in
PrivateKey.addSubkey()
(#1423)Breaking change: when generating new subkeys through
key.addSubkey()
, we now checkconfig.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
- 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 toreformatKey
. - 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
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 ofgenerateKey
: (#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 aconfig
to a single function call, perhaps after warning the user / confirming that they want to allow this; for example:Of course, if at all possible, it's better to (ask users to) generate new keys, instead.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; } }
- Add
- … and a few smaller configuration changes
Library size reductions
openpgp.HKP
has been moved to a separate package: openpgpjs/hkp-clientopenpgp.WKD
has been moved to a separate package: openpgpjs/wkd-clientopenpgp.Keyring
andLocalStore
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
TransformStream
s 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
- Replace
openpgp.key.read/readArmored
withopenpgp.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
- It takes an options object (either
- Add
openpgp.readPrivateKey
andopenpgp.readPrivateKeys
- These behave identically to
readKey
andreadKeys
, but additionally guarantee that a private key is returned - They throw an error if the parsed key is a public key
- These behave identically to
- Remove
Key.prototype.encrypt/decrypt
in favor ofopenpgp.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
- These functions take an option object (
- Replace
openpgp.message.read/readArmored
withopenpgp.readMessage
,openpgp.signature.read/readArmored
withopenpgp.readSignature
, andopenpgp.cleartext.readArmored
withopenpgp.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
withopenpgp.createMessage
, andopenpgp.cleartext.fromText
withopenpgp.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 beawait
ed, like all the other top-level functions
- In
openpgp.generateKey
,reformatKey
, andrevokeKey
, return a simplified "key pair" object:- The returned object now contains
privateKey
andpublicKey
properties, and no longer containskey
,privateKeyArmored
, andpublicKeyArmored
- These functions now take the option
format: 'armored' | 'binary' | 'object'
to determine the format ofprivateKey
andprivateKey
(defaulting to'armored'
) - The object returned from
generateKey
andreformatKey
still contains arevocationCertificate
property, which is not affected by theformat
option
- The returned object now contains
- In
openpgp.encrypt
,sign
,encryptSessionKey
,encryptKey
anddecryptKey
, return the result directly without wrapping it in a "results" object - In all top-level functions, rename
public/privateKeys
toencryption/decryption/signing/verificationKeys
depending on their use- In
openpgp.encrypt
,encryptSessionKey
, andgenerateSessionKey
(see below),publicKeys
is now calledencryptionKeys
- In
openpgp.encrypt
andsign
,privateKeys
is now calledsigningKeys
- In
openpgp.decrypt
anddecryptSessionKeys
,privateKeys
is now calleddecryptionKeys
- In
openpgp.decrypt
andverify
,publicKeys
is now calledverificationKeys
- Similarly, rename
toUserIDs
toencryptionUserIDs
andfromUserIDs
tosigningUserIDs
- In
- Remove the
detached
option ofopenpgp.encrypt
. You can separately callopenpgp.sign({ message, privateKeys, detached: true })
instead (don't forget to remove theprivateKeys
option fromopenpgp.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 ofopenpgp.encrypt
. You can separately callopenpgp.generateSessionKey({ encryptionKeys: publicKeys })
instead and callopenpgp.encrypt({ sessionKey })
with the result. - Remove the
streaming
option ofopenpgp.encrypt
,decrypt
,sign
andverify
. These functions now only use streaming when aReadableStream
or Node.jsReadable
stream is passed. - Replace the
armor
option inopenpgp.encrypt
andsign
with aformat
option accepting the values'armored'
(default),'binary'
or'object'
:format: 'armored'
is equivalent toarmor: true
format: 'object'
is equivalent toarmor: false
format: 'binary'
returns the message serialized as a Uint8Array (this was not supported before)
- Add an
expectSigned
option toopenpgp.encrypt
andverify
, which causes these functions to throw if there was no valid signature in the message. - Throughout the API, `user...
v5.0.0-6
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:
v5.0.0-5
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
toconfig.ignoreUnsupportedPackets
, addconfig.ignoreMalformedPackets
(#1386) - TypeScript: add missing
config
options to the type definitions (#1385) - Update dependencies (#1383)
v5.0.0-4
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 toSubkey
, andkey.subKeys
tokey.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 fromUser
methods, and renameUser.sign
toUser.certify
(#1329) - Always generate RSA keys of exact bit length (#1336)
- Drop
capabilities
,keyID
args inkey.getExpirationTime()
and consider direct-key signatures (#1319) - Add
Signature#getSigningKeyIDs
method to get Issuer Key IDs from aSignature
(#1331) - Always throw on unexpected packets, but ignore
Trust
andMarker
packets on parsing (#1340) - TypeScript: make the
packets
,keyPacket
andmainKey
propertiesreadonly
(#1337) - Simplify return value of
generateKey
,reformatKey
andrevokeKey
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, removekey.isPublic()
(#1347) - Lint: enforce single quotes and do not error on class methods without
this
(#1341) - Remove
valid
anderror
from the verification result ofopenpgp.verify
anddecrypt
, in favor ofawait verified
(#1348) - Update README to use
openpgp.readPrivateKey()
where applicable (#1362) - Extend
BaseStream<>
fromAsyncIterable<>
, to enable the use offor await (chunk of result)
on returned streams in TypeScript (#1373) - Replace
armor
option withformat
inopenpgp.encrypt
,sign
andencryptSessionKey
(#1354, #1377)- If
format: 'armor'
is passed (the default), an armored signed/encrypted message is returned (same asarmor: true
) - If
format: 'binary'
is passed, a binary signed/encrypted message is returned (same asarmor: false
in previous v5 prereleases) - If
format: 'object'
is passed, aMessage
orSignature
object is returned (same asarmor: false
in v4)
- If
- 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
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
tonew Uint8Array
(fixes #1076) - Update pako (fixes #1301)
- Replace
Key
withPrivateKey
andPublicKey
classes (#1300)- Add
PrivateKey
andPublicKey
classes. APrivateKey
can always be passed where aPublicKey
key is expected, but not vice versa. - Unexport
Key
, and exportPrivateKey
andPublicKey
. - Rename
Key.packetlist2structure
toKey.packetListToStructure
. - Change
Key.update
to return a new updated key, rather than modifying the destination one in place. - Add
openpgp.readPrivateKey
andopenpgp.readPrivateKeys
to avoid having to downcast the result ofreadKey(s)
in TypeScript.
- Add
- Fix link in README's table of contents (#1307)
- Rename
public/privateKeys
toencryption/decryption/signing/verificationKeys
(#1299)- Rename
publicKeys
toencryptionKeys
orverificationKeys
depending on their use - Rename
privateKeys
todecryptionKeys
orsigningKeys
depending on their use - Similarly, rename
toUserIDs
toencryptionUserIDs
andfromUserIDs
tosigningUserIDs
- Rename
- Unexport
openpgp.stream
(#1291)
This change allows us to only load theReadableStream
polyfill when needed without behaving inconsistently in the external API.
Users of the library should use the globalReadableStream
or Node.jsstream.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 ifconfig.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 ofArray
and updatePacket.tag
types (#1289) - Add
PacketList.fromBinary
and add missing config param in some functions (#1294)
v5.0.0-2
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
andKey.prototype.decrypt
in favor ofopenpgp.encryptKey
andopenpgp.decryptKey
(#1285) - Fix encoded length in unencrypted v5 secret key packets (#1278)
- Add
expectSigned
option toopenpgp.decrypt
andopenpgp.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
andreadCleartextMessage
- Remove top-level streaming options, and only return a stream if a stream was passed
- Replace
Message.fromText
andMessage.fromBinary
withcreateMessage
, and replaceCleartextMessage.fromText
withcreateCleartextMessage
- 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
propertiesstatic
- 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 havesignature.valid=false
(instead ofsignature.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
toconfig.preferredCompressionAlgorithm
- Rename
config.encryptionCipher
toconfig.preferredSymmetricAlgorithm
- Rename
config.preferHashAlgorithm
toconfig.preferredHashAlgorithm
- Rename
config.aeadMode
toconfig.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)
- Rename
v5.0.0-1
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
toconfig.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