Observable Timing Discrepancy vulnerability in potatosalad jose allows recovery of a long-term X25519 private key through a timing side channel in the pure-Erlang Curve25519 fallback.
The pure-Erlang X25519 implementation performs scalar multiplication over the secret private key using a recursive Montgomery ladder (josejwax25519:curve25519/2) whose clauses branch on individual bits of the clamped private scalar, and whose per-step operations (josejwax25519:montgomeryadd/3 and josejwax25519:montgomerydouble/1) and field inversion are built from value-dependent Erlang bignum arithmetic (multiplication, remainder, and modular exponentiation). Although the ladder is operation-count balanced and fixed at 254 steps, the concrete integer values processed at each step, and therefore the running time, depend on the secret scalar. The execution time of the public entry points josejwax25519:scalarmult/2, josejwax25519:x25519/2, josejwax25519:x25519base/1, and josejwax25519:sktopk/1 is thus a function of the private key.
This path is reachable from the public API, most notably when a service decrypts attacker-supplied ECDH-ES (JWE) messages with a long-term X25519 key (for example through JOSE.JWK.boxdecryptecdhes/2, which reaches josejwacurve25519:x25519sharedsecret/2 and the ladder). An attacker who can submit ciphertexts and measure decryption timing can, in principle, recover the private key bit by bit, defeating the resistance X25519 was designed to provide. Recovery of the key compromises the confidentiality and authenticity of all data protected by it.
This issue affects jose: from 1.10.0 before 1.11.13.
The jose library accepts ECDSA (ES256, ES256K, ES384, and ES512) JWS signatures of a non-canonical length. When verifying an ECDSA signature, josejwkktyec:verify/4 in src/jwk/josejwkktyec.erl splits the raw signature in half by its actual byte length (splitbinary(Signature, bytesize(Signature) div 2)) instead of enforcing the fixed length required by RFC 7518 §3.4, and then decodes each half with crypto:bytestointeger/1, which ignores leading zero bytes.
As a result, an attacker who holds one valid token can prepend leading zero bytes to each of the R and S halves to produce longer signatures that still split evenly and decode to the same (r, s) pair, so many distinct signature byte strings all verify against the same key (signature malleability). This does not allow forging new payloads or impersonating a signer, because the underlying (r, s) values are unchanged. It does defeat any security control that assumes a valid token or its signature is unique, such as token-revocation-by-hash, replay or idempotency caches, and deduplication logic.
This issue affects jose: from 1.0.0 before 1.11.13.
Inefficient Algorithmic Complexity in erlang-jose allows an unauthenticated remote attacker to cause a CPU-exhaustion denial of service by submitting a crafted RSA JSON Web Key.
When an application parses an untrusted JWK through the public josejwk:frombinary/1 or josejwk:frommap/1 API (or the Elixir wrappers JOSE.JWK.frombinary/1 and JOSE.JWK.frommap/1), an RSA private key that supplies d, e, and n but omits all Chinese Remainder Theorem parameters (p, q, dp, dq, qi) is routed into convertsfmtocrt/1, which tries to recover the missing primes by factoring the modulus. The three integers are read straight from the base64url input and are never validated as a real RSA key. When the modulus cannot be split into two factors (for example, when it is itself prime), the search loop convertsfmtocrtfindp/6 performs a modular exponentiation over the attacker-sized modulus on every iteration and never succeeds. A single request of a few hundred bytes forces close to one CPU-second of work before an internal one-second watchdog kills the worker, and a larger modulus raises the cost further. By flooding such requests an attacker can saturate every scheduler and deny service, without credentials, a valid RSA key, or any special configuration.
This issue affects jose: from 1.7.7 before 1.11.13.
Use of Password Hash With Insufficient Computational Effort in the jose library allows an attacker who obtains a password-encrypted private-key export to recover the private key through an offline password-cracking attack.
When jose exports a password-encrypted private key with josejwk:toder/2 (JOSE.JWK.toder/2), the josejwkder:tobinary/3 function in src/jwk/josejwkder.erl serializes the key as a PKCS#8 EncryptedPrivateKeyInfo using the PBES2 scheme with hardcoded key-derivation parameters: PBKDF2-HMAC-SHA256 with an iteration count of 2048 and an 8-byte (64-bit) random salt. Neither value can be overridden by the caller.
Both values are far below current guidance (OWASP recommends on the order of 600,000 iterations for PBKDF2-HMAC-SHA256, and NIST recommends salts of at least 128 bits), so the key-stretching work factor is roughly 300 times lower than intended and the short salt reduces the cost of precomputation across keys. The symmetric encryption (AES-256-CBC with a 16-byte random IV) is not affected; the weakness is entirely in the derivation strength. An attacker who obtains one of these exported blobs (for example from a backup, configuration repository, object storage, or logs) can mount a cheap offline dictionary or brute-force attack against the export password, and success yields the full private key.
This issue affects jose: from 1.10.0 before 1.11.13.
josejwk:sharedsecret/2 (exposed in Elixir as JOSE.JWK.sharedsecret/2) derives an ECDH shared secret from two JWKs. Its first function clause only matches when both JWKs carry the same key type (kty) module, and its fallback clause re-invokes itself as sharedsecret(from(YourJWK), from(MyJWK)). Because from/1 returns an already-parsed josejwk record unchanged, passing two JWKs whose key types differ (for example an EC key and an RSA key) makes the fallback clause re-enter with identical arguments, producing an unbounded tail-recursive loop that never returns and never crashes.
An application that derives a shared secret from an externally supplied peer public key without first checking that the peer key's type and curve match its own key can be driven into this loop by an attacker-controlled JWK, pinning an Erlang scheduler at 100% CPU. Repeated requests exhaust the available schedulers, resulting in a denial of service.
This issue affects jose: from 1.5.0 before 1.11.13.
The pure-Erlang RSAES-PKCS1-v15 decryption in the josejwapkcs1 module is a Bleichenbacher padding oracle. emepkcs1decode/2 returns the recovered message when a decrypted block has valid PKCS#1 v1.5 padding (a leading 0x00 0x02, a padding string of at least eight non-zero bytes, and a 0x00 separator) and returns a distinct error result otherwise, and rsaespkcs1decrypt/2 additionally returns {error, {badsize, }} when the length check fails. The implementation applies no implicit-rejection countermeasure (RFC 8017 §7.2.2): it does not substitute a deterministic pseudo-random plaintext on a padding failure, so a one-bit padding-conformance signal leaks to the caller. Because the underlying modular exponentiation in dp/2 is a raw, non-blinded crypto:modpow/3, the padding check also stays distinguishable by timing even when an application normalizes every failure to a single error value.
An unauthenticated attacker who knows only the public RSA key and can submit ciphertexts to a decryption endpoint that unwraps RSA15 JWE keys (or otherwise calls decryptprivate/3 with rsapkcs1padding) can use this oracle to recover the plaintext of intercepted ciphertexts, such as content-encryption keys and thus the protected message, or to forge signatures, through roughly 10^4 to 10^6 adaptive chosen-ciphertext queries.
This issue affects jose: from 1.3.0 before 1.11.13.
The pure-Erlang AES-GCM fallback in josejwaaes derives the number of verified authentication tag bits from the attacker-supplied tag itself. In the {aesgcm, Bits} clause of blockdecrypt/4, the incoming tag is guarded only by isbinary/1, with no check on its length. Verification in gcmblockdecrypt/6 then computes TBits = bitsize(T) and compares only those bits of the real 128-bit tag. An attacker who controls a JWE token can truncate the authentication tag to as little as 8 bits, so only 8 bits are ever checked. This reduces forgery resistance from 2^-128 to 2^-8, and a forged ciphertext is reliably accepted as authentic within 256 attempts without knowledge of the key (an authentication tag forgery and integrity bypass). Supplying a tag longer than 128 bits instead causes a badmatch that crashes the decrypting process. The fallback is reached through the public JWE decryption path whenever AES-GCM is not provided by a native crypto NIF, for example on FIPS or stripped or older OTP builds.
This issue affects jose: from 1.2.0 before 1.11.13.