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