Every submodule under the 'crypto' directory was exported-imported
even if a handful of functions where actually needed.
We now only export entire modules behind default exports if it makes
sense for readability and if the different submodules would be
imported together anyway (e.g. `cipherMode` exports are all needed
by the SEIPD class).
We've also dropped exports that are not used outside of the crypto modules,
e.g. pkcs5 helpers.
This adds back support for decrypting password-protected messages which
were encrypted in OpenPGP.js v5 with custom config settings
`config.aeadProtect = true` together with
`config.preferredAEADAlgorithm = openpgp.enums.aead.experimentalGCM`.
Public-key-encrypted messages are affected if they were encrypted using the same config, while also providing `encryptionKeys` that declared `experimentalGCM` in their AEAD prefs.
Such keys could be generated in OpenPGP.js v5 by setting the aforementioned config values.
The `expect().to.not.throw` check as written is a no-op.
In fact, `throw` should have been called as a function.
We drop the relevant check altogether since if the wrapped
operation throws, the test will naturally fail due to the
unexpected error.
This affects the preferences of newly generated keys, which by default will
have SHA512 as first hash algo preference.
SHA512 will also be used when signing, as long as the recipient keys declare
support for the algorithm.
If given, the signature will be generated using the preferred hash algo from the recipient keys.
Otherwise, the signing key preferences are used (this was also the existing behavior).
Note: when signing through `openpgp.encrypt`, the `encryptionKeys` are automatically used as recipient keys.
In `openpgp.sign`, the signing key preferences are considered instead,
since no "recipient keys" are available.
The hash algo selection logic has been reworked as follows:
if `config.preferredHashAlgo` appears in the prefs of all recipients, we pick it;
otherwise, we use the strongest supported algo (note: SHA256 is always implicitly supported by all keys),
as long as it is compatible with the signing key (e.g. ECC keys require minimum digest sizes).
Previously, only the preferences of the signing key were used to determine the hash algo to use,
but this is in contrast to the RFC: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.16-2 .
Also, an algo stronger than `config.preferredHashAlgo` would be used, if the signing key
declared it as first preference.
With this change, `config.preferredHashAlgo` is picked even if it's weaker than the
preferences of the recipient keys.
For v3 SKESK and PKESK packets, the session key algorithm is part of the payload,
so we can check the session key size on packet decryption.
This is helpful to catch errors early, when using e.g. `decryptSessionKeys`.
In v6 packets, the session key size check can only be done on SEIPDv2 decryption.
This is especially important for SEIPDv2 session keys,
as a key derivation step is run where the resulting key
will always match the expected cipher size,
but we want to ensure that the input key isn't e.g. too short.
Signature parsing would fail in case of unexpected payload sizes, causing key parsing to always throw
when processing e.g. an (unsupported) Curve448Legacy subkey instead of ignoring it.
To address this, we now throw on signature verification instead of parsing (as done for ECDSA).
NB: the bug and this fix are not relevant for the new Ed25519/Ed448 entities as standardized by the crypto-refresh.
To avoid returning dummy key packets, and improving error reporting.
This new behavior is also better aligned with that of `Key.getSigningKey()`.
This is a breaking change for apps that call `getDecryptionKeys()` directly.
The related error messages returned by `openpgp.decrypt` have also changed,
becoming more specific.
This change is also made in preparation of supporting private keys with
public key packets.
Stick more closely to the algorithm preferences when creating an SEIPDv2
message, by trying additional combinations of the preferred symmetric algorithm
and the preferred AEAD algorithm. If one of them is supported but not the
other, we still use it (with the mandatory-to-implement algorithm for the other
one).
RFC9580 says that:
Argon2 is only used with AEAD (S2K usage octet 253). An
implementation MUST NOT create and MUST reject as malformed any
secret key packet where the S2K usage octet is not AEAD (253) and
the S2K specifier type is Argon2.
Therefore, we disallow reading and writing Argon2 keys without AEAD.
And:
[The Simple and Salted S2K methods] are used only for reading in
backwards compatibility mode.
Since v6 keys don't need backwards compatibility, we also disallow
reading Simple S2K there. We still allow reading Salted S2K since the
spec says it may be used "when [the password] is high entropy".
Parsing of v5 keys, v5 signatures and AEAD-encrypted data packets now requires turning on
the corresponding config flag.
The affected entities are non-standard, and in the crypto-refresh RFC they have been superseded by
v6 keys, v6 signatures and SEIPDv2 encrypted data, respectively.
However, generation of v5 entities was supported behind config flag in OpenPGP.js v5, and some other libraries,
hence parsing them might be necessary in some cases.
When given a streamed `message` and a detached `signature` in input,
the function would return an empty array as `data` instead of
the input stream, meaning it was not possible to pull it, causing
the `verified` promise to hang indefinitely.
The above issue was introduced v5.0.0-2, and thus affects all v5 releases
up to v5.11.1.
Previously, `readKey` and `readPrivateKey` would throw when given a block
of keys as input.
With this change, the first parsable key is returned by both functions:
the behaviour is equivalent to calling `readKeys` (resp. `readPrivateKeys`)
and taking the first array entry.
The logic was updated in github.com/openpgpjs/openpgpjs/pull/1678 .
The tests worked anyway thanks to the config option matching the (monkey patched)
keys' feature flags, which are the deciding factor for whether to use AEAD.
We relaxed constraints in a previous commit, but excluded unicode chars, which are however allowed in v5.
We now drop almost all email address constraints, by primarily rejecting
control and spaces char classes.
Library users are strongly encouraged to implement additional checks as needed,
based on their supported email address format.
NB: the validity checks in question affect the userID inputs accepted by e.g.
`generateKey` and `reformatKey`, not the values parsed from existing entities,
e.g. using `readKey` (where almost no validation is performed).
We need to include the checksum to work around a GnuPG bug where data fails to
be decoded if the base64 ends with no padding chars (=) (see https://dev.gnupg.org/T7071).
Pure v6 artifacts are unaffected and won't include the checksum, as mandated by
the spec.
Breaking change:
`openpgp.armor` takes an additional `emitChecksum` argument (defaults to
false).
NB: some types of data must not include the checksum, but compliance is left as
responsibility of the caller: this function does not carry out any checks.
Refer to the crypto-refresh RFC for more details.
---------
Co-authored-by: Daniel Huigens <d.huigens@protonmail.com>
EdDSA is known to be vulnerable to fault attacks which can lead to secret key
extraction if two signatures over the same data can be collected. Randomly
occurring bitflips in specific parts of the computation might in principle
result in vulnerable faulty signatures being generated.
To protect signatures generated using v4 and v5 keys from this possibility, we
randomise each signature by adding a custom notation with a random value,
functioning as a salt.
For simplicity, we add the salt to all algos, not just EdDSA, as it may also
serve as protection in case of weaknesses in the hash algo, potentially
hindering e.g. some chosen-prefix attacks.
v6 signatures do not need to rely on this, as they are non-deterministic by
design.
While this notation solution is interoperable, it will reveal that the
signature has been generated using OpenPGP.js, which may not be desirable in
some cases.
For this reason, the option `config.nonDeterministicSignaturesViaNotation`
(defaulting to true) has been added to turn off the feature.
Unclear motivation for adding the original config option; if an expiration is there, it should
be honoured.
Breaking change:
the option used to default to `false`, and ignore revocation expirations. We now honour
those expirations, namely match the behaviour resulting from setting the option to `true`.
The cleartext session key symmetric algorithm was accidentally included in the packet.
As a result, the generated messages may fail to parse and/or decrypt in other implementations.
The messages would still decrypt successfully in OpenPGP.js, due to an overly permissive parsing procedure,
which simply discarded the unused additional byte.
We know also throw on unexpected cleartext symmetric algo in PKESK v6.
New checks align with the HTML5 W3C spec and should be more lax than the
existing ones (meaning, addresses which passed validation before should
continue to be valid).
Addresses such as `@localhost` are now allowed too, since presence of "." is no
longer enforced.
These checks should not be considered exhaustive: library users are encouraged
to implement separate checks for email validity if needed.
Co-authored-by: Daniel Huigens <d.huigens@protonmail.com>
Breaking change: the requirements of `config.minRSABits`, `rejectPublicKeyAlgorithms` and `rejectCurves`
are now applied to the primary key, aside from the selected subkey.
The motivation is that the subkeys are certified by the primary key, but if the latter is
weak, arbitrary subkeys could potentially be added.
Note that the change does not affect decryption, to allow decrypting older messages.
This is a breaking change, as NIST curves identifiers and values in
`enums.curves` have been renamed:
- the identifiers `enums.curve.p256`, `.p384`, `.p521` are now marked as
`@deprecated`
- the new identifiers are, respectively: `enums.curve.nistP256`, `.nistP384`,
`.nistP521`.
- the corresponding values have been changed from `'p256'`,`'p384'`,`'p521'` to
`'nistP256'`, `'nistP384'`, `'nistP521'`.
Affected high-level API functions:
- in `generateKey`, the `options.curve` argument will expect the updated string
values
- `Key.getAlgorithmInfo()` will return the updated `curve` values
Breaking change: the `config.deflateLevel` is removed as the API does not accept a deflate level
in input, and the setting is of limited importance. Plus, using compression
is discouraged on security grounds.
Breaking change: all functions taking streams as inputs will now require passing Web Streams in Node.js . If given a native `stream.Readable` input, they will throw. The browser build is unaffected by this change.
Utils to convert from and to Web Streams in Node are available from v17,
see https://nodejs.org/api/stream.html#streamreadabletowebstreamreadable-options .
Previously, we automatically converted between Node native streams and custom, Web-like Readable streams.
This led to occasional issues.
The config option must be set when reading v4 private keys (e.g. those
generated in OpenPGP.js by default, without setting `config.v5Keys = true`)
which were encrypted by OpenPGP.js v5 (or older) using `config.aeadProtect = true`.
Otherwise, key parsing and/or key decryption will fail.
Additional context: OpenPGP.js up to v5 used to support encrypting v4 keys
using AEAD as specified by draft RFC4880bis
(https://www.ietf.org/archive/id/draft-ietf-openpgp-rfc4880bis-10.html#section-5.5.3-3.5).
Said AEAD mechanism was not standardized as-is, and it's been replaced in the
crypto-refresh with a new version that guarantees full key integrity on decryption.
The legacy AEAD format is incompatible, but fundamentally indistinguishable,
from that of the crypto-refresh for v4 keys. Thus, we rely on the caller to
instruct us to process the key as legacy, via the new config flag.
Co-authored-by: Daniel Huigens <d.huigens@protonmail.com>
This is to signal support to senders who wish to use these algos.
Note that SHA256 remains as first default preference, followed by SHA512,
as in the context of OpenPGP signatures they provide
better performance/security ratio than their SHA3 counterparts.
Key flags are needed to restrict key usage to specific purposes:
https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#section-5.2.3.29 .
Some older keys (e.g. from OpenPGP.js v1) do not declare any key flags.
In previous OpenPGP.js versions, we've allowed such keys to be used for any operation for which they were compatible.
This behaviour has now changed, and these keys are not allowed to be used for any operation.
The setting `config.allowMissingKeyFlags` has been added to selectively revert to the past behaviour.