Convert binary to string in Python with Base58Check encoding

Converting binary to string can be useful when you need to represent a number in a recognizable, short package. For this task, Base64 is often used.

Base64 contains characters that can be confused with each other. This can lead to transcription mistakes. It is also harder to copy & paste due to special characters.

Base58 encoding prevents confusion between letters. The resulting expression is also selectable by double clicking the mouse.

19wWTEnNTWna86WmtFsTAr5 (“Hello world!”)

Base58Check encoding

Base58Check consists of a version number, a payload, and a double sha256 hash of the payload.

First, pip install base58.

import base58

text = base58.b58encode_check(b'\0'+b'Hello world!')
>> b'19wWTEnNTWna86WmtFsTAr5'
payload = base58.b58decode_check(text)[1:]
>> b'Hello world!'

If the version byte is 0, the result always starts with a 1!

Base58Check size overhead

The Base58Check encoded string is about 37% larger for most payloads.

Generating QR Codes

19wWTEnNTWna86WmtFsTAr5 (“Hello world!”)
import base58
import pyqrcode

text = base58.b58encode_check(b'0'+b'Hello world!')
qr = pyqrcode.create(text)
qr.png('{0}.png'.format(text), scale=8)

Encrypted payloads

1R7wpD8bnXx9KnhyuKT3kVb9XY6Pd3mHtDk2gwsaiP6mFbQe3btnTHTgpErwtvnAYMY6gnHb (ciphertext of “Hello world!” encrypted with private key “password”).

Here is the code to encode ciphertext in base58:

import base58
from Crypto.Cipher import AES
from Crypto import Random

def pad(s,bs=32):
    return s+(bs-len(s)%bs)*chr(bs-len(s)%bs)

def unpad(s):
    return s[:-ord(s[len(s)-1:])]

def encrypt(key,payload):
    key = pad(key)
    payload = pad(payload)
    iv = Random.new().read(AES.block_size)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    ciphertext = base58.b58encode_check(b'\0'+iv+cipher.encrypt(payload))
    return ciphertext

def decrypt(key,ciphertext):
    key = pad(key)
    ciphertext = base58.b58decode_check(ciphertext)
    iv = ciphertext[1:AES.block_size+1]
    cipher = AES.new(key, AES.MODE_CBC, iv)
    payload = unpad(cipher.decrypt(ciphertext[AES.block_size+1:]))
    return payload

Now let’s run it!

key = 'password'
payload = 'Hello world!'
ciphertext = encrypt(key,payload)
>> "1R7wpD8bnXx9KnhyuKT3kVb9XY6Pd3mHtDk2gwsaiP6mFbQe3btnTHTgpErwtvnAYMY6gnHb"
payload = decrypt(key,ciphertext)
>> "Hello world!"

Each time this function is run results in different cyphertext. This is because of the initalization vector used in AES.

Performance

I created the following perturbation functions:

  • Neighboring swap
  • Insertion
  • Removal
  • Substitution
  • Repeat
  • Repeat with neighbor removal

No base58check string was invalid for 1 million trials of each function! This test is not enough for security purposes, but it is a good test for manual entry


About the author



Hi, I'm Nathan. I'm an electrical engineer in the Los Angeles area. Keep an eye out for more content being posted soon.


Leave a Reply

Your email address will not be published. Required fields are marked *