Skip to content

Commit 48b2377

Browse files
authored
Merge pull request #1 from Hexaville/base64
use third party's Base64Encoder to avoid crash
2 parents 2dc00c1 + f87564e commit 48b2377

File tree

2 files changed

+366
-6
lines changed

2 files changed

+366
-6
lines changed
Lines changed: 362 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,362 @@
1+
/// Encodes and decodes bytes using the
2+
/// Base64 encoding
3+
///
4+
/// https://en.wikipedia.org/wiki/Base64
5+
6+
extension UInt8 {
7+
/// '\t'
8+
public static let horizontalTab: UInt8 = 0x9
9+
10+
/// '\n'
11+
public static let newLine: UInt8 = 0xA
12+
13+
/// '\r'
14+
public static let carriageReturn: UInt8 = 0xD
15+
16+
/// ' '
17+
public static let space: UInt8 = 0x20
18+
19+
/// !
20+
public static let exclamation: UInt8 = 0x21
21+
22+
/// "
23+
public static let quote: UInt8 = 0x22
24+
25+
/// #
26+
public static let numberSign: UInt8 = 0x23
27+
28+
/// $
29+
public static let dollar: UInt8 = 0x24
30+
31+
/// %
32+
public static let percent: UInt8 = 0x25
33+
34+
/// &
35+
public static let ampersand: UInt8 = 0x26
36+
37+
/// '
38+
public static let apostrophe: UInt8 = 0x27
39+
40+
/// (
41+
public static let leftParenthesis: UInt8 = 0x28
42+
43+
/// )
44+
public static let rightParenthesis: UInt8 = 0x29
45+
46+
/// *
47+
public static let asterisk: UInt8 = 0x2A
48+
49+
/// +
50+
public static let plus: UInt8 = 0x2B
51+
52+
/// ,
53+
public static let comma: UInt8 = 0x2C
54+
55+
/// -
56+
public static let hyphen: UInt8 = 0x2D
57+
58+
/// .
59+
public static let period: UInt8 = 0x2E
60+
61+
/// /
62+
public static let forwardSlash: UInt8 = 0x2F
63+
64+
/// 0
65+
public static let zero: UInt8 = 0x30
66+
67+
/// 9
68+
public static let nine: UInt8 = 0x39
69+
70+
/// :
71+
public static let colon: UInt8 = 0x3A
72+
73+
/// ;
74+
public static let semicolon: UInt8 = 0x3B
75+
76+
/// =
77+
public static let equals: UInt8 = 0x3D
78+
79+
/// ?
80+
public static let questionMark: UInt8 = 0x3F
81+
82+
/// @
83+
public static let at: UInt8 = 0x40
84+
85+
/// A
86+
public static let A: UInt8 = 0x41
87+
88+
/// B
89+
public static let B: UInt8 = 0x42
90+
91+
/// C
92+
public static let C: UInt8 = 0x43
93+
94+
/// D
95+
public static let D: UInt8 = 0x44
96+
97+
/// E
98+
public static let E: UInt8 = 0x45
99+
100+
/// F
101+
public static let F: UInt8 = 0x46
102+
103+
/// Z
104+
public static let Z: UInt8 = 0x5A
105+
106+
/// [
107+
public static let leftSquareBracket: UInt8 = 0x5B
108+
109+
/// \
110+
public static let backSlash: UInt8 = 0x5C
111+
112+
/// ]
113+
public static let rightSquareBracket: UInt8 = 0x5D
114+
115+
/// _
116+
public static let underscore: UInt8 = 0x5F
117+
118+
/// a
119+
public static let a: UInt8 = 0x61
120+
121+
/// f
122+
public static let f: UInt8 = 0x66
123+
124+
/// z
125+
public static let z: UInt8 = 0x7A
126+
127+
/// ~
128+
public static let tilda: UInt8 = 0x7E
129+
}
130+
131+
final class Base64Encoder {
132+
/// Static shared instance
133+
static let shared = Base64Encoder.regular
134+
135+
/// Standard Base64Encoder
136+
static var regular: Base64Encoder {
137+
return Base64Encoder()
138+
}
139+
140+
// Base64URLEncoder
141+
// - note: uses hyphens and underscores
142+
// in place of plus and forwardSlash
143+
static var url: Base64Encoder {
144+
let encodeMap: Base64Encoder.ByteMap = { byte in
145+
switch byte {
146+
case 62:
147+
return 0x2D
148+
case 63:
149+
return 0x5F
150+
default:
151+
return nil
152+
}
153+
}
154+
155+
let decodeMap: Base64Encoder.ByteMap = { byte in
156+
switch byte {
157+
case 0x2D:
158+
return 62
159+
case 0x5F:
160+
return 63
161+
default:
162+
return nil
163+
}
164+
}
165+
166+
return Base64Encoder(
167+
padding: nil,
168+
encodeMap: encodeMap,
169+
decodeMap: decodeMap
170+
)
171+
}
172+
173+
/// Maps binary format to base64 encoding
174+
static fileprivate let encodingTable: [UInt8: UInt8] = [
175+
0: 0x41, 1: 0x42, 2: 0x43, 3: 0x44,
176+
4: 0x45, 5: 0x46, 6: 0x47, 7: 0x48,
177+
8: 0x49, 9: 0x4A, 10: 0x4B, 11: 0x4C,
178+
12: 0x4D, 13: 0x4E, 14: 0x4F, 15: 0x50,
179+
16: 0x51, 17: 0x52, 18: 0x53, 19: 0x54,
180+
20: 0x55, 21: 0x56, 22: 0x57, 23: 0x58,
181+
24: 0x59, 25: 0x5A, 26: 0x61, 27: 0x62,
182+
28: 0x63, 29: 0x64, 30: 0x65, 31: 0x66,
183+
32: 0x67, 33: 0x68, 34: 0x69, 35: 0x6A,
184+
36: 0x6B, 37: 0x6C, 38: 0x6D, 39: 0x6E,
185+
40: 0x6F, 41: 0x70, 42: 0x71, 43: 0x72,
186+
44: 0x73, 45: 0x74, 46: 0x75, 47: 0x76,
187+
48: 0x77, 49: 0x78, 50: 0x79, 51: 0x7A,
188+
52: 0x30, 53: 0x31, 54: 0x32, 55: 0x33,
189+
56: 0x34, 57: 0x35, 58: 0x36, 59: 0x37,
190+
60: 0x38, 61: 0x39, 62: .plus, 63: .forwardSlash
191+
]
192+
193+
/// Maps base64 encoding into binary format
194+
static fileprivate let decodingTable: [UInt8: UInt8] = [
195+
0x41: 0, 0x42: 1, 0x43: 2, 0x44: 3,
196+
0x45: 4, 0x46: 5, 0x47: 6, 0x48: 7,
197+
0x49: 8, 0x4A: 9, 0x4B: 10, 0x4C: 11,
198+
0x4D: 12, 0x4E: 13, 0x4F: 14, 0x50: 15,
199+
0x51: 16, 0x52: 17, 0x53: 18, 0x54: 19,
200+
0x55: 20, 0x56: 21, 0x57: 22, 0x58: 23,
201+
0x59: 24, 0x5A: 25, 0x61: 26, 0x62: 27,
202+
0x63: 28, 0x64: 29, 0x65: 30, 0x66: 31,
203+
0x67: 32, 0x68: 33, 0x69: 34, 0x6A: 35,
204+
0x6B: 36, 0x6C: 37, 0x6D: 38, 0x6E: 39,
205+
0x6F: 40, 0x70: 41, 0x71: 42, 0x72: 43,
206+
0x73: 44, 0x74: 45, 0x75: 46, 0x76: 47,
207+
0x77: 48, 0x78: 49, 0x79: 50, 0x7A: 51,
208+
0x30: 52, 0x31: 53, 0x32: 54, 0x33: 55,
209+
0x34: 56, 0x35: 57, 0x36: 58, 0x37: 59,
210+
0x38: 60, 0x39: 61, .plus: 62, .forwardSlash: 63
211+
]
212+
213+
/// Typealias for optionally mapping a byte
214+
fileprivate typealias ByteMap = (UInt8) -> UInt8?
215+
216+
/// Byte to use for padding base64
217+
/// if nil, no padding will be used
218+
fileprivate let padding: UInt8?
219+
220+
/// If set, bytes returned will have priority
221+
/// over the encoding table. Encoding table
222+
/// will be used as a fallback
223+
fileprivate let encodeMap: ByteMap?
224+
225+
/// If set, bytes returned will have priority
226+
/// over the decoding table. Decoding table
227+
/// will be used as a fallback
228+
fileprivate let decodeMap: ByteMap?
229+
230+
/// Creates a new Base64 encoder
231+
fileprivate init(
232+
padding: UInt8? = .equals,
233+
encodeMap: ByteMap? = nil,
234+
decodeMap: ByteMap? = nil
235+
) {
236+
self.padding = padding
237+
self.encodeMap = encodeMap
238+
self.decodeMap = decodeMap
239+
}
240+
241+
/// Encodes bytes into Base64 format
242+
func encode(_ bytes: [UInt8]) -> [UInt8] {
243+
if bytes.count == 0 {
244+
return []
245+
}
246+
247+
let len = bytes.count
248+
var offset: Int = 0
249+
var c1: UInt8
250+
var c2: UInt8
251+
var result: [UInt8] = []
252+
253+
while offset < len {
254+
c1 = bytes[offset] & 0xff
255+
offset += 1
256+
result.append(encode((c1 >> 2) & 0x3f))
257+
c1 = (c1 & 0x03) << 4
258+
if offset >= len {
259+
result.append(encode(c1 & 0x3f))
260+
if let padding = self.padding {
261+
result.append(padding)
262+
result.append(padding)
263+
}
264+
break
265+
}
266+
267+
c2 = bytes[offset] & 0xff
268+
offset += 1
269+
c1 |= (c2 >> 4) & 0x0f
270+
result.append(encode(c1 & 0x3f))
271+
c1 = (c2 & 0x0f) << 2
272+
if offset >= len {
273+
result.append(encode(c1 & 0x3f))
274+
if let padding = self.padding {
275+
result.append(padding)
276+
}
277+
break
278+
}
279+
280+
c2 = bytes[offset] & 0xff
281+
offset += 1
282+
c1 |= (c2 >> 6) & 0x03
283+
result.append(encode(c1 & 0x3f))
284+
result.append(encode(c2 & 0x3f))
285+
}
286+
287+
return result
288+
}
289+
290+
/// Decodes bytes into binary format
291+
func decode(_ s: [UInt8]) -> [UInt8] {
292+
let maxolen = s.count
293+
294+
var off: Int = 0
295+
var olen: Int = 0
296+
var result = [UInt8](repeating: 0, count: maxolen)
297+
298+
var c1: UInt8
299+
var c2: UInt8
300+
var c3: UInt8
301+
var c4: UInt8
302+
var o: UInt8
303+
304+
while off < s.count - 1 && olen < maxolen {
305+
c1 = decode(s[off])
306+
off += 1
307+
c2 = decode(s[off])
308+
off += 1
309+
if c1 == UInt8.max || c2 == UInt8.max {
310+
break
311+
}
312+
313+
o = c1 << 2
314+
o |= (c2 & 0x30) >> 4
315+
result[olen] = o
316+
olen += 1
317+
if olen >= maxolen || off >= s.count {
318+
break
319+
}
320+
321+
c3 = decode(s[off])
322+
off += 1
323+
if c3 == UInt8.max {
324+
break
325+
}
326+
327+
o = (c2 & 0x0f) << 4
328+
o |= (c3 & 0x3c) >> 2
329+
result[olen] = o
330+
olen += 1
331+
if olen >= maxolen || off >= s.count {
332+
break
333+
}
334+
335+
c4 = decode(s[off])
336+
off += 1
337+
if c4 == UInt8.max {
338+
break
339+
}
340+
o = (c3 & 0x03) << 6
341+
o |= c4
342+
result[olen] = o
343+
olen += 1
344+
}
345+
346+
return Array(result[0..<olen])
347+
}
348+
349+
// MARK: Private
350+
351+
private func encode(_ x: UInt8) -> UInt8 {
352+
return encodeMap?(x)
353+
?? Base64Encoder.encodingTable[x]
354+
?? UInt8.max
355+
}
356+
357+
private func decode(_ x: UInt8) -> UInt8 {
358+
return decodeMap?(x)
359+
?? Base64Encoder.decodingTable[x]
360+
?? UInt8.max
361+
}
362+
}

Sources/DynamodbSessionStore/DynamodbSessionStore.swift

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,16 @@ public struct DynamodbSessionStore: SessionStoreProvider {
3333
throw DynamodbSessionStoreError.couldNotFindItem
3434
}
3535

36-
guard let data = Data(base64Encoded: jsonStr, options: Data.Base64DecodingOptions(rawValue: 0)) else {
37-
return nil
38-
}
39-
40-
return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
36+
let encodedData = Data(bytes: Base64Encoder.shared.decode(Array(jsonStr.utf8)))
37+
return try JSONSerialization.jsonObject(with: encodedData, options: []) as? [String: Any]
4138
}
4239

4340
public func write(value: [String : Any], forKey: String, ttl: Int?) throws {
4441
let data = try JSONSerialization.data(withJSONObject: value, options: [])
42+
let stringValue = String(bytes: Base64Encoder.shared.encode(data.bytes), encoding: .utf8) ?? ""
4543
var item: [String: Dynamodb.AttributeValue] = [
4644
"session_id" : Dynamodb.AttributeValue(s: forKey),
47-
"value": Dynamodb.AttributeValue(s: data.base64EncodedString())
45+
"value": Dynamodb.AttributeValue(s: stringValue)
4846
]
4947

5048
if let ttl = ttl {

0 commit comments

Comments
 (0)