Skip to content

Commit e661637

Browse files
committed
get rid of a bunch of unnecessary complexity around secret key generation, it's just too much and doesn't add anything. deleting code and simplifying
1 parent 80f72e7 commit e661637

File tree

5 files changed

+21
-88
lines changed

5 files changed

+21
-88
lines changed

README.md

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ $ python -m cryptos.sha256 testfile.txt
1717

1818
### Keys
1919

20-
`getnewaddress.py` is a cli entryway to the code that generates a new Bitcoin secret/public key pair and the corresponding (base58 compressed) address:
20+
`getnewaddress.py` is a cli entryway to generate a new Bitcoin secret/public key pair and the corresponding (base58check compressed) address:
2121

2222
```bash
2323
$ python getnewaddress.py
@@ -30,21 +30,6 @@ compressed bitcoin address (b58check format):
3030
1DBGfUXnwTS2PRu8h3JefU9uYwYnyaTd2z
3131
```
3232

33-
You can also generate your own entropy from keyboard timings if you call the cli as `$ python getnewaddress.py user`, or you can verify that the implementation is not broken by reproducing the [Mastering Bitcoin Chapter 4](https://github.com/bitcoinbook/bitcoinbook/blob/develop/ch04.asciidoc) example:
34-
35-
```bash
36-
$ python getnewaddress.py mastering
37-
generated secret key:
38-
0x3aba4162c7251c891207b747840551a71939b0de081f85c4e44cf7c13e41daa6
39-
corresponding public key:
40-
x: 5C0DE3B9C8AB18DD04E3511243EC2952002DBFADC864B9628910169D9B9B00EC
41-
y: 243BCEFDD4347074D44BD7356D6A53C495737DD96295E2A9374BF5F02EBFC176
42-
compressed bitcoin address (b58check format):
43-
14cxpo3MBCYYWCgF74SWTdcmxipnGUsPw3
44-
```
45-
46-
Where we see that after the all crazy hashing and elliptic curve over finite fields gymnastics the bitcoin address `14cxpo3MBCYYWCgF74SWTdcmxipnGUsPw3` matches, phew :).
47-
4833
### Digital Signatures
4934

5035
Elliptic Curve Digital Signature Algorithm (ECDSA) implemented in `cryptos/ecdsa.py`, example usage:

cryptos/ecdsa.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ def sign(secret_key: int, message: bytes) -> Signature:
7777
# generate a new secret/public key pair at random
7878
# TODO: make deterministic
7979
# TODO: make take constant time to mitigate timing attacks
80-
k = gen_secret_key(n, 'os')
80+
k = gen_secret_key(n)
8181
P = PublicKey.from_sk(k)
8282

8383
# calculate the signature

cryptos/keys.py

Lines changed: 3 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -7,69 +7,21 @@
77
import os
88
import time
99

10-
from .sha256 import sha256
1110
from .curves import Point
1211
from .bitcoin import BITCOIN
1312

1413
# -----------------------------------------------------------------------------
15-
# Secret key generation utilities
14+
# Secret key generation. We're going to leave secret key as just a super plain int
1615

