Skip to content

Commit

Permalink
Merge pull request #51 from outfoxx/fix/base64
Browse files Browse the repository at this point in the history
Allow unpadded base64 when decoding
  • Loading branch information
kdubb committed Feb 20, 2023
2 parents e724100 + b690408 commit 87b95c9
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 4 deletions.
15 changes: 13 additions & 2 deletions Sources/PotentCBOR/CBORDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ public struct CBORDecoderTransform: InternalDecoderTransform, InternalValueDeser
switch value {
case .byteString(let data), .tagged(_, .byteString(let data)): return data
case .tagged(.base64, .utf8String(let string)):
guard let data = Data(base64Encoded: string) else {
guard let data = Data(base64EncodedUnpadded: string) else {
throw DecodingError.dataCorruptedError(in: decoder, debugDescription: "Expected Base64 encoded string")
}
return data
Expand Down Expand Up @@ -575,8 +575,19 @@ public struct CBORDecoderTransform: InternalDecoderTransform, InternalValueDeser

extension Data {

init?(base64EncodedUnpadded string: String) {
self.init(base64Encoded: Data.padBase64(string))
}

init?(base64UrlEncoded string: String) {
self.init(base64Encoded: string.replacingOccurrences(of: "-", with: "+").replacingOccurrences(of: "_", with: "/"))
let base64String = string.replacingOccurrences(of: "-", with: "+").replacingOccurrences(of: "_", with: "/")
self.init(base64EncodedUnpadded: base64String)
}

private static func padBase64(_ string: String) -> String {
let offset = string.count % 4
guard offset != 0 else { return string }
return string.padding(toLength: string.count + 4 - offset, withPad: "=", startingAt: 0)
}

}
Expand Down
16 changes: 15 additions & 1 deletion Sources/PotentJSON/JSONDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ public struct JSONDecoderTransform: InternalDecoderTransform, InternalValueDeser
throw DecodingError.typeMismatch(at: decoder.codingPath, expectation: type, reality: value)
}

guard let data = Data(base64Encoded: string) else {
guard let data = Data(base64EncodedUnpadded: string) else {
throw DecodingError.dataCorrupted(.init(
codingPath: decoder.codingPath,
debugDescription: "Encountered Data is not valid Base64."
Expand Down Expand Up @@ -535,6 +535,20 @@ public struct JSONDecoderTransform: InternalDecoderTransform, InternalValueDeser

}

extension Data {

init?(base64EncodedUnpadded string: String) {
self.init(base64Encoded: Data.padBase64(string))
}

private static func padBase64(_ string: String) -> String {
let offset = string.count % 4
guard offset != 0 else { return string }
return string.padding(toLength: string.count + 4 - offset, withPad: "=", startingAt: 0)
}

}


#if canImport(Combine)

Expand Down
16 changes: 15 additions & 1 deletion Sources/PotentYAML/YAMLDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ public struct YAMLDecoderTransform: InternalDecoderTransform, InternalValueDeser
throw DecodingError.typeMismatch(at: decoder.codingPath, expectation: type, reality: value)
}

guard let data = Data(base64Encoded: string) else {
guard let data = Data(base64EncodedUnpadded: string) else {
throw DecodingError.dataCorrupted(.init(
codingPath: decoder.codingPath,
debugDescription: "Encountered Data is not valid Base64."
Expand Down Expand Up @@ -578,6 +578,20 @@ public struct YAMLDecoderTransform: InternalDecoderTransform, InternalValueDeser

}

extension Data {

init?(base64EncodedUnpadded string: String) {
self.init(base64Encoded: Data.padBase64(string))
}

private static func padBase64(_ string: String) -> String {
let offset = string.count % 4
guard offset != 0 else { return string }
return string.padding(toLength: string.count + 4 - offset, withPad: "=", startingAt: 0)
}

}



#if canImport(Combine)
Expand Down
12 changes: 12 additions & 0 deletions Tests/CBORDecoderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,12 @@ class CBORDecoderTests: XCTestCase {
0x44, 0x42, 0x41, 0x55, 0x2B])),
Data([1, 2, 3, 4, 5, 62])
)
// Unpadded
XCTAssertEqual(
try CBORDecoder.default.decode(Data.self, from: Data([0xD8, 0x22, 0x66, 0x41, 0x51, 0x49,
0x44, 0x42, 0x41])),
Data([1, 2, 3, 4])
)
XCTAssertThrowsError(
try CBORDecoder.default.decode(Data.self, from: Data([0xD8, 0x22, 0x68, 0x41, 0x51, 0x49,
0x44, 0x42, 0x41, 0x55, 0x60]))
Expand All @@ -941,6 +947,12 @@ class CBORDecoderTests: XCTestCase {
0x44, 0x42, 0x41, 0x55, 0x2D])),
Data([1, 2, 3, 4, 5, 62])
)
// Unpadded
XCTAssertEqual(
try CBORDecoder.default.decode(Data.self, from: Data([0xD8, 0x21, 0x66, 0x41, 0x51, 0x49,
0x44, 0x42, 0x41])),
Data([1, 2, 3, 4])
)
XCTAssertThrowsError(
try CBORDecoder.default.decode(Data.self, from: Data([0xD8, 0x21, 0x68, 0x41, 0x51, 0x49,
0x44, 0x42, 0x41, 0x55, 0x60]))
Expand Down
17 changes: 17 additions & 0 deletions Tests/JSONDecoderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,23 @@ class JSONDecoderTests: XCTestCase {
XCTAssertEqual(testValue.data, data)
}

func testDecodeBase64DataUnpadded() throws {

let data = "1234".data(using: .utf8)!

struct TestValue: Codable {
var data: Data
}

let json = #"{"data":"\#(data.base64EncodedString().replacingOccurrences(of: "=", with: ""))"}"#

let decoder = JSON.Decoder()
decoder.dataDecodingStrategy = .base64

let testValue = try decoder.decode(TestValue.self, from: json)
XCTAssertEqual(testValue.data, data)
}

func testDecodeDeferredToData() throws {

let data = "Hello World!".data(using: .utf8)!
Expand Down
23 changes: 23 additions & 0 deletions Tests/YAMLDecoderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,29 @@ class YAMLDecoderTests: XCTestCase {
XCTAssertEqual(testValue.data, data)
}

func testDecodeBase64DataUnpadded() throws {

let data = "1234".data(using: .utf8)!

struct TestValue: Codable {
var data: Data
}

let yaml =
"""
---
data: \(data.base64EncodedString().replacingOccurrences(of: "=", with: ""))
...
"""

let decoder = YAML.Decoder()
decoder.dataDecodingStrategy = .base64

let testValue = try decoder.decode(TestValue.self, from: yaml)
XCTAssertEqual(testValue.data, data)
}

func testDecodeDeferredToData() throws {

let data = "Hello World!".data(using: .utf8)!
Expand Down

0 comments on commit 87b95c9

Please sign in to comment.