How to implement Base58 to Bytes in Crystal?

I’m trying to figure out how to verify a signature that’s generated using a Tezos crypto wallet on the client-side. From the wallet, I get the signed message and the wallet’s public key, and the message is known to me because it’s generated on the server before signing.

I found an example in JavaScript: https://repl.it/@DalyIng/verifyTezosSignature

I already found GitHub - didactic-drunk/sodium.cr: Crystal wrapper for the libsodium crypto API, but I’m struggling with base58 decoding.

The JS counterpart of the code is here: base-x/index.js at master · cryptocoinjs/base-x · GitHub

Can anyone help me on the way here? Or is my best option to port the JS variant?

Don’t know what all you need, but if it’s just converting Bytes to String and vice versa, this should probably do it:

require "big"
T= "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"

def encode (bytes : Bytes) : String
  r= Array(Char).new
  big= bytes.reduce(BigInt.new){|b, c| b*256+c}
  while big > 0
    r << T[(big%58).to_i32]
    big//= 58
  end
  r.reverse.join
end

def decode (string : String) : Bytes
  r= IO::Memory.new
  big= string.chars.reduce(BigInt.new){|b, c| b*58+(T.index(c)||(raise "nope!"))}
  while big > 0
    r.write_byte (big%256).to_u8
    big//= 256
  end
  r.to_slice.reverse!
end

If this seems to fit your needs, make sure before you use it for anything serious, that it works really always as you expect it, because I didn’t do so.

Also, depending on how often you have to use it, this likely won’t be the most performant option (BigInt does the job, but in this particular case it probably shouldn’t be your first choice should performance matter).

1 Like

Judging by GitHub code search, there are a number of base58 implementations available in Crystal: Sign in to GitHub · GitHub

1 Like

This is another implementation I wrote some time ago: Carcin (context: Suggestion: Add Random#alphanumeric · Issue #8993 · crystal-lang/crystal · GitHub). It’s also based on BigInt, though because it uses a generic mechanism.

1 Like

Thank you both! Yesterday I went through all the base58 implementations for Ruby until I came across

GitHub - dougal/base58: Base58 is a Ruby library/gem for converting ints to and from base58..

That lib made me realise I was using the wrong alphabet. GitHub - russ/base58: base58 for Crystal references the one used by Flickr, while I needed the one for Bitcoin.

I decided to port it to Crystal:

Your solutions look more elegant, so if that’s ok with you, I’m going to rework the shard a bit starting off the examples above. Maybe rename it base_x so it isn’t restricted to base58.

1 Like