Author

Topic: What is the safest way to convert a base6 number into a WIF private key? (Read 3771 times)

legendary
Activity: 1974
Merit: 1030
I'll donate you and dserrano5 some symbolic satoshis, thanks again.

Thank you!
legendary
Activity: 1022
Merit: 1008
Delusional crypto obsessionist
Lol, bussy for hours reading stuff and trying things.
Nice to figure out you can just feed this script a private hex key as an argument Grin

But, always nice to learn the inner workings of bitcoin.
Thanks again, this was precisely what I wanted.
legendary
Activity: 1456
Merit: 1081
I may write code in exchange for bitcoins.
Sure, to be fair, most of this code was from Ken Sherrif's bitcoins the hard way blog and the linked repo.  I did some modifications myself to make it generate both the compressed and compressed versions and I started modifying it to work on testnet.   Here's the entire script I've been playing with.  You can see that if you just run it it's going to generate a random bitcoin address.  However, if you do as we discussed before, calling it with python -i then you can call the methods you're interested in one-at-time.  Or, you could make your own script at the bottom.  Or, you could go ahead and convert it into a python module.  In any case, here's all the methods I've been using/playing around with.

The method you're looking for are privateKeyToPublicKey, see the bottom of the script for how I call it.

Code:
#!/usr/bin/env python2.7
# for my education, following along with bitcoins the hard way blog post:
# http://www.righto.com/2014/02/bitcoins-hard-way-using-raw-bitcoin.html
import random
import hashlib
import ecdsa
import struct

b58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'

# Input is a hex-encoded, DER-encoded signature
# Output is a 64-byte hex-encoded signature
def derSigToHexSig(s):
  s, junk = ecdsa.der.remove_sequence(s.decode('hex'))
  if junk != '':
    print 'JUNK', junk.encode('hex')
  assert(junk == '')
  x, s = ecdsa.der.remove_integer(s)
  y, s = ecdsa.der.remove_integer(s)
  return '%064x%064x' % (x, y)



# Substitutes the scriptPubKey into the transaction, appends SIGN_ALL to make
# the version of the transaction that can be signed
def getSignableTxn(parsed):
  first, sig, pub, rest = parsed
  inputAddr = base58CheckDecode(pubKeyToAddr(pub))
  return first + "1976a914" + inputAddr.encode('hex') + "88ac" + rest + "01000000"

# Returns [first, sig, pub, rest]
def parseTxn(txn):
  first = txn[0:41*2]
  scriptLen = int(txn[41*2:42*2], 16)
  script = txn[42*2:42*2+2*scriptLen]
  sigLen = int(script[0:2], 16)
  sig = script[2:2+sigLen*2]
  pubLen = int(script[2+sigLen*2:2+sigLen*2+2], 16)
  pub = script[2+sigLen*2+2:]
           
  assert(len(pub) == pubLen*2)
  rest = txn[42*2+2*scriptLen:]
  return [first, sig, pub, rest]   

# Verifies that a transaction is properly signed, assuming the generated scriptPubKey matches
# the one in the previous transaction's output
def verifyTxnSignature(txn):                   
  parsed = parseTxn(txn)     
  signableTxn = getSignableTxn(parsed)
  hashToSign = hashlib.sha256(hashlib.sha256(signableTxn.decode('hex')).digest()).digest().encode('hex')
  assert(parsed[1][-2:] == '01') # hashtype
  sig = keyUtils.derSigToHexSig(parsed[1][:-2])
  public_key = parsed[2]
  vk = ecdsa.VerifyingKey.from_string(public_key[2:].decode('hex'), curve=ecdsa.SECP256k1)
  assert(vk.verify_digest(sig.decode('hex'), hashToSign.decode('hex')))


