STEEM internals #1 : WIF continued.

in steem •  8 years ago 

Thanks to @xeroc I managed to revert my last post as I had a logic error there. The information should be more accurate now, but anyone please correct me if I am wrong. I hope I understood it correctly. The concept is extremely complex and I am trying to analyze it in simple words and provide a high level overview.

How a private key is generated.

A private key can be generated from a brain key or a secure passphrase. Brain key is generated from a seed brain key which is a string of 16 words out of a predefined dictionary with 49744 words.

The dictionary is available to be seen but is too huge to be pasted, it is just a big list of words.

[ ... direct,direful,direly,dirempt,dirge,dirgler,dirhem,dirk ... ]

a random brain key may be suggested by taking random words from that dictionary. There is actually a quite interesting way to memorize the brain key based passphrases, I will perhaps explain it later. The private key is then generated as:

privkey = SHA256(SHA512(brainkey + " " + sequence))

A very high level explanation would be that two cryptographic hash functions are used to generate the key.

Using a similar technique a key may be generated using an account number and a custom passphrase.

privkey = SHA256(account + account role + passphrase)

STOP. What is a hash?

Hash algorithms are one way functions. They turn any amount of data into a fixed-length "fingerprint" that cannot be reversed. In this case they generate a raw private key from a string and it cannot be reversed.

How a private key is converted to WIF.

The private key is Base58check-encoded using a WIF version prefix 0x80. I will try to explain the encoding and Base58 alphabet and why it is used in the next post.

if _format.lower() == "wif" :
            return base58CheckEncode(0x80, self._hex)

What is important to know that 'check' after Base58 means that an additional checksum is appended to the end before the encoding happens. Therefore the full WIF is Base58 encoded from 0x80 + key + checksum from 2xSHA256 hashed key.

How WIF is encrypted.

WIF is encrypted using BIP38 encryption. Example source code by @xeroc from a python-graphenelib.

def encrypt(privkey, passphrase) :
    """ BIP0038 non-ec-multiply encryption. Returns BIP0038 encrypted privkey.
    :param privkey: Private key
    :type privkey: Base58
    :param str passphrase: UTF-8 encoded passphrase for encryption
    :return: BIP0038 non-ec-multiply encrypted wif key
    :rtype: Base58
    """
    privkeyhex    = repr(privkey)   # hex
    addr          = format(privkey.uncompressed.address, "BTC")
    a             = bytes(addr, 'ascii')
    salt          = hashlib.sha256(hashlib.sha256(a).digest()).digest()[0:4]
    key           = scrypt.hash(passphrase, salt, 16384, 8, 8)
    (derived_half1, derived_half2) = (key[:32], key[32:])
    aes             = AES.new(derived_half2)
    encrypted_half1 = _encrypt_xor(privkeyhex[:32], derived_half1[:16], aes)
    encrypted_half2 = _encrypt_xor(privkeyhex[32:], derived_half1[16:], aes)
    " flag byte is forced 0xc0 because BTS only uses compressed keys "
    payload    = (b'\x01' + b'\x42' + b'\xc0' +
                  salt + encrypted_half1 + encrypted_half2)
    " Checksum "
    checksum   = hashlib.sha256(hashlib.sha256(payload).digest()).digest()[:4]
    privatkey  = hexlify(payload + checksum).decode('ascii')
    return Base58(privatkey)

This looks extremely confusing but the recipe is as follows:

  • take your private wif key
  • take a passphrase that protects the key
  • generate SHA256 salt and hash
  • divide hash'ed key into two parts
  • encrypt the second part with AES encryption
  • apply XOR operation on both parts using key, AES and first part of the hash'ed key
  • create a 'payload' from the mixture above
  • calculate SHA256 checksum
  • add checksum to the payload
  • apply BASE58 encoding

And hooray, you have your encrypted WIF!

I know that quite a lot of this information may sound confusing. I am also still digesting it. I could have started with something easier, but this is needed to understand the internals.

DISCLAIMER: THE INFORMATION IS DELIVERED FREE OF CHARGE AND 'AS IS' WITHOUT WARRANTY OF ANY KIND. I HOPE IT IS ACCURATE AND FREE OF ERRORS AND YOU FIND IT USEFUL.

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!