Skip to content

Decode minikeys (like Casascius coin ones) #141

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ library. Non-consensus critical modules include the following:
bitcoin.bloom - Bloom filters (incomplete)
bitcoin.net - Network communication (in flux)
bitcoin.messages - Network messages (in flux)
bitcoin.minikey - Minikey decoding
bitcoin.rpc - Bitcoin Core RPC interface support
bitcoin.wallet - Wallet-related code, currently Bitcoin address and
private key support
Expand Down
50 changes: 50 additions & 0 deletions bitcoin/minikey.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Copyright (C) 2013-2014 The python-bitcoinlib developers
#
# This file is part of python-bitcoinlib.
#
# It is subject to the license terms in the LICENSE file found in the top-level
# directory of this distribution.
#
# No part of python-bitcoinlib, including this file, may be copied, modified,
# propagated, or distributed except according to the terms contained in the
# LICENSE file.

"""
Minikey Handling

Minikeys are an old key format, for details see
https://en.bitcoin.it/wiki/Mini_private_key_format.
"""

from __future__ import absolute_import, division, print_function, unicode_literals

import sys
_bord = (lambda x: x) if sys.version > '3' else ord

from hashlib import sha256

from bitcoin.wallet import CBitcoinSecret

class InvalidMinikeyError(Exception):
"""Raised for invalid minikeys"""
pass

def decode_minikey(minikey):
"""Decode minikey from str or bytes to a CBitcoinSecret"""
if isinstance(minikey, str):
minikey = minikey.encode('ascii')
length = len(minikey)
if length not in [22, 30]:
raise InvalidMinikeyError('Minikey length %d is not 22 or 30' % length)
h0 = sha256(minikey)
h1 = h0.copy()
h1.update(b'?')
checksum = _bord(h1.digest()[0])
if checksum != 0:
raise InvalidMinikeyError('Minikey checksum %s is not 0' % checksum)
return CBitcoinSecret.from_secret_bytes(h0.digest(), False)

__all__ = (
'InvalidMinikeyError',
'decode_minikey'
)
50 changes: 50 additions & 0 deletions bitcoin/tests/test_minikey.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Copyright (C) 2013-2014 The python-bitcoinlib developers
#
# This file is part of python-bitcoinlib.
#
# It is subject to the license terms in the LICENSE file found in the top-level
# directory of this distribution.
#
# No part of python-bitcoinlib, including this file, may be copied, modified,
# propagated, or distributed except according to the terms contained in the
# LICENSE file.

from __future__ import absolute_import, division, print_function, unicode_literals

import unittest

from bitcoin.minikey import *
from bitcoin.wallet import CBitcoinSecret

class Test_minikey(unittest.TestCase):

valid_minikeys = [
('S6c56bnXQiBjk9mqSYE7ykVQ7NzrRy', '5JPy8Zg7z4P7RSLsiqcqyeAF1935zjNUdMxcDeVrtU1oarrgnB7'),
('SVY4eSFCF4tMtMohEkpXkoN9FHxDV7', '5JSyovgwfVcuFZBAp8LAta2tMsmscxXv3FvzvJWeKBfycLAmjuZ'),
('S6c56bnXQiBjk9mqSYEa30', '5KM4V1haDBMEcgzPuAWdHSBAVAEJNp4he2meirV3JNvZz9aWBNH')
]
invalid_minikeys = [
('', 'Minikey length 0 is not 22 or 30'),
('S6c56bnXQiBjk9mqSYE7ykVQ7NzrR', 'Minikey length 29 is not 22 or 30'),
('S6c56bnXQiBjk9mqSYE7ykVQ7NzrRyz', 'Minikey length 31 is not 22 or 30'),
('S6c56bnXQiBjk9mqSYE7ykVQ7NzrRz', 'Minikey checksum 213 is not 0'),
('S6c56bnXQiBjk9mqSYE7yk', 'Minikey checksum 46 is not 0')
]

def test_decode_minikey_bytes(self):
for minikey, exp_base58_key in self.valid_minikeys:
secret_key = decode_minikey(minikey.encode('ascii'))
self.assertIsInstance(secret_key, CBitcoinSecret)
self.assertEqual(str(secret_key), exp_base58_key)

def test_decode_minikey_str(self):
for minikey, exp_base58_key in self.valid_minikeys:
secret_key = decode_minikey(minikey)
self.assertIsInstance(secret_key, CBitcoinSecret)
self.assertEqual(str(secret_key), exp_base58_key)

def test_invalid(self):
for minikey, msg in self.invalid_minikeys:
with self.assertRaises(InvalidMinikeyError) as cm:
decode_minikey(minikey)
self.assertEqual(str(cm.exception), msg)
35 changes: 35 additions & 0 deletions examples/minikey.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env python3
#
# Copyright (C) 2013-2015 The python-bitcoinlib developers
#
# This file is part of python-bitcoinlib.
#
# It is subject to the license terms in the LICENSE file found in the top-level
# directory of this distribution.
#
# No part of python-bitcoinlib, including this file, may be copied, modified,
# propagated, or distributed except according to the terms contained in the
# LICENSE file.

from __future__ import absolute_import, division, print_function, unicode_literals

from bitcoin.minikey import decode_minikey

def parser():
import argparse
parser = argparse.ArgumentParser(
description='Decode a minikey to base58 format.',
epilog='Security warning: arguments may be visible to other users on the same host.')
parser.add_argument(
'minikey',
help='the minikey')
return parser

if __name__ == '__main__':
args = parser().parse_args()
try:
secret_key = str(decode_minikey(args.minikey))
print(secret_key)
except Exception as error:
print('%s: %s' % (error.__class__.__name__, str(error)))
exit(1)