infra-utils/nginx-lua/b32tob58.lua

131 lines
4.0 KiB
Lua

--------------------------------------------------------------------------------
-- encoding configuration
--------------------------------------------------------------------------------
local base48Alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
local base32Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
local base32PadMap = { "", "======", "====", "===", "=" }
--------------------------------------------------------------------------------
-- encoding function for base58
--------------------------------------------------------------------------------
local function encode_base58(s)
local q, b
local et = {}
local zn = 0 -- number of leading zero bytes in s
-- assume s is a large, little-endian binary number
-- with base256 digits (each byte is a "digit")
local nt = {} -- number to divide in base 256, big endian
local dt = {} -- result of nt // 58, in base 256
local more = true -- used to count leading zero bytes
for i = 1, #s do
b = string.byte(s, i)
if more and b == 0 then
zn = zn + 1
else
more = false
end
nt[i] = b
end
if #s == zn then --take care of strings empty or with only nul bytes
return string.rep('1', zn)
end
more = true
while more do
local r = 0
more = false
for i = 1, #nt do
b = nt[i] + (256 * r)
q = math.floor(b / 58)
-- if q is not null at least once, we are good
-- for another division by 58
more = more or q > 0
r = b % 58
dt[i] = q
end
-- r is the next base58 digit. insert it before previous ones
-- to get a big-endian base58 number
table.insert(et, 1, string.char(string.byte(base48Alphabet, r+1)))
-- now copy dt into nt before another round of division by 58
nt = {}
for i = 1, #dt do nt[i] = dt[i] end
dt = {}
end--while
-- don't forget the leading zeros ('1' is digit 0 in bitcoin base58 alphabet)
return string.rep('1', zn) .. table.concat(et)
end
--------------------------------------------------------------------------------
-- various helpers for base32 decoding
--------------------------------------------------------------------------------
local function number_to_bit( num, length )
local bits = {}
while num > 0 do
local rest = math.floor( math.fmod( num, 2 ) )
table.insert( bits, rest )
num = ( num - rest ) / 2
end
while #bits < length do
table.insert( bits, "0" )
end
return string.reverse( table.concat( bits ) )
end
local function ignore_set( str, set )
if set then
str = str:gsub( "["..set.."]", "" )
end
return str
end
local function pure_from_bit( str )
return ( str:gsub( '........', function ( cc )
return string.char( tonumber( cc, 2 ) )
end ) )
end
local function unexpected_char_error( str, pos )
local c = string.sub( str, pos, pos )
return string.format( "unexpected character at position %d: '%s'", pos, c )
end
--------------------------------------------------------------------------------
-- generic function to decode and encode base32/base64
--------------------------------------------------------------------------------
local function from_basexx( str, alphabet, bits )
local result = {}
for i = 1, #str do
local c = string.sub( str, i, i )
if c ~= '=' then
local index = string.find( alphabet, c, 1, true )
if not index then
return nil, unexpected_char_error( str, i )
end
table.insert( result, number_to_bit( index - 1, bits ) )
end
end
local value = table.concat( result )
local pad = #value % 8
return pure_from_bit( string.sub( value, 1, #value - pad ) )
end
function decode_base32( str, ignore )
str = string.upper(str)
str = ignore_set( str, ignore )
return from_basexx( string.upper( str ), base32Alphabet, 5 )
end
--------------------------------------------------------------------------------
-- testing
--------------------------------------------------------------------------------
print(encode_base58('test'))
print(decode_base32('ORSXG5A='))