-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathbase64url.lua
124 lines (113 loc) · 3.57 KB
/
base64url.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
--[[lit-meta
name = "creationix/base64url"
description = "A pure lua implemention of base64url using bitop"
tags = {"crypto", "base64", "base64url", "bitop"}
version = "2.0.0"
license = "MIT"
homepage = "https://github.com/creationix/luvit-jwt/blob/master/libs/base64url.lua"
author = { name = "Tim Caswell" }
]]
local bit = require 'bit'
local rshift = bit.rshift
local lshift = bit.lshift
local bor = bit.bor
local band = bit.band
local char = string.char
local byte = string.byte
local concat = table.concat
local codes = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_='
-- Loop over input 3 bytes at a time
-- a,b,c are 3 x 8-bit numbers
-- they are encoded into groups of 4 x 6-bit numbers
-- aaaaaa aabbbb bbbbcc cccccc
-- if there is no c, then pad the 4th with =
-- if there is also no b then pad the 3rd with =
local function base64Encode(str)
local parts = {}
local j = 1
for i = 1, #str, 3 do
local a, b, c = byte(str, i, i + 2)
parts[j] = char(
-- Higher 6 bits of a
byte(codes, rshift(a, 2) + 1),
-- Lower 2 bits of a + high 4 bits of b
byte(codes, bor(
lshift(band(a, 3), 4),
b and rshift(b, 4) or 0
) + 1),
-- Low 4 bits of b + High 2 bits of c
b and byte(codes, bor(
lshift(band(b, 15), 2),
c and rshift(c, 6) or 0
) + 1) or 61, -- 61 is '='
-- Lower 6 bits of c
c and byte(codes, band(c, 63) + 1) or 61 -- 61 is '='
)
j = j + 1
end
if #parts > 0 then
j = j - 1
local last = parts[j]
local i = string.find(last, "=", 1, true)
if i then
parts[j] = string.sub(last, 1, i - 1)
end
end
return concat(parts)
end
-- Reverse map from character code to 6-bit integer
local map = {}
for i = 1, #codes do
map[byte(codes, i)] = i - 1
end
-- loop over input 4 characters at a time
-- The characters are mapped to 4 x 6-bit integers a,b,c,d
-- They need to be reassalbled into 3 x 8-bit bytes
-- aaaaaabb bbbbcccc ccdddddd
-- if d is padding then there is no 3rd byte
-- if c is padding then there is no 2nd byte
local function base64Decode(data)
local bytes = {}
local j = 1
for i = 1, #data, 4 do
local a = map[byte(data, i)]
local b = map[byte(data, i + 1)]
local c = map[byte(data, i + 2)] or 64
local d = map[byte(data, i + 3)] or 64
-- higher 6 bits are the first char
-- lower 2 bits are upper 2 bits of second char
bytes[j] = char(bor(lshift(a, 2), rshift(b, 4)))
-- if the third char is not padding, we have a second byte
if c < 64 then
-- high 4 bits come from lower 4 bits in b
-- low 4 bits come from high 4 bits in c
bytes[j + 1] = char(bor(lshift(band(b, 0xf), 4), rshift(c, 2)))
-- if the fourth char is not padding, we have a third byte
if d < 64 then
-- Upper 2 bits come from Lower 2 bits of c
-- Lower 6 bits come from d
bytes[j + 2] = char(bor(lshift(band(c, 3), 6), d))
end
end
j = j + 3
end
return concat(bytes)
end
assert(base64Encode("") == "")
assert(base64Encode("f") == "Zg")
assert(base64Encode("fo") == "Zm8")
assert(base64Encode("foo") == "Zm9v")
assert(base64Encode("foob") == "Zm9vYg")
assert(base64Encode("fooba") == "Zm9vYmE")
assert(base64Encode("foobar") == "Zm9vYmFy")
assert(base64Decode("") == "")
assert(base64Decode("Zg==") == "f")
assert(base64Decode("Zm8=") == "fo")
assert(base64Decode("Zm9v") == "foo")
assert(base64Decode("Zm9vYg==") == "foob")
assert(base64Decode("Zm9vYmE=") == "fooba")
assert(base64Decode("Zm9vYmFy") == "foobar")
return {
encode = base64Encode,
decode = base64Decode,
}