Regression introduced in https://github.com/openpgpjs/openpgpjs/pull/1826
(v6.2.0) .
Due to internal fflate lib changes, part of the compressed data ended up being discarded,
leading to a corrupted compressed payload for the encrypted/signed message,
which cannot be decompressed.
Compression is disabled by default in openpgpjs.
Hence, the issue affects only users who enabled zlib compression via e.g.
`config.preferredCompressionAlgorithm = openpgp.enums.compression.zlib`
and encrypted or signed data larger than 65KB.
Since all major browsers have shipped support for the curve
in WebCrypto, we only load the JS fallback if needed.
Also, add native/non-native ECDH test for Curve25519Legacy.
(The more modern X25519/X448 algo implementations cannot be
tested that way since they include an HKDF step for which
we assume native support and do not implement a fallback.)
For Ed25519/Ed25519Legacy native validation code does a sign-verify check over random data.
This is faster than re-deriving the public point using tweetnacl.
If the native implementation is not available, we fall back to re-deriving
the public point only.
For X25519/Curve25519Legacy, both the native and fallback flows do an ecdh exchange;
in the fallback case, this results in slower performance compared to the existing check,
but encryption subkeys are hardly ever validated directly (only in case of gnu-dummy keys),
and this solution keeps the code simpler.
Separately, all validation tests have been updated to use valid params from a different
key, rather than corrupted parameters.
Refactor & simplify the handling of the packet stream and errors in
packet parsing & grammar validation.
This PR also makes the following observable changes:
- Packet parsing errors in not-yet-authenticated streams (i.e. SEIPDv1
with `allowUnauthenticatedStream: true`) get delayed until the
decrypted data stream is authenticated (i.e. the MDC has been
validated)
- Non-critical unknown packets get turned into `UnparseablePacket`
objects on the packet stream instead of being ignored
- The grammar validation internals are changed to a state machine where
each input packet is only checked once, for efficiency (before, the
entire partial packet sequence was checked for every packet)
Co-authored-by: larabr <larabr+github@protonmail.com>
It enforces a message structure as defined in
https://www.rfc-editor.org/rfc/rfc9580.html#section-10.3
(but slightly more permissive with Padding packets allowed in all cases).
Since we are unclear on whether this change might
impact handling of some messages in the wild, generated by
odd use-cases or non-conformant implementations, we
also add the option to disable the grammar check via
`config.enforceGrammar`.
GrammarErrors are only sensitive in the context of
unauthenticated decrypted streams.
In case of equal creation timestamps, pick the signing/encryption subkey
with the highest algorithm ID, on the assumption that that's the most
modern/secure algorithm.
The tests work correctly in Epiphany, but not in the WebKit build,
where the native X25519 implementation throws non-standard errors on
importKey (DataError) and generateKey (OperationError).
Patching this would be simply a matter of catching such errors and falling back
to the JS implementation, but since only the CI WebKit build seems to be
affected, we prefer not to relax fallback checks in the context of crypto
operations without issues reported in the wild.
Reverting commit ccb040ae96acd127a29161ffaf3b82b5b18c062f .
Firefox has fixed support in v132 (https://bugzilla.mozilla.org/show_bug.cgi?id=1918354)
usage of v130 and 131, which have a broken implementation, is now below 1%.
Also, Chrome has released support in v133.
iOS tests sometimes fail to start due to some "server disconnect" issue on BS side.
This seems more prominent on certain devices (e.g. iPhone 16 with iOS 18).
So, we also change the 'iOS latest' target to a more stable one.
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.
We were previously testing the webkit engine on Linux, which however relies on a
different WebCrypto API implementation compared to the macOS version (behind Safari).
Also, increase mocha timeouts, as the argon2 memory-heavy test takes longer in Firefox.
Dropping Safari since Web Secure Sockets do not seem to work with
the 'networkLogs' capability, which is in turn required for the HTTPS
connection to work without insecure certs warnings.
To have tests work Browserstack Safari (also below iOS 15), as the tests are run in an iframe,
rewriting localhost as hostname, making WebCrypto not available.
We keep HTTP for the non-browserstack tests so that in local testing,
generating self-signed certs is not required.
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.
These points do not pose a security threat in the context of OpenPGP ECDH,
and would simply result in an all-zero shared secret being generated.
However, they represent unexpected inputs, so we prefer to warn the user.
Fixes regression from changes in #1782, as the spec mandates that
legacy x25519 store the secret scalar already clamped.
Keys generated using v6.0.0-beta.3 are still expected to be functional,
since the scalar is to be clamped before computing the ECDH shared secret.
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.
We now throw on unexpected leading byte.
This change is primarily intended to help with debugging, in case of malformed params.
In fact, in case of wrong point size, the operations would already fail anyway,
just in lower-level functions.
We got a report of a message including a PKESK packet where
the ECDH x25519Legacy point was missing the leading byte (0x40).
While decryption naturally would naturally fail afterwards, this
change ensures we fail at a higher level, and do not blindly pass
down invalid data to the low-level crypto functions.