# Verifies that a transaction is properly signed, assuming the generated scriptPubKey matches
# the one in the previous transaction's output
def verifyTxnSignature(txn):                   
    parsed = parseTxn(txn)     
    signableTxn = getSignableTxn(parsed)
    hashToSign = hashlib.sha256(hashlib.sha256(signableTxn.decode('hex')).digest()).digest().encode('hex')
    assert(parsed[1][-2:] == '01') # hashtype
    sig = derSigToHexSig(parsed[1][:-2])
    public_key = parsed[2]
    vk = ecdsa.VerifyingKey.from_string(public_key[2:].decode('hex'), curve=ecdsa.SECP256k1)
    assert(vk.verify_digest(sig.decode('hex'), hashToSign.decode('hex')))

# Takes and returns byte string value, not hex string
def varstr(s):
  return varint(len(s)) + s

# Returns byte string value, not hex string
def varint(n):
  if n < 0xfd: return struct.pack('  elif n < 0xffff: return struct.pack('  elif n < 0xffffffff: return struct.pack('  else: return struct.pack('

def base58encode(n):
  result = ''
  while n > 0:
    result = b58[n%58] + result
    n /= 58
  return result

def base58decode(s):
  result = 0
  for i in range(0, len(s)):
    result = result * 58 + b58.index(s[i])
  return result

def base256encode(n):
  result = ''
  while n > 0:
    result = chr(n % 256) + result
    n /= 256
  return result

def base256decode(s):
  result = 0
  for c in s:
    result = result * 256 + ord(c)
  return result

def countLeadingChars(s, ch):
  count = 0
  for c in s:
    if c == ch:
      count += 1
    else:
      break
  return count

# https://en.bitcoin.it/wiki/Base58Check_encoding
def base58CheckEncode(version, payload):
  s = chr(version) + payload
  checksum = hashlib.sha256(hashlib.sha256(s).digest()).digest()[0:4]
  result = s + checksum
  leadingZeros = countLeadingChars(result, '\0')
  return '1' * leadingZeros + base58encode(base256decode(result))


def base58CheckDecode(s):
  leadingOnes = countLeadingChars(s, '1')
  s = base256encode(base58decode(s))
  result = '\0' * leadingOnes + s[:-4]
  chk = s[-4:]
  checksum = hashlib.sha256(hashlib.sha256(result).digest()).digest()[0:4]
  assert(chk == checksum)
  version = result[0]
  return result[1:]

def privateKeyToWif(key_hex, compressed=False):
  if compressed:
    key_hex=key_hex+'01'
  return base58CheckEncode(0x80, key_hex.decode('hex'))

def wifToPrivateKey(s):
  b = base58CheckDecode(s)
  return b.encode('hex')

def privateKeyToPublicKey(s, compressed=False):

  sk = ecdsa.SigningKey.from_string(s.decode('hex'), curve=ecdsa.SECP256k1)
  vk = sk.verifying_key

  if compressed:
    from ecdsa.util import number_to_string
    order = vk.pubkey.order
    # print "order", order
    x_str = number_to_string(vk.pubkey.point.x(), order).encode('hex')
    # print "x_str", x_str
    sign = '02' if vk.pubkey.point.y() % 2 == 0 else '03'
    # print "sign", sign
    return (sign+x_str)
  else:
    return ('\04' + vk.to_string()).encode('hex')


def pubKeyToAddr(s,testnet=False):
  ripemd160 = hashlib.new('ripemd160')
  ripemd160.update(hashlib.sha256(s.decode('hex')).digest())
  if testnet:
    return base58CheckEncode(0x6F, ripemd160.digest())
  return base58CheckEncode(0, ripemd160.digest())

def makeRawTransaction(outputTransactionHash, sourceIndex, scriptSig, outputs):
  def makeOutput(data):
    redemptionSatoshis, outputScript = data
    return (struct.pack("           '%02x' % len(outputScript.decode('hex')) + outputScript)
  formattedOutputs = ''.join(map(makeOutput, outputs))
  return (
    "01000000" + # 4 bytes version
    "01" + # variant for number of inputs
    outputTransactionHash.decode('hex')[::-1].encode('hex') + # reverse OutputTransactionHash
    struct.pack('    '%02x' % len(scriptSig.decode('hex')) + scriptSig +
    "ffffffff" + # sequence
    "%02x" % len(outputs) + # number of outputs
    formattedOutputs +
    "00000000" # lockTime
  )

def makeSignedTransaction(privateKey, outputTransactionHash, sourceIndex, scriptPubKey, outputs, compressed=False):
  myTxn_forSig = (makeRawTransaction(outputTransactionHash, sourceIndex, scriptPubKey, outputs) + "01000000") # hash code
  s256 = hashlib.sha256(hashlib.sha256(myTxn_forSig.decode('hex')).digest()).digest()
  sk = ecdsa.SigningKey.from_string(privateKey.decode('hex'), curve=ecdsa.SECP256k1)
  sig = sk.sign_digest(s256, sigencode=ecdsa.util.sigencode_der) + '\01' # 01 is hashtype
  pubKey = privateKeyToPublicKey(privateKey,compressed)
  scriptSig = varstr(sig).encode('hex') + varstr(pubKey.decode('hex')).encode('hex')
  signed_txn = makeRawTransaction(outputTransactionHash, sourceIndex, scriptSig, outputs)
  # verifyTxnSignature(signed_txn)
  return signed_txn


def addrHashToScriptPubKey(b58str):
    assert(len(b58str) == 34)
    # 76     A9      14 (20 bytes)                               88             AC
    return '76a914' + base58CheckDecode(b58str).encode('hex') + '88ac'

# private_key = hex(i)
#for i in range(1,100):
#  private_key = hex(i)[2:].zfill(64)
#  print privateKeyToWif(private_key), "2011-09-04T00:00:01Z"

import sys
import getopt

private_key = None

try:
  opts, args = getopt.gnu_getopt(sys.argv[1:], "dtr:", ["dec","testnet","mrt"])
except getopt.GetoptError as err:
  print(err)
  sys.exit(2)

testnet=False
deckey=False
mrt=False

for o,a in opts:
  if o in ("-t", "--testnet"):
    testnet=True
  if o in ("-d","--dec"):
    deckey=True
  if o in ("-r","--mrt"):
    mrt=True


if len(args)>0:
  if deckey:
    private_key = args[0].zfill(64)
  else:
    private_key = '%064x' % int(args[0],16)
else: private_key = ''.join(['%x' % random.randrange(16) for x in range(0,64)])

print "A private key: ", private_key

print "The uncompressed WIF: ",privateKeyToWif(private_key)
print "The WIF: ",privateKeyToWif(private_key, compressed=True)

public_key = privateKeyToPublicKey(private_key)
cpublic_key = privateKeyToPublicKey(private_key,compressed=True)
print "The uncompressed bitcoin pubkey: ", public_key
print "The bitcoin pubkey: ", cpublic_key
print "The uncompressed bitcoin address: ", pubKeyToAddr(public_key,testnet=testnet)
print "The bitcoin address: ", pubKeyToAddr(cpublic_key,testnet=testnet)
legendary
Activity: 1022
Merit: 1008
Delusional crypto obsessionist
Excellent.
I already found out the hashlib myself Smiley but you greatly helped me with the rest.
I tested it with a Base6 number, converted to 256hex through 'bc' and then your script.
The result was the same when I used the strings at bitaddress.org.

I'll donate you and dserrano5 some symbolic satoshis, thanks again.

Glad you got it working!  I find that -i (to drop you into the interpreter after execution) particularly helpful when debugging python programs.

Satoshis always apprecated (profile address works fine), symbolic or otherwise Smiley

Do you also have a routine to create a bitcoin public address with python?

edit: I think this is what I was looking for

Too bad, way too much source code.
I'm still interested in a routine for public key generation
legendary
Activity: 1456
Merit: 1081
I may write code in exchange for bitcoins.
Excellent.
I already found out the hashlib myself Smiley but you greatly helped me with the rest.
I tested it with a Base6 number, converted to 256hex through 'bc' and then your script.
The result was the same when I used the strings at bitaddress.org.

I'll donate you and dserrano5 some symbolic satoshis, thanks again.

Glad you got it working!  I find that -i (to drop you into the interpreter after execution) particularly helpful when debugging python programs.

Satoshis always apprecated (profile address works fine), symbolic or otherwise Smiley
legendary
Activity: 1022
Merit: 1008
Delusional crypto obsessionist
Let me know if you're still stuck.


Excellent.
I already found out the hashlib myself Smiley but you greatly helped me with the rest.
I tested it with a Base6 number, converted to 256hex through 'bc' and then your script.
The result was the same when I used the strings at bitaddress.org.

I'll donate you and dserrano5 some symbolic satoshis, thanks again.
legendary
Activity: 1456
Merit: 1081
I may write code in exchange for bitcoins.
findftp, if I recall corrctly, to import a python script as a module you need to have it in a directory along with a file calld __init__.py (which is sometimes empty but can include init routines for classes).  To run the code snippet I sent you, you could do one of two things pretty easily:

1) open a python intepreter and just copy/paste in the function defs
2) run the python executable with the -i flag

But, actually, I went ahead and tried it and realized there were two missing methods (countLeadingChars, base256decode) and a missing import (hashlib) in the snippet I gave you.  Here's the entire snippet again with the missing methods included and a shebang line for good measure.

Code:
#!/usr/bin/env python

import hashlib
b58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'

def countLeadingChars(s, ch):
  count = 0
  for c in s:
    if c == ch:
      count += 1
    else:
      break
  return count

def base256decode(s):
  result = 0
  for c in s:
    result = result * 256 + ord(c)
  return result

def base58encode(n):
  result = ''
  while n > 0:
    result = b58[n%58] + result
    n /= 58
  return result

# https://en.bitcoin.it/wiki/Base58Check_encoding
def base58CheckEncode(version, payload):
  s = chr(version) + payload
  checksum = hashlib.sha256(hashlib.sha256(s).digest()).digest()[0:4]
  result = s + checksum
  leadingZeros = countLeadingChars(result, '\0')
  return '1' * leadingZeros + base58encode(base256decode(result))

def privateKeyToWif(key_hex, compressed=False):
  if compressed:
    key_hex=key_hex+'01'
  return base58CheckEncode(0x80, key_hex.decode('hex'))

^^That should work for defining methods, if, say, you put that into a file called bitcoin.py you should now be able to do this to load those definitions and play at the intepreter:

Code:
tsp@computer:/tmp$ python -i bitcoin.py
>>> k="1111111111111111111111111111111111111111111111111111111111111111"
>>> privateKeyToWif(k)
'5HwoXVkHoRM8sL2KmNRS217n1g8mPPBomrY7yehCuXC1115WWsh'
>>> privateKeyToWif(k,compressed=True)
'KwntMbt59tTsj8xqpqYqRRWufyjGunvhSyeMo3NTYpFYzZbXJ5Hp'
>>>

Let me know if you're still stuck.
legendary
Activity: 1064
Merit: 1000
Throwing a dice is easy, but converting it into a WIF private key isn't.
[…]

Is there anyone who can give me some other suggestions?

You're on a real operating system, right? If so, you can use bc, the calculator.

Code:
$ bc
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
obase=16
ibase=6
10
6
11
7
12
8
13
9
14
A
15
B
16
B
36212631625312563
9FEE7990BF5

My paper wallets have been created from dice rolls. Chinese ones, so I'm free from any tinkering from the NSA Tongue.

I like the bc method best, it reeks of simplicity. Thanks for the share. Smiley
legendary
Activity: 1022
Merit: 1008
Delusional crypto obsessionist

I assume you can figure out how to convert your number into 256bit hex? 

Yes, I could do it manually but dserrano5 above pointed me to function 'bc' which I can use on my linux machine.

Quote
If so, the following works fine for me:
Code:
b58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'

def base58encode(n):
  result = ''
  while n > 0:
    result = b58[n%58] + result
    n /= 58
  return result

# https://en.bitcoin.it/wiki/Base58Check_encoding
def base58CheckEncode(version, payload):
  s = chr(version) + payload
  checksum = hashlib.sha256(hashlib.sha256(s).digest()).digest()[0:4]
  result = s + checksum
  leadingZeros = countLeadingChars(result, '\0')
  return '1' * leadingZeros + base58encode(base256decode(result))

def privateKeyToWif(key_hex, compressed=False):
  if compressed:
    key_hex=key_hex+'01'
  return base58CheckEncode(0x80, key_hex.decode('hex'))


^^^ That's python, but you could use another language if you want.  In the above, key_hex is a string.  Here's an example of me using it.:

So I've put that code into a file called 'bitoin.py'
Then I entered python and figured I had to import that code as a module(?)
I did this

Code:
import bitcoin
No problem so far.

Quote
Code:
>>> k="1111111111111111111111111111111111111111111111111111111111111111"
>>> privateKeyToWif(k)
'5HwoXVkHoRM8sL2KmNRS217n1g8mPPBomrY7yehCuXC1115WWsh'
>>> privateKeyToWif(k, compressed=True)
'KwntMbt59tTsj8xqpqYqRRWufyjGunvhSyeMo3NTYpFYzZbXJ5Hp'

This is where I'm stuck.
I can enter the 'k', but then I get this:

Code:
>>> privateKeyToWif(k)
Traceback (most recent call last):
  File "", line 1, in
NameError: name 'privateKeyToWif' is not defined

What am I doing wrong?
I don't have any experience with python. (too many languages to know them all)
legendary
Activity: 1022
Merit: 1008
Delusional crypto obsessionist
Throwing a dice is easy, but converting it into a WIF private key isn't.
[…]

Is there anyone who can give me some other suggestions?

You're on a real operating system, right? If so, you can use bc, the calculator.

Well, I hope linux mint 17.2 is real enough Smiley

Quote
Code:
$ bc
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
obase=16
ibase=6
10
6
11
7
12
8
13
9
14
A
15
B
16
B
36212631625312563
9FEE7990BF5

My paper wallets have been created from dice rolls. Chinese ones, so I'm free from any tinkering from the NSA Tongue.

Great! This looks even more promising to me.
Thanks.
legendary
Activity: 1022
Merit: 1008
Delusional crypto obsessionist
So I'm planning to throw 99 times with a dice and punch the number into a metal strip or engrave it into marble to produce some bullet proof single address wallet.
  Wow, seriously?  Marble engraving?

I should expand on this.
I own a metal detector and for a hobby I go to public or private places to scan the soil for historic metallic artifacts.
Very often I find metal discs which people used as some sort of value token (coins Wink )

I would like to bury this marble engraved bitcoin address in a public place (a park, a forest, a field) and make sure it's not detectable with a metal detector.
If I use metal I'm not so sure about my hobby collegues not finding it.
I think it would be pretty safe if only I know where it is burried (and some trusties).
It's not like I want to put all my coins on that address, just a few.
I like the idea that whatever happens to me or my trusties the address is still accesible.
I like the idea that it is possible for someone (a kid digging a hole) to find this thing in let's say 200 years and know what it is and what to do with it.

Quote
Quote
Before I go such lengths I want to make sure this approach works. So I want to test each step and even fill this address with a few satoshis and spend them as well before I fill it with more serious amounts.

Throwing a dice is easy, but converting it into a WIF private key isn't.
The only way I found is through www.bitaddress.org, but I am not able to review the source and I don't want to rely at one source only.

Is there anyone who can give me some other suggestions?

I assume you can figure out how to convert your number into 256bit hex?  If so, the following works fine for me:

Code:
b58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'

def base58encode(n):
  result = ''
  while n > 0:
    result = b58[n%58] + result
    n /= 58
  return result

# https://en.bitcoin.it/wiki/Base58Check_encoding
def base58CheckEncode(version, payload):
  s = chr(version) + payload
  checksum = hashlib.sha256(hashlib.sha256(s).digest()).digest()[0:4]
  result = s + checksum
  leadingZeros = countLeadingChars(result, '\0')
  return '1' * leadingZeros + base58encode(base256decode(result))

def privateKeyToWif(key_hex, compressed=False):
  if compressed:
    key_hex=key_hex+'01'
  return base58CheckEncode(0x80, key_hex.decode('hex'))


^^^ That's python, but you could use another language if you want.  In the above, key_hex is a string.  Here's an example of me using it.:

Code:
>>> k="1111111111111111111111111111111111111111111111111111111111111111"
>>> privateKeyToWif(k)
'5HwoXVkHoRM8sL2KmNRS217n1g8mPPBomrY7yehCuXC1115WWsh'
>>> privateKeyToWif(k, compressed=True)
'KwntMbt59tTsj8xqpqYqRRWufyjGunvhSyeMo3NTYpFYzZbXJ5Hp'



Thanks for the code, I go and play with it.
legendary
Activity: 1974
Merit: 1030
Throwing a dice is easy, but converting it into a WIF private key isn't.
[…]

Is there anyone who can give me some other suggestions?

You're on a real operating system, right? If so, you can use bc, the calculator.

Code:
$ bc
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
obase=16
ibase=6
10
6
11
7
12
8
13
9
14
A
15
B
16
B
36212631625312563
9FEE7990BF5

My paper wallets have been created from dice rolls. Chinese ones, so I'm free from any tinkering from the NSA Tongue.
legendary
Activity: 1456
Merit: 1081
I may write code in exchange for bitcoins.
So I'm planning to throw 99 times with a dice and punch the number into a metal strip or engrave it into marble to produce some bullet proof single address wallet.
  Wow, seriously?  Marble engraving?

Quote
Before I go such lengths I want to make sure this approach works. So I want to test each step and even fill this address with a few satoshis and spend them as well before I fill it with more serious amounts.

Throwing a dice is easy, but converting it into a WIF private key isn't.
The only way I found is through www.bitaddress.org, but I am not able to review the source and I don't want to rely at one source only.

Is there anyone who can give me some other suggestions?

I assume you can figure out how to convert your number into 256bit hex?  If so, the following works fine for me:

Code:
b58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'

def base58encode(n):
  result = ''
  while n > 0:
    result = b58[n%58] + result
    n /= 58
  return result

# https://en.bitcoin.it/wiki/Base58Check_encoding
def base58CheckEncode(version, payload):
  s = chr(version) + payload
  checksum = hashlib.sha256(hashlib.sha256(s).digest()).digest()[0:4]
  result = s + checksum
  leadingZeros = countLeadingChars(result, '\0')
  return '1' * leadingZeros + base58encode(base256decode(result))

def privateKeyToWif(key_hex, compressed=False):
  if compressed:
    key_hex=key_hex+'01'
  return base58CheckEncode(0x80, key_hex.decode('hex'))


^^^ That's python, but you could use another language if you want.  In the above, key_hex is a string.  Here's an example of me using it.:

Code:
>>> k="1111111111111111111111111111111111111111111111111111111111111111"
>>> privateKeyToWif(k)
'5HwoXVkHoRM8sL2KmNRS217n1g8mPPBomrY7yehCuXC1115WWsh'
>>> privateKeyToWif(k, compressed=True)
'KwntMbt59tTsj8xqpqYqRRWufyjGunvhSyeMo3NTYpFYzZbXJ5Hp'

sr. member
Activity: 392
Merit: 268
Tips welcomed: 1CF4GhXX1RhCaGzWztgE1YZZUcSpoqTbsJ
While I have not validated the source of bitaddress myself, it has been validated by others. You can also just generate a private exponent from dice rolls and encode it into Base58Check using an appropriate library and programming language. You'd only need to audit a single function, namely that for Base58Check encoding.
legendary
Activity: 1022
Merit: 1008
Delusional crypto obsessionist
So I'm planning to throw 99 times with a dice and punch the number into a metal strip or engrave it into marble to produce some bullet proof single address wallet.

Before I go such lengths I want to make sure this approach works. So I want to test each step and even fill this address with a few satoshis and spend them as well before I fill it with more serious amounts.

Throwing a dice is easy, but converting it into a WIF private key isn't.
The only way I found is through www.bitaddress.org, but I am not able to review the source and I don't want to rely at one source only.

Is there anyone who can give me some other suggestions?
Jump to: