The X-Wing decapsulation path accepts attacker-controlled encapsulated ciphertext bytes without enforcing the required fixed ciphertext length. The decapsulation call is forwarded into a C API, which expects a compile-time fixed-size ciphertext buffer of 1120 bytes. This creates an FFI memory-safety boundary issue when a shorter Data value is passed in, because the C code may read beyond the Swift buffer.
The issue is reachable through initialization of an HPKE.Recipient, which decapsulates the provided encapsulatedKey during construction. A malformed encapsulatedKey can therefore trigger undefined behavior instead of a safe length-validation error.
The decapsulate function of OpenSSLXWingPrivateKeyImpl does not perform a length check before passing the encapsulated data to the C API.
func decapsulate(_ encapsulated: Data) throws -> SymmetricKey {
try SymmetricKey(unsafeUninitializedCapacity: Int(XWING_SHARED_SECRET_BYTES)) { sharedSecretBytes, count in
try encapsulated.withUnsafeBytes { encapsulatedSecretBytes in
let rc = CCryptoBoringSSL_XWING_decap(
sharedSecretBytes.baseAddress,
encapsulatedSecretBytes.baseAddress,
&self.privateKey
)
guard rc == 1 else {
throw CryptoKitError.internalBoringSSLError()
}
count = Int(XWING_SHARED_SECRET_BYTES)
}
}
}
The C API does not have a runtime length parameter and instead expects a fixed-size buffer of 1120 bytes.
#define XWING_CIPHERTEXT_BYTES 1120
OPENSSL_EXPORT int XWING_decap(
uint8_t out_shared_secret[XWING_SHARED_SECRET_BYTES],
const uint8_t ciphertext[XWING_CIPHERTEXT_BYTES],
const struct XWING_private_key *private_key);
Since decapsulate accepts arguments of any length, an attacker controlled input can trigger an out-of-bounds read. The vulnerable code path can be reached through by...
4.3.1Exploitability
AV:NAC:LAT:NPR:NUI:NVulnerable System
VC:LVI:NVA:HSubsequent System
SC:NSI:NSA:N8.8/CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:H/SC:N/SI:N/SA:N