131 lines
4.0 KiB
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='))
|