Summary
When parsing a BER indefinite-length constructed type whose contents are truncated before the mandatory end-of-contents (EOC) octets 00 00, the parser silently drops the last child element instead of throwing an error.
The loop at ASN1.swift:258-263:
let lastIndex = nodes.endIndex - 1
repeat {
try _parseNode(from: &data, encoding: rules, depth: depth + 1, into: &nodes)
} while data.count > 0 && nodes.last!.isEndMarker == false
let endMarker = nodes.popLast()!
let encodedBytes = originalData[..<endMarker.encodedBytes.endIndex]
nodes[lastIndex].encodedBytes = encodedBytes
The loop exits in two ways:
nodes.last!.isEndMarker == true - an EOC marker was found (correct behavior).
data.count == 0 - data is exhausted before any EOC appears (truncated input).
In case 2, nodes.popLast()! removes the last legitimately parsed child and treats it as the EOC marker, silently dropping it from the result. The parent node's encodedBytes is then set to an incorrect range derived from the dropped child's position.
Expected behavior
When the input is exhausted without encountering an EOC marker, the parser should throw an ASN1Error indicating truncated or malformed input, per X.690 §8.1.3.6.1 which requires end-of-contents octets to terminate all indefinite-length encodings.
Reproducer
import XCTest
import SwiftASN1
final class BERIndefiniteLengthTruncationTests: XCTestCase {
func testTruncatedIndefiniteLengthDropsChild() throws {
// SEQUENCE (indefinite), one OCTET STRING child 'A', NO end-of-contents marker.
let bytes: [UInt8] = [0x30, 0x80, 0x04, 0x01, 0x41]
// Currently: parses without error, but the SEQUENCE has zero children
// (the OCTET STRING was silently popped as a fake EOC marker).
// Expected: should throw ASN1Error indicating missing EOC.
let parsed = try? BER.parse(bytes)
if let parsed = parsed {
var iterator = parsed.makeIterator()
// The child that was parsed (OCTET STRING) should be present:
XCTAssertNotNil(iterator.next(), "Child was silently dropped — data loss on truncated BER")
}
}
func testTruncatedIndefiniteLengthShouldThrow() {
let bytes: [UInt8] = [0x30, 0x80, 0x04, 0x01, 0x41]
XCTAssertThrowsError(try BER.parse(bytes),
"Truncated indefinite-length input should throw, not silently drop data")
}
}
Proposed fix
After the loop, check whether the exit was due to data exhaustion rather than an EOC marker:
repeat {
try _parseNode(from: &data, encoding: rules, depth: depth + 1, into: &nodes)
} while data.count > 0 && nodes.last!.isEndMarker == false
guard nodes.last!.isEndMarker else {
throw ASN1Error.truncatedASN1Field(
reason: "Indefinite-length constructed type is missing end-of-contents octets"
)
}
let endMarker = nodes.popLast()!
// ... rest unchanged
Context
This code was introduced in commit 28d0e07 ("Add support for parsing BER") and was not touched by the CVE-2025-0343 fix (commit ae33e59, which hardened DER.swift and per-type decoders). This is a correctness issue, not a crash or memory-safety bug.
References
- X.690 §8.1.3.6.1 (end-of-contents octets requirement for indefinite-length)
- RFC 8825 §3 (BER encoding rules)
Summary
When parsing a BER indefinite-length constructed type whose contents are truncated before the mandatory end-of-contents (EOC) octets
00 00, the parser silently drops the last child element instead of throwing an error.The loop at
ASN1.swift:258-263:The loop exits in two ways:
nodes.last!.isEndMarker == true- an EOC marker was found (correct behavior).data.count == 0- data is exhausted before any EOC appears (truncated input).In case 2,
nodes.popLast()!removes the last legitimately parsed child and treats it as the EOC marker, silently dropping it from the result. The parent node'sencodedBytesis then set to an incorrect range derived from the dropped child's position.Expected behavior
When the input is exhausted without encountering an EOC marker, the parser should throw an
ASN1Errorindicating truncated or malformed input, per X.690 §8.1.3.6.1 which requires end-of-contents octets to terminate all indefinite-length encodings.Reproducer
Proposed fix
After the loop, check whether the exit was due to data exhaustion rather than an EOC marker:
Context
This code was introduced in commit
28d0e07("Add support for parsing BER") and was not touched by the CVE-2025-0343 fix (commitae33e59, which hardened DER.swift and per-type decoders). This is a correctness issue, not a crash or memory-safety bug.References