Skip to content

Commit 54f610c

Browse files
committed
Allow more than necessary padding characters in ignoreUnknownCharacters mode
1 parent 95025a4 commit 54f610c

File tree

2 files changed

+64
-18
lines changed

2 files changed

+64
-18
lines changed

Sources/FoundationEssentials/Data/Data+Base64.swift

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -639,8 +639,8 @@ extension Base64 {
639639
var x: UInt32 = d0[Int(a0)] | d1[Int(a1)] | d2[Int(a2)] | d3[Int(a3)]
640640

641641
if x >= Self.badCharacter {
642-
if a3 == Self.encodePaddingCharacter {
643-
break // the loop
642+
if a3 == Self.encodePaddingCharacter || a2 == Self.encodePaddingCharacter || a1 == Self.encodePaddingCharacter || a0 == Self.encodePaddingCharacter {
643+
break fastLoop // the loop
644644
}
645645

646646
// error fast path. we assume that illeagal errors are at the boundary.
@@ -721,19 +721,11 @@ extension Base64 {
721721
}
722722

723723
guard inIndex + 3 < inBuffer.count else {
724-
if options.contains(.ignoreUnknownCharacters) {
725-
// ensure that all remaining characters are unknown
726-
while inIndex < inBuffer.count {
727-
let value = inBuffer[inIndex]
728-
if self.isValidBase64Byte(value, options: options) || value == Self.encodePaddingCharacter {
729-
throw DecodingError.invalidCharacter(inBuffer[inIndex])
730-
}
731-
inIndex &+= 1
732-
}
733-
length = outIndex
734-
return
735-
}
736-
throw DecodingError.invalidLength
724+
// ensure that all remaining characters are unknown or padding
725+
try Self.validateRemainingBytesAreInvalidOrPadding(in: inBuffer, from: inIndex, options: options)
726+
if outIndex == 0 && inBuffer.count > 0 { throw DecodingError.invalidLength }
727+
length = outIndex
728+
return
737729
}
738730

739731
let a0 = inBuffer[inIndex]
@@ -780,10 +772,14 @@ extension Base64 {
780772
break scanForValidCharacters
781773
}
782774
} else if value == Self.encodePaddingCharacter {
783-
guard b0 != nil, b1 != nil else {
775+
if b0 == nil {
776+
try Self.validateRemainingBytesAreInvalidOrPadding(in: inBuffer, from: inIndex, options: options)
777+
if outIndex == 0 && inBuffer.count > 0 { throw DecodingError.invalidLength }
778+
length = outIndex
779+
return
780+
} else if b1 == nil {
784781
throw DecodingError.invalidLength
785-
}
786-
if b2 == nil {
782+
} else if b2 == nil {
787783
padding2 = true
788784
b2 = 65
789785
} else if b3 == nil {
@@ -825,6 +821,22 @@ extension Base64 {
825821
}
826822
}
827823

824+
static func validateRemainingBytesAreInvalidOrPadding(
825+
in inBuffer: UnsafeBufferPointer<UInt8>,
826+
from index: Int,
827+
options: Data.Base64DecodingOptions
828+
) throws(DecodingError) {
829+
var inIndex = index
830+
// ensure that all remaining characters are unknown or padding
831+
while inIndex < inBuffer.count {
832+
let value = inBuffer[inIndex]
833+
if self.isValidBase64Byte(value, options: options) {
834+
throw DecodingError.invalidCharacter(inBuffer[inIndex])
835+
}
836+
inIndex &+= 1
837+
}
838+
}
839+
828840
static func withUnsafeDecodingTablesAsBufferPointers<R, E: Swift.Error>(options: Data.Base64DecodingOptions, _ body: (UnsafeBufferPointer<UInt32>, UnsafeBufferPointer<UInt32>, UnsafeBufferPointer<UInt32>, UnsafeBufferPointer<UInt32>) throws(E) -> R) throws(E) -> R {
829841
let decoding0 = Self.decoding0
830842
let decoding1 = Self.decoding1

Tests/FoundationEssentialsTests/DataTests.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2148,6 +2148,40 @@ extension DataTests {
21482148
XCTAssertNil(Data(base64Encoded: "A===="))
21492149
}
21502150

2151+
func test_base64Decode_MorePaddingThanNecessaryIgnoreWhitespace() {
2152+
XCTAssertEqual(Data(base64Encoded: "", options: .ignoreUnknownCharacters), Data())
2153+
XCTAssertNil(Data(base64Encoded: "=", options: .ignoreUnknownCharacters))
2154+
XCTAssertNil(Data(base64Encoded: "==", options: .ignoreUnknownCharacters))
2155+
XCTAssertNil(Data(base64Encoded: "===", options: .ignoreUnknownCharacters))
2156+
XCTAssertNil(Data(base64Encoded: "====", options: .ignoreUnknownCharacters))
2157+
for x in 5..<1000 {
2158+
XCTAssertNil(Data(base64Encoded: String(repeating: "=", count: x), options: .ignoreUnknownCharacters))
2159+
}
2160+
2161+
XCTAssertEqual(Data(base64Encoded: "AAAA", options: .ignoreUnknownCharacters), Data([0, 0, 0]))
2162+
XCTAssertEqual(Data(base64Encoded: "AAAA=", options: .ignoreUnknownCharacters), Data([0, 0, 0]))
2163+
XCTAssertEqual(Data(base64Encoded: "AAAA =", options: .ignoreUnknownCharacters), Data([0, 0, 0]))
2164+
XCTAssertEqual(Data(base64Encoded: "AAAA==", options: .ignoreUnknownCharacters), Data([0, 0, 0]))
2165+
XCTAssertEqual(Data(base64Encoded: "AAAA = =", options: .ignoreUnknownCharacters), Data([0, 0, 0]))
2166+
XCTAssertEqual(Data(base64Encoded: "AAAA===", options: .ignoreUnknownCharacters), Data([0, 0, 0]))
2167+
XCTAssertEqual(Data(base64Encoded: "AAAA = = = ", options: .ignoreUnknownCharacters), Data([0, 0, 0]))
2168+
XCTAssertEqual(Data(base64Encoded: "AAAA====", options: .ignoreUnknownCharacters), Data([0, 0, 0]))
2169+
XCTAssertEqual(Data(base64Encoded: "AAAA = = = =", options: .ignoreUnknownCharacters), Data([0, 0, 0]))
2170+
XCTAssertEqual(Data(base64Encoded: "AAA=", options: .ignoreUnknownCharacters), Data([0, 0]))
2171+
XCTAssertEqual(Data(base64Encoded: "AAA==", options: .ignoreUnknownCharacters), Data([0, 0]))
2172+
XCTAssertEqual(Data(base64Encoded: "AAA===", options: .ignoreUnknownCharacters), Data([0, 0]))
2173+
XCTAssertEqual(Data(base64Encoded: "AAA====", options: .ignoreUnknownCharacters), Data([0, 0]))
2174+
XCTAssertNil(Data(base64Encoded: "AA=", options: .ignoreUnknownCharacters))
2175+
XCTAssertEqual(Data(base64Encoded: "AA==", options: .ignoreUnknownCharacters), Data([0]))
2176+
XCTAssertEqual(Data(base64Encoded: "AA===", options: .ignoreUnknownCharacters), Data([0]))
2177+
XCTAssertEqual(Data(base64Encoded: "AA====", options: .ignoreUnknownCharacters), Data([0]))
2178+
XCTAssertNil(Data(base64Encoded: "A=", options: .ignoreUnknownCharacters))
2179+
XCTAssertNil(Data(base64Encoded: "A==", options: .ignoreUnknownCharacters))
2180+
XCTAssertNil(Data(base64Encoded: "A===", options: .ignoreUnknownCharacters))
2181+
XCTAssertNil(Data(base64Encoded: "A====", options: .ignoreUnknownCharacters))
2182+
}
2183+
2184+
21512185
func test_base64Data_medium() {
21522186
let data = Data("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut at tincidunt arcu. Suspendisse nec sodales erat, sit amet imperdiet ipsum. Etiam sed ornare felis. Nunc mauris turpis, bibendum non lectus quis, malesuada placerat turpis. Nam adipiscing non massa et semper. Nulla convallis semper bibendum. Aliquam dictum nulla cursus mi ultricies, at tincidunt mi sagittis. Nulla faucibus at dui quis sodales. Morbi rutrum, dui id ultrices venenatis, arcu urna egestas felis, vel suscipit mauris arcu quis risus. Nunc venenatis ligula at orci tristique, et mattis purus pulvinar. Etiam ultricies est odio. Nunc eleifend malesuada justo, nec euismod sem ultrices quis. Etiam nec nibh sit amet lorem faucibus dapibus quis nec leo. Praesent sit amet mauris vel lacus hendrerit porta mollis consectetur mi. Donec eget tortor dui. Morbi imperdiet, arcu sit amet elementum interdum, quam nisl tempor quam, vitae feugiat augue purus sed lacus. In ac urna adipiscing purus venenatis volutpat vel et metus. Nullam nec auctor quam. Phasellus porttitor felis ac nibh gravida suscipit tempus at ante. Nunc pellentesque iaculis sapien a mattis. Aenean eleifend dolor non nunc laoreet, non dictum massa aliquam. Aenean quis turpis augue. Praesent augue lectus, mollis nec elementum eu, dignissim at velit. Ut congue neque id ullamcorper pellentesque. Maecenas euismod in elit eu vehicula. Nullam tristique dui nulla, nec convallis metus suscipit eget. Cras semper augue nec cursus blandit. Nulla rhoncus et odio quis blandit. Praesent lobortis dignissim velit ut pulvinar. Duis interdum quam adipiscing dolor semper semper. Nunc bibendum convallis dui, eget mollis magna hendrerit et. Morbi facilisis, augue eu fringilla convallis, mauris est cursus dolor, eu posuere odio nunc quis orci. Ut eu justo sem. Phasellus ut erat rhoncus, faucibus arcu vitae, vulputate erat. Aliquam nec magna viverra, interdum est vitae, rhoncus sapien. Duis tincidunt tempor ipsum ut dapibus. Nullam commodo varius metus, sed sollicitudin eros. Etiam nec odio et dui tempor blandit posuere.".utf8)
21532187
let base64 = data.base64EncodedString()

0 commit comments

Comments
 (0)