Most rules are derived from the `airbnb` template.
Some "bad" rule exceptions remain, but they require too many changes to fix, so
we leave it to a future refactoring.
When parsing errors are being ignored, packets that fail to parse are now
included in the resulting packet list as `UnparseablePacket`s . This way, when
parsing keys that contain unparsable (sub)key, we avoid associating the
following non-key packets to the wrong key entity.
On serialization, `UnparseablePacket`s are also included by writing their raw
packet body as it was read.
Breaking changes:
- throw error on key generation if the requested public key algorithm is
included in `config.rejectPublicKeyAlgorithms`;
- add `config.rejectCurves` to blacklist a set of ECC curves, to prevent keys
using those curves from being generated, or being used to
encrypt/decrypt/sign/verify messages.
By default, `config.rejectCurves` includes the brainpool curves
(`brainpoolP256r1`, `brainpoolP384r1`, `brainpoolP512r1`) and the Bitcoin curve
(`secp256k1`). This is because it's unclear whether these curves will be
standardised[1], and we prefer to blacklist them already, rather than introduce
a breaking change after release.
[1] https://gitlab.com/openpgp-wg/rfc4880bis/-/merge_requests/47#note_634199141
API changes:
- `Key.isPublic()` has been removed, since it was redundant and it would
introduce TypeScript issues. Call `!Key.isPrivate()` instead.
TypeScript changes:
- the `openpgp.readKey(s)` functions are now declared as returning a `Key`
instead of a `PublicKey`. This is just a readability improvement to make it
clearer that the result could also be a `PrivateKey`.
- All `Key` methods that return a key object now have the narrowest possible
return type.
- The `Key.isPrivate()` method can now be used for type inference, allowing the
compiler to distinguish between `PrivateKey` and `PublicKey`.
Calling `key.isPrivate()` is the recommended way of distinguishing between a
`PrivateKey` and `PublicKey` at runtime, over using `key instanceof ...`, since
the latter depends on the specifics of the `Key` class hierarchy.
- Fix#1159: `Key.verifyPrimaryKey` considers expiration time subpackets in
direct-key signatures to determine whether the key is expired.
- `Key.getExpirationTime()` does not take the `capabilities` and `keyID` arguments
anymore, and simply returns the expiration date of the primary key. Also, like
for `verifyPrimaryKey`, direct-key signatures are now taken into account.
- Keys and signatures are considered expired at the time of expiry, instead of
one second later.
Breaking change:
`Key.getExpirationTime(capabilities, keyID, userID, config)` ->
`.getExpirationTime(userID, config)`
- Add `User.mainKey` field to store a reference to the corresponding `Key`,
allowing to simplify calling some `User` methods.
- Rename `User.sign` to `User.certify`, since it's used for third-party
certifications and not as a counterpart of `User.verify`, which deals with
self-signatures.
- Change `Key.update` behaviour to store a copy of newly added users and
subkeys. Pointing to the same instance could give issues as the lists of
certifications and signatures could be altered by both the source key and the
updated one.
Breaking changes in `User` methods:
- `User.constructor(userPacket)` -> `constructor(userPacket, mainKey)`
- `User.sign(primaryKey, signingKeys, date, config)` -> `.certify(signingKeys,
date, config)`
- `User.verify(primaryKey, date = new Date(), config)` -> `.verify(date = new
Date(), config)`
- `User.verifyCertificate(primaryKey, certificate, verificationKeys, date = new
Date(), config)` -> `.verifyCertificate(certificate, verificationKeys, date =
new Date(), config)`
- `User.verifyAllCertifications(primaryKey, verificationKeys, date = new
Date(), config)` -> `.verifyAllCertifications(verificationKeys, date = new
Date(), config)`
- `User.isRevoked(primaryKey, certificate, keyPacket, date = new Date(),
config)` -> `.isRevoked(certificate, keyPacket, date = new Date(), config)`
- `User.update(sourceUser, primaryKey, date, config)` -> `.update(sourceUser,
date, config)`
- Throw on signature parsing (e.g. in `openpgp.readSignature`) if the
creation time subpacket is missing
- `SignaturePacket.verify` now directly checks for signature creation
and expiration times. This makes it easier to thoroughly check the
validity of signatures. Also:
- `openpgp.revokeKey` now takes a `date` to check the provided
revocation certificate
- `openpgp.decryptSessionKeys` now takes a `date` to check the
validity of the provided private keys
- whenever a `date` is used internally, the function accepts a
`date` param to allow passing the correct date
- Add tests for all of the above
- Like `openpgp.generateKey`, `openpgp.reformatKey` now also requires
`options.userIDs`
- Simplify calling `SubKey.isRevoked/update/getExpirationTime` by
adding the `SubKey.mainKey` field to hold the reference of the
corresponding `Key`
Breaking changes in low-level functions:
- Added/removed `date` params:
- `Key.update(key, config)` -> `update(key, date, config)`
- `Key.applyRevocationCertificate(revocationCertificate, config)` ->
`applyRevocationCertificate(revocationCertificate, date, config)`
- `Key.signAllUsers(privateKeys, config)` ->
`signAllUsers(privateKeys, date, config)`
- `Key.verifyAllUsers(keys, config)` ->
`verifyAllUsers(keys, date, config)`
- `new SignaturePacket(date)` -> `new SignaturePacket()`
- `SignaturePacket.sign(key, data, detached)` ->
`sign(key, data, date, detached)`
- `Message.sign(primaryKey, privateKeys, config)` ->
`sign(primaryKey, privateKeys, date, config)`
- `Message.decrypt(privateKeys, passwords, sessionKeys, config)` ->
`decrypt(privateKeys, passwords, sessionKeys, date, config)`
- `Message.decryptSessionKeys(privateKeys, passwords, config)` ->
`decryptSessionKeys(privateKeys, passwords, date, config)`
- Removed `primaryKey` params:
- `SubKey.isRevoked(primaryKey, signature, key, date, config)` ->
`isRevoked(signature, key, date, config)`
- `SubKey.update(subKey, primaryKey, date, config)` ->
`update(subKey, date, config)`
- `SubKey.getExpirationTime(primaryKey, date, config)` ->
`getExpirationTime(date, config)`
- 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.
- Make fingerprint and key ID computation async, and rely on Web Crypto
for hashing if available
- Always set fingerprint and keyID on key parsing / generation
- Introduce `*KeyPacket.computeFingerprint()` and
`*KeyPacket.computeFingerprintAndKeyID()`
- Change `getKeyID` and `getFingerprint*` functions to return the
pre-computed key ID and fingerprint, respectively
- Make `PublicKeyPacket.read` async
- Add `PacketList.fromBinary` which parses binary data and returns a
`PacketList`. Using it instead of `PacketList.read` avoids being left
with partially read data in case of errors.
- Rename `toPacketlist` to `toPacketList` in `Key`, `Subkey` and `User`
classes
- In `readMessage`, pass down `config` to `PacketList.read`
- Add `config` param to `CompressedDataPacket.decompress`,
`AEADEncryptedDataPacket.decrypt` and `Message.appendSignature`
Changes:
- Implementation:
- Remove `PacketList.prototype.concat` and `push`
(we solely rely on `Array.push` instead)
- Fix https://github.com/openpgpjs/openpgpjs/issues/907 by
correctly handling result of `filterByTag`
- Implement `write()` method for `Trust` and `Marker` packets,
to make them compatible with the `BasePacket` interface
- Types:
- Simplify and updated `PacketList` type definitions
- Fix types for `Packet.tag`, which is `static` since
https://github.com/openpgpjs/openpgpjs/pull/1268
- Prevent passing SubkeyPackets where KeyPackets are expected,
and vice versa
To encrypt/decrypt a key, the top-level functions `openpgp.encryptKey` and
`openpgp.decryptKey` should be used instead: these don't mutate the key;
instead, they either return a new encrypted/decrypted key object or throw an
error.
With `Key.prototype.encrypt` and `decrypt`, which mutated the key, it was
possible to end up in an inconsistent state if some (sub)keys could be
decrypted but others couldn't, they would both mutate the key and throw an
error, which is unexpected.
Note that the `keyID` parameter is not supported by `encryptKey`/`decryptKey`,
since partial key decryption is not recommended. If you still need to decrypt
a single subkey or primary key `k`, you can call `k.keyPacket.decrypt(...)`,
followed by `k.keyPacket.validate(...)`. Similarly, for encryption, call
`k.keyPacket.encrypt(...)`.
Additionally, `openpgp.generateKey` now requires `options.userIDs` again,
since otherwise the key is basically unusable. This was a regression from v4,
since we now allow parsing keys without user IDs (but still not using them).
- Use PascalCase for classes, with uppercase acronyms.
- Use camelCase for function and variables. First word/acronym is always
lowercase, otherwise acronyms are uppercase.
Also, make the packet classes' `tag` properties `static`.
- Add `config.rejectPublicKeyAlgorithms` to disallow using the given algorithms
to verify, sign or encrypt new messages or third-party certifications.
- Consider `config.minRsaBits` when signing, verifying and encrypting messages
and third-party certifications, not just on key generation.
- When verifying a message, if the verification key is not found (i.e. not
provided or too weak), the corresponding `signature` will have
`signature.valid=false` (used to be `signature.valid=null`).
`signature.error` will detail whether the key is missing/too weak/etc.
Generating and verifying key certification signatures is still permitted in all cases.
Refactor functions to take the configuration as a parameter.
This allows setting a config option for a single function call, whereas
setting `openpgp.config` could lead to concurrency-related issues when
multiple async function calls are made at the same time.
`openpgp.config` is used as default for unset config values in top-level
functions.
`openpgp.config` is used as default config object in low-level functions
(i.e., when calling a low-level function, it may be required to pass
`{ ...openpgp.config, modifiedConfig: modifiedValue }`).
Also,
- remove `config.rsaBlinding`: blinding is now always applied to RSA decryption
- remove `config.debug`: debugging mode can be enabled by setting
`process.env.NODE_ENV = 'development'`
- remove `config.useNative`: native crypto is always used when available
`key.isDecrypted()` now returns true if either the primary key or any subkey
is decrypted.
Additionally, implement `SecretKeyPacket.prototype.makeDummy` for encrypted
keys.
- Changes `openpgp.generateKey` to accept an explicit `type` parameter,
instead of inferring its value from the `curve` or `rsaBits` params
- Introduces `config.minRsaBits` to set minimum key size of RSA key generation
- Remove the boolean return value of various internal functions that throw on
error (the returned value was unused in most cases)
- Update and fix type definitions
Use `key.keyPacket.validate` instead of `crypto.publicKey.validateParams`, see
https://github.com/openpgpjs/openpgpjs/pull/1116#discussion_r447781386.
Also, `key.decrypt` now only throws on error, no other value is returned.
Also, fix typo (rebase error) that caused tests to fail in Safari for p521.
Also, switch from returning false to throwing errors in most verify*()
functions, as well as in `await signatures[*].verified`, in order to be
able to show more informative error messages.
This function checks whether the private and public key parameters
of the primary key match.
This check is necessary when using your own private key to encrypt
data if the private key was stored on an untrusted medium, and
trust is derived from being able to decrypt the private key.
This also has the effect that we only throw on them when trying to use
the key, instead of when parsing it, and that we don't throw when the
authorized revocation key is specified in a separate direct-key
signature instead of a User ID self-signature (the spec only specifies
including it in a direct-key signature, so that means that we
effectively don't reject them anymore. This is because users that
wanted to use the key, could remove this separate signature, anyway.)