17-
def random_bytes_os():
18-
"""
19-
Use os provided entropy, e.g. on macs sourced from /dev/urandom, eg available as:
20-
$ head -c 32 /dev/urandom
21-
22-
According to Apple Platform Security docs
23-
https://support.apple.com/en-ie/guide/security/seca0c73a75b/web
24-
the kernel CPRNG is a Fortuna-derived design targeting a 256-bit security level
25-
where the entropy is sourced from:
26-
- The Secure Enclave’s hardware RNG
27-
- Timing-based jitter collected during boot
28-
- Entropy collected from hardware interrupts
29-
- A seed file used to persist entropy across boots
30-
- Intel random instructions, i.e. RDSEED and RDRAND (macOS only)
31-
"""
32-
return os.urandom(32)
33-
34-
35-
def random_bytes_user():
36-
"""
37-
Collect some entropy from time and user and generate a key with SHA-256
38-
"""
39-
entropy = ''
40-
for i in range(5):
41-
s = input("Enter some word #%d/5: " % (i+1,))
42-
entropy += s + '|' + str(int(time.time() * 1000000)) + '|'
43-
return sha256(entropy.encode('ascii'))
44-
45-
46-
def mastering_bitcoin_bytes():
47-
"""
48-
The example from Mastering Bitcoin, Chapter 4
49-
https://github.com/bitcoinbook/bitcoinbook/blob/develop/ch04.asciidoc
50-
"""
51-
sk = '3aba4162c7251c891207b747840551a71939b0de081f85c4e44cf7c13e41daa6'
52-
return bytes.fromhex(sk)
53-
54-
55-
def gen_secret_key(n: int, source: str = 'os') -> int:
16+
def gen_secret_key(n: int) -> int:
5617
"""
5718
n is the upper bound on the key, typically the order of the elliptic curve
5819
we are using. The function will return a valid key, i.e. 1 <= key < n.
5920
"""
60-
61-
assert source in ['os', 'user', 'mastering'], "The source must be one of 'os' or 'user' or 'mastering'"
62-
bytes_fn = {
63-
'os': random_bytes_os,
64-
'user': random_bytes_user,
65-
'mastering': mastering_bitcoin_bytes,
66-
}[source]
67-
6821
while True:
69-
key = int.from_bytes(bytes_fn(), 'big')
22+
key = int.from_bytes(os.urandom(32), 'big')
7023
if 1 <= key < n:
7124
break # the key is valid, break out
72-
7325
return key
7426

7527
# -----------------------------------------------------------------------------
@@ -125,12 +77,3 @@ def encode(self, compressed=True):
12577
return prefix + self.x.to_bytes(32, 'big')
12678
else:
12779
return b'\x04' + self.x.to_bytes(32, 'big') + self.y.to_bytes(32, 'big')
128-
129-
# -----------------------------------------------------------------------------
130-
# convenience functions
131-
132-
def gen_key_pair(source: str = 'os'):
133-
""" convenience function to quickly generate a secret/public keypair """
134-
sk = gen_secret_key(BITCOIN.gen.n, source)
135-
pk = PublicKey.from_sk(sk)
136-
return sk, pk

getnewaddress.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,22 @@
66
- the bitcoin address
77
"""
88

9-
import sys
10-
11-
from cryptos.keys import gen_key_pair
9+
from cryptos.keys import gen_secret_key, PublicKey
1210
from cryptos.btc_address import pk_to_address
11+
from cryptos.bitcoin import BITCOIN
1312

1413
if __name__ == '__main__':
1514

1615
# generate a secret/public key pair
17-
source = sys.argv[1] if len(sys.argv) == 2 else 'os' # can also be 'user' | 'mastering'
18-
secret_key, public_key = gen_key_pair(source) # represented as int
16+
secret_key = gen_secret_key(BITCOIN.gen.n)
17+
public_key = PublicKey.from_sk(secret_key)
1918
print('generated secret key:')
2019
print(hex(secret_key))
2120
print('corresponding public key:')
22-
print('x:', format(public_key.x, '064x').upper()) # (strip the 0x part denoting hex number)
21+
print('x:', format(public_key.x, '064x').upper())
2322
print('y:', format(public_key.y, '064x').upper())
2423

25-
# calculate the bitcoin address
26-
addr = pk_to_address(public_key, net='main', compressed=True) # is a string in b58check format
24+
# get the associated bitcoin address
25+
addr = pk_to_address(public_key, net='main', compressed=True)
2726
print('compressed bitcoin address (b58check format):')
2827
print(addr)

tests/test_ecdsa.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,21 @@
55
import os
66
from io import BytesIO
77

8-
from cryptos.keys import gen_key_pair
8+
from cryptos.bitcoin import BITCOIN
9+
from cryptos.keys import gen_secret_key, PublicKey
910
from cryptos.ecdsa import Signature, sign, verify
1011
from cryptos.transaction import Tx
1112

1213
def test_ecdsa():
1314

15+
def gen_key_pair():
16+
sk = gen_secret_key(BITCOIN.gen.n)
17+
pk = PublicKey.from_sk(sk)
18+
return sk, pk
19+
1420
# let's create two identities
15-
sk1, pk1 = gen_key_pair('os')
16-
sk2, pk2 = gen_key_pair('os')
21+
sk1, pk1 = gen_key_pair()
22+
sk2, pk2 = gen_key_pair()
1723

1824
message = ('user pk1 would like to pay user pk2 1 BTC kkthx').encode('ascii')
1925

0 commit comments

Comments
 (0)