VYPR
High severity7.5NVD Advisory· Published Jan 15, 2025· Updated Apr 15, 2026

CVE-2025-0343

CVE-2025-0343

Description

Swift ASN.1 can be caused to crash when parsing certain BER/DER constructions. This crash is caused by a confusion in the ASN.1 library itself which assumes that certain objects can only be provided in either constructed or primitive forms, and will trigger a preconditionFailure if that constraint isn't met.

Importantly, these constraints are actually required to be true in DER, but that correctness wasn't enforced on the early node parser side so it was incorrect to rely on it later on in decoding, which is what the library did.

These crashes can be triggered when parsing any DER/BER format object. There is no memory-safety issue here: the crash is a graceful one from the Swift runtime. The impact of this is that it can be used as a denial-of-service vector when parsing BER/DER data from unknown sources, e.g. when parsing TLS certificates.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/apple/swift-asn1SwiftURL
< 1.3.11.3.1

Patches

1
ae33e5941bb8

Merge commit from fork

https://github.com/apple/swift-asn1Cory BenfieldJan 14, 2025via ghsa
8 files changed · +75 25
  • Sources/SwiftASN1/Basic ASN1 Types/ASN1BitString.swift+1 1 modified
    @@ -57,7 +57,7 @@ public struct ASN1BitString: DERImplicitlyTaggable, BERImplicitlyTaggable {
             }
     
             guard case .primitive(let content) = node.content else {
    -            preconditionFailure("ASN.1 parser generated primitive node with constructed content")
    +            throw ASN1Error.invalidASN1Object(reason: "ASN1BitString encoded with constructed encoding")
             }
     
             // The initial octet explains how many of the bits in the _final_ octet are not part of the bitstring.
    
  • Sources/SwiftASN1/Basic ASN1 Types/ASN1Integer.swift+2 2 modified
    @@ -53,7 +53,7 @@ extension ASN1IntegerRepresentable {
             }
     
             guard case .primitive(var dataBytes) = node.content else {
    -            preconditionFailure("ASN.1 parser generated primitive node with constructed content")
    +            throw ASN1Error.invalidASN1Object(reason: "INTEGER encoded with constructed encoding")
             }
     
             // Zero bytes of integer is not an acceptable encoding.
    @@ -93,7 +93,7 @@ extension ASN1IntegerRepresentable {
             }
     
             guard case .primitive(var dataBytes) = node.content else {
    -            preconditionFailure("ASN.1 parser generated primitive node with constructed content")
    +            throw ASN1Error.invalidASN1Object(reason: "INTEGER encoded with constructed encoding")
             }
     
             // Zero bytes of integer is not an acceptable encoding.
    
  • Sources/SwiftASN1/Basic ASN1 Types/ASN1OctetString.swift+1 1 modified
    @@ -30,7 +30,7 @@ public struct ASN1OctetString: DERImplicitlyTaggable, BERImplicitlyTaggable {
             }
     
             guard case .primitive(let content) = node.content else {
    -            preconditionFailure("ASN.1 parser generated primitive node with constructed content")
    +            throw ASN1Error.invalidASN1Object(reason: "ASN1OctetString encoded with constructed encoding")
             }
     
             self.bytes = content
    
  • Sources/SwiftASN1/Basic ASN1 Types/GeneralizedTime.swift+3 9 modified
    @@ -206,21 +206,15 @@ public struct GeneralizedTime: DERImplicitlyTaggable, BERImplicitlyTaggable, Has
     
         @inlinable
         public init(derEncoded node: ASN1Node, withIdentifier identifier: ASN1Identifier) throws {
    -        guard node.identifier == identifier else {
    -            throw ASN1Error.unexpectedFieldType(node.identifier)
    -        }
    -
    -        guard case .primitive(let content) = node.content else {
    -            preconditionFailure("ASN.1 parser generated primitive node with constructed content")
    -        }
    -
    +        let content = try ASN1OctetString(derEncoded: node, withIdentifier: identifier).bytes
             self = try TimeUtilities.generalizedTimeFromBytes(content)
         }
     
         @inlinable
         public init(berEncoded node: ASN1Node, withIdentifier identifier: ASN1Identifier) throws {
             // TODO: BER supports relaxed timestamp parsing, which is not yet supported
    -        self = try .init(derEncoded: node, withIdentifier: identifier)
    +        let content = try ASN1OctetString(berEncoded: node, withIdentifier: identifier).bytes
    +        self = try TimeUtilities.generalizedTimeFromBytes(content)
         }
     
         @inlinable
    
  • Sources/SwiftASN1/Basic ASN1 Types/ObjectIdentifier.swift+1 1 modified
    @@ -42,7 +42,7 @@ public struct ASN1ObjectIdentifier: DERImplicitlyTaggable, BERImplicitlyTaggable
             }
     
             guard case .primitive(let content) = node.content else {
    -            preconditionFailure("ASN.1 parser generated primitive node with constructed content")
    +            throw ASN1Error.invalidASN1Object(reason: "OID encoded with constructed encoding")
             }
     
             try Self.validateObjectIdentifierInEncodedForm(content)
    
  • Sources/SwiftASN1/Basic ASN1 Types/UTCTime.swift+3 9 modified
    @@ -128,20 +128,14 @@ public struct UTCTime: DERImplicitlyTaggable, BERImplicitlyTaggable, Hashable, S
     
         @inlinable
         public init(derEncoded node: ASN1Node, withIdentifier identifier: ASN1Identifier) throws {
    -        guard node.identifier == identifier else {
    -            throw ASN1Error.unexpectedFieldType(node.identifier)
    -        }
    -
    -        guard case .primitive(let content) = node.content else {
    -            preconditionFailure("ASN.1 parser generated primitive node with constructed content")
    -        }
    -
    +        let content = try ASN1OctetString(derEncoded: node, withIdentifier: identifier).bytes
             self = try TimeUtilities.utcTimeFromBytes(content)
         }
     
         @inlinable
         public init(berEncoded node: ASN1Node, withIdentifier identifier: ASN1Identifier) throws {
    -        self = try .init(derEncoded: node, withIdentifier: identifier)
    +        let content = try ASN1OctetString(berEncoded: node, withIdentifier: identifier).bytes
    +        self = try TimeUtilities.utcTimeFromBytes(content)
         }
     
         @inlinable
    
  • Sources/SwiftASN1/DER.swift+3 2 modified
    @@ -233,8 +233,9 @@ extension DER {
     
             // We expect a single child.
             guard case .constructed(let nodes) = node.content else {
    -            // This error is an internal parser error: the tag above is always constructed.
    -            preconditionFailure("Explicit tags are always constructed")
    +            throw ASN1Error.invalidASN1Object(
    +                reason: "Explicit tags should always be constructed, got \(node.identifier) which is not."
    +            )
             }
     
             var nodeIterator = nodes.makeIterator()
    
  • Tests/SwiftASN1Tests/ASN1Tests.swift+61 0 modified
    @@ -967,6 +967,23 @@ class ASN1Tests: XCTestCase {
             }
         }
     
    +    func testPrimitiveTaggedObject() throws {
    +        // We should error if primitive encoding is used for an explicitly tagged object.
    +        let weirdASN1: [UInt8] = [
    +            0x30, 0x05,  // Sequence, containing...
    +            0x82, 0x03,  // Context specific tag 2, 3 byte body, containing...
    +            0x02, 0x01, 0x00,  // Integer 0
    +        ]
    +        let parsed = try DER.parse(weirdASN1)
    +        try DER.sequence(parsed, identifier: .sequence) { nodes in
    +            XCTAssertThrowsError(
    +                try DER.optionalExplicitlyTagged(&nodes, tagNumber: 2, tagClass: .contextSpecific, { _ in })
    +            ) { error in
    +                XCTAssertEqual((error as? ASN1Error)?.code, .invalidASN1Object)
    +            }
    +        }
    +    }
    +
         func testSPKIWithUnexpectedKeyTypeOID() throws {
             // This is an SPKI object for RSA instead of EC. This is a 1024-bit RSA key, so hopefully no-one will think to use it.
             let rsaSPKI =
    @@ -1366,4 +1383,48 @@ class ASN1Tests: XCTestCase {
             XCTAssertEqual(asn1OctetString.bytes, [0xFE, 0xED, 0xFA, 0xCE])
             XCTAssertThrowsError(try DER.parse(berOctetString))
         }
    +
    +    func testConstructedBoolean() throws {
    +        let weirdASN1: [UInt8] = [0x21, 0x00]
    +        let node = try DER.parse(weirdASN1)
    +        XCTAssertThrowsError(try Bool(berEncoded: node))
    +        XCTAssertThrowsError(try Bool(derEncoded: node))
    +    }
    +
    +    func testConstructedInteger() throws {
    +        let weirdASN1: [UInt8] = [0x22, 0x00]
    +        let node = try DER.parse(weirdASN1)
    +        XCTAssertThrowsError(try Int(berEncoded: node))
    +        XCTAssertThrowsError(try Int(derEncoded: node))
    +    }
    +
    +    func testConstructedBitString() throws {
    +        let weirdASN1: [UInt8] = [0x23, 0x08, 0x03, 0x02, 0x00, 0xAB, 0x03, 0x02, 0x04, 0xC]
    +        let node = try DER.parse(weirdASN1)
    +        // Not yet supported
    +        // XCTAssertEqual(try ASN1BitString(berEncoded: node), ASN1BitString(bytes: [0xAB, 0xC], paddingBits: 4))
    +        XCTAssertThrowsError(try ASN1BitString(berEncoded: node))
    +        XCTAssertThrowsError(try ASN1BitString(derEncoded: node))
    +    }
    +
    +    func testConstructedOctetString() throws {
    +        let weirdASN1: [UInt8] = [0x24, 0x06, 0x04, 0x01, 0xAB, 0x04, 0x01, 0xCD]
    +        let node = try DER.parse(weirdASN1)
    +        XCTAssertEqual(try ASN1OctetString(berEncoded: node), ASN1OctetString(contentBytes: [0xAB, 0xCD]))
    +        XCTAssertThrowsError(try ASN1OctetString(derEncoded: node))
    +    }
    +
    +    func testConstructedNull() throws {
    +        let weirdASN1: [UInt8] = [0x25, 0x00]
    +        let node = try DER.parse(weirdASN1)
    +        XCTAssertThrowsError(try ASN1Null(berEncoded: node))
    +        XCTAssertThrowsError(try ASN1Null(derEncoded: node))
    +    }
    +
    +    func testConstructedOID() throws {
    +        let weirdASN1: [UInt8] = [0x26, 0x03, 0x02, 0x01, 0x00]
    +        let node = try DER.parse(weirdASN1)
    +        XCTAssertThrowsError(try ASN1ObjectIdentifier(berEncoded: node))
    +        XCTAssertThrowsError(try ASN1ObjectIdentifier(derEncoded: node))
    +    }
     }
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.