We are trying to implement the verification of the new EU corona virus test/vaccination certificates, but can't get the base45 decoding working.
Specification is here: https://datatracker.ietf.org/doc/draft-faltstrom-base45/
We nearly finished our class, but we sometimes get wrong values back..
Target is this:
Encoding example 1: The string "AB" is the byte sequence [65 66].The 16 bit value is 65 * 256 + 66 = 16706. 16706 equals 11 + 45 * 11+ 45 * 45 * 8 so the sequence in base 45 is [11 11 8]. By looking upthese values in the Table 1 we get the encoded string "BB8".Encoding example 2: The string "Hello!!" as ASCII is the bytesequence [72 101 108 108 111 33 33]. If we look at each 16 bitvalue, it is [18533 27756 28449 33]. Note the 33 for the last byte.When looking at the values modulo 45, we get [[38 6 9] [36 31 13] [92 14] [33 0]] where the last byte is represented by two. By lookingup these values in the Table 1 we get the encoded string "%69VD92EX0".Encoding example 3: The string "base-45" as ASCII is the bytesequence [98 97 115 101 45 52 53]. If we look at each 16 bit value,it is [25185 29541 11572 53]. Note the 53 for the last byte. Whenlooking at the values modulo 45, we get [[30 19 12] [21 26 14] [7 325] [8 1]] where the last byte is represented by two. By looking upthese values in the Table 1 we get the encoded string "UJCLQE7W581".
Here is my current code, which produces wrong values:
class Base45 ALPHABET = {"00" => "0","01" => "1","02" => "2","03" => "3","04" => "4","05" => "5","06" => "6","07" => "7","08" => "8","09" => "9","10" => "A","11" => "B","12" => "C","13" => "D","14" => "E","15" => "F","16" => "G","17" => "H","18" => "I","19" => "J","20" => "K","21" => "L","22" => "M","23" => "N","24" => "O","25" => "P","26" => "Q","27" => "R","28" => "S","29" => "T","30" => "U","31" => "V","32" => "W","33" => "X","34" => "Y","35" => "Z","36" => " ","37" => "$","38" => "%","39" => "*","40" => "+","41" => "-","42" => ".","43" => "/","44" => ":" }.freeze def self.encode_base45(text) restsumme = text.unpack('S>*') # not sure what this is doing, but without it, it works worse :D restsumme << text.bytes[-1] if text.bytes.size > 2 && text.bytes[-1] < 256 bytearr = restsumme.map do |bytes| arr = [] multiplier, rest = bytes.divmod(45**2) arr << multiplier if multiplier > 0 multiplier, rest = rest.divmod(45) arr << multiplier if multiplier > 0 arr << rest if rest > 0 arr.reverse end return bytearr.flatten.map{|a| ALPHABET[a.to_s.rjust(2, "0")]}.join end def self.decode_base45(text) arr = text.split("").map do |char| ALPHABET.invert[char] end textarr = arr.each_slice(3).to_a.map do |group| subarr = group.map.with_index do |val, index| val.to_i * (45**index) end ap subarr subarr.sum end return textarr.pack("S>*") # returns wrong values endend
Results:
Base45.encode_base45("AB")=> "BB8" # worksBase45.decode_base45("BB8")=> "AB" # worksBase45.encode_base45("Hello!!")=> "%69 VD92EX" # worksBase45.decode_base45("BB8")=> "Hello!\x00!" # wrong \x00Base45.encode_base45("base-45")=> "UJCLQE7W581" # worksBase45.decode_base45("UJCLQE7W581")=> "base-4\x005" # wrong \x00
Any hints appreciated :(