Fix sslcrypto thread safety (#2454)

* Use sslcrypto instead of pyelliptic and pybitcointools

* Fix CryptMessage

* Support Python 3.4

* Fix user creation

* Get rid of pyelliptic and pybitcointools

* Fix typo

* Delete test file

* Add sslcrypto to tree

* Update sslcrypto

* Add pyaes to src/lib

* Fix typo in tests

* Update sslcrypto version

* Use privatekey_bin instead of privatekey for bytes objects

* Fix sslcrypto

* Fix Benchmark plugin

* Don't calculate the same thing twice

* Only import sslcrypto once

* Handle fallback sslcrypto implementation during tests

* Fix sslcrypto fallback implementation selection

* Fix thread safety

* Add derivation

* Bring split back

* Fix typo

* v3.3

* Fix custom OpenSSL discovery
This commit is contained in:
ZeroNet 2020-03-05 17:54:46 +01:00 committed by GitHub
parent 7ba2c9344d
commit 296e4aab57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 3781 additions and 7306 deletions

View File

@ -21,7 +21,7 @@ stages:
- python -m pytest -x plugins/Multiuser/Test --color=yes
- mv plugins/disabled-Bootstrapper plugins/Bootstrapper
- python -m pytest -x plugins/Bootstrapper/Test --color=yes
- flake8 . --count --select=E9,F63,F72,F82 --show-source --statistics --exclude=src/lib/pybitcointools/
- flake8 . --count --select=E9,F63,F72,F82 --show-source --statistics --exclude=src/lib/pyaes/
test:py3.4:
image: python:3.4.3

View File

@ -33,10 +33,13 @@ script:
- export ZERONET_LOG_DIR="log/Bootstrapper"; mv plugins/disabled-Bootstrapper plugins/Bootstrapper && catchsegv python -m pytest -x plugins/Bootstrapper/Test
- find src -name "*.json" | xargs -n 1 python3 -c "import json, sys; print(sys.argv[1], end=' '); json.load(open(sys.argv[1])); print('[OK]')"
- find plugins -name "*.json" | xargs -n 1 python3 -c "import json, sys; print(sys.argv[1], end=' '); json.load(open(sys.argv[1])); print('[OK]')"
- flake8 . --count --select=E9,F63,F72,F82 --show-source --statistics --exclude=src/lib/pybitcointools/
- flake8 . --count --select=E9,F63,F72,F82 --show-source --statistics --exclude=src/lib/pyaes/
after_failure:
- zip -r log.zip log/
- curl --upload-file ./log.zip https://transfer.sh/log.zip
after_success:
- codecov
- coveralls --rcfile=src/Test/coverage.ini
notifications:
email:
recipients:

View File

@ -104,8 +104,8 @@ class ActionsPlugin:
tests.extend([
{"func": self.testHdPrivatekey, "num": 50, "time_standard": 0.57},
{"func": self.testSign, "num": 20, "time_standard": 0.46},
{"func": self.testVerify, "kwargs": {"lib_verify": "btctools"}, "num": 20, "time_standard": 0.38},
{"func": self.testVerify, "kwargs": {"lib_verify": "openssl"}, "num": 200, "time_standard": 0.30},
{"func": self.testVerify, "kwargs": {"lib_verify": "sslcrypto_fallback"}, "num": 20, "time_standard": 0.38},
{"func": self.testVerify, "kwargs": {"lib_verify": "sslcrypto"}, "num": 200, "time_standard": 0.30},
{"func": self.testVerify, "kwargs": {"lib_verify": "libsecp256k1"}, "num": 200, "time_standard": 0.10},
{"func": self.testPackMsgpack, "num": 100, "time_standard": 0.35},

View File

@ -1,28 +1,22 @@
import hashlib
import base64
import struct
import lib.pybitcointools as btctools
from lib import sslcrypto
from Crypt import Crypt
ecc_cache = {}
curve = sslcrypto.ecc.get_curve("secp256k1")
def eciesEncrypt(data, pubkey, ephemcurve=None, ciphername='aes-256-cbc'):
from lib import pyelliptic
pubkey_openssl = toOpensslPublickey(base64.b64decode(pubkey))
curve, pubkey_x, pubkey_y, i = pyelliptic.ECC._decode_pubkey(pubkey_openssl)
if ephemcurve is None:
ephemcurve = curve
ephem = pyelliptic.ECC(curve=ephemcurve)
key = hashlib.sha512(ephem.raw_get_ecdh_key(pubkey_x, pubkey_y)).digest()
key_e, key_m = key[:32], key[32:]
pubkey = ephem.get_pubkey()
iv = pyelliptic.OpenSSL.rand(pyelliptic.OpenSSL.get_cipher(ciphername).get_blocksize())
ctx = pyelliptic.Cipher(key_e, iv, 1, ciphername)
ciphertext = iv + pubkey + ctx.ciphering(data)
mac = pyelliptic.hmac_sha256(key_m, ciphertext)
return key_e, ciphertext + mac
def eciesEncrypt(data, pubkey, ciphername="aes-256-cbc"):
ciphertext, key_e = curve.encrypt(
data,
base64.b64decode(pubkey),
algo=ciphername,
derivation="sha512",
return_aes_key=True
)
return key_e, ciphertext
@Crypt.thread_pool_crypt.wrap
@ -37,9 +31,8 @@ def eciesDecryptMulti(encrypted_datas, privatekey):
return texts
def eciesDecrypt(encrypted_data, privatekey):
ecc_key = getEcc(privatekey)
return ecc_key.decrypt(base64.b64decode(encrypted_data))
def eciesDecrypt(ciphertext, privatekey):
return curve.decrypt(base64.b64decode(ciphertext), curve.wif_to_private(privatekey), derivation="sha512")
def decodePubkey(pubkey):
@ -63,29 +56,3 @@ def split(encrypted):
ciphertext = encrypted[16 + i:-32]
return iv, ciphertext
def getEcc(privatekey=None):
from lib import pyelliptic
global ecc_cache
if privatekey not in ecc_cache:
if privatekey:
publickey_bin = btctools.encode_pubkey(btctools.privtopub(privatekey), "bin")
publickey_openssl = toOpensslPublickey(publickey_bin)
privatekey_openssl = toOpensslPrivatekey(privatekey)
ecc_cache[privatekey] = pyelliptic.ECC(curve='secp256k1', privkey=privatekey_openssl, pubkey=publickey_openssl)
else:
ecc_cache[None] = pyelliptic.ECC()
return ecc_cache[privatekey]
def toOpensslPrivatekey(privatekey):
privatekey_bin = btctools.encode_privkey(privatekey, "bin")
return b'\x02\xca\x00\x20' + privatekey_bin
def toOpensslPublickey(publickey):
publickey_bin = btctools.encode_pubkey(publickey, "bin")
publickey_bin = publickey_bin[1:]
publickey_openssl = b'\x02\xca\x00 ' + publickey_bin[:32] + b'\x00 ' + publickey_bin[32:]
return publickey_openssl

View File

@ -5,24 +5,22 @@ import gevent
from Plugin import PluginManager
from Crypt import CryptBitcoin, CryptHash
import lib.pybitcointools as btctools
from Config import config
import sslcrypto
from . import CryptMessage
curve = sslcrypto.ecc.get_curve("secp256k1")
@PluginManager.registerTo("UiWebsocket")
class UiWebsocketPlugin(object):
def eciesDecrypt(self, encrypted, privatekey):
back = CryptMessage.getEcc(privatekey).decrypt(encrypted)
return back.decode("utf8")
# - Actions -
# Returns user's public key unique to site
# Return: Public key
def actionUserPublickey(self, to, index=0):
publickey = self.user.getEncryptPublickey(self.site.address, index)
self.response(to, publickey)
self.response(to, self.user.getEncryptPublickey(self.site.address, index))
# Encrypt a text using the publickey or user's sites unique publickey
# Return: Encrypted text using base64 encoding
@ -55,23 +53,16 @@ class UiWebsocketPlugin(object):
# Encrypt a text using AES
# Return: Iv, AES key, Encrypted text
def actionAesEncrypt(self, to, text, key=None, iv=None):
from lib import pyelliptic
def actionAesEncrypt(self, to, text, key=None):
if key:
key = base64.b64decode(key)
else:
key = os.urandom(32)
if iv: # Generate new AES key if not definied
iv = base64.b64decode(iv)
else:
iv = pyelliptic.Cipher.gen_IV('aes-256-cbc')
key = sslcrypto.aes.new_key()
if text:
encrypted = pyelliptic.Cipher(key, iv, 1, ciphername='aes-256-cbc').ciphering(text.encode("utf8"))
encrypted, iv = sslcrypto.aes.encrypt(text.encode("utf8"), key)
else:
encrypted = b""
encrypted, iv = b"", b""
res = [base64.b64encode(item).decode("utf8") for item in [key, iv, encrypted]]
self.response(to, res)
@ -79,8 +70,6 @@ class UiWebsocketPlugin(object):
# Decrypt a text using AES
# Return: Decrypted text
def actionAesDecrypt(self, to, *args):
from lib import pyelliptic
if len(args) == 3: # Single decrypt
encrypted_texts = [(args[0], args[1])]
keys = [args[2]]
@ -93,9 +82,8 @@ class UiWebsocketPlugin(object):
iv = base64.b64decode(iv)
text = None
for key in keys:
ctx = pyelliptic.Cipher(base64.b64decode(key), iv, 0, ciphername='aes-256-cbc')
try:
decrypted = ctx.ciphering(encrypted_text)
decrypted = sslcrypto.aes.decrypt(encrypted_text, iv, base64.b64decode(key))
if decrypted and decrypted.decode("utf8"): # Valid text decoded
text = decrypted.decode("utf8")
except Exception as err:
@ -122,12 +110,11 @@ class UiWebsocketPlugin(object):
# Gets the publickey of a given privatekey
def actionEccPrivToPub(self, to, privatekey):
self.response(to, btctools.privtopub(privatekey))
self.response(to, curve.private_to_public(curve.wif_to_private(privatekey)))
# Gets the address of a given publickey
def actionEccPubToAddr(self, to, publickey):
address = btctools.pubtoaddr(btctools.decode_pubkey(publickey))
self.response(to, address)
self.response(to, curve.public_to_address(bytes.fromhex(publickey)))
@PluginManager.registerTo("User")
@ -163,7 +150,7 @@ class UserPlugin(object):
if "encrypt_publickey_%s" % index not in site_data:
privatekey = self.getEncryptPrivatekey(address, param_index)
publickey = btctools.encode_pubkey(btctools.privtopub(privatekey), "bin_compressed")
publickey = curve.private_to_public(curve.wif_to_private(privatekey))
site_data["encrypt_publickey_%s" % index] = base64.b64encode(publickey).decode("utf8")
return site_data["encrypt_publickey_%s" % index]
@ -200,8 +187,8 @@ class ActionsPlugin:
aes_key, encrypted = CryptMessage.eciesEncrypt(self.utf8_text.encode("utf8"), self.publickey)
for i in range(num_run):
assert len(aes_key) == 32
ecc = CryptMessage.getEcc(self.privatekey)
assert ecc.decrypt(encrypted) == self.utf8_text.encode("utf8"), "%s != %s" % (ecc.decrypt(encrypted), self.utf8_text.encode("utf8"))
decrypted = CryptMessage.eciesDecrypt(base64.b64encode(encrypted), self.privatekey)
assert decrypted == self.utf8_text.encode("utf8"), "%s != %s" % (decrypted, self.utf8_text.encode("utf8"))
yield "."
def testCryptEciesDecryptMulti(self, num_run=1):
@ -223,23 +210,16 @@ class ActionsPlugin:
gevent.joinall(threads)
def testCryptAesEncrypt(self, num_run=1):
from lib import pyelliptic
for i in range(num_run):
key = os.urandom(32)
iv = pyelliptic.Cipher.gen_IV('aes-256-cbc')
encrypted = pyelliptic.Cipher(key, iv, 1, ciphername='aes-256-cbc').ciphering(self.utf8_text.encode("utf8"))
encrypted = sslcrypto.aes.encrypt(self.utf8_text.encode("utf8"), key)
yield "."
def testCryptAesDecrypt(self, num_run=1):
from lib import pyelliptic
key = os.urandom(32)
iv = pyelliptic.Cipher.gen_IV('aes-256-cbc')
encrypted_text = pyelliptic.Cipher(key, iv, 1, ciphername='aes-256-cbc').ciphering(self.utf8_text.encode("utf8"))
encrypted_text, iv = sslcrypto.aes.encrypt(self.utf8_text.encode("utf8"), key)
for i in range(num_run):
ctx = pyelliptic.Cipher(key, iv, 0, ciphername='aes-256-cbc')
decrypted = ctx.ciphering(encrypted_text).decode("utf8")
decrypted = sslcrypto.aes.decrypt(encrypted_text, iv, key).decode("utf8")
assert decrypted == self.utf8_text
yield "."

View File

@ -18,13 +18,10 @@ class TestCrypt:
assert len(aes_key) == 32
# assert len(encrypted) == 134 + int(len(text) / 16) * 16 # Not always true
ecc = CryptMessage.getEcc(self.privatekey)
assert ecc.decrypt(encrypted) == text_repeated
assert CryptMessage.eciesDecrypt(base64.b64encode(encrypted), self.privatekey) == text_repeated
def testDecryptEcies(self, user):
encrypted = base64.b64decode(self.ecies_encrypted_text)
ecc = CryptMessage.getEcc(self.privatekey)
assert ecc.decrypt(encrypted) == b"hello"
assert CryptMessage.eciesDecrypt(self.ecies_encrypted_text, self.privatekey) == b"hello"
def testPublickey(self, ui_websocket):
pub = ui_websocket.testAction("UserPublickey", 0)

View File

@ -9,5 +9,4 @@ pyasn1
websocket_client
gevent-ws
coincurve
python-bitcoinlib
maxminddb

View File

@ -1,16 +1,21 @@
import logging
import base64
import binascii
import time
import hashlib
from util import OpensslFindPatch
from lib import pybitcointools as btctools
from util.Electrum import dbl_format
from Config import config
lib_verify_best = "btctools"
lib_verify_best = "sslcrypto"
import sslcrypto
sslcurve_native = sslcrypto.ecc.get_curve("secp256k1")
sslcurve_fallback = sslcrypto.fallback.ecc.get_curve("secp256k1")
sslcurve = sslcurve_native
def loadLib(lib_name, silent=False):
global bitcoin, libsecp256k1message, lib_verify_best
global sslcurve, libsecp256k1message, lib_verify_best
if lib_name == "libsecp256k1":
s = time.time()
from lib import libsecp256k1message
@ -21,24 +26,10 @@ def loadLib(lib_name, silent=False):
"Libsecpk256k1 loaded: %s in %.3fs" %
(type(coincurve._libsecp256k1.lib).__name__, time.time() - s)
)
elif lib_name == "openssl":
s = time.time()
import bitcoin.signmessage
import bitcoin.core.key
import bitcoin.wallet
try:
# OpenSSL 1.1.0
ssl_version = bitcoin.core.key._ssl.SSLeay()
except AttributeError:
# OpenSSL 1.1.1+
ssl_version = bitcoin.core.key._ssl.OpenSSL_version_num()
if not silent:
logging.info(
"OpenSSL loaded: %s, version: %.9X in %.3fs" %
(bitcoin.core.key._ssl, ssl_version, time.time() - s)
)
elif lib_name == "sslcrypto":
sslcurve = sslcurve_native
elif lib_name == "sslcrypto_fallback":
sslcurve = sslcurve_fallback
try:
if not config.use_libsecp256k1:
@ -46,35 +37,30 @@ try:
loadLib("libsecp256k1")
lib_verify_best = "libsecp256k1"
except Exception as err:
logging.info("Libsecp256k1 load failed: %s, try to load OpenSSL" % err)
try:
if not config.use_openssl:
raise Exception("Disabled by config")
loadLib("openssl")
lib_verify_best = "openssl"
except Exception as err:
logging.info("OpenSSL load failed: %s, falling back to slow bitcoin verify" % err)
logging.info("Libsecp256k1 load failed: %s" % err)
def newPrivatekey(uncompressed=True): # Return new private key
privatekey = btctools.encode_privkey(btctools.random_key(), "wif")
return privatekey
def newPrivatekey(): # Return new private key
return sslcurve.private_to_wif(sslcurve.new_private_key()).decode()
def newSeed():
return btctools.random_key()
return binascii.hexlify(sslcurve.new_private_key()).decode()
def hdPrivatekey(seed, child):
masterkey = btctools.bip32_master_key(bytes(seed, "ascii"))
childkey = btctools.bip32_ckd(masterkey, child % 100000000) # Too large child id could cause problems
key = btctools.bip32_extract_key(childkey)
return btctools.encode_privkey(key, "wif")
# Too large child id could cause problems
privatekey_bin = sslcurve.derive_child(seed.encode(), child % 100000000)
return sslcurve.private_to_wif(privatekey_bin).decode()
def privatekeyToAddress(privatekey): # Return address from private key
try:
return btctools.privkey_to_address(privatekey)
if len(privatekey) == 64:
privatekey_bin = bytes.fromhex(privatekey)
else:
privatekey_bin = sslcurve.wif_to_private(privatekey.encode())
return sslcurve.private_to_address(privatekey_bin, is_compressed=False).decode()
except Exception: # Invalid privatekey
return False
@ -82,8 +68,13 @@ def privatekeyToAddress(privatekey): # Return address from private key
def sign(data, privatekey): # Return sign to data using private key
if privatekey.startswith("23") and len(privatekey) > 52:
return None # Old style private key not supported
sign = btctools.ecdsa_sign(data, privatekey)
return sign
return base64.b64encode(sslcurve.sign(
data.encode(),
sslcurve.wif_to_private(privatekey.encode()),
is_compressed=False,
recoverable=True,
hash=dbl_format
)).decode()
def verify(data, valid_address, sign, lib_verify=None): # Verify data using address and sign
@ -95,17 +86,9 @@ def verify(data, valid_address, sign, lib_verify=None): # Verify data using add
if lib_verify == "libsecp256k1":
sign_address = libsecp256k1message.recover_address(data.encode("utf8"), sign).decode("utf8")
elif lib_verify == "openssl":
sig = base64.b64decode(sign)
message = bitcoin.signmessage.BitcoinMessage(data)
hash = message.GetHash()
pubkey = bitcoin.core.key.CPubKey.recover_compact(hash, sig)
sign_address = str(bitcoin.wallet.P2PKHBitcoinAddress.from_pubkey(pubkey))
elif lib_verify == "btctools": # Use pure-python
pub = btctools.ecdsa_recover(data, sign)
sign_address = btctools.pubtoaddr(pub)
elif lib_verify in ("sslcrypto", "sslcrypto_fallback"):
publickey = sslcurve.recover(base64.b64decode(sign), data.encode(), hash=dbl_format)
sign_address = sslcurve.public_to_address(publickey).decode()
else:
raise Exception("No library enabled for signature verification")

View File

@ -436,7 +436,7 @@ def db(request):
return db
@pytest.fixture(params=["btctools", "openssl", "libsecp256k1"])
@pytest.fixture(params=["sslcrypto", "sslcrypto_fallback", "libsecp256k1"])
def crypt_bitcoin_lib(request, monkeypatch):
monkeypatch.setattr(CryptBitcoin, "lib_verify_best", request.param)
CryptBitcoin.loadLib(request.param)

View File

@ -1,9 +1,9 @@
import hashlib
import struct
import base64
from coincurve import PrivateKey, PublicKey
from base58 import b58encode_check, b58decode_check
from hmac import compare_digest
from util.Electrum import format as zero_format
RECID_MIN = 0
RECID_MAX = 3
@ -97,7 +97,7 @@ def sign_data(secretkey, byte_string):
"""Sign [byte_string] with [secretkey].
Return serialized signature compatible with Electrum (ZeroNet)."""
# encode the message
encoded = _zero_format(byte_string)
encoded = zero_format(byte_string)
# sign the message and get a coincurve signature
signature = secretkey.sign_recoverable(encoded)
# reserialize signature and return it
@ -110,7 +110,7 @@ def verify_data(key_digest, electrum_signature, byte_string):
# reserialize signature
signature = coincurve_sig(electrum_signature)
# encode the message
encoded = _zero_format(byte_string)
encoded = zero_format(byte_string)
# recover full public key from signature
# "which guarantees a correct signature"
publickey = recover_public_key(signature, encoded)
@ -130,45 +130,10 @@ def verify_sig(publickey, signature, byte_string):
def verify_key(publickey, key_digest):
return compare_digest(key_digest, public_digest(publickey))
# Electrum, the heck?!
def bchr(i):
return struct.pack('B', i)
def _zero_encode(val, base, minlen=0):
base, minlen = int(base), int(minlen)
code_string = b''.join([bchr(x) for x in range(256)])
result = b''
while val > 0:
index = val % base
result = code_string[index:index + 1] + result
val //= base
return code_string[0:1] * max(minlen - len(result), 0) + result
def _zero_insane_int(x):
x = int(x)
if x < 253:
return bchr(x)
elif x < 65536:
return bchr(253) + _zero_encode(x, 256, 2)[::-1]
elif x < 4294967296:
return bchr(254) + _zero_encode(x, 256, 4)[::-1]
else:
return bchr(255) + _zero_encode(x, 256, 8)[::-1]
def _zero_magic(message):
return b'\x18Bitcoin Signed Message:\n' + _zero_insane_int(len(message)) + message
def _zero_format(message):
padded = _zero_magic(message)
return hashlib.sha256(padded).digest()
def recover_address(data, sign):
sign_bytes = base64.b64decode(sign)
is_compressed = ((sign_bytes[0] - 27) & 4) != 0
publickey = recover_public_key(coincurve_sig(sign_bytes), _zero_format(data))
publickey = recover_public_key(coincurve_sig(sign_bytes), zero_format(data))
return compute_public_address(publickey, compressed=is_compressed)
__all__ = [

View File

@ -1,12 +1,6 @@
This code is public domain. Everyone has the right to do whatever they want
with it for any purpose.
In case your jurisdiction does not consider the above disclaimer valid or
enforceable, here's an MIT license for you:
The MIT License (MIT)
Copyright (c) 2013 Vitalik Buterin
Copyright (c) 2014 Richard Moore
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -25,3 +19,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

363
src/lib/pyaes/README.md Normal file
View File

@ -0,0 +1,363 @@
pyaes
=====
A pure-Python implementation of the AES block cipher algorithm and the common modes of operation (CBC, CFB, CTR, ECB and OFB).
Features
--------
* Supports all AES key sizes
* Supports all AES common modes
* Pure-Python (no external dependencies)
* BlockFeeder API allows streams to easily be encrypted and decrypted
* Python 2.x and 3.x support (make sure you pass in bytes(), not strings for Python 3)
API
---
All keys may be 128 bits (16 bytes), 192 bits (24 bytes) or 256 bits (32 bytes) long.
To generate a random key use:
```python
import os
# 128 bit, 192 bit and 256 bit keys
key_128 = os.urandom(16)
key_192 = os.urandom(24)
key_256 = os.urandom(32)
```
To generate keys from simple-to-remember passwords, consider using a _password-based key-derivation function_ such as [scrypt](https://github.com/ricmoo/pyscrypt).
### Common Modes of Operation
There are many modes of operations, each with various pros and cons. In general though, the **CBC** and **CTR** modes are recommended. The **ECB is NOT recommended.**, and is included primarily for completeness.
Each of the following examples assumes the following key:
```python
import pyaes
# A 256 bit (32 byte) key
key = "This_key_for_demo_purposes_only!"
# For some modes of operation we need a random initialization vector
# of 16 bytes
iv = "InitializationVe"
```
#### Counter Mode of Operation (recommended)
```python
aes = pyaes.AESModeOfOperationCTR(key)
plaintext = "Text may be any length you wish, no padding is required"
ciphertext = aes.encrypt(plaintext)
# '''\xb6\x99\x10=\xa4\x96\x88\xd1\x89\x1co\xe6\x1d\xef;\x11\x03\xe3\xee
# \xa9V?wY\xbfe\xcdO\xe3\xdf\x9dV\x19\xe5\x8dk\x9fh\xb87>\xdb\xa3\xd6
# \x86\xf4\xbd\xb0\x97\xf1\t\x02\xe9 \xed'''
print repr(ciphertext)
# The counter mode of operation maintains state, so decryption requires
# a new instance be created
aes = pyaes.AESModeOfOperationCTR(key)
decrypted = aes.decrypt(ciphertext)
# True
print decrypted == plaintext
# To use a custom initial value
counter = pyaes.Counter(initial_value = 100)
aes = pyaes.AESModeOfOperationCTR(key, counter = counter)
ciphertext = aes.encrypt(plaintext)
# '''WZ\x844\x02\xbfoY\x1f\x12\xa6\xce\x03\x82Ei)\xf6\x97mX\x86\xe3\x9d
# _1\xdd\xbd\x87\xb5\xccEM_4\x01$\xa6\x81\x0b\xd5\x04\xd7Al\x07\xe5
# \xb2\x0e\\\x0f\x00\x13,\x07'''
print repr(ciphertext)
```
#### Cipher-Block Chaining (recommended)
```python
aes = pyaes.AESModeOfOperationCBC(key, iv = iv)
plaintext = "TextMustBe16Byte"
ciphertext = aes.encrypt(plaintext)
# '\xd6:\x18\xe6\xb1\xb3\xc3\xdc\x87\xdf\xa7|\x08{k\xb6'
print repr(ciphertext)
# The cipher-block chaining mode of operation maintains state, so
# decryption requires a new instance be created
aes = pyaes.AESModeOfOperationCBC(key, iv = iv)
decrypted = aes.decrypt(ciphertext)
# True
print decrypted == plaintext
```
#### Cipher Feedback
```python
# Each block into the mode of operation must be a multiple of the segment
# size. For this example we choose 8 bytes.
aes = pyaes.AESModeOfOperationCFB(key, iv = iv, segment_size = 8)
plaintext = "TextMustBeAMultipleOfSegmentSize"
ciphertext = aes.encrypt(plaintext)
# '''v\xa9\xc1w"\x8aL\x93\xcb\xdf\xa0/\xf8Y\x0b\x8d\x88i\xcb\x85rmp
# \x85\xfe\xafM\x0c)\xd5\xeb\xaf'''
print repr(ciphertext)
# The cipher-block chaining mode of operation maintains state, so
# decryption requires a new instance be created
aes = pyaes.AESModeOfOperationCFB(key, iv = iv, segment_size = 8)
decrypted = aes.decrypt(ciphertext)
# True
print decrypted == plaintext
```
#### Output Feedback Mode of Operation
```python
aes = pyaes.AESModeOfOperationOFB(key, iv = iv)
plaintext = "Text may be any length you wish, no padding is required"
ciphertext = aes.encrypt(plaintext)
# '''v\xa9\xc1wO\x92^\x9e\rR\x1e\xf7\xb1\xa2\x9d"l1\xc7\xe7\x9d\x87(\xc26s
# \xdd8\xc8@\xb6\xd9!\xf5\x0cM\xaa\x9b\xc4\xedLD\xe4\xb9\xd8\xdf\x9e\xac
# \xa1\xb8\xea\x0f\x8ev\xb5'''
print repr(ciphertext)
# The counter mode of operation maintains state, so decryption requires
# a new instance be created
aes = pyaes.AESModeOfOperationOFB(key, iv = iv)
decrypted = aes.decrypt(ciphertext)
# True
print decrypted == plaintext
```
#### Electronic Codebook (NOT recommended)
```python
aes = pyaes.AESModeOfOperationECB(key)
plaintext = "TextMustBe16Byte"
ciphertext = aes.encrypt(plaintext)
# 'L6\x95\x85\xe4\xd9\xf1\x8a\xfb\xe5\x94X\x80|\x19\xc3'
print repr(ciphertext)
# Since there is no state stored in this mode of operation, it
# is not necessary to create a new aes object for decryption.
#aes = pyaes.AESModeOfOperationECB(key)
decrypted = aes.decrypt(ciphertext)
# True
print decrypted == plaintext
```
### BlockFeeder
Since most of the modes of operations require data in specific block-sized or segment-sized blocks, it can be difficult when working with large arbitrary streams or strings of data.
The BlockFeeder class is meant to make life easier for you, by buffering bytes across multiple calls and returning bytes as they are available, as well as padding or stripping the output when finished, if necessary.
```python
import pyaes
# Any mode of operation can be used; for this example CBC
key = "This_key_for_demo_purposes_only!"
iv = "InitializationVe"
ciphertext = ''
# We can encrypt one line at a time, regardles of length
encrypter = pyaes.Encrypter(pyaes.AESModeOfOperationCBC(key, iv))
for line in file('/etc/passwd'):
ciphertext += encrypter.feed(line)
# Make a final call to flush any remaining bytes and add paddin
ciphertext += encrypter.feed()
# We can decrypt the cipher text in chunks (here we split it in half)
decrypter = pyaes.Decrypter(pyaes.AESModeOfOperationCBC(key, iv))
decrypted = decrypter.feed(ciphertext[:len(ciphertext) / 2])
decrypted += decrypter.feed(ciphertext[len(ciphertext) / 2:])
# Again, make a final call to flush any remaining bytes and strip padding
decrypted += decrypter.feed()
print file('/etc/passwd').read() == decrypted
```
### Stream Feeder
This is meant to make it even easier to encrypt and decrypt streams and large files.
```python
import pyaes
# Any mode of operation can be used; for this example CTR
key = "This_key_for_demo_purposes_only!"
# Create the mode of operation to encrypt with
mode = pyaes.AESModeOfOperationCTR(key)
# The input and output files
file_in = file('/etc/passwd')
file_out = file('/tmp/encrypted.bin', 'wb')
# Encrypt the data as a stream, the file is read in 8kb chunks, be default
pyaes.encrypt_stream(mode, file_in, file_out)
# Close the files
file_in.close()
file_out.close()
```
Decrypting is identical, except you would use `pyaes.decrypt_stream`, and the encrypted file would be the `file_in` and target for decryption the `file_out`.
### AES block cipher
Generally you should use one of the modes of operation above. This may however be useful for experimenting with a custom mode of operation or dealing with encrypted blocks.
The block cipher requires exactly one block of data to encrypt or decrypt, and each block should be an array with each element an integer representation of a byte.
```python
import pyaes
# 16 byte block of plain text
plaintext = "Hello World!!!!!"
plaintext_bytes = [ ord(c) for c in plaintext ]
# 32 byte key (256 bit)
key = "This_key_for_demo_purposes_only!"
# Our AES instance
aes = pyaes.AES(key)
# Encrypt!
ciphertext = aes.encrypt(plaintext_bytes)
# [55, 250, 182, 25, 185, 208, 186, 95, 206, 115, 50, 115, 108, 58, 174, 115]
print repr(ciphertext)
# Decrypt!
decrypted = aes.decrypt(ciphertext)
# True
print decrypted == plaintext_bytes
```
What is a key?
--------------
This seems to be a point of confusion for many people new to using encryption. You can think of the key as the *"password"*. However, these algorithms require the *"password"* to be a specific length.
With AES, there are three possible key lengths, 16-bytes, 24-bytes or 32-bytes. When you create an AES object, the key size is automatically detected, so it is important to pass in a key of the correct length.
Often, you wish to provide a password of arbitrary length, for example, something easy to remember or write down. In these cases, you must come up with a way to transform the password into a key, of a specific length. A **Password-Based Key Derivation Function** (PBKDF) is an algorithm designed for this exact purpose.
Here is an example, using the popular (possibly obsolete?) *crypt* PBKDF:
```
# See: https://www.dlitz.net/software/python-pbkdf2/
import pbkdf2
password = "HelloWorld"
# The crypt PBKDF returns a 48-byte string
key = pbkdf2.crypt(password)
# A 16-byte, 24-byte and 32-byte key, respectively
key_16 = key[:16]
key_24 = key[:24]
key_32 = key[:32]
```
The [scrypt](https://github.com/ricmoo/pyscrypt) PBKDF is intentionally slow, to make it more difficult to brute-force guess a password:
```
# See: https://github.com/ricmoo/pyscrypt
import pyscrypt
password = "HelloWorld"
# Salt is required, and prevents Rainbow Table attacks
salt = "SeaSalt"
# N, r, and p are parameters to specify how difficult it should be to
# generate a key; bigger numbers take longer and more memory
N = 1024
r = 1
p = 1
# A 16-byte, 24-byte and 32-byte key, respectively; the scrypt algorithm takes
# a 6-th parameter, indicating key length
key_16 = pyscrypt.hash(password, salt, N, r, p, 16)
key_24 = pyscrypt.hash(password, salt, N, r, p, 24)
key_32 = pyscrypt.hash(password, salt, N, r, p, 32)
```
Another possibility, is to use a hashing function, such as SHA256 to hash the password, but this method may be vulnerable to [Rainbow Attacks](http://en.wikipedia.org/wiki/Rainbow_table), unless you use a [salt](http://en.wikipedia.org/wiki/Salt_(cryptography)).
```python
import hashlib
password = "HelloWorld"
# The SHA256 hash algorithm returns a 32-byte string
hashed = hashlib.sha256(password).digest()
# A 16-byte, 24-byte and 32-byte key, respectively
key_16 = hashed[:16]
key_24 = hashed[:24]
key_32 = hashed
```
Performance
-----------
There is a test case provided in _/tests/test-aes.py_ which does some basic performance testing (its primary purpose is moreso as a regression test).
Based on that test, in **CPython**, this library is about 30x slower than [PyCrypto](https://www.dlitz.net/software/pycrypto/) for CBC, ECB and OFB; about 80x slower for CFB; and 300x slower for CTR.
Based on that same test, in **Pypy**, this library is about 4x slower than [PyCrypto](https://www.dlitz.net/software/pycrypto/) for CBC, ECB and OFB; about 12x slower for CFB; and 19x slower for CTR.
The PyCrypto documentation makes reference to the counter call being responsible for the speed problems of the counter (CTR) mode of operation, which is why they use a specially optimized counter. I will investigate this problem further in the future.
FAQ
---
#### Why do this?
The short answer, *why not?*
The longer answer, is for my [pyscrypt](https://github.com/ricmoo/pyscrypt) library. I required a pure-Python AES implementation that supported 256-bit keys with the counter (CTR) mode of operation. After searching, I found several implementations, but all were missing CTR or only supported 128 bit keys. After all the work of learning AES inside and out to implement the library, it was only a marginal amount of extra work to library-ify a more general solution. So, *why not?*
#### How do I get a question I have added?
E-mail me at pyaes@ricmoo.com with any questions, suggestions, comments, et cetera.
#### Can I give you my money?
Umm... Ok? :-)
_Bitcoin_ - `18UDs4qV1shu2CgTS2tKojhCtM69kpnWg9`

53
src/lib/pyaes/__init__.py Normal file
View File

@ -0,0 +1,53 @@
# The MIT License (MIT)
#
# Copyright (c) 2014 Richard Moore
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
# This is a pure-Python implementation of the AES algorithm and AES common
# modes of operation.
# See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
# See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
# Supported key sizes:
# 128-bit
# 192-bit
# 256-bit
# Supported modes of operation:
# ECB - Electronic Codebook
# CBC - Cipher-Block Chaining
# CFB - Cipher Feedback
# OFB - Output Feedback
# CTR - Counter
# See the README.md for API details and general information.
# Also useful, PyCrypto, a crypto library implemented in C with Python bindings:
# https://www.dlitz.net/software/pycrypto/
VERSION = [1, 3, 0]
from .aes import AES, AESModeOfOperationCTR, AESModeOfOperationCBC, AESModeOfOperationCFB, AESModeOfOperationECB, AESModeOfOperationOFB, AESModesOfOperation, Counter
from .blockfeeder import decrypt_stream, Decrypter, encrypt_stream, Encrypter
from .blockfeeder import PADDING_NONE, PADDING_DEFAULT

589
src/lib/pyaes/aes.py Normal file
View File

@ -0,0 +1,589 @@
# The MIT License (MIT)
#
# Copyright (c) 2014 Richard Moore
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
# This is a pure-Python implementation of the AES algorithm and AES common
# modes of operation.
# See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
# Honestly, the best description of the modes of operations are the wonderful
# diagrams on Wikipedia. They explain in moments what my words could never
# achieve. Hence the inline documentation here is sparer than I'd prefer.
# See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
# Also useful, PyCrypto, a crypto library implemented in C with Python bindings:
# https://www.dlitz.net/software/pycrypto/
# Supported key sizes:
# 128-bit
# 192-bit
# 256-bit
# Supported modes of operation:
# ECB - Electronic Codebook
# CBC - Cipher-Block Chaining
# CFB - Cipher Feedback
# OFB - Output Feedback
# CTR - Counter
# See the README.md for API details and general information.
import copy
import struct
__all__ = ["AES", "AESModeOfOperationCTR", "AESModeOfOperationCBC", "AESModeOfOperationCFB",
"AESModeOfOperationECB", "AESModeOfOperationOFB", "AESModesOfOperation", "Counter"]
def _compact_word(word):
return (word[0] << 24) | (word[1] << 16) | (word[2] << 8) | word[3]
def _string_to_bytes(text):
return list(ord(c) for c in text)
def _bytes_to_string(binary):
return "".join(chr(b) for b in binary)
def _concat_list(a, b):
return a + b
# Python 3 compatibility
try:
xrange
except Exception:
xrange = range
# Python 3 supports bytes, which is already an array of integers
def _string_to_bytes(text):
if isinstance(text, bytes):
return text
return [ord(c) for c in text]
# In Python 3, we return bytes
def _bytes_to_string(binary):
return bytes(binary)
# Python 3 cannot concatenate a list onto a bytes, so we bytes-ify it first
def _concat_list(a, b):
return a + bytes(b)
# Based *largely* on the Rijndael implementation
# See: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
class AES(object):
'''Encapsulates the AES block cipher.
You generally should not need this. Use the AESModeOfOperation classes
below instead.'''
# Number of rounds by keysize
number_of_rounds = {16: 10, 24: 12, 32: 14}
# Round constant words
rcon = [ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ]
# S-box and Inverse S-box (S is for Substitution)
S = [ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ]
Si =[ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d ]
# Transformations for encryption
T1 = [ 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a ]
T2 = [ 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616 ]
T3 = [ 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16 ]
T4 = [ 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c ]
# Transformations for decryption
T5 = [ 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742 ]
T6 = [ 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857 ]
T7 = [ 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8 ]
T8 = [ 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0 ]
# Transformations for decryption key expansion
U1 = [ 0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3 ]
U2 = [ 0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697 ]
U3 = [ 0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46 ]
U4 = [ 0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d ]
def __init__(self, key):
if len(key) not in (16, 24, 32):
raise ValueError('Invalid key size')
rounds = self.number_of_rounds[len(key)]
# Encryption round keys
self._Ke = [[0] * 4 for i in xrange(rounds + 1)]
# Decryption round keys
self._Kd = [[0] * 4 for i in xrange(rounds + 1)]
round_key_count = (rounds + 1) * 4
KC = len(key) // 4
# Convert the key into ints
tk = [ struct.unpack('>i', key[i:i + 4])[0] for i in xrange(0, len(key), 4) ]
# Copy values into round key arrays
for i in xrange(0, KC):
self._Ke[i // 4][i % 4] = tk[i]
self._Kd[rounds - (i // 4)][i % 4] = tk[i]
# Key expansion (fips-197 section 5.2)
rconpointer = 0
t = KC
while t < round_key_count:
tt = tk[KC - 1]
tk[0] ^= ((self.S[(tt >> 16) & 0xFF] << 24) ^
(self.S[(tt >> 8) & 0xFF] << 16) ^
(self.S[ tt & 0xFF] << 8) ^
self.S[(tt >> 24) & 0xFF] ^
(self.rcon[rconpointer] << 24))
rconpointer += 1
if KC != 8:
for i in xrange(1, KC):
tk[i] ^= tk[i - 1]
# Key expansion for 256-bit keys is "slightly different" (fips-197)
else:
for i in xrange(1, KC // 2):
tk[i] ^= tk[i - 1]
tt = tk[KC // 2 - 1]
tk[KC // 2] ^= (self.S[ tt & 0xFF] ^
(self.S[(tt >> 8) & 0xFF] << 8) ^
(self.S[(tt >> 16) & 0xFF] << 16) ^
(self.S[(tt >> 24) & 0xFF] << 24))
for i in xrange(KC // 2 + 1, KC):
tk[i] ^= tk[i - 1]
# Copy values into round key arrays
j = 0
while j < KC and t < round_key_count:
self._Ke[t // 4][t % 4] = tk[j]
self._Kd[rounds - (t // 4)][t % 4] = tk[j]
j += 1
t += 1
# Inverse-Cipher-ify the decryption round key (fips-197 section 5.3)
for r in xrange(1, rounds):
for j in xrange(0, 4):
tt = self._Kd[r][j]
self._Kd[r][j] = (self.U1[(tt >> 24) & 0xFF] ^
self.U2[(tt >> 16) & 0xFF] ^
self.U3[(tt >> 8) & 0xFF] ^
self.U4[ tt & 0xFF])
def encrypt(self, plaintext):
'Encrypt a block of plain text using the AES block cipher.'
if len(plaintext) != 16:
raise ValueError('wrong block length')
rounds = len(self._Ke) - 1
(s1, s2, s3) = [1, 2, 3]
a = [0, 0, 0, 0]
# Convert plaintext to (ints ^ key)
t = [(_compact_word(plaintext[4 * i:4 * i + 4]) ^ self._Ke[0][i]) for i in xrange(0, 4)]
# Apply round transforms
for r in xrange(1, rounds):
for i in xrange(0, 4):
a[i] = (self.T1[(t[ i ] >> 24) & 0xFF] ^
self.T2[(t[(i + s1) % 4] >> 16) & 0xFF] ^
self.T3[(t[(i + s2) % 4] >> 8) & 0xFF] ^
self.T4[ t[(i + s3) % 4] & 0xFF] ^
self._Ke[r][i])
t = copy.copy(a)
# The last round is special
result = [ ]
for i in xrange(0, 4):
tt = self._Ke[rounds][i]
result.append((self.S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
result.append((self.S[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
result.append((self.S[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF)
result.append((self.S[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF)
return result
def decrypt(self, ciphertext):
'Decrypt a block of cipher text using the AES block cipher.'
if len(ciphertext) != 16:
raise ValueError('wrong block length')
rounds = len(self._Kd) - 1
(s1, s2, s3) = [3, 2, 1]
a = [0, 0, 0, 0]
# Convert ciphertext to (ints ^ key)
t = [(_compact_word(ciphertext[4 * i:4 * i + 4]) ^ self._Kd[0][i]) for i in xrange(0, 4)]
# Apply round transforms
for r in xrange(1, rounds):
for i in xrange(0, 4):
a[i] = (self.T5[(t[ i ] >> 24) & 0xFF] ^
self.T6[(t[(i + s1) % 4] >> 16) & 0xFF] ^
self.T7[(t[(i + s2) % 4] >> 8) & 0xFF] ^
self.T8[ t[(i + s3) % 4] & 0xFF] ^
self._Kd[r][i])
t = copy.copy(a)
# The last round is special
result = [ ]
for i in xrange(0, 4):
tt = self._Kd[rounds][i]
result.append((self.Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
result.append((self.Si[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
result.append((self.Si[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF)
result.append((self.Si[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF)
return result
class Counter(object):
'''A counter object for the Counter (CTR) mode of operation.
To create a custom counter, you can usually just override the
increment method.'''
def __init__(self, initial_value = 1):
# Convert the value into an array of bytes long
self._counter = [ ((initial_value >> i) % 256) for i in xrange(128 - 8, -1, -8) ]
value = property(lambda s: s._counter)
def increment(self):
'''Increment the counter (overflow rolls back to 0).'''
for i in xrange(len(self._counter) - 1, -1, -1):
self._counter[i] += 1
if self._counter[i] < 256: break
# Carry the one
self._counter[i] = 0
# Overflow
else:
self._counter = [ 0 ] * len(self._counter)
class AESBlockModeOfOperation(object):
'''Super-class for AES modes of operation that require blocks.'''
def __init__(self, key):
self._aes = AES(key)
def decrypt(self, ciphertext):
raise Exception('not implemented')
def encrypt(self, plaintext):
raise Exception('not implemented')
class AESStreamModeOfOperation(AESBlockModeOfOperation):
'''Super-class for AES modes of operation that are stream-ciphers.'''
class AESSegmentModeOfOperation(AESStreamModeOfOperation):
'''Super-class for AES modes of operation that segment data.'''
segment_bytes = 16
class AESModeOfOperationECB(AESBlockModeOfOperation):
'''AES Electronic Codebook Mode of Operation.
o Block-cipher, so data must be padded to 16 byte boundaries
Security Notes:
o This mode is not recommended
o Any two identical blocks produce identical encrypted values,
exposing data patterns. (See the image of Tux on wikipedia)
Also see:
o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_.28ECB.29
o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.1'''
name = "Electronic Codebook (ECB)"
def encrypt(self, plaintext):
if len(plaintext) != 16:
raise ValueError('plaintext block must be 16 bytes')
plaintext = _string_to_bytes(plaintext)
return _bytes_to_string(self._aes.encrypt(plaintext))
def decrypt(self, ciphertext):
if len(ciphertext) != 16:
raise ValueError('ciphertext block must be 16 bytes')
ciphertext = _string_to_bytes(ciphertext)
return _bytes_to_string(self._aes.decrypt(ciphertext))
class AESModeOfOperationCBC(AESBlockModeOfOperation):
'''AES Cipher-Block Chaining Mode of Operation.
o The Initialization Vector (IV)
o Block-cipher, so data must be padded to 16 byte boundaries
o An incorrect initialization vector will only cause the first
block to be corrupt; all other blocks will be intact
o A corrupt bit in the cipher text will cause a block to be
corrupted, and the next block to be inverted, but all other
blocks will be intact.
Security Notes:
o This method (and CTR) ARE recommended.
Also see:
o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29
o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.2'''
name = "Cipher-Block Chaining (CBC)"
def __init__(self, key, iv = None):
if iv is None:
self._last_cipherblock = [ 0 ] * 16
elif len(iv) != 16:
raise ValueError('initialization vector must be 16 bytes')
else:
self._last_cipherblock = _string_to_bytes(iv)
AESBlockModeOfOperation.__init__(self, key)
def encrypt(self, plaintext):
if len(plaintext) != 16:
raise ValueError('plaintext block must be 16 bytes')
plaintext = _string_to_bytes(plaintext)
precipherblock = [ (p ^ l) for (p, l) in zip(plaintext, self._last_cipherblock) ]
self._last_cipherblock = self._aes.encrypt(precipherblock)
return _bytes_to_string(self._last_cipherblock)
def decrypt(self, ciphertext):
if len(ciphertext) != 16:
raise ValueError('ciphertext block must be 16 bytes')
cipherblock = _string_to_bytes(ciphertext)
plaintext = [ (p ^ l) for (p, l) in zip(self._aes.decrypt(cipherblock), self._last_cipherblock) ]
self._last_cipherblock = cipherblock
return _bytes_to_string(plaintext)
class AESModeOfOperationCFB(AESSegmentModeOfOperation):
'''AES Cipher Feedback Mode of Operation.
o A stream-cipher, so input does not need to be padded to blocks,
but does need to be padded to segment_size
Also see:
o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_.28CFB.29
o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.3'''
name = "Cipher Feedback (CFB)"
def __init__(self, key, iv, segment_size = 1):
if segment_size == 0: segment_size = 1
if iv is None:
self._shift_register = [ 0 ] * 16
elif len(iv) != 16:
raise ValueError('initialization vector must be 16 bytes')
else:
self._shift_register = _string_to_bytes(iv)
self._segment_bytes = segment_size
AESBlockModeOfOperation.__init__(self, key)
segment_bytes = property(lambda s: s._segment_bytes)
def encrypt(self, plaintext):
if len(plaintext) % self._segment_bytes != 0:
raise ValueError('plaintext block must be a multiple of segment_size')
plaintext = _string_to_bytes(plaintext)
# Break block into segments
encrypted = [ ]
for i in xrange(0, len(plaintext), self._segment_bytes):
plaintext_segment = plaintext[i: i + self._segment_bytes]
xor_segment = self._aes.encrypt(self._shift_register)[:len(plaintext_segment)]
cipher_segment = [ (p ^ x) for (p, x) in zip(plaintext_segment, xor_segment) ]
# Shift the top bits out and the ciphertext in
self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment)
encrypted.extend(cipher_segment)
return _bytes_to_string(encrypted)
def decrypt(self, ciphertext):
if len(ciphertext) % self._segment_bytes != 0:
raise ValueError('ciphertext block must be a multiple of segment_size')
ciphertext = _string_to_bytes(ciphertext)
# Break block into segments
decrypted = [ ]
for i in xrange(0, len(ciphertext), self._segment_bytes):
cipher_segment = ciphertext[i: i + self._segment_bytes]
xor_segment = self._aes.encrypt(self._shift_register)[:len(cipher_segment)]
plaintext_segment = [ (p ^ x) for (p, x) in zip(cipher_segment, xor_segment) ]
# Shift the top bits out and the ciphertext in
self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment)
decrypted.extend(plaintext_segment)
return _bytes_to_string(decrypted)
class AESModeOfOperationOFB(AESStreamModeOfOperation):
'''AES Output Feedback Mode of Operation.
o A stream-cipher, so input does not need to be padded to blocks,
allowing arbitrary length data.
o A bit twiddled in the cipher text, twiddles the same bit in the
same bit in the plain text, which can be useful for error
correction techniques.
Also see:
o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_.28OFB.29
o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.4'''
name = "Output Feedback (OFB)"
def __init__(self, key, iv = None):
if iv is None:
self._last_precipherblock = [ 0 ] * 16
elif len(iv) != 16:
raise ValueError('initialization vector must be 16 bytes')
else:
self._last_precipherblock = _string_to_bytes(iv)
self._remaining_block = [ ]
AESBlockModeOfOperation.__init__(self, key)
def encrypt(self, plaintext):
encrypted = [ ]
for p in _string_to_bytes(plaintext):
if len(self._remaining_block) == 0:
self._remaining_block = self._aes.encrypt(self._last_precipherblock)
self._last_precipherblock = [ ]
precipherbyte = self._remaining_block.pop(0)
self._last_precipherblock.append(precipherbyte)
cipherbyte = p ^ precipherbyte
encrypted.append(cipherbyte)
return _bytes_to_string(encrypted)
def decrypt(self, ciphertext):
# AES-OFB is symetric
return self.encrypt(ciphertext)
class AESModeOfOperationCTR(AESStreamModeOfOperation):
'''AES Counter Mode of Operation.
o A stream-cipher, so input does not need to be padded to blocks,
allowing arbitrary length data.
o The counter must be the same size as the key size (ie. len(key))
o Each block independant of the other, so a corrupt byte will not
damage future blocks.
o Each block has a uniue counter value associated with it, which
contributes to the encrypted value, so no data patterns are
leaked.
o Also known as: Counter Mode (CM), Integer Counter Mode (ICM) and
Segmented Integer Counter (SIC
Security Notes:
o This method (and CBC) ARE recommended.
o Each message block is associated with a counter value which must be
unique for ALL messages with the same key. Otherwise security may be
compromised.
Also see:
o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29
o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.5
and Appendix B for managing the initial counter'''
name = "Counter (CTR)"
def __init__(self, key, counter = None):
AESBlockModeOfOperation.__init__(self, key)
if counter is None:
counter = Counter()
self._counter = counter
self._remaining_counter = [ ]
def encrypt(self, plaintext):
while len(self._remaining_counter) < len(plaintext):
self._remaining_counter += self._aes.encrypt(self._counter.value)
self._counter.increment()
plaintext = _string_to_bytes(plaintext)
encrypted = [ (p ^ c) for (p, c) in zip(plaintext, self._remaining_counter) ]
self._remaining_counter = self._remaining_counter[len(encrypted):]
return _bytes_to_string(encrypted)
def decrypt(self, crypttext):
# AES-CTR is symetric
return self.encrypt(crypttext)
# Simple lookup table for each mode
AESModesOfOperation = dict(
ctr = AESModeOfOperationCTR,
cbc = AESModeOfOperationCBC,
cfb = AESModeOfOperationCFB,
ecb = AESModeOfOperationECB,
ofb = AESModeOfOperationOFB,
)

View File

@ -0,0 +1,227 @@
# The MIT License (MIT)
#
# Copyright (c) 2014 Richard Moore
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
from .aes import AESBlockModeOfOperation, AESSegmentModeOfOperation, AESStreamModeOfOperation
from .util import append_PKCS7_padding, strip_PKCS7_padding, to_bufferable
# First we inject three functions to each of the modes of operations
#
# _can_consume(size)
# - Given a size, determine how many bytes could be consumed in
# a single call to either the decrypt or encrypt method
#
# _final_encrypt(data, padding = PADDING_DEFAULT)
# - call and return encrypt on this (last) chunk of data,
# padding as necessary; this will always be at least 16
# bytes unless the total incoming input was less than 16
# bytes
#
# _final_decrypt(data, padding = PADDING_DEFAULT)
# - same as _final_encrypt except for decrypt, for
# stripping off padding
#
PADDING_NONE = 'none'
PADDING_DEFAULT = 'default'
# @TODO: Ciphertext stealing and explicit PKCS#7
# PADDING_CIPHERTEXT_STEALING
# PADDING_PKCS7
# ECB and CBC are block-only ciphers
def _block_can_consume(self, size):
if size >= 16: return 16
return 0
# After padding, we may have more than one block
def _block_final_encrypt(self, data, padding = PADDING_DEFAULT):
if padding == PADDING_DEFAULT:
data = append_PKCS7_padding(data)
elif padding == PADDING_NONE:
if len(data) != 16:
raise Exception('invalid data length for final block')
else:
raise Exception('invalid padding option')
if len(data) == 32:
return self.encrypt(data[:16]) + self.encrypt(data[16:])
return self.encrypt(data)
def _block_final_decrypt(self, data, padding = PADDING_DEFAULT):
if padding == PADDING_DEFAULT:
return strip_PKCS7_padding(self.decrypt(data))
if padding == PADDING_NONE:
if len(data) != 16:
raise Exception('invalid data length for final block')
return self.decrypt(data)
raise Exception('invalid padding option')
AESBlockModeOfOperation._can_consume = _block_can_consume
AESBlockModeOfOperation._final_encrypt = _block_final_encrypt
AESBlockModeOfOperation._final_decrypt = _block_final_decrypt
# CFB is a segment cipher
def _segment_can_consume(self, size):
return self.segment_bytes * int(size // self.segment_bytes)
# CFB can handle a non-segment-sized block at the end using the remaining cipherblock
def _segment_final_encrypt(self, data, padding = PADDING_DEFAULT):
if padding != PADDING_DEFAULT:
raise Exception('invalid padding option')
faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes)))
padded = data + to_bufferable(faux_padding)
return self.encrypt(padded)[:len(data)]
# CFB can handle a non-segment-sized block at the end using the remaining cipherblock
def _segment_final_decrypt(self, data, padding = PADDING_DEFAULT):
if padding != PADDING_DEFAULT:
raise Exception('invalid padding option')
faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes)))
padded = data + to_bufferable(faux_padding)
return self.decrypt(padded)[:len(data)]
AESSegmentModeOfOperation._can_consume = _segment_can_consume
AESSegmentModeOfOperation._final_encrypt = _segment_final_encrypt
AESSegmentModeOfOperation._final_decrypt = _segment_final_decrypt
# OFB and CTR are stream ciphers
def _stream_can_consume(self, size):
return size
def _stream_final_encrypt(self, data, padding = PADDING_DEFAULT):
if padding not in [PADDING_NONE, PADDING_DEFAULT]:
raise Exception('invalid padding option')
return self.encrypt(data)
def _stream_final_decrypt(self, data, padding = PADDING_DEFAULT):
if padding not in [PADDING_NONE, PADDING_DEFAULT]:
raise Exception('invalid padding option')
return self.decrypt(data)
AESStreamModeOfOperation._can_consume = _stream_can_consume
AESStreamModeOfOperation._final_encrypt = _stream_final_encrypt
AESStreamModeOfOperation._final_decrypt = _stream_final_decrypt
class BlockFeeder(object):
'''The super-class for objects to handle chunking a stream of bytes
into the appropriate block size for the underlying mode of operation
and applying (or stripping) padding, as necessary.'''
def __init__(self, mode, feed, final, padding = PADDING_DEFAULT):
self._mode = mode
self._feed = feed
self._final = final
self._buffer = to_bufferable("")
self._padding = padding
def feed(self, data = None):
'''Provide bytes to encrypt (or decrypt), returning any bytes
possible from this or any previous calls to feed.
Call with None or an empty string to flush the mode of
operation and return any final bytes; no further calls to
feed may be made.'''
if self._buffer is None:
raise ValueError('already finished feeder')
# Finalize; process the spare bytes we were keeping
if data is None:
result = self._final(self._buffer, self._padding)
self._buffer = None
return result
self._buffer += to_bufferable(data)
# We keep 16 bytes around so we can determine padding
result = to_bufferable('')
while len(self._buffer) > 16:
can_consume = self._mode._can_consume(len(self._buffer) - 16)
if can_consume == 0: break
result += self._feed(self._buffer[:can_consume])
self._buffer = self._buffer[can_consume:]
return result
class Encrypter(BlockFeeder):
'Accepts bytes of plaintext and returns encrypted ciphertext.'
def __init__(self, mode, padding = PADDING_DEFAULT):
BlockFeeder.__init__(self, mode, mode.encrypt, mode._final_encrypt, padding)
class Decrypter(BlockFeeder):
'Accepts bytes of ciphertext and returns decrypted plaintext.'
def __init__(self, mode, padding = PADDING_DEFAULT):
BlockFeeder.__init__(self, mode, mode.decrypt, mode._final_decrypt, padding)
# 8kb blocks
BLOCK_SIZE = (1 << 13)
def _feed_stream(feeder, in_stream, out_stream, block_size = BLOCK_SIZE):
'Uses feeder to read and convert from in_stream and write to out_stream.'
while True:
chunk = in_stream.read(block_size)
if not chunk:
break
converted = feeder.feed(chunk)
out_stream.write(converted)
converted = feeder.feed()
out_stream.write(converted)
def encrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT):
'Encrypts a stream of bytes from in_stream to out_stream using mode.'
encrypter = Encrypter(mode, padding = padding)
_feed_stream(encrypter, in_stream, out_stream, block_size)
def decrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT):
'Decrypts a stream of bytes from in_stream to out_stream using mode.'
decrypter = Decrypter(mode, padding = padding)
_feed_stream(decrypter, in_stream, out_stream, block_size)

60
src/lib/pyaes/util.py Normal file
View File

@ -0,0 +1,60 @@
# The MIT License (MIT)
#
# Copyright (c) 2014 Richard Moore
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
# Why to_bufferable?
# Python 3 is very different from Python 2.x when it comes to strings of text
# and strings of bytes; in Python 3, strings of bytes do not exist, instead to
# represent arbitrary binary data, we must use the "bytes" object. This method
# ensures the object behaves as we need it to.
def to_bufferable(binary):
return binary
def _get_byte(c):
return ord(c)
try:
xrange
except:
def to_bufferable(binary):
if isinstance(binary, bytes):
return binary
return bytes(ord(b) for b in binary)
def _get_byte(c):
return c
def append_PKCS7_padding(data):
pad = 16 - (len(data) % 16)
return data + to_bufferable(chr(pad) * pad)
def strip_PKCS7_padding(data):
if len(data) % 16 != 0:
raise ValueError("invalid length")
pad = _get_byte(data[-1])
if pad > 16:
raise ValueError("invalid padding byte")
return data[:-pad]

View File

@ -1,10 +0,0 @@
from .py2specials import *
from .py3specials import *
from .main import *
from .transaction import *
from .deterministic import *
from .bci import *
from .composite import *
from .stealth import *
from .blocks import *
from .mnemonic import *

View File

@ -1,528 +0,0 @@
#!/usr/bin/python
import json, re
import random
import sys
try:
from urllib.request import build_opener
except:
from urllib2 import build_opener
# Makes a request to a given URL (first arg) and optional params (second arg)
def make_request(*args):
opener = build_opener()
opener.addheaders = [('User-agent',
'Mozilla/5.0'+str(random.randrange(1000000)))]
try:
return opener.open(*args).read().strip()
except Exception as e:
try:
p = e.read().strip()
except:
p = e
raise Exception(p)
def is_testnet(inp):
'''Checks if inp is a testnet address or if UTXO is a known testnet TxID'''
if isinstance(inp, (list, tuple)) and len(inp) >= 1:
return any([is_testnet(x) for x in inp])
elif not isinstance(inp, basestring): # sanity check
raise TypeError("Input must be str/unicode, not type %s" % str(type(inp)))
if not inp or (inp.lower() in ("btc", "testnet")):
pass
## ADDRESSES
if inp[0] in "123mn":
if re.match("^[2mn][a-km-zA-HJ-NP-Z0-9]{26,33}$", inp):
return True
elif re.match("^[13][a-km-zA-HJ-NP-Z0-9]{26,33}$", inp):
return False
else:
#sys.stderr.write("Bad address format %s")
return None
## TXID
elif re.match('^[0-9a-fA-F]{64}$', inp):
base_url = "http://api.blockcypher.com/v1/btc/{network}/txs/{txid}?includesHex=false"
try:
# try testnet fetchtx
make_request(base_url.format(network="test3", txid=inp.lower()))
return True
except:
# try mainnet fetchtx
make_request(base_url.format(network="main", txid=inp.lower()))
return False
sys.stderr.write("TxID %s has no match for testnet or mainnet (Bad TxID)")
return None
else:
raise TypeError("{0} is unknown input".format(inp))
def set_network(*args):
'''Decides if args for unspent/fetchtx/pushtx are mainnet or testnet'''
r = []
for arg in args:
if not arg:
pass
if isinstance(arg, basestring):
r.append(is_testnet(arg))
elif isinstance(arg, (list, tuple)):
return set_network(*arg)
if any(r) and not all(r):
raise Exception("Mixed Testnet/Mainnet queries")
return "testnet" if any(r) else "btc"
def parse_addr_args(*args):
# Valid input formats: unspent([addr1, addr2, addr3])
# unspent([addr1, addr2, addr3], network)
# unspent(addr1, addr2, addr3)
# unspent(addr1, addr2, addr3, network)
addr_args = args
network = "btc"
if len(args) == 0:
return [], 'btc'
if len(args) >= 1 and args[-1] in ('testnet', 'btc'):
network = args[-1]
addr_args = args[:-1]
if len(addr_args) == 1 and isinstance(addr_args, list):
network = set_network(*addr_args[0])
addr_args = addr_args[0]
if addr_args and isinstance(addr_args, tuple) and isinstance(addr_args[0], list):
addr_args = addr_args[0]
network = set_network(addr_args)
return network, addr_args
# Gets the unspent outputs of one or more addresses
def bci_unspent(*args):
network, addrs = parse_addr_args(*args)
u = []
for a in addrs:
try:
data = make_request('https://blockchain.info/unspent?active='+a)
except Exception as e:
if str(e) == 'No free outputs to spend':
continue
else:
raise Exception(e)
try:
jsonobj = json.loads(data.decode("utf-8"))
for o in jsonobj["unspent_outputs"]:
h = o['tx_hash'].decode('hex')[::-1].encode('hex')
u.append({
"output": h+':'+str(o['tx_output_n']),
"value": o['value']
})
except:
raise Exception("Failed to decode data: "+data)
return u
def blockr_unspent(*args):
# Valid input formats: blockr_unspent([addr1, addr2,addr3])
# blockr_unspent(addr1, addr2, addr3)
# blockr_unspent([addr1, addr2, addr3], network)
# blockr_unspent(addr1, addr2, addr3, network)
# Where network is 'btc' or 'testnet'
network, addr_args = parse_addr_args(*args)
if network == 'testnet':
blockr_url = 'http://tbtc.blockr.io/api/v1/address/unspent/'
elif network == 'btc':
blockr_url = 'http://btc.blockr.io/api/v1/address/unspent/'
else:
raise Exception(
'Unsupported network {0} for blockr_unspent'.format(network))
if len(addr_args) == 0:
return []
elif isinstance(addr_args[0], list):
addrs = addr_args[0]
else:
addrs = addr_args
res = make_request(blockr_url+','.join(addrs))
data = json.loads(res.decode("utf-8"))['data']
o = []
if 'unspent' in data:
data = [data]
for dat in data:
for u in dat['unspent']:
o.append({
"output": u['tx']+':'+str(u['n']),
"value": int(u['amount'].replace('.', ''))
})
return o
def helloblock_unspent(*args):
addrs, network = parse_addr_args(*args)
if network == 'testnet':
url = 'https://testnet.helloblock.io/v1/addresses/%s/unspents?limit=500&offset=%s'
elif network == 'btc':
url = 'https://mainnet.helloblock.io/v1/addresses/%s/unspents?limit=500&offset=%s'
o = []
for addr in addrs:
for offset in xrange(0, 10**9, 500):
res = make_request(url % (addr, offset))
data = json.loads(res.decode("utf-8"))["data"]
if not len(data["unspents"]):
break
elif offset:
sys.stderr.write("Getting more unspents: %d\n" % offset)
for dat in data["unspents"]:
o.append({
"output": dat["txHash"]+':'+str(dat["index"]),
"value": dat["value"],
})
return o
unspent_getters = {
'bci': bci_unspent,
'blockr': blockr_unspent,
'helloblock': helloblock_unspent
}
def unspent(*args, **kwargs):
f = unspent_getters.get(kwargs.get('source', ''), bci_unspent)
return f(*args)
# Gets the transaction output history of a given set of addresses,
# including whether or not they have been spent
def history(*args):
# Valid input formats: history([addr1, addr2,addr3])
# history(addr1, addr2, addr3)
if len(args) == 0:
return []
elif isinstance(args[0], list):
addrs = args[0]
else:
addrs = args
txs = []
for addr in addrs:
offset = 0
while 1:
gathered = False
while not gathered:
try:
data = make_request(
'https://blockchain.info/address/%s?format=json&offset=%s' %
(addr, offset))
gathered = True
except Exception as e:
try:
sys.stderr.write(e.read().strip())
except:
sys.stderr.write(str(e))
gathered = False
try:
jsonobj = json.loads(data.decode("utf-8"))
except:
raise Exception("Failed to decode data: "+data)
txs.extend(jsonobj["txs"])
if len(jsonobj["txs"]) < 50:
break
offset += 50
sys.stderr.write("Fetching more transactions... "+str(offset)+'\n')
outs = {}
for tx in txs:
for o in tx["out"]:
if o.get('addr', None) in addrs:
key = str(tx["tx_index"])+':'+str(o["n"])
outs[key] = {
"address": o["addr"],
"value": o["value"],
"output": tx["hash"]+':'+str(o["n"]),
"block_height": tx.get("block_height", None)
}
for tx in txs:
for i, inp in enumerate(tx["inputs"]):
if "prev_out" in inp:
if inp["prev_out"].get("addr", None) in addrs:
key = str(inp["prev_out"]["tx_index"]) + \
':'+str(inp["prev_out"]["n"])
if outs.get(key):
outs[key]["spend"] = tx["hash"]+':'+str(i)
return [outs[k] for k in outs]
# Pushes a transaction to the network using https://blockchain.info/pushtx
def bci_pushtx(tx):
if not re.match('^[0-9a-fA-F]*$', tx):
tx = tx.encode('hex')
return make_request('https://blockchain.info/pushtx', 'tx='+tx)
def eligius_pushtx(tx):
if not re.match('^[0-9a-fA-F]*$', tx):
tx = tx.encode('hex')
s = make_request(
'http://eligius.st/~wizkid057/newstats/pushtxn.php',
'transaction='+tx+'&send=Push')
strings = re.findall('string[^"]*"[^"]*"', s)
for string in strings:
quote = re.findall('"[^"]*"', string)[0]
if len(quote) >= 5:
return quote[1:-1]
def blockr_pushtx(tx, network='btc'):
if network == 'testnet':
blockr_url = 'http://tbtc.blockr.io/api/v1/tx/push'
elif network == 'btc':
blockr_url = 'http://btc.blockr.io/api/v1/tx/push'
else:
raise Exception(
'Unsupported network {0} for blockr_pushtx'.format(network))
if not re.match('^[0-9a-fA-F]*$', tx):
tx = tx.encode('hex')
return make_request(blockr_url, '{"hex":"%s"}' % tx)
def helloblock_pushtx(tx):
if not re.match('^[0-9a-fA-F]*$', tx):
tx = tx.encode('hex')
return make_request('https://mainnet.helloblock.io/v1/transactions',
'rawTxHex='+tx)
pushtx_getters = {
'bci': bci_pushtx,
'blockr': blockr_pushtx,
'helloblock': helloblock_pushtx
}
def pushtx(*args, **kwargs):
f = pushtx_getters.get(kwargs.get('source', ''), bci_pushtx)
return f(*args)
def last_block_height(network='btc'):
if network == 'testnet':
data = make_request('http://tbtc.blockr.io/api/v1/block/info/last')
jsonobj = json.loads(data.decode("utf-8"))
return jsonobj["data"]["nb"]
data = make_request('https://blockchain.info/latestblock')
jsonobj = json.loads(data.decode("utf-8"))
return jsonobj["height"]
# Gets a specific transaction
def bci_fetchtx(txhash):
if isinstance(txhash, list):
return [bci_fetchtx(h) for h in txhash]
if not re.match('^[0-9a-fA-F]*$', txhash):
txhash = txhash.encode('hex')
data = make_request('https://blockchain.info/rawtx/'+txhash+'?format=hex')
return data
def blockr_fetchtx(txhash, network='btc'):
if network == 'testnet':
blockr_url = 'http://tbtc.blockr.io/api/v1/tx/raw/'
elif network == 'btc':
blockr_url = 'http://btc.blockr.io/api/v1/tx/raw/'
else:
raise Exception(
'Unsupported network {0} for blockr_fetchtx'.format(network))
if isinstance(txhash, list):
txhash = ','.join([x.encode('hex') if not re.match('^[0-9a-fA-F]*$', x)
else x for x in txhash])
jsondata = json.loads(make_request(blockr_url+txhash).decode("utf-8"))
return [d['tx']['hex'] for d in jsondata['data']]
else:
if not re.match('^[0-9a-fA-F]*$', txhash):
txhash = txhash.encode('hex')
jsondata = json.loads(make_request(blockr_url+txhash).decode("utf-8"))
return jsondata['data']['tx']['hex']
def helloblock_fetchtx(txhash, network='btc'):
if isinstance(txhash, list):
return [helloblock_fetchtx(h) for h in txhash]
if not re.match('^[0-9a-fA-F]*$', txhash):
txhash = txhash.encode('hex')
if network == 'testnet':
url = 'https://testnet.helloblock.io/v1/transactions/'
elif network == 'btc':
url = 'https://mainnet.helloblock.io/v1/transactions/'
else:
raise Exception(
'Unsupported network {0} for helloblock_fetchtx'.format(network))
data = json.loads(make_request(url + txhash).decode("utf-8"))["data"]["transaction"]
o = {
"locktime": data["locktime"],
"version": data["version"],
"ins": [],
"outs": []
}
for inp in data["inputs"]:
o["ins"].append({
"script": inp["scriptSig"],
"outpoint": {
"index": inp["prevTxoutIndex"],
"hash": inp["prevTxHash"],
},
"sequence": 4294967295
})
for outp in data["outputs"]:
o["outs"].append({
"value": outp["value"],
"script": outp["scriptPubKey"]
})
from .transaction import serialize
from .transaction import txhash as TXHASH
tx = serialize(o)
assert TXHASH(tx) == txhash
return tx
fetchtx_getters = {
'bci': bci_fetchtx,
'blockr': blockr_fetchtx,
'helloblock': helloblock_fetchtx
}
def fetchtx(*args, **kwargs):
f = fetchtx_getters.get(kwargs.get('source', ''), bci_fetchtx)
return f(*args)
def firstbits(address):
if len(address) >= 25:
return make_request('https://blockchain.info/q/getfirstbits/'+address)
else:
return make_request(
'https://blockchain.info/q/resolvefirstbits/'+address)
def get_block_at_height(height):
j = json.loads(make_request("https://blockchain.info/block-height/" +
str(height)+"?format=json").decode("utf-8"))
for b in j['blocks']:
if b['main_chain'] is True:
return b
raise Exception("Block at this height not found")
def _get_block(inp):
if len(str(inp)) < 64:
return get_block_at_height(inp)
else:
return json.loads(make_request(
'https://blockchain.info/rawblock/'+inp).decode("utf-8"))
def bci_get_block_header_data(inp):
j = _get_block(inp)
return {
'version': j['ver'],
'hash': j['hash'],
'prevhash': j['prev_block'],
'timestamp': j['time'],
'merkle_root': j['mrkl_root'],
'bits': j['bits'],
'nonce': j['nonce'],
}
def blockr_get_block_header_data(height, network='btc'):
if network == 'testnet':
blockr_url = "http://tbtc.blockr.io/api/v1/block/raw/"
elif network == 'btc':
blockr_url = "http://btc.blockr.io/api/v1/block/raw/"
else:
raise Exception(
'Unsupported network {0} for blockr_get_block_header_data'.format(network))
k = json.loads(make_request(blockr_url + str(height)).decode("utf-8"))
j = k['data']
return {
'version': j['version'],
'hash': j['hash'],
'prevhash': j['previousblockhash'],
'timestamp': j['time'],
'merkle_root': j['merkleroot'],
'bits': int(j['bits'], 16),
'nonce': j['nonce'],
}
def get_block_timestamp(height, network='btc'):
if network == 'testnet':
blockr_url = "http://tbtc.blockr.io/api/v1/block/info/"
elif network == 'btc':
blockr_url = "http://btc.blockr.io/api/v1/block/info/"
else:
raise Exception(
'Unsupported network {0} for get_block_timestamp'.format(network))
import time, calendar
if isinstance(height, list):
k = json.loads(make_request(blockr_url + ','.join([str(x) for x in height])).decode("utf-8"))
o = {x['nb']: calendar.timegm(time.strptime(x['time_utc'],
"%Y-%m-%dT%H:%M:%SZ")) for x in k['data']}
return [o[x] for x in height]
else:
k = json.loads(make_request(blockr_url + str(height)).decode("utf-8"))
j = k['data']['time_utc']
return calendar.timegm(time.strptime(j, "%Y-%m-%dT%H:%M:%SZ"))
block_header_data_getters = {
'bci': bci_get_block_header_data,
'blockr': blockr_get_block_header_data
}
def get_block_header_data(inp, **kwargs):
f = block_header_data_getters.get(kwargs.get('source', ''),
bci_get_block_header_data)
return f(inp, **kwargs)
def get_txs_in_block(inp):
j = _get_block(inp)
hashes = [t['hash'] for t in j['tx']]
return hashes
def get_block_height(txhash):
j = json.loads(make_request('https://blockchain.info/rawtx/'+txhash).decode("utf-8"))
return j['block_height']
# fromAddr, toAddr, 12345, changeAddress
def get_tx_composite(inputs, outputs, output_value, change_address=None, network=None):
"""mktx using blockcypher API"""
inputs = [inputs] if not isinstance(inputs, list) else inputs
outputs = [outputs] if not isinstance(outputs, list) else outputs
network = set_network(change_address or inputs) if not network else network.lower()
url = "http://api.blockcypher.com/v1/btc/{network}/txs/new?includeToSignTx=true".format(
network=('test3' if network=='testnet' else 'main'))
is_address = lambda a: bool(re.match("^[123mn][a-km-zA-HJ-NP-Z0-9]{26,33}$", a))
if any([is_address(x) for x in inputs]):
inputs_type = 'addresses' # also accepts UTXOs, only addresses supported presently
if any([is_address(x) for x in outputs]):
outputs_type = 'addresses' # TODO: add UTXO support
data = {
'inputs': [{inputs_type: inputs}],
'confirmations': 0,
'preference': 'high',
'outputs': [{outputs_type: outputs, "value": output_value}]
}
if change_address:
data["change_address"] = change_address #
jdata = json.loads(make_request(url, data))
hash, txh = jdata.get("tosign")[0], jdata.get("tosign_tx")[0]
assert bin_dbl_sha256(txh.decode('hex')).encode('hex') == hash, "checksum mismatch %s" % hash
return txh.encode("utf-8")
blockcypher_mktx = get_tx_composite

View File

@ -1,50 +0,0 @@
from .main import *
def serialize_header(inp):
o = encode(inp['version'], 256, 4)[::-1] + \
inp['prevhash'].decode('hex')[::-1] + \
inp['merkle_root'].decode('hex')[::-1] + \
encode(inp['timestamp'], 256, 4)[::-1] + \
encode(inp['bits'], 256, 4)[::-1] + \
encode(inp['nonce'], 256, 4)[::-1]
h = bin_sha256(bin_sha256(o))[::-1].encode('hex')
assert h == inp['hash'], (sha256(o), inp['hash'])
return o.encode('hex')
def deserialize_header(inp):
inp = inp.decode('hex')
return {
"version": decode(inp[:4][::-1], 256),
"prevhash": inp[4:36][::-1].encode('hex'),
"merkle_root": inp[36:68][::-1].encode('hex'),
"timestamp": decode(inp[68:72][::-1], 256),
"bits": decode(inp[72:76][::-1], 256),
"nonce": decode(inp[76:80][::-1], 256),
"hash": bin_sha256(bin_sha256(inp))[::-1].encode('hex')
}
def mk_merkle_proof(header, hashes, index):
nodes = [h.decode('hex')[::-1] for h in hashes]
if len(nodes) % 2 and len(nodes) > 2:
nodes.append(nodes[-1])
layers = [nodes]
while len(nodes) > 1:
newnodes = []
for i in range(0, len(nodes) - 1, 2):
newnodes.append(bin_sha256(bin_sha256(nodes[i] + nodes[i+1])))
if len(newnodes) % 2 and len(newnodes) > 2:
newnodes.append(newnodes[-1])
nodes = newnodes
layers.append(nodes)
# Sanity check, make sure merkle root is valid
assert nodes[0][::-1].encode('hex') == header['merkle_root']
merkle_siblings = \
[layers[i][(index >> i) ^ 1] for i in range(len(layers)-1)]
return {
"hash": hashes[index],
"siblings": [x[::-1].encode('hex') for x in merkle_siblings],
"header": header
}

View File

@ -1,128 +0,0 @@
from .main import *
from .transaction import *
from .bci import *
from .deterministic import *
from .blocks import *
# Takes privkey, address, value (satoshis), fee (satoshis)
def send(frm, to, value, fee=10000, **kwargs):
return sendmultitx(frm, to + ":" + str(value), fee, **kwargs)
# Takes privkey, "address1:value1,address2:value2" (satoshis), fee (satoshis)
def sendmultitx(frm, *args, **kwargs):
tv, fee = args[:-1], int(args[-1])
outs = []
outvalue = 0
for a in tv:
outs.append(a)
outvalue += int(a.split(":")[1])
u = unspent(privtoaddr(frm), **kwargs)
u2 = select(u, int(outvalue)+int(fee))
argz = u2 + outs + [privtoaddr(frm), fee]
tx = mksend(*argz)
tx2 = signall(tx, frm)
return pushtx(tx2, **kwargs)
# Takes address, address, value (satoshis), fee(satoshis)
def preparetx(frm, to, value, fee=10000, **kwargs):
tovalues = to + ":" + str(value)
return preparemultitx(frm, tovalues, fee, **kwargs)
# Takes address, address:value, address:value ... (satoshis), fee(satoshis)
def preparemultitx(frm, *args, **kwargs):
tv, fee = args[:-1], int(args[-1])
outs = []
outvalue = 0
for a in tv:
outs.append(a)
outvalue += int(a.split(":")[1])
u = unspent(frm, **kwargs)
u2 = select(u, int(outvalue)+int(fee))
argz = u2 + outs + [frm, fee]
return mksend(*argz)
# BIP32 hierarchical deterministic multisig script
def bip32_hdm_script(*args):
if len(args) == 3:
keys, req, path = args
else:
i, keys, path = 0, [], []
while len(args[i]) > 40:
keys.append(args[i])
i += 1
req = int(args[i])
path = map(int, args[i+1:])
pubs = sorted(map(lambda x: bip32_descend(x, path), keys))
return mk_multisig_script(pubs, req)
# BIP32 hierarchical deterministic multisig address
def bip32_hdm_addr(*args):
return scriptaddr(bip32_hdm_script(*args))
# Setup a coinvault transaction
def setup_coinvault_tx(tx, script):
txobj = deserialize(tx)
N = deserialize_script(script)[-2]
for inp in txobj["ins"]:
inp["script"] = serialize_script([None] * (N+1) + [script])
return serialize(txobj)
# Sign a coinvault transaction
def sign_coinvault_tx(tx, priv):
pub = privtopub(priv)
txobj = deserialize(tx)
subscript = deserialize_script(txobj['ins'][0]['script'])
oscript = deserialize_script(subscript[-1])
k, pubs = oscript[0], oscript[1:-2]
for j in range(len(txobj['ins'])):
scr = deserialize_script(txobj['ins'][j]['script'])
for i, p in enumerate(pubs):
if p == pub:
scr[i+1] = multisign(tx, j, subscript[-1], priv)
if len(filter(lambda x: x, scr[1:-1])) >= k:
scr = [None] + filter(lambda x: x, scr[1:-1])[:k] + [scr[-1]]
txobj['ins'][j]['script'] = serialize_script(scr)
return serialize(txobj)
# Inspects a transaction
def inspect(tx, **kwargs):
d = deserialize(tx)
isum = 0
ins = {}
for _in in d['ins']:
h = _in['outpoint']['hash']
i = _in['outpoint']['index']
prevout = deserialize(fetchtx(h, **kwargs))['outs'][i]
isum += prevout['value']
a = script_to_address(prevout['script'])
ins[a] = ins.get(a, 0) + prevout['value']
outs = []
osum = 0
for _out in d['outs']:
outs.append({'address': script_to_address(_out['script']),
'value': _out['value']})
osum += _out['value']
return {
'fee': isum - osum,
'outs': outs,
'ins': ins
}
def merkle_prove(txhash):
blocknum = str(get_block_height(txhash))
header = get_block_header_data(blocknum)
hashes = get_txs_in_block(blocknum)
i = hashes.index(txhash)
return mk_merkle_proof(header, hashes, i)

View File

@ -1,199 +0,0 @@
from .main import *
import hmac
import hashlib
from binascii import hexlify
# Electrum wallets
def electrum_stretch(seed):
return slowsha(seed)
# Accepts seed or stretched seed, returns master public key
def electrum_mpk(seed):
if len(seed) == 32:
seed = electrum_stretch(seed)
return privkey_to_pubkey(seed)[2:]
# Accepts (seed or stretched seed), index and secondary index
# (conventionally 0 for ordinary addresses, 1 for change) , returns privkey
def electrum_privkey(seed, n, for_change=0):
if len(seed) == 32:
seed = electrum_stretch(seed)
mpk = electrum_mpk(seed)
offset = dbl_sha256(from_int_representation_to_bytes(n)+b':'+from_int_representation_to_bytes(for_change)+b':'+binascii.unhexlify(mpk))
return add_privkeys(seed, offset)
# Accepts (seed or stretched seed or master pubkey), index and secondary index
# (conventionally 0 for ordinary addresses, 1 for change) , returns pubkey
def electrum_pubkey(masterkey, n, for_change=0):
if len(masterkey) == 32:
mpk = electrum_mpk(electrum_stretch(masterkey))
elif len(masterkey) == 64:
mpk = electrum_mpk(masterkey)
else:
mpk = masterkey
bin_mpk = encode_pubkey(mpk, 'bin_electrum')
offset = bin_dbl_sha256(from_int_representation_to_bytes(n)+b':'+from_int_representation_to_bytes(for_change)+b':'+bin_mpk)
return add_pubkeys('04'+mpk, privtopub(offset))
# seed/stretched seed/pubkey -> address (convenience method)
def electrum_address(masterkey, n, for_change=0, version=0):
return pubkey_to_address(electrum_pubkey(masterkey, n, for_change), version)
# Given a master public key, a private key from that wallet and its index,
# cracks the secret exponent which can be used to generate all other private
# keys in the wallet
def crack_electrum_wallet(mpk, pk, n, for_change=0):
bin_mpk = encode_pubkey(mpk, 'bin_electrum')
offset = dbl_sha256(str(n)+':'+str(for_change)+':'+bin_mpk)
return subtract_privkeys(pk, offset)
# Below code ASSUMES binary inputs and compressed pubkeys
MAINNET_PRIVATE = b'\x04\x88\xAD\xE4'
MAINNET_PUBLIC = b'\x04\x88\xB2\x1E'
TESTNET_PRIVATE = b'\x04\x35\x83\x94'
TESTNET_PUBLIC = b'\x04\x35\x87\xCF'
PRIVATE = [MAINNET_PRIVATE, TESTNET_PRIVATE]
PUBLIC = [MAINNET_PUBLIC, TESTNET_PUBLIC]
# BIP32 child key derivation
def raw_bip32_ckd(rawtuple, i):
vbytes, depth, fingerprint, oldi, chaincode, key = rawtuple
i = int(i)
if vbytes in PRIVATE:
priv = key
pub = privtopub(key)
else:
pub = key
if i >= 2**31:
if vbytes in PUBLIC:
raise Exception("Can't do private derivation on public key!")
I = hmac.new(chaincode, b'\x00'+priv[:32]+encode(i, 256, 4), hashlib.sha512).digest()
else:
I = hmac.new(chaincode, pub+encode(i, 256, 4), hashlib.sha512).digest()
if vbytes in PRIVATE:
newkey = add_privkeys(I[:32]+B'\x01', priv)
fingerprint = bin_hash160(privtopub(key))[:4]
if vbytes in PUBLIC:
newkey = add_pubkeys(compress(privtopub(I[:32])), key)
fingerprint = bin_hash160(key)[:4]
return (vbytes, depth + 1, fingerprint, i, I[32:], newkey)
def bip32_serialize(rawtuple):
vbytes, depth, fingerprint, i, chaincode, key = rawtuple
i = encode(i, 256, 4)
chaincode = encode(hash_to_int(chaincode), 256, 32)
keydata = b'\x00'+key[:-1] if vbytes in PRIVATE else key
bindata = vbytes + from_int_to_byte(depth % 256) + fingerprint + i + chaincode + keydata
return changebase(bindata+bin_dbl_sha256(bindata)[:4], 256, 58)
def bip32_deserialize(data):
dbin = changebase(data, 58, 256)
if bin_dbl_sha256(dbin[:-4])[:4] != dbin[-4:]:
raise Exception("Invalid checksum")
vbytes = dbin[0:4]
depth = from_byte_to_int(dbin[4])
fingerprint = dbin[5:9]
i = decode(dbin[9:13], 256)
chaincode = dbin[13:45]
key = dbin[46:78]+b'\x01' if vbytes in PRIVATE else dbin[45:78]
return (vbytes, depth, fingerprint, i, chaincode, key)
def raw_bip32_privtopub(rawtuple):
vbytes, depth, fingerprint, i, chaincode, key = rawtuple
newvbytes = MAINNET_PUBLIC if vbytes == MAINNET_PRIVATE else TESTNET_PUBLIC
return (newvbytes, depth, fingerprint, i, chaincode, privtopub(key))
def bip32_privtopub(data):
return bip32_serialize(raw_bip32_privtopub(bip32_deserialize(data)))
def bip32_ckd(data, i):
return bip32_serialize(raw_bip32_ckd(bip32_deserialize(data), i))
def bip32_master_key(seed, vbytes=MAINNET_PRIVATE):
I = hmac.new(from_string_to_bytes("Bitcoin seed"), seed, hashlib.sha512).digest()
return bip32_serialize((vbytes, 0, b'\x00'*4, 0, I[32:], I[:32]+b'\x01'))
def bip32_bin_extract_key(data):
return bip32_deserialize(data)[-1]
def bip32_extract_key(data):
return safe_hexlify(bip32_deserialize(data)[-1])
# Exploits the same vulnerability as above in Electrum wallets
# Takes a BIP32 pubkey and one of the child privkeys of its corresponding
# privkey and returns the BIP32 privkey associated with that pubkey
def raw_crack_bip32_privkey(parent_pub, priv):
vbytes, depth, fingerprint, i, chaincode, key = priv
pvbytes, pdepth, pfingerprint, pi, pchaincode, pkey = parent_pub
i = int(i)
if i >= 2**31:
raise Exception("Can't crack private derivation!")
I = hmac.new(pchaincode, pkey+encode(i, 256, 4), hashlib.sha512).digest()
pprivkey = subtract_privkeys(key, I[:32]+b'\x01')
newvbytes = MAINNET_PRIVATE if vbytes == MAINNET_PUBLIC else TESTNET_PRIVATE
return (newvbytes, pdepth, pfingerprint, pi, pchaincode, pprivkey)
def crack_bip32_privkey(parent_pub, priv):
dsppub = bip32_deserialize(parent_pub)
dspriv = bip32_deserialize(priv)
return bip32_serialize(raw_crack_bip32_privkey(dsppub, dspriv))
def coinvault_pub_to_bip32(*args):
if len(args) == 1:
args = args[0].split(' ')
vals = map(int, args[34:])
I1 = ''.join(map(chr, vals[:33]))
I2 = ''.join(map(chr, vals[35:67]))
return bip32_serialize((MAINNET_PUBLIC, 0, b'\x00'*4, 0, I2, I1))
def coinvault_priv_to_bip32(*args):
if len(args) == 1:
args = args[0].split(' ')
vals = map(int, args[34:])
I2 = ''.join(map(chr, vals[35:67]))
I3 = ''.join(map(chr, vals[72:104]))
return bip32_serialize((MAINNET_PRIVATE, 0, b'\x00'*4, 0, I2, I3+b'\x01'))
def bip32_descend(*args):
if len(args) == 2 and isinstance(args[1], list):
key, path = args
else:
key, path = args[0], map(int, args[1:])
for p in path:
key = bip32_ckd(key, p)
return bip32_extract_key(key)

File diff suppressed because it is too large Load Diff

View File

@ -1,581 +0,0 @@
#!/usr/bin/python
from .py2specials import *
from .py3specials import *
import binascii
import hashlib
import re
import sys
import os
import base64
import time
import random
import hmac
from .ripemd import *
# Elliptic curve parameters (secp256k1)
P = 2**256 - 2**32 - 977
N = 115792089237316195423570985008687907852837564279074904382605163141518161494337
A = 0
B = 7
Gx = 55066263022277343669578718895168534326250603453777594175500187360389116729240
Gy = 32670510020758816978083085130507043184471273380659243275938904335757337482424
G = (Gx, Gy)
def change_curve(p, n, a, b, gx, gy):
global P, N, A, B, Gx, Gy, G
P, N, A, B, Gx, Gy = p, n, a, b, gx, gy
G = (Gx, Gy)
def getG():
return G
# Extended Euclidean Algorithm
def inv(a, n):
if a == 0:
return 0
lm, hm = 1, 0
low, high = a % n, n
while low > 1:
r = high//low
nm, new = hm-lm*r, high-low*r
lm, low, hm, high = nm, new, lm, low
return lm % n
# JSON access (for pybtctool convenience)
def access(obj, prop):
if isinstance(obj, dict):
if prop in obj:
return obj[prop]
elif '.' in prop:
return obj[float(prop)]
else:
return obj[int(prop)]
else:
return obj[int(prop)]
def multiaccess(obj, prop):
return [access(o, prop) for o in obj]
def slice(obj, start=0, end=2**200):
return obj[int(start):int(end)]
def count(obj):
return len(obj)
_sum = sum
def sum(obj):
return _sum(obj)
def isinf(p):
return p[0] == 0 and p[1] == 0
def to_jacobian(p):
o = (p[0], p[1], 1)
return o
def jacobian_double(p):
if not p[1]:
return (0, 0, 0)
ysq = (p[1] ** 2) % P
S = (4 * p[0] * ysq) % P
M = (3 * p[0] ** 2 + A * p[2] ** 4) % P
nx = (M**2 - 2 * S) % P
ny = (M * (S - nx) - 8 * ysq ** 2) % P
nz = (2 * p[1] * p[2]) % P
return (nx, ny, nz)
def jacobian_add(p, q):
if not p[1]:
return q
if not q[1]:
return p
U1 = (p[0] * q[2] ** 2) % P
U2 = (q[0] * p[2] ** 2) % P
S1 = (p[1] * q[2] ** 3) % P
S2 = (q[1] * p[2] ** 3) % P
if U1 == U2:
if S1 != S2:
return (0, 0, 1)
return jacobian_double(p)
H = U2 - U1
R = S2 - S1
H2 = (H * H) % P
H3 = (H * H2) % P
U1H2 = (U1 * H2) % P
nx = (R ** 2 - H3 - 2 * U1H2) % P
ny = (R * (U1H2 - nx) - S1 * H3) % P
nz = (H * p[2] * q[2]) % P
return (nx, ny, nz)
def from_jacobian(p):
z = inv(p[2], P)
return ((p[0] * z**2) % P, (p[1] * z**3) % P)
def jacobian_multiply(a, n):
if a[1] == 0 or n == 0:
return (0, 0, 1)
if n == 1:
return a
if n < 0 or n >= N:
return jacobian_multiply(a, n % N)
if (n % 2) == 0:
return jacobian_double(jacobian_multiply(a, n//2))
if (n % 2) == 1:
return jacobian_add(jacobian_double(jacobian_multiply(a, n//2)), a)
def fast_multiply(a, n):
return from_jacobian(jacobian_multiply(to_jacobian(a), n))
def fast_add(a, b):
return from_jacobian(jacobian_add(to_jacobian(a), to_jacobian(b)))
# Functions for handling pubkey and privkey formats
def get_pubkey_format(pub):
if is_python2:
two = '\x02'
three = '\x03'
four = '\x04'
else:
two = 2
three = 3
four = 4
if isinstance(pub, (tuple, list)): return 'decimal'
elif len(pub) == 65 and pub[0] == four: return 'bin'
elif len(pub) == 130 and pub[0:2] == '04': return 'hex'
elif len(pub) == 33 and pub[0] in [two, three]: return 'bin_compressed'
elif len(pub) == 66 and pub[0:2] in ['02', '03']: return 'hex_compressed'
elif len(pub) == 64: return 'bin_electrum'
elif len(pub) == 128: return 'hex_electrum'
else: raise Exception("Pubkey not in recognized format")
def encode_pubkey(pub, formt):
if not isinstance(pub, (tuple, list)):
pub = decode_pubkey(pub)
if formt == 'decimal': return pub
elif formt == 'bin': return b'\x04' + encode(pub[0], 256, 32) + encode(pub[1], 256, 32)
elif formt == 'bin_compressed':
return from_int_to_byte(2+(pub[1] % 2)) + encode(pub[0], 256, 32)
elif formt == 'hex': return '04' + encode(pub[0], 16, 64) + encode(pub[1], 16, 64)
elif formt == 'hex_compressed':
return '0'+str(2+(pub[1] % 2)) + encode(pub[0], 16, 64)
elif formt == 'bin_electrum': return encode(pub[0], 256, 32) + encode(pub[1], 256, 32)
elif formt == 'hex_electrum': return encode(pub[0], 16, 64) + encode(pub[1], 16, 64)
else: raise Exception("Invalid format!")
def decode_pubkey(pub, formt=None):
if not formt: formt = get_pubkey_format(pub)
if formt == 'decimal': return pub
elif formt == 'bin': return (decode(pub[1:33], 256), decode(pub[33:65], 256))
elif formt == 'bin_compressed':
x = decode(pub[1:33], 256)
beta = pow(int(x*x*x+A*x+B), int((P+1)//4), int(P))
y = (P-beta) if ((beta + from_byte_to_int(pub[0])) % 2) else beta
return (x, y)
elif formt == 'hex': return (decode(pub[2:66], 16), decode(pub[66:130], 16))
elif formt == 'hex_compressed':
return decode_pubkey(safe_from_hex(pub), 'bin_compressed')
elif formt == 'bin_electrum':
return (decode(pub[:32], 256), decode(pub[32:64], 256))
elif formt == 'hex_electrum':
return (decode(pub[:64], 16), decode(pub[64:128], 16))
else: raise Exception("Invalid format!")
def get_privkey_format(priv):
if isinstance(priv, int_types): return 'decimal'
elif len(priv) == 32: return 'bin'
elif len(priv) == 33: return 'bin_compressed'
elif len(priv) == 64: return 'hex'
elif len(priv) == 66: return 'hex_compressed'
else:
bin_p = b58check_to_bin(priv)
if len(bin_p) == 32: return 'wif'
elif len(bin_p) == 33: return 'wif_compressed'
else: raise Exception("WIF does not represent privkey")
def encode_privkey(priv, formt, vbyte=0):
if not isinstance(priv, int_types):
return encode_privkey(decode_privkey(priv), formt, vbyte)
if formt == 'decimal': return priv
elif formt == 'bin': return encode(priv, 256, 32)
elif formt == 'bin_compressed': return encode(priv, 256, 32)+b'\x01'
elif formt == 'hex': return encode(priv, 16, 64)
elif formt == 'hex_compressed': return encode(priv, 16, 64)+'01'
elif formt == 'wif':
return bin_to_b58check(encode(priv, 256, 32), 128+int(vbyte))
elif formt == 'wif_compressed':
return bin_to_b58check(encode(priv, 256, 32)+b'\x01', 128+int(vbyte))
else: raise Exception("Invalid format!")
def decode_privkey(priv,formt=None):
if not formt: formt = get_privkey_format(priv)
if formt == 'decimal': return priv
elif formt == 'bin': return decode(priv, 256)
elif formt == 'bin_compressed': return decode(priv[:32], 256)
elif formt == 'hex': return decode(priv, 16)
elif formt == 'hex_compressed': return decode(priv[:64], 16)
elif formt == 'wif': return decode(b58check_to_bin(priv),256)
elif formt == 'wif_compressed':
return decode(b58check_to_bin(priv)[:32],256)
else: raise Exception("WIF does not represent privkey")
def add_pubkeys(p1, p2):
f1, f2 = get_pubkey_format(p1), get_pubkey_format(p2)
return encode_pubkey(fast_add(decode_pubkey(p1, f1), decode_pubkey(p2, f2)), f1)
def add_privkeys(p1, p2):
f1, f2 = get_privkey_format(p1), get_privkey_format(p2)
return encode_privkey((decode_privkey(p1, f1) + decode_privkey(p2, f2)) % N, f1)
def mul_privkeys(p1, p2):
f1, f2 = get_privkey_format(p1), get_privkey_format(p2)
return encode_privkey((decode_privkey(p1, f1) * decode_privkey(p2, f2)) % N, f1)
def multiply(pubkey, privkey):
f1, f2 = get_pubkey_format(pubkey), get_privkey_format(privkey)
pubkey, privkey = decode_pubkey(pubkey, f1), decode_privkey(privkey, f2)
# http://safecurves.cr.yp.to/twist.html
if not isinf(pubkey) and (pubkey[0]**3+B-pubkey[1]*pubkey[1]) % P != 0:
raise Exception("Point not on curve")
return encode_pubkey(fast_multiply(pubkey, privkey), f1)
def divide(pubkey, privkey):
factor = inv(decode_privkey(privkey), N)
return multiply(pubkey, factor)
def compress(pubkey):
f = get_pubkey_format(pubkey)
if 'compressed' in f: return pubkey
elif f == 'bin': return encode_pubkey(decode_pubkey(pubkey, f), 'bin_compressed')
elif f == 'hex' or f == 'decimal':
return encode_pubkey(decode_pubkey(pubkey, f), 'hex_compressed')
def decompress(pubkey):
f = get_pubkey_format(pubkey)
if 'compressed' not in f: return pubkey
elif f == 'bin_compressed': return encode_pubkey(decode_pubkey(pubkey, f), 'bin')
elif f == 'hex_compressed' or f == 'decimal':
return encode_pubkey(decode_pubkey(pubkey, f), 'hex')
def privkey_to_pubkey(privkey):
f = get_privkey_format(privkey)
privkey = decode_privkey(privkey, f)
if privkey >= N:
raise Exception("Invalid privkey")
if f in ['bin', 'bin_compressed', 'hex', 'hex_compressed', 'decimal']:
return encode_pubkey(fast_multiply(G, privkey), f)
else:
return encode_pubkey(fast_multiply(G, privkey), f.replace('wif', 'hex'))
privtopub = privkey_to_pubkey
def privkey_to_address(priv, magicbyte=0):
return pubkey_to_address(privkey_to_pubkey(priv), magicbyte)
privtoaddr = privkey_to_address
def neg_pubkey(pubkey):
f = get_pubkey_format(pubkey)
pubkey = decode_pubkey(pubkey, f)
return encode_pubkey((pubkey[0], (P-pubkey[1]) % P), f)
def neg_privkey(privkey):
f = get_privkey_format(privkey)
privkey = decode_privkey(privkey, f)
return encode_privkey((N - privkey) % N, f)
def subtract_pubkeys(p1, p2):
f1, f2 = get_pubkey_format(p1), get_pubkey_format(p2)
k2 = decode_pubkey(p2, f2)
return encode_pubkey(fast_add(decode_pubkey(p1, f1), (k2[0], (P - k2[1]) % P)), f1)
def subtract_privkeys(p1, p2):
f1, f2 = get_privkey_format(p1), get_privkey_format(p2)
k2 = decode_privkey(p2, f2)
return encode_privkey((decode_privkey(p1, f1) - k2) % N, f1)
# Hashes
def bin_hash160(string):
intermed = hashlib.sha256(string).digest()
digest = ''
try:
digest = hashlib.new('ripemd160', intermed).digest()
except:
digest = RIPEMD160(intermed).digest()
return digest
def hash160(string):
return safe_hexlify(bin_hash160(string))
def bin_sha256(string):
binary_data = string if isinstance(string, bytes) else bytes(string, 'utf-8')
return hashlib.sha256(binary_data).digest()
def sha256(string):
return bytes_to_hex_string(bin_sha256(string))
def bin_ripemd160(string):
try:
digest = hashlib.new('ripemd160', string).digest()
except:
digest = RIPEMD160(string).digest()
return digest
def ripemd160(string):
return safe_hexlify(bin_ripemd160(string))
def bin_dbl_sha256(s):
bytes_to_hash = from_string_to_bytes(s)
return hashlib.sha256(hashlib.sha256(bytes_to_hash).digest()).digest()
def dbl_sha256(string):
return safe_hexlify(bin_dbl_sha256(string))
def bin_slowsha(string):
string = from_string_to_bytes(string)
orig_input = string
for i in range(100000):
string = hashlib.sha256(string + orig_input).digest()
return string
def slowsha(string):
return safe_hexlify(bin_slowsha(string))
def hash_to_int(x):
if len(x) in [40, 64]:
return decode(x, 16)
return decode(x, 256)
def num_to_var_int(x):
x = int(x)
if x < 253: return from_int_to_byte(x)
elif x < 65536: return from_int_to_byte(253)+encode(x, 256, 2)[::-1]
elif x < 4294967296: return from_int_to_byte(254) + encode(x, 256, 4)[::-1]
else: return from_int_to_byte(255) + encode(x, 256, 8)[::-1]
# WTF, Electrum?
def electrum_sig_hash(message):
padded = b"\x18Bitcoin Signed Message:\n" + num_to_var_int(len(message)) + from_string_to_bytes(message)
return bin_dbl_sha256(padded)
def random_key():
# Gotta be secure after that java.SecureRandom fiasco...
entropy = random_string(32) \
+ str(random.randrange(2**256)) \
+ str(int(time.time() * 1000000))
return sha256(entropy)
def random_electrum_seed():
entropy = os.urandom(32) \
+ str(random.randrange(2**256)) \
+ str(int(time.time() * 1000000))
return sha256(entropy)[:32]
# Encodings
def b58check_to_bin(inp):
leadingzbytes = len(re.match('^1*', inp).group(0))
data = b'\x00' * leadingzbytes + changebase(inp, 58, 256)
assert bin_dbl_sha256(data[:-4])[:4] == data[-4:]
return data[1:-4]
def get_version_byte(inp):
leadingzbytes = len(re.match('^1*', inp).group(0))
data = b'\x00' * leadingzbytes + changebase(inp, 58, 256)
assert bin_dbl_sha256(data[:-4])[:4] == data[-4:]
return ord(data[0])
def hex_to_b58check(inp, magicbyte=0):
return bin_to_b58check(binascii.unhexlify(inp), magicbyte)
def b58check_to_hex(inp):
return safe_hexlify(b58check_to_bin(inp))
def pubkey_to_address(pubkey, magicbyte=0):
if isinstance(pubkey, (list, tuple)):
pubkey = encode_pubkey(pubkey, 'bin')
if len(pubkey) in [66, 130]:
return bin_to_b58check(
bin_hash160(binascii.unhexlify(pubkey)), magicbyte)
return bin_to_b58check(bin_hash160(pubkey), magicbyte)
pubtoaddr = pubkey_to_address
def is_privkey(priv):
try:
get_privkey_format(priv)
return True
except:
return False
def is_pubkey(pubkey):
try:
get_pubkey_format(pubkey)
return True
except:
return False
def is_address(addr):
ADDR_RE = re.compile("^[123mn][a-km-zA-HJ-NP-Z0-9]{26,33}$")
return bool(ADDR_RE.match(addr))
# EDCSA
def encode_sig(v, r, s):
vb, rb, sb = from_int_to_byte(v), encode(r, 256), encode(s, 256)
result = base64.b64encode(vb+b'\x00'*(32-len(rb))+rb+b'\x00'*(32-len(sb))+sb)
return result if is_python2 else str(result, 'utf-8')
def decode_sig(sig):
bytez = base64.b64decode(sig)
return from_byte_to_int(bytez[0]), decode(bytez[1:33], 256), decode(bytez[33:], 256)
# https://tools.ietf.org/html/rfc6979#section-3.2
def deterministic_generate_k(msghash, priv):
v = b'\x01' * 32
k = b'\x00' * 32
priv = encode_privkey(priv, 'bin')
msghash = encode(hash_to_int(msghash), 256, 32)
k = hmac.new(k, v+b'\x00'+priv+msghash, hashlib.sha256).digest()
v = hmac.new(k, v, hashlib.sha256).digest()
k = hmac.new(k, v+b'\x01'+priv+msghash, hashlib.sha256).digest()
v = hmac.new(k, v, hashlib.sha256).digest()
return decode(hmac.new(k, v, hashlib.sha256).digest(), 256)
def ecdsa_raw_sign(msghash, priv):
z = hash_to_int(msghash)
k = deterministic_generate_k(msghash, priv)
r, y = fast_multiply(G, k)
s = inv(k, N) * (z + r*decode_privkey(priv)) % N
v, r, s = 27+((y % 2) ^ (0 if s * 2 < N else 1)), r, s if s * 2 < N else N - s
if 'compressed' in get_privkey_format(priv):
v += 4
return v, r, s
def ecdsa_sign(msg, priv):
v, r, s = ecdsa_raw_sign(electrum_sig_hash(msg), priv)
sig = encode_sig(v, r, s)
assert ecdsa_verify(msg, sig,
privtopub(priv)), "Bad Sig!\t %s\nv = %d\n,r = %d\ns = %d" % (sig, v, r, s)
return sig
def ecdsa_raw_verify(msghash, vrs, pub):
v, r, s = vrs
if not (27 <= v <= 34):
return False
w = inv(s, N)
z = hash_to_int(msghash)
u1, u2 = z*w % N, r*w % N
x, y = fast_add(fast_multiply(G, u1), fast_multiply(decode_pubkey(pub), u2))
return bool(r == x and (r % N) and (s % N))
# For BitcoinCore, (msg = addr or msg = "") be default
def ecdsa_verify_addr(msg, sig, addr):
assert is_address(addr)
Q = ecdsa_recover(msg, sig)
magic = get_version_byte(addr)
return (addr == pubtoaddr(Q, int(magic))) or (addr == pubtoaddr(compress(Q), int(magic)))
def ecdsa_verify(msg, sig, pub):
if is_address(pub):
return ecdsa_verify_addr(msg, sig, pub)
return ecdsa_raw_verify(electrum_sig_hash(msg), decode_sig(sig), pub)
def ecdsa_raw_recover(msghash, vrs):
v, r, s = vrs
if not (27 <= v <= 34):
raise ValueError("%d must in range 27-31" % v)
x = r
xcubedaxb = (x*x*x+A*x+B) % P
beta = pow(xcubedaxb, (P+1)//4, P)
y = beta if v % 2 ^ beta % 2 else (P - beta)
# If xcubedaxb is not a quadratic residue, then r cannot be the x coord
# for a point on the curve, and so the sig is invalid
if (xcubedaxb - y*y) % P != 0 or not (r % N) or not (s % N):
return False
z = hash_to_int(msghash)
Gz = jacobian_multiply((Gx, Gy, 1), (N - z) % N)
XY = jacobian_multiply((x, y, 1), s)
Qr = jacobian_add(Gz, XY)
Q = jacobian_multiply(Qr, inv(r, N))
Q = from_jacobian(Q)
# if ecdsa_raw_verify(msghash, vrs, Q):
return Q
# return False
def ecdsa_recover(msg, sig):
v,r,s = decode_sig(sig)
Q = ecdsa_raw_recover(electrum_sig_hash(msg), (v,r,s))
return encode_pubkey(Q, 'hex_compressed') if v >= 31 else encode_pubkey(Q, 'hex')

View File

@ -1,127 +0,0 @@
import hashlib
import os.path
import binascii
import random
from bisect import bisect_left
wordlist_english=list(open(os.path.join(os.path.dirname(os.path.realpath(__file__)),'english.txt'),'r'))
def eint_to_bytes(entint,entbits):
a=hex(entint)[2:].rstrip('L').zfill(32)
print(a)
return binascii.unhexlify(a)
def mnemonic_int_to_words(mint,mint_num_words,wordlist=wordlist_english):
backwords=[wordlist[(mint >> (11*x)) & 0x7FF].strip() for x in range(mint_num_words)]
return backwords[::-1]
def entropy_cs(entbytes):
entropy_size=8*len(entbytes)
checksum_size=entropy_size//32
hd=hashlib.sha256(entbytes).hexdigest()
csint=int(hd,16) >> (256-checksum_size)
return csint,checksum_size
def entropy_to_words(entbytes,wordlist=wordlist_english):
if(len(entbytes) < 4 or len(entbytes) % 4 != 0):
raise ValueError("The size of the entropy must be a multiple of 4 bytes (multiple of 32 bits)")
entropy_size=8*len(entbytes)
csint,checksum_size = entropy_cs(entbytes)
entint=int(binascii.hexlify(entbytes),16)
mint=(entint << checksum_size) | csint
mint_num_words=(entropy_size+checksum_size)//11
return mnemonic_int_to_words(mint,mint_num_words,wordlist)
def words_bisect(word,wordlist=wordlist_english):
lo=bisect_left(wordlist,word)
hi=len(wordlist)-bisect_left(wordlist[:lo:-1],word)
return lo,hi
def words_split(wordstr,wordlist=wordlist_english):
def popword(wordstr,wordlist):
for fwl in range(1,9):
w=wordstr[:fwl].strip()
lo,hi=words_bisect(w,wordlist)
if(hi-lo == 1):
return w,wordstr[fwl:].lstrip()
wordlist=wordlist[lo:hi]
raise Exception("Wordstr %s not found in list" %(w))
words=[]
tail=wordstr
while(len(tail)):
head,tail=popword(tail,wordlist)
words.append(head)
return words
def words_to_mnemonic_int(words,wordlist=wordlist_english):
if(isinstance(words,str)):
words=words_split(words,wordlist)
return sum([wordlist.index(w) << (11*x) for x,w in enumerate(words[::-1])])
def words_verify(words,wordlist=wordlist_english):
if(isinstance(words,str)):
words=words_split(words,wordlist)
mint = words_to_mnemonic_int(words,wordlist)
mint_bits=len(words)*11
cs_bits=mint_bits//32
entropy_bits=mint_bits-cs_bits
eint=mint >> cs_bits
csint=mint & ((1 << cs_bits)-1)
ebytes=_eint_to_bytes(eint,entropy_bits)
return csint == entropy_cs(ebytes)
def mnemonic_to_seed(mnemonic_phrase,passphrase=b''):
try:
from hashlib import pbkdf2_hmac
def pbkdf2_hmac_sha256(password,salt,iters=2048):
return pbkdf2_hmac(hash_name='sha512',password=password,salt=salt,iterations=iters)
except:
try:
from Crypto.Protocol.KDF import PBKDF2
from Crypto.Hash import SHA512,HMAC
def pbkdf2_hmac_sha256(password,salt,iters=2048):
return PBKDF2(password=password,salt=salt,dkLen=64,count=iters,prf=lambda p,s: HMAC.new(p,s,SHA512).digest())
except:
try:
from pbkdf2 import PBKDF2
import hmac
def pbkdf2_hmac_sha256(password,salt,iters=2048):
return PBKDF2(password,salt, iterations=iters, macmodule=hmac, digestmodule=hashlib.sha512).read(64)
except:
raise RuntimeError("No implementation of pbkdf2 was found!")
return pbkdf2_hmac_sha256(password=mnemonic_phrase,salt=b'mnemonic'+passphrase)
def words_mine(prefix,entbits,satisfunction,wordlist=wordlist_english,randombits=random.getrandbits):
prefix_bits=len(prefix)*11
mine_bits=entbits-prefix_bits
pint=words_to_mnemonic_int(prefix,wordlist)
pint<<=mine_bits
dint=randombits(mine_bits)
count=0
while(not satisfunction(entropy_to_words(eint_to_bytes(pint+dint,entbits)))):
dint=randombits(mine_bits)
if((count & 0xFFFF) == 0):
print("Searched %f percent of the space" % (float(count)/float(1 << mine_bits)))
return entropy_to_words(eint_to_bytes(pint+dint,entbits))
if __name__=="__main__":
import json
testvectors=json.load(open('vectors.json','r'))
passed=True
for v in testvectors['english']:
ebytes=binascii.unhexlify(v[0])
w=' '.join(entropy_to_words(ebytes))
seed=mnemonic_to_seed(w,passphrase='TREZOR')
passed = passed and w==v[1]
passed = passed and binascii.hexlify(seed)==v[2]
print("Tests %s." % ("Passed" if passed else "Failed"))

View File

@ -1,98 +0,0 @@
import sys, re
import binascii
import os
import hashlib
if sys.version_info.major == 2:
string_types = (str, unicode)
string_or_bytes_types = string_types
int_types = (int, float, long)
# Base switching
code_strings = {
2: '01',
10: '0123456789',
16: '0123456789abcdef',
32: 'abcdefghijklmnopqrstuvwxyz234567',
58: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz',
256: ''.join([chr(x) for x in range(256)])
}
def bin_dbl_sha256(s):
bytes_to_hash = from_string_to_bytes(s)
return hashlib.sha256(hashlib.sha256(bytes_to_hash).digest()).digest()
def lpad(msg, symbol, length):
if len(msg) >= length:
return msg
return symbol * (length - len(msg)) + msg
def get_code_string(base):
if base in code_strings:
return code_strings[base]
else:
raise ValueError("Invalid base!")
def changebase(string, frm, to, minlen=0):
if frm == to:
return lpad(string, get_code_string(frm)[0], minlen)
return encode(decode(string, frm), to, minlen)
def bin_to_b58check(inp, magicbyte=0):
if magicbyte == 0:
inp = '\x00' + inp
while magicbyte > 0:
inp = chr(int(magicbyte % 256)) + inp
magicbyte //= 256
leadingzbytes = len(re.match('^\x00*', inp).group(0))
checksum = bin_dbl_sha256(inp)[:4]
return '1' * leadingzbytes + changebase(inp+checksum, 256, 58)
def bytes_to_hex_string(b):
return b.encode('hex')
def safe_from_hex(s):
return s.decode('hex')
def from_int_representation_to_bytes(a):
return str(a)
def from_int_to_byte(a):
return chr(a)
def from_byte_to_int(a):
return ord(a)
def from_bytes_to_string(s):
return s
def from_string_to_bytes(a):
return a
def safe_hexlify(a):
return binascii.hexlify(a)
def encode(val, base, minlen=0):
base, minlen = int(base), int(minlen)
code_string = get_code_string(base)
result = ""
while val > 0:
result = code_string[val % base] + result
val //= base
return code_string[0] * max(minlen - len(result), 0) + result
def decode(string, base):
base = int(base)
code_string = get_code_string(base)
result = 0
if base == 16:
string = string.lower()
while len(string) > 0:
result *= base
result += code_string.find(string[0])
string = string[1:]
return result
def random_string(x):
return os.urandom(x)

View File

@ -1,123 +0,0 @@
import sys, os
import binascii
import hashlib
if sys.version_info.major == 3:
string_types = (str)
string_or_bytes_types = (str, bytes)
int_types = (int, float)
# Base switching
code_strings = {
2: '01',
10: '0123456789',
16: '0123456789abcdef',
32: 'abcdefghijklmnopqrstuvwxyz234567',
58: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz',
256: ''.join([chr(x) for x in range(256)])
}
def bin_dbl_sha256(s):
bytes_to_hash = from_string_to_bytes(s)
return hashlib.sha256(hashlib.sha256(bytes_to_hash).digest()).digest()
def lpad(msg, symbol, length):
if len(msg) >= length:
return msg
return symbol * (length - len(msg)) + msg
def get_code_string(base):
if base in code_strings:
return code_strings[base]
else:
raise ValueError("Invalid base!")
def changebase(string, frm, to, minlen=0):
if frm == to:
return lpad(string, get_code_string(frm)[0], minlen)
return encode(decode(string, frm), to, minlen)
def bin_to_b58check(inp, magicbyte=0):
if magicbyte == 0:
inp = from_int_to_byte(0) + inp
while magicbyte > 0:
inp = from_int_to_byte(magicbyte % 256) + inp
magicbyte //= 256
leadingzbytes = 0
for x in inp:
if x != 0:
break
leadingzbytes += 1
checksum = bin_dbl_sha256(inp)[:4]
return '1' * leadingzbytes + changebase(inp+checksum, 256, 58)
def bytes_to_hex_string(b):
if isinstance(b, str):
return b
return ''.join('{:02x}'.format(y) for y in b)
def safe_from_hex(s):
return bytes.fromhex(s)
def from_int_representation_to_bytes(a):
return bytes(str(a), 'utf-8')
def from_int_to_byte(a):
return bytes([a])
def from_byte_to_int(a):
return a
def from_string_to_bytes(a):
return a if isinstance(a, bytes) else bytes(a, 'utf-8')
def safe_hexlify(a):
return str(binascii.hexlify(a), 'utf-8')
def encode(val, base, minlen=0):
base, minlen = int(base), int(minlen)
code_string = get_code_string(base)
result_bytes = bytes()
while val > 0:
curcode = code_string[val % base]
result_bytes = bytes([ord(curcode)]) + result_bytes
val //= base
pad_size = minlen - len(result_bytes)
padding_element = b'\x00' if base == 256 else b'1' \
if base == 58 else b'0'
if (pad_size > 0):
result_bytes = padding_element*pad_size + result_bytes
result_string = ''.join([chr(y) for y in result_bytes])
result = result_bytes if base == 256 else result_string
return result
def decode(string, base):
if base == 256 and isinstance(string, str):
string = bytes(bytearray.fromhex(string))
base = int(base)
code_string = get_code_string(base)
result = 0
if base == 256:
def extract(d, cs):
return d
else:
def extract(d, cs):
return cs.find(d if isinstance(d, str) else chr(d))
if base == 16:
string = string.lower()
while len(string) > 0:
result *= base
result += extract(string[0], code_string)
string = string[1:]
return result
def random_string(x):
return str(os.urandom(x))

View File

@ -1,414 +0,0 @@
## ripemd.py - pure Python implementation of the RIPEMD-160 algorithm.
## Bjorn Edstrom <be@bjrn.se> 16 december 2007.
##
## Copyrights
## ==========
##
## This code is a derived from an implementation by Markus Friedl which is
## subject to the following license. This Python implementation is not
## subject to any other license.
##
##/*
## * Copyright (c) 2001 Markus Friedl. All rights reserved.
## *
## * Redistribution and use in source and binary forms, with or without
## * modification, are permitted provided that the following conditions
## * are met:
## * 1. Redistributions of source code must retain the above copyright
## * notice, this list of conditions and the following disclaimer.
## * 2. Redistributions in binary form must reproduce the above copyright
## * notice, this list of conditions and the following disclaimer in the
## * documentation and/or other materials provided with the distribution.
## *
## * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
## * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
## * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
## * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
## * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
## * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
## * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
## * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
## * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
## * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
## */
##/*
## * Preneel, Bosselaers, Dobbertin, "The Cryptographic Hash Function RIPEMD-160",
## * RSA Laboratories, CryptoBytes, Volume 3, Number 2, Autumn 1997,
## * ftp://ftp.rsasecurity.com/pub/cryptobytes/crypto3n2.pdf
## */
try:
import psyco
psyco.full()
except ImportError:
pass
import sys
is_python2 = sys.version_info.major == 2
#block_size = 1
digest_size = 20
digestsize = 20
try:
range = xrange
except:
pass
class RIPEMD160:
"""Return a new RIPEMD160 object. An optional string argument
may be provided; if present, this string will be automatically
hashed."""
def __init__(self, arg=None):
self.ctx = RMDContext()
if arg:
self.update(arg)
self.dig = None
def update(self, arg):
"""update(arg)"""
RMD160Update(self.ctx, arg, len(arg))
self.dig = None
def digest(self):
"""digest()"""
if self.dig:
return self.dig
ctx = self.ctx.copy()
self.dig = RMD160Final(self.ctx)
self.ctx = ctx
return self.dig
def hexdigest(self):
"""hexdigest()"""
dig = self.digest()
hex_digest = ''
for d in dig:
if (is_python2):
hex_digest += '%02x' % ord(d)
else:
hex_digest += '%02x' % d
return hex_digest
def copy(self):
"""copy()"""
import copy
return copy.deepcopy(self)
def new(arg=None):
"""Return a new RIPEMD160 object. An optional string argument
may be provided; if present, this string will be automatically
hashed."""
return RIPEMD160(arg)
#
# Private.
#
class RMDContext:
def __init__(self):
self.state = [0x67452301, 0xEFCDAB89, 0x98BADCFE,
0x10325476, 0xC3D2E1F0] # uint32
self.count = 0 # uint64
self.buffer = [0]*64 # uchar
def copy(self):
ctx = RMDContext()
ctx.state = self.state[:]
ctx.count = self.count
ctx.buffer = self.buffer[:]
return ctx
K0 = 0x00000000
K1 = 0x5A827999
K2 = 0x6ED9EBA1
K3 = 0x8F1BBCDC
K4 = 0xA953FD4E
KK0 = 0x50A28BE6
KK1 = 0x5C4DD124
KK2 = 0x6D703EF3
KK3 = 0x7A6D76E9
KK4 = 0x00000000
def ROL(n, x):
return ((x << n) & 0xffffffff) | (x >> (32 - n))
def F0(x, y, z):
return x ^ y ^ z
def F1(x, y, z):
return (x & y) | (((~x) % 0x100000000) & z)
def F2(x, y, z):
return (x | ((~y) % 0x100000000)) ^ z
def F3(x, y, z):
return (x & z) | (((~z) % 0x100000000) & y)
def F4(x, y, z):
return x ^ (y | ((~z) % 0x100000000))
def R(a, b, c, d, e, Fj, Kj, sj, rj, X):
a = ROL(sj, (a + Fj(b, c, d) + X[rj] + Kj) % 0x100000000) + e
c = ROL(10, c)
return a % 0x100000000, c
PADDING = [0x80] + [0]*63
import sys
import struct
def RMD160Transform(state, block): #uint32 state[5], uchar block[64]
x = [0]*16
if sys.byteorder == 'little':
if is_python2:
x = struct.unpack('<16L', ''.join([chr(x) for x in block[0:64]]))
else:
x = struct.unpack('<16L', bytes(block[0:64]))
else:
raise "Error!!"
a = state[0]
b = state[1]
c = state[2]
d = state[3]
e = state[4]
#/* Round 1 */
a, c = R(a, b, c, d, e, F0, K0, 11, 0, x);
e, b = R(e, a, b, c, d, F0, K0, 14, 1, x);
d, a = R(d, e, a, b, c, F0, K0, 15, 2, x);
c, e = R(c, d, e, a, b, F0, K0, 12, 3, x);
b, d = R(b, c, d, e, a, F0, K0, 5, 4, x);
a, c = R(a, b, c, d, e, F0, K0, 8, 5, x);
e, b = R(e, a, b, c, d, F0, K0, 7, 6, x);
d, a = R(d, e, a, b, c, F0, K0, 9, 7, x);
c, e = R(c, d, e, a, b, F0, K0, 11, 8, x);
b, d = R(b, c, d, e, a, F0, K0, 13, 9, x);
a, c = R(a, b, c, d, e, F0, K0, 14, 10, x);
e, b = R(e, a, b, c, d, F0, K0, 15, 11, x);
d, a = R(d, e, a, b, c, F0, K0, 6, 12, x);
c, e = R(c, d, e, a, b, F0, K0, 7, 13, x);
b, d = R(b, c, d, e, a, F0, K0, 9, 14, x);
a, c = R(a, b, c, d, e, F0, K0, 8, 15, x); #/* #15 */
#/* Round 2 */
e, b = R(e, a, b, c, d, F1, K1, 7, 7, x);
d, a = R(d, e, a, b, c, F1, K1, 6, 4, x);
c, e = R(c, d, e, a, b, F1, K1, 8, 13, x);
b, d = R(b, c, d, e, a, F1, K1, 13, 1, x);
a, c = R(a, b, c, d, e, F1, K1, 11, 10, x);
e, b = R(e, a, b, c, d, F1, K1, 9, 6, x);
d, a = R(d, e, a, b, c, F1, K1, 7, 15, x);
c, e = R(c, d, e, a, b, F1, K1, 15, 3, x);
b, d = R(b, c, d, e, a, F1, K1, 7, 12, x);
a, c = R(a, b, c, d, e, F1, K1, 12, 0, x);
e, b = R(e, a, b, c, d, F1, K1, 15, 9, x);
d, a = R(d, e, a, b, c, F1, K1, 9, 5, x);
c, e = R(c, d, e, a, b, F1, K1, 11, 2, x);
b, d = R(b, c, d, e, a, F1, K1, 7, 14, x);
a, c = R(a, b, c, d, e, F1, K1, 13, 11, x);
e, b = R(e, a, b, c, d, F1, K1, 12, 8, x); #/* #31 */
#/* Round 3 */
d, a = R(d, e, a, b, c, F2, K2, 11, 3, x);
c, e = R(c, d, e, a, b, F2, K2, 13, 10, x);
b, d = R(b, c, d, e, a, F2, K2, 6, 14, x);
a, c = R(a, b, c, d, e, F2, K2, 7, 4, x);
e, b = R(e, a, b, c, d, F2, K2, 14, 9, x);
d, a = R(d, e, a, b, c, F2, K2, 9, 15, x);
c, e = R(c, d, e, a, b, F2, K2, 13, 8, x);
b, d = R(b, c, d, e, a, F2, K2, 15, 1, x);
a, c = R(a, b, c, d, e, F2, K2, 14, 2, x);
e, b = R(e, a, b, c, d, F2, K2, 8, 7, x);
d, a = R(d, e, a, b, c, F2, K2, 13, 0, x);
c, e = R(c, d, e, a, b, F2, K2, 6, 6, x);
b, d = R(b, c, d, e, a, F2, K2, 5, 13, x);
a, c = R(a, b, c, d, e, F2, K2, 12, 11, x);
e, b = R(e, a, b, c, d, F2, K2, 7, 5, x);
d, a = R(d, e, a, b, c, F2, K2, 5, 12, x); #/* #47 */
#/* Round 4 */
c, e = R(c, d, e, a, b, F3, K3, 11, 1, x);
b, d = R(b, c, d, e, a, F3, K3, 12, 9, x);
a, c = R(a, b, c, d, e, F3, K3, 14, 11, x);
e, b = R(e, a, b, c, d, F3, K3, 15, 10, x);
d, a = R(d, e, a, b, c, F3, K3, 14, 0, x);
c, e = R(c, d, e, a, b, F3, K3, 15, 8, x);
b, d = R(b, c, d, e, a, F3, K3, 9, 12, x);
a, c = R(a, b, c, d, e, F3, K3, 8, 4, x);
e, b = R(e, a, b, c, d, F3, K3, 9, 13, x);
d, a = R(d, e, a, b, c, F3, K3, 14, 3, x);
c, e = R(c, d, e, a, b, F3, K3, 5, 7, x);
b, d = R(b, c, d, e, a, F3, K3, 6, 15, x);
a, c = R(a, b, c, d, e, F3, K3, 8, 14, x);
e, b = R(e, a, b, c, d, F3, K3, 6, 5, x);
d, a = R(d, e, a, b, c, F3, K3, 5, 6, x);
c, e = R(c, d, e, a, b, F3, K3, 12, 2, x); #/* #63 */
#/* Round 5 */
b, d = R(b, c, d, e, a, F4, K4, 9, 4, x);
a, c = R(a, b, c, d, e, F4, K4, 15, 0, x);
e, b = R(e, a, b, c, d, F4, K4, 5, 5, x);
d, a = R(d, e, a, b, c, F4, K4, 11, 9, x);
c, e = R(c, d, e, a, b, F4, K4, 6, 7, x);
b, d = R(b, c, d, e, a, F4, K4, 8, 12, x);
a, c = R(a, b, c, d, e, F4, K4, 13, 2, x);
e, b = R(e, a, b, c, d, F4, K4, 12, 10, x);
d, a = R(d, e, a, b, c, F4, K4, 5, 14, x);
c, e = R(c, d, e, a, b, F4, K4, 12, 1, x);
b, d = R(b, c, d, e, a, F4, K4, 13, 3, x);
a, c = R(a, b, c, d, e, F4, K4, 14, 8, x);
e, b = R(e, a, b, c, d, F4, K4, 11, 11, x);
d, a = R(d, e, a, b, c, F4, K4, 8, 6, x);
c, e = R(c, d, e, a, b, F4, K4, 5, 15, x);
b, d = R(b, c, d, e, a, F4, K4, 6, 13, x); #/* #79 */
aa = a;
bb = b;
cc = c;
dd = d;
ee = e;
a = state[0]
b = state[1]
c = state[2]
d = state[3]
e = state[4]
#/* Parallel round 1 */
a, c = R(a, b, c, d, e, F4, KK0, 8, 5, x)
e, b = R(e, a, b, c, d, F4, KK0, 9, 14, x)
d, a = R(d, e, a, b, c, F4, KK0, 9, 7, x)
c, e = R(c, d, e, a, b, F4, KK0, 11, 0, x)
b, d = R(b, c, d, e, a, F4, KK0, 13, 9, x)
a, c = R(a, b, c, d, e, F4, KK0, 15, 2, x)
e, b = R(e, a, b, c, d, F4, KK0, 15, 11, x)
d, a = R(d, e, a, b, c, F4, KK0, 5, 4, x)
c, e = R(c, d, e, a, b, F4, KK0, 7, 13, x)
b, d = R(b, c, d, e, a, F4, KK0, 7, 6, x)
a, c = R(a, b, c, d, e, F4, KK0, 8, 15, x)
e, b = R(e, a, b, c, d, F4, KK0, 11, 8, x)
d, a = R(d, e, a, b, c, F4, KK0, 14, 1, x)
c, e = R(c, d, e, a, b, F4, KK0, 14, 10, x)
b, d = R(b, c, d, e, a, F4, KK0, 12, 3, x)
a, c = R(a, b, c, d, e, F4, KK0, 6, 12, x) #/* #15 */
#/* Parallel round 2 */
e, b = R(e, a, b, c, d, F3, KK1, 9, 6, x)
d, a = R(d, e, a, b, c, F3, KK1, 13, 11, x)
c, e = R(c, d, e, a, b, F3, KK1, 15, 3, x)
b, d = R(b, c, d, e, a, F3, KK1, 7, 7, x)
a, c = R(a, b, c, d, e, F3, KK1, 12, 0, x)
e, b = R(e, a, b, c, d, F3, KK1, 8, 13, x)
d, a = R(d, e, a, b, c, F3, KK1, 9, 5, x)
c, e = R(c, d, e, a, b, F3, KK1, 11, 10, x)
b, d = R(b, c, d, e, a, F3, KK1, 7, 14, x)
a, c = R(a, b, c, d, e, F3, KK1, 7, 15, x)
e, b = R(e, a, b, c, d, F3, KK1, 12, 8, x)
d, a = R(d, e, a, b, c, F3, KK1, 7, 12, x)
c, e = R(c, d, e, a, b, F3, KK1, 6, 4, x)
b, d = R(b, c, d, e, a, F3, KK1, 15, 9, x)
a, c = R(a, b, c, d, e, F3, KK1, 13, 1, x)
e, b = R(e, a, b, c, d, F3, KK1, 11, 2, x) #/* #31 */
#/* Parallel round 3 */
d, a = R(d, e, a, b, c, F2, KK2, 9, 15, x)
c, e = R(c, d, e, a, b, F2, KK2, 7, 5, x)
b, d = R(b, c, d, e, a, F2, KK2, 15, 1, x)
a, c = R(a, b, c, d, e, F2, KK2, 11, 3, x)
e, b = R(e, a, b, c, d, F2, KK2, 8, 7, x)
d, a = R(d, e, a, b, c, F2, KK2, 6, 14, x)
c, e = R(c, d, e, a, b, F2, KK2, 6, 6, x)
b, d = R(b, c, d, e, a, F2, KK2, 14, 9, x)
a, c = R(a, b, c, d, e, F2, KK2, 12, 11, x)
e, b = R(e, a, b, c, d, F2, KK2, 13, 8, x)
d, a = R(d, e, a, b, c, F2, KK2, 5, 12, x)
c, e = R(c, d, e, a, b, F2, KK2, 14, 2, x)
b, d = R(b, c, d, e, a, F2, KK2, 13, 10, x)
a, c = R(a, b, c, d, e, F2, KK2, 13, 0, x)
e, b = R(e, a, b, c, d, F2, KK2, 7, 4, x)
d, a = R(d, e, a, b, c, F2, KK2, 5, 13, x) #/* #47 */
#/* Parallel round 4 */
c, e = R(c, d, e, a, b, F1, KK3, 15, 8, x)
b, d = R(b, c, d, e, a, F1, KK3, 5, 6, x)
a, c = R(a, b, c, d, e, F1, KK3, 8, 4, x)
e, b = R(e, a, b, c, d, F1, KK3, 11, 1, x)
d, a = R(d, e, a, b, c, F1, KK3, 14, 3, x)
c, e = R(c, d, e, a, b, F1, KK3, 14, 11, x)
b, d = R(b, c, d, e, a, F1, KK3, 6, 15, x)
a, c = R(a, b, c, d, e, F1, KK3, 14, 0, x)
e, b = R(e, a, b, c, d, F1, KK3, 6, 5, x)
d, a = R(d, e, a, b, c, F1, KK3, 9, 12, x)
c, e = R(c, d, e, a, b, F1, KK3, 12, 2, x)
b, d = R(b, c, d, e, a, F1, KK3, 9, 13, x)
a, c = R(a, b, c, d, e, F1, KK3, 12, 9, x)
e, b = R(e, a, b, c, d, F1, KK3, 5, 7, x)
d, a = R(d, e, a, b, c, F1, KK3, 15, 10, x)
c, e = R(c, d, e, a, b, F1, KK3, 8, 14, x) #/* #63 */
#/* Parallel round 5 */
b, d = R(b, c, d, e, a, F0, KK4, 8, 12, x)
a, c = R(a, b, c, d, e, F0, KK4, 5, 15, x)
e, b = R(e, a, b, c, d, F0, KK4, 12, 10, x)
d, a = R(d, e, a, b, c, F0, KK4, 9, 4, x)
c, e = R(c, d, e, a, b, F0, KK4, 12, 1, x)
b, d = R(b, c, d, e, a, F0, KK4, 5, 5, x)
a, c = R(a, b, c, d, e, F0, KK4, 14, 8, x)
e, b = R(e, a, b, c, d, F0, KK4, 6, 7, x)
d, a = R(d, e, a, b, c, F0, KK4, 8, 6, x)
c, e = R(c, d, e, a, b, F0, KK4, 13, 2, x)
b, d = R(b, c, d, e, a, F0, KK4, 6, 13, x)
a, c = R(a, b, c, d, e, F0, KK4, 5, 14, x)
e, b = R(e, a, b, c, d, F0, KK4, 15, 0, x)
d, a = R(d, e, a, b, c, F0, KK4, 13, 3, x)
c, e = R(c, d, e, a, b, F0, KK4, 11, 9, x)
b, d = R(b, c, d, e, a, F0, KK4, 11, 11, x) #/* #79 */
t = (state[1] + cc + d) % 0x100000000;
state[1] = (state[2] + dd + e) % 0x100000000;
state[2] = (state[3] + ee + a) % 0x100000000;
state[3] = (state[4] + aa + b) % 0x100000000;
state[4] = (state[0] + bb + c) % 0x100000000;
state[0] = t % 0x100000000;
pass
def RMD160Update(ctx, inp, inplen):
if type(inp) == str:
inp = [ord(i)&0xff for i in inp]
have = int((ctx.count // 8) % 64)
inplen = int(inplen)
need = 64 - have
ctx.count += 8 * inplen
off = 0
if inplen >= need:
if have:
for i in range(need):
ctx.buffer[have+i] = inp[i]
RMD160Transform(ctx.state, ctx.buffer)
off = need
have = 0
while off + 64 <= inplen:
RMD160Transform(ctx.state, inp[off:]) #<---
off += 64
if off < inplen:
# memcpy(ctx->buffer + have, input+off, len-off);
for i in range(inplen - off):
ctx.buffer[have+i] = inp[off+i]
def RMD160Final(ctx):
size = struct.pack("<Q", ctx.count)
padlen = 64 - ((ctx.count // 8) % 64)
if padlen < 1+8:
padlen += 64
RMD160Update(ctx, PADDING, padlen-8)
RMD160Update(ctx, size, 8)
return struct.pack("<5L", *ctx.state)
assert '37f332f68db77bd9d7edd4969571ad671cf9dd3b' == \
new('The quick brown fox jumps over the lazy dog').hexdigest()
assert '132072df690933835eb8b6ad0b77e7b6f14acad7' == \
new('The quick brown fox jumps over the lazy cog').hexdigest()
assert '9c1185a5c5e9fc54612808977ee8f548b2258d31' == \
new('').hexdigest()

View File

@ -1,100 +0,0 @@
from . import main as main
from . import transaction as tx
# Shared secrets and uncovering pay keys
def shared_secret_sender(scan_pubkey, ephem_privkey):
shared_point = main.multiply(scan_pubkey, ephem_privkey)
shared_secret = main.sha256(main.encode_pubkey(shared_point, 'bin_compressed'))
return shared_secret
def shared_secret_receiver(ephem_pubkey, scan_privkey):
shared_point = main.multiply(ephem_pubkey, scan_privkey)
shared_secret = main.sha256(main.encode_pubkey(shared_point, 'bin_compressed'))
return shared_secret
def uncover_pay_pubkey_sender(scan_pubkey, spend_pubkey, ephem_privkey):
shared_secret = shared_secret_sender(scan_pubkey, ephem_privkey)
return main.add_pubkeys(spend_pubkey, main.privtopub(shared_secret))
def uncover_pay_pubkey_receiver(scan_privkey, spend_pubkey, ephem_pubkey):
shared_secret = shared_secret_receiver(ephem_pubkey, scan_privkey)
return main.add_pubkeys(spend_pubkey, main.privtopub(shared_secret))
def uncover_pay_privkey(scan_privkey, spend_privkey, ephem_pubkey):
shared_secret = shared_secret_receiver(ephem_pubkey, scan_privkey)
return main.add_privkeys(spend_privkey, shared_secret)
# Address encoding
# Functions for basic stealth addresses,
# i.e. one scan key, one spend key, no prefix
def pubkeys_to_basic_stealth_address(scan_pubkey, spend_pubkey, magic_byte=42):
# magic_byte = 42 for mainnet, 43 for testnet.
hex_scankey = main.encode_pubkey(scan_pubkey, 'hex_compressed')
hex_spendkey = main.encode_pubkey(spend_pubkey, 'hex_compressed')
hex_data = '00{0:066x}01{1:066x}0100'.format(int(hex_scankey, 16), int(hex_spendkey, 16))
addr = main.hex_to_b58check(hex_data, magic_byte)
return addr
def basic_stealth_address_to_pubkeys(stealth_address):
hex_data = main.b58check_to_hex(stealth_address)
if len(hex_data) != 140:
raise Exception('Stealth address is not of basic type (one scan key, one spend key, no prefix)')
scan_pubkey = hex_data[2:68]
spend_pubkey = hex_data[70:136]
return scan_pubkey, spend_pubkey
# Sending stealth payments
def mk_stealth_metadata_script(ephem_pubkey, nonce):
op_return = '6a'
msg_size = '26'
version = '06'
return op_return + msg_size + version + '{0:08x}'.format(nonce) + main.encode_pubkey(ephem_pubkey, 'hex_compressed')
def mk_stealth_tx_outputs(stealth_addr, value, ephem_privkey, nonce, network='btc'):
scan_pubkey, spend_pubkey = basic_stealth_address_to_pubkeys(stealth_addr)
if network == 'btc':
btc_magic_byte = 42
if stealth_addr != pubkeys_to_basic_stealth_address(scan_pubkey, spend_pubkey, btc_magic_byte):
raise Exception('Invalid btc mainnet stealth address: ' + stealth_addr)
magic_byte_addr = 0
elif network == 'testnet':
testnet_magic_byte = 43
if stealth_addr != pubkeys_to_basic_stealth_address(scan_pubkey, spend_pubkey, testnet_magic_byte):
raise Exception('Invalid testnet stealth address: ' + stealth_addr)
magic_byte_addr = 111
ephem_pubkey = main.privkey_to_pubkey(ephem_privkey)
output0 = {'script': mk_stealth_metadata_script(ephem_pubkey, nonce),
'value': 0}
pay_pubkey = uncover_pay_pubkey_sender(scan_pubkey, spend_pubkey, ephem_privkey)
pay_addr = main.pubkey_to_address(pay_pubkey, magic_byte_addr)
output1 = {'address': pay_addr,
'value': value}
return [output0, output1]
# Receiving stealth payments
def ephem_pubkey_from_tx_script(stealth_tx_script):
if len(stealth_tx_script) != 80:
raise Exception('Wrong format for stealth tx output')
return stealth_tx_script[14:]

View File

@ -1,514 +0,0 @@
#!/usr/bin/python
import binascii, re, json, copy, sys
from .main import *
from _functools import reduce
### Hex to bin converter and vice versa for objects
def json_is_base(obj, base):
if not is_python2 and isinstance(obj, bytes):
return False
alpha = get_code_string(base)
if isinstance(obj, string_types):
for i in range(len(obj)):
if alpha.find(obj[i]) == -1:
return False
return True
elif isinstance(obj, int_types) or obj is None:
return True
elif isinstance(obj, list):
for i in range(len(obj)):
if not json_is_base(obj[i], base):
return False
return True
else:
for x in obj:
if not json_is_base(obj[x], base):
return False
return True
def json_changebase(obj, changer):
if isinstance(obj, string_or_bytes_types):
return changer(obj)
elif isinstance(obj, int_types) or obj is None:
return obj
elif isinstance(obj, list):
return [json_changebase(x, changer) for x in obj]
return dict((x, json_changebase(obj[x], changer)) for x in obj)
# Transaction serialization and deserialization
def deserialize(tx):
if isinstance(tx, str) and re.match('^[0-9a-fA-F]*$', tx):
#tx = bytes(bytearray.fromhex(tx))
return json_changebase(deserialize(binascii.unhexlify(tx)),
lambda x: safe_hexlify(x))
# http://stackoverflow.com/questions/4851463/python-closure-write-to-variable-in-parent-scope
# Python's scoping rules are demented, requiring me to make pos an object
# so that it is call-by-reference
pos = [0]
def read_as_int(bytez):
pos[0] += bytez
return decode(tx[pos[0]-bytez:pos[0]][::-1], 256)
def read_var_int():
pos[0] += 1
val = from_byte_to_int(tx[pos[0]-1])
if val < 253:
return val
return read_as_int(pow(2, val - 252))
def read_bytes(bytez):
pos[0] += bytez
return tx[pos[0]-bytez:pos[0]]
def read_var_string():
size = read_var_int()
return read_bytes(size)
obj = {"ins": [], "outs": []}
obj["version"] = read_as_int(4)
ins = read_var_int()
for i in range(ins):
obj["ins"].append({
"outpoint": {
"hash": read_bytes(32)[::-1],
"index": read_as_int(4)
},
"script": read_var_string(),
"sequence": read_as_int(4)
})
outs = read_var_int()
for i in range(outs):
obj["outs"].append({
"value": read_as_int(8),
"script": read_var_string()
})
obj["locktime"] = read_as_int(4)
return obj
def serialize(txobj):
#if isinstance(txobj, bytes):
# txobj = bytes_to_hex_string(txobj)
o = []
if json_is_base(txobj, 16):
json_changedbase = json_changebase(txobj, lambda x: binascii.unhexlify(x))
hexlified = safe_hexlify(serialize(json_changedbase))
return hexlified
o.append(encode(txobj["version"], 256, 4)[::-1])
o.append(num_to_var_int(len(txobj["ins"])))
for inp in txobj["ins"]:
o.append(inp["outpoint"]["hash"][::-1])
o.append(encode(inp["outpoint"]["index"], 256, 4)[::-1])
o.append(num_to_var_int(len(inp["script"]))+(inp["script"] if inp["script"] or is_python2 else bytes()))
o.append(encode(inp["sequence"], 256, 4)[::-1])
o.append(num_to_var_int(len(txobj["outs"])))
for out in txobj["outs"]:
o.append(encode(out["value"], 256, 8)[::-1])
o.append(num_to_var_int(len(out["script"]))+out["script"])
o.append(encode(txobj["locktime"], 256, 4)[::-1])
return ''.join(o) if is_python2 else reduce(lambda x,y: x+y, o, bytes())
# Hashing transactions for signing
SIGHASH_ALL = 1
SIGHASH_NONE = 2
SIGHASH_SINGLE = 3
# this works like SIGHASH_ANYONECANPAY | SIGHASH_ALL, might as well make it explicit while
# we fix the constant
SIGHASH_ANYONECANPAY = 0x81
def signature_form(tx, i, script, hashcode=SIGHASH_ALL):
i, hashcode = int(i), int(hashcode)
if isinstance(tx, string_or_bytes_types):
return serialize(signature_form(deserialize(tx), i, script, hashcode))
newtx = copy.deepcopy(tx)
for inp in newtx["ins"]:
inp["script"] = ""
newtx["ins"][i]["script"] = script
if hashcode == SIGHASH_NONE:
newtx["outs"] = []
elif hashcode == SIGHASH_SINGLE:
newtx["outs"] = newtx["outs"][:len(newtx["ins"])]
for out in newtx["outs"][:len(newtx["ins"]) - 1]:
out['value'] = 2**64 - 1
out['script'] = ""
elif hashcode == SIGHASH_ANYONECANPAY:
newtx["ins"] = [newtx["ins"][i]]
else:
pass
return newtx
# Making the actual signatures
def der_encode_sig(v, r, s):
b1, b2 = safe_hexlify(encode(r, 256)), safe_hexlify(encode(s, 256))
if len(b1) and b1[0] in '89abcdef':
b1 = '00' + b1
if len(b2) and b2[0] in '89abcdef':
b2 = '00' + b2
left = '02'+encode(len(b1)//2, 16, 2)+b1
right = '02'+encode(len(b2)//2, 16, 2)+b2
return '30'+encode(len(left+right)//2, 16, 2)+left+right
def der_decode_sig(sig):
leftlen = decode(sig[6:8], 16)*2
left = sig[8:8+leftlen]
rightlen = decode(sig[10+leftlen:12+leftlen], 16)*2
right = sig[12+leftlen:12+leftlen+rightlen]
return (None, decode(left, 16), decode(right, 16))
def is_bip66(sig):
"""Checks hex DER sig for BIP66 consistency"""
#https://raw.githubusercontent.com/bitcoin/bips/master/bip-0066.mediawiki
#0x30 [total-len] 0x02 [R-len] [R] 0x02 [S-len] [S] [sighash]
sig = bytearray.fromhex(sig) if re.match('^[0-9a-fA-F]*$', sig) else bytearray(sig)
if (sig[0] == 0x30) and (sig[1] == len(sig)-2): # check if sighash is missing
sig.extend(b"\1") # add SIGHASH_ALL for testing
#assert (sig[-1] & 124 == 0) and (not not sig[-1]), "Bad SIGHASH value"
if len(sig) < 9 or len(sig) > 73: return False
if (sig[0] != 0x30): return False
if (sig[1] != len(sig)-3): return False
rlen = sig[3]
if (5+rlen >= len(sig)): return False
slen = sig[5+rlen]
if (rlen + slen + 7 != len(sig)): return False
if (sig[2] != 0x02): return False
if (rlen == 0): return False
if (sig[4] & 0x80): return False
if (rlen > 1 and (sig[4] == 0x00) and not (sig[5] & 0x80)): return False
if (sig[4+rlen] != 0x02): return False
if (slen == 0): return False
if (sig[rlen+6] & 0x80): return False
if (slen > 1 and (sig[6+rlen] == 0x00) and not (sig[7+rlen] & 0x80)):
return False
return True
def txhash(tx, hashcode=None):
if isinstance(tx, str) and re.match('^[0-9a-fA-F]*$', tx):
tx = changebase(tx, 16, 256)
if hashcode:
return dbl_sha256(from_string_to_bytes(tx) + encode(int(hashcode), 256, 4)[::-1])
else:
return safe_hexlify(bin_dbl_sha256(tx)[::-1])
def bin_txhash(tx, hashcode=None):
return binascii.unhexlify(txhash(tx, hashcode))
def ecdsa_tx_sign(tx, priv, hashcode=SIGHASH_ALL):
rawsig = ecdsa_raw_sign(bin_txhash(tx, hashcode), priv)
return der_encode_sig(*rawsig)+encode(hashcode, 16, 2)
def ecdsa_tx_verify(tx, sig, pub, hashcode=SIGHASH_ALL):
return ecdsa_raw_verify(bin_txhash(tx, hashcode), der_decode_sig(sig), pub)
def ecdsa_tx_recover(tx, sig, hashcode=SIGHASH_ALL):
z = bin_txhash(tx, hashcode)
_, r, s = der_decode_sig(sig)
left = ecdsa_raw_recover(z, (0, r, s))
right = ecdsa_raw_recover(z, (1, r, s))
return (encode_pubkey(left, 'hex'), encode_pubkey(right, 'hex'))
# Scripts
def mk_pubkey_script(addr):
# Keep the auxiliary functions around for altcoins' sake
return '76a914' + b58check_to_hex(addr) + '88ac'
def mk_scripthash_script(addr):
return 'a914' + b58check_to_hex(addr) + '87'
# Address representation to output script
def address_to_script(addr):
if addr[0] == '3' or addr[0] == '2':
return mk_scripthash_script(addr)
else:
return mk_pubkey_script(addr)
# Output script to address representation
def script_to_address(script, vbyte=0):
if re.match('^[0-9a-fA-F]*$', script):
script = binascii.unhexlify(script)
if script[:3] == b'\x76\xa9\x14' and script[-2:] == b'\x88\xac' and len(script) == 25:
return bin_to_b58check(script[3:-2], vbyte) # pubkey hash addresses
else:
if vbyte in [111, 196]:
# Testnet
scripthash_byte = 196
elif vbyte == 0:
# Mainnet
scripthash_byte = 5
else:
scripthash_byte = vbyte
# BIP0016 scripthash addresses
return bin_to_b58check(script[2:-1], scripthash_byte)
def p2sh_scriptaddr(script, magicbyte=5):
if re.match('^[0-9a-fA-F]*$', script):
script = binascii.unhexlify(script)
return hex_to_b58check(hash160(script), magicbyte)
scriptaddr = p2sh_scriptaddr
def deserialize_script(script):
if isinstance(script, str) and re.match('^[0-9a-fA-F]*$', script):
return json_changebase(deserialize_script(binascii.unhexlify(script)),
lambda x: safe_hexlify(x))
out, pos = [], 0
while pos < len(script):
code = from_byte_to_int(script[pos])
if code == 0:
out.append(None)
pos += 1
elif code <= 75:
out.append(script[pos+1:pos+1+code])
pos += 1 + code
elif code <= 78:
szsz = pow(2, code - 76)
sz = decode(script[pos+szsz: pos:-1], 256)
out.append(script[pos + 1 + szsz:pos + 1 + szsz + sz])
pos += 1 + szsz + sz
elif code <= 96:
out.append(code - 80)
pos += 1
else:
out.append(code)
pos += 1
return out
def serialize_script_unit(unit):
if isinstance(unit, int):
if unit < 16:
return from_int_to_byte(unit + 80)
else:
return from_int_to_byte(unit)
elif unit is None:
return b'\x00'
else:
if len(unit) <= 75:
return from_int_to_byte(len(unit))+unit
elif len(unit) < 256:
return from_int_to_byte(76)+from_int_to_byte(len(unit))+unit
elif len(unit) < 65536:
return from_int_to_byte(77)+encode(len(unit), 256, 2)[::-1]+unit
else:
return from_int_to_byte(78)+encode(len(unit), 256, 4)[::-1]+unit
if is_python2:
def serialize_script(script):
if json_is_base(script, 16):
return binascii.hexlify(serialize_script(json_changebase(script,
lambda x: binascii.unhexlify(x))))
return ''.join(map(serialize_script_unit, script))
else:
def serialize_script(script):
if json_is_base(script, 16):
return safe_hexlify(serialize_script(json_changebase(script,
lambda x: binascii.unhexlify(x))))
result = bytes()
for b in map(serialize_script_unit, script):
result += b if isinstance(b, bytes) else bytes(b, 'utf-8')
return result
def mk_multisig_script(*args): # [pubs],k or pub1,pub2...pub[n],k
if isinstance(args[0], list):
pubs, k = args[0], int(args[1])
else:
pubs = list(filter(lambda x: len(str(x)) >= 32, args))
k = int(args[len(pubs)])
return serialize_script([k]+pubs+[len(pubs)]+[0xae])
# Signing and verifying
def verify_tx_input(tx, i, script, sig, pub):
if re.match('^[0-9a-fA-F]*$', tx):
tx = binascii.unhexlify(tx)
if re.match('^[0-9a-fA-F]*$', script):
script = binascii.unhexlify(script)
if not re.match('^[0-9a-fA-F]*$', sig):
sig = safe_hexlify(sig)
hashcode = decode(sig[-2:], 16)
modtx = signature_form(tx, int(i), script, hashcode)
return ecdsa_tx_verify(modtx, sig, pub, hashcode)
def sign(tx, i, priv, hashcode=SIGHASH_ALL):
i = int(i)
if (not is_python2 and isinstance(re, bytes)) or not re.match('^[0-9a-fA-F]*$', tx):
return binascii.unhexlify(sign(safe_hexlify(tx), i, priv))
if len(priv) <= 33:
priv = safe_hexlify(priv)
pub = privkey_to_pubkey(priv)
address = pubkey_to_address(pub)
signing_tx = signature_form(tx, i, mk_pubkey_script(address), hashcode)
sig = ecdsa_tx_sign(signing_tx, priv, hashcode)
txobj = deserialize(tx)
txobj["ins"][i]["script"] = serialize_script([sig, pub])
return serialize(txobj)
def signall(tx, priv):
# if priv is a dictionary, assume format is
# { 'txinhash:txinidx' : privkey }
if isinstance(priv, dict):
for e, i in enumerate(deserialize(tx)["ins"]):
k = priv["%s:%d" % (i["outpoint"]["hash"], i["outpoint"]["index"])]
tx = sign(tx, e, k)
else:
for i in range(len(deserialize(tx)["ins"])):
tx = sign(tx, i, priv)
return tx
def multisign(tx, i, script, pk, hashcode=SIGHASH_ALL):
if re.match('^[0-9a-fA-F]*$', tx):
tx = binascii.unhexlify(tx)
if re.match('^[0-9a-fA-F]*$', script):
script = binascii.unhexlify(script)
modtx = signature_form(tx, i, script, hashcode)
return ecdsa_tx_sign(modtx, pk, hashcode)
def apply_multisignatures(*args):
# tx,i,script,sigs OR tx,i,script,sig1,sig2...,sig[n]
tx, i, script = args[0], int(args[1]), args[2]
sigs = args[3] if isinstance(args[3], list) else list(args[3:])
if isinstance(script, str) and re.match('^[0-9a-fA-F]*$', script):
script = binascii.unhexlify(script)
sigs = [binascii.unhexlify(x) if x[:2] == '30' else x for x in sigs]
if isinstance(tx, str) and re.match('^[0-9a-fA-F]*$', tx):
return safe_hexlify(apply_multisignatures(binascii.unhexlify(tx), i, script, sigs))
# Not pushing empty elements on the top of the stack if passing no
# script (in case of bare multisig inputs there is no script)
script_blob = [] if script.__len__() == 0 else [script]
txobj = deserialize(tx)
txobj["ins"][i]["script"] = serialize_script([None]+sigs+script_blob)
return serialize(txobj)
def is_inp(arg):
return len(arg) > 64 or "output" in arg or "outpoint" in arg
def mktx(*args):
# [in0, in1...],[out0, out1...] or in0, in1 ... out0 out1 ...
ins, outs = [], []
for arg in args:
if isinstance(arg, list):
for a in arg: (ins if is_inp(a) else outs).append(a)
else:
(ins if is_inp(arg) else outs).append(arg)
txobj = {"locktime": 0, "version": 1, "ins": [], "outs": []}
for i in ins:
if isinstance(i, dict) and "outpoint" in i:
txobj["ins"].append(i)
else:
if isinstance(i, dict) and "output" in i:
i = i["output"]
txobj["ins"].append({
"outpoint": {"hash": i[:64], "index": int(i[65:])},
"script": "",
"sequence": 4294967295
})
for o in outs:
if isinstance(o, string_or_bytes_types):
addr = o[:o.find(':')]
val = int(o[o.find(':')+1:])
o = {}
if re.match('^[0-9a-fA-F]*$', addr):
o["script"] = addr
else:
o["address"] = addr
o["value"] = val
outobj = {}
if "address" in o:
outobj["script"] = address_to_script(o["address"])
elif "script" in o:
outobj["script"] = o["script"]
else:
raise Exception("Could not find 'address' or 'script' in output.")
outobj["value"] = o["value"]
txobj["outs"].append(outobj)
return serialize(txobj)
def select(unspent, value):
value = int(value)
high = [u for u in unspent if u["value"] >= value]
high.sort(key=lambda u: u["value"])
low = [u for u in unspent if u["value"] < value]
low.sort(key=lambda u: -u["value"])
if len(high):
return [high[0]]
i, tv = 0, 0
while tv < value and i < len(low):
tv += low[i]["value"]
i += 1
if tv < value:
raise Exception("Not enough funds")
return low[:i]
# Only takes inputs of the form { "output": blah, "value": foo }
def mksend(*args):
argz, change, fee = args[:-2], args[-2], int(args[-1])
ins, outs = [], []
for arg in argz:
if isinstance(arg, list):
for a in arg:
(ins if is_inp(a) else outs).append(a)
else:
(ins if is_inp(arg) else outs).append(arg)
isum = sum([i["value"] for i in ins])
osum, outputs2 = 0, []
for o in outs:
if isinstance(o, string_types):
o2 = {
"address": o[:o.find(':')],
"value": int(o[o.find(':')+1:])
}
else:
o2 = o
outputs2.append(o2)
osum += o2["value"]
if isum < osum+fee:
raise Exception("Not enough money")
elif isum > osum+fee+5430:
outputs2 += [{"address": change, "value": isum-osum-fee}]
return mktx(ins, outputs2)

View File

@ -1,674 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

View File

@ -1,96 +0,0 @@
# PyElliptic
PyElliptic is a high level wrapper for the cryptographic library : OpenSSL.
Under the GNU General Public License
Python3 compatible. For GNU/Linux and Windows.
Require OpenSSL
## Version
The [upstream pyelliptic](https://github.com/yann2192/pyelliptic) has been
deprecated by the author at 1.5.8 and ECC API has been removed.
This version is a fork of the pyelliptic extracted from the [BitMessage source
tree](https://github.com/Bitmessage/PyBitmessage), and does contain the ECC
API. To minimize confusion but to avoid renaming the module, major version has
been bumped.
BitMessage is actively maintained, and this fork of pyelliptic will track and
incorporate any changes to pyelliptic from BitMessage. Ideally, in the future,
BitMessage would import this module as a dependency instead of maintaining a
copy of the source in its repository.
The BitMessage fork forked from v1.3 of upstream pyelliptic. The commits in
this repository are the commits extracted from the BitMessage repository and
applied to pyelliptic v1.3 upstream repository (i.e. to the base of the fork),
so history with athorship is preserved.
Some of the changes in upstream pyelliptic between 1.3 and 1.5.8 came from
BitMessage, those changes are present in this fork. Other changes do not exist
in this fork (they may be added in the future).
Also, a few minor changes exist in this fork but is not (yet) present in
BitMessage source. See:
git log 1.3-PyBitmessage-37489cf7feff8d5047f24baa8f6d27f353a6d6ac..HEAD
## Features
### Asymmetric cryptography using Elliptic Curve Cryptography (ECC)
* Key agreement : ECDH
* Digital signatures : ECDSA
* Hybrid encryption : ECIES (like RSA)
### Symmetric cryptography
* AES-128 (CBC, OFB, CFB, CTR)
* AES-256 (CBC, OFB, CFB, CTR)
* Blowfish (CFB and CBC)
* RC4
### Other
* CSPRNG
* HMAC (using SHA512)
* PBKDF2 (SHA256 and SHA512)
## Example
```python
#!/usr/bin/python
import pyelliptic
# Symmetric encryption
iv = pyelliptic.Cipher.gen_IV('aes-256-cfb')
ctx = pyelliptic.Cipher("secretkey", iv, 1, ciphername='aes-256-cfb')
ciphertext = ctx.update('test1')
ciphertext += ctx.update('test2')
ciphertext += ctx.final()
ctx2 = pyelliptic.Cipher("secretkey", iv, 0, ciphername='aes-256-cfb')
print ctx2.ciphering(ciphertext)
# Asymmetric encryption
alice = pyelliptic.ECC() # default curve: sect283r1
bob = pyelliptic.ECC(curve='sect571r1')
ciphertext = alice.encrypt("Hello Bob", bob.get_pubkey())
print bob.decrypt(ciphertext)
signature = bob.sign("Hello Alice")
# alice's job :
print pyelliptic.ECC(pubkey=bob.get_pubkey()).verify(signature, "Hello Alice")
# ERROR !!!
try:
key = alice.get_ecdh_key(bob.get_pubkey())
except: print("For ECDH key agreement, the keys must be defined on the same curve !")
alice = pyelliptic.ECC(curve='sect571r1')
print alice.get_ecdh_key(bob.get_pubkey()).encode('hex')
print bob.get_ecdh_key(alice.get_pubkey()).encode('hex')
```

View File

@ -1,19 +0,0 @@
# Copyright (C) 2010
# Author: Yann GUIBET
# Contact: <yannguibet@gmail.com>
__version__ = '1.3'
__all__ = [
'OpenSSL',
'ECC',
'Cipher',
'hmac_sha256',
'hmac_sha512',
'pbkdf2'
]
from .openssl import OpenSSL
from .ecc import ECC
from .cipher import Cipher
from .hash import hmac_sha256, hmac_sha512, pbkdf2

View File

@ -1,144 +0,0 @@
# pylint: disable=missing-docstring,too-many-function-args
import hashlib
import re
P = 2**256 - 2**32 - 2**9 - 2**8 - 2**7 - 2**6 - 2**4 - 1
A = 0
Gx = 55066263022277343669578718895168534326250603453777594175500187360389116729240
Gy = 32670510020758816978083085130507043184471273380659243275938904335757337482424
G = (Gx, Gy)
def inv(a, n):
lm, hm = 1, 0
low, high = a % n, n
while low > 1:
r = high / low
nm, new = hm - lm * r, high - low * r
lm, low, hm, high = nm, new, lm, low
return lm % n
def get_code_string(base):
if base == 2:
return '01'
elif base == 10:
return '0123456789'
elif base == 16:
return "0123456789abcdef"
elif base == 58:
return "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
elif base == 256:
return ''.join([chr(x) for x in range(256)])
else:
raise ValueError("Invalid base!")
def encode(val, base, minlen=0):
code_string = get_code_string(base)
result = ""
while val > 0:
result = code_string[val % base] + result
val /= base
if len(result) < minlen:
result = code_string[0] * (minlen - len(result)) + result
return result
def decode(string, base):
code_string = get_code_string(base)
result = 0
if base == 16:
string = string.lower()
while string:
result *= base
result += code_string.find(string[0])
string = string[1:]
return result
def changebase(string, frm, to, minlen=0):
return encode(decode(string, frm), to, minlen)
def base10_add(a, b):
if a is None:
return b[0], b[1]
if b is None:
return a[0], a[1]
if a[0] == b[0]:
if a[1] == b[1]:
return base10_double(a[0], a[1])
return None
m = ((b[1] - a[1]) * inv(b[0] - a[0], P)) % P
x = (m * m - a[0] - b[0]) % P
y = (m * (a[0] - x) - a[1]) % P
return (x, y)
def base10_double(a):
if a is None:
return None
m = ((3 * a[0] * a[0] + A) * inv(2 * a[1], P)) % P
x = (m * m - 2 * a[0]) % P
y = (m * (a[0] - x) - a[1]) % P
return (x, y)
def base10_multiply(a, n):
if n == 0:
return G
if n == 1:
return a
if (n % 2) == 0:
return base10_double(base10_multiply(a, n / 2))
if (n % 2) == 1:
return base10_add(base10_double(base10_multiply(a, n / 2)), a)
return None
def hex_to_point(h):
return (decode(h[2:66], 16), decode(h[66:], 16))
def point_to_hex(p):
return '04' + encode(p[0], 16, 64) + encode(p[1], 16, 64)
def multiply(privkey, pubkey):
return point_to_hex(base10_multiply(hex_to_point(pubkey), decode(privkey, 16)))
def privtopub(privkey):
return point_to_hex(base10_multiply(G, decode(privkey, 16)))
def add(p1, p2):
if len(p1) == 32:
return encode(decode(p1, 16) + decode(p2, 16) % P, 16, 32)
return point_to_hex(base10_add(hex_to_point(p1), hex_to_point(p2)))
def hash_160(string):
intermed = hashlib.sha256(string).digest()
ripemd160 = hashlib.new('ripemd160')
ripemd160.update(intermed)
return ripemd160.digest()
def dbl_sha256(string):
return hashlib.sha256(hashlib.sha256(string).digest()).digest()
def bin_to_b58check(inp):
inp_fmtd = '\x00' + inp
leadingzbytes = len(re.match('^\x00*', inp_fmtd).group(0))
checksum = dbl_sha256(inp_fmtd)[:4]
return '1' * leadingzbytes + changebase(inp_fmtd + checksum, 256, 58)
# Convert a public key (in hex) to a Bitcoin address
def pubkey_to_address(pubkey):
return bin_to_b58check(hash_160(changebase(pubkey, 16, 256)))

View File

@ -1,84 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2011 Yann GUIBET <yannguibet@gmail.com>
# See LICENSE for details.
from .openssl import OpenSSL
class Cipher:
"""
Symmetric encryption
import pyelliptic
iv = pyelliptic.Cipher.gen_IV('aes-256-cfb')
ctx = pyelliptic.Cipher("secretkey", iv, 1, ciphername='aes-256-cfb')
ciphertext = ctx.update('test1')
ciphertext += ctx.update('test2')
ciphertext += ctx.final()
ctx2 = pyelliptic.Cipher("secretkey", iv, 0, ciphername='aes-256-cfb')
print ctx2.ciphering(ciphertext)
"""
def __init__(self, key, iv, do, ciphername='aes-256-cbc'):
"""
do == 1 => Encrypt; do == 0 => Decrypt
"""
self.cipher = OpenSSL.get_cipher(ciphername)
self.ctx = OpenSSL.EVP_CIPHER_CTX_new()
if do == 1 or do == 0:
k = OpenSSL.malloc(key, len(key))
IV = OpenSSL.malloc(iv, len(iv))
OpenSSL.EVP_CipherInit_ex(
self.ctx, self.cipher.get_pointer(), 0, k, IV, do)
else:
raise Exception("RTFM ...")
@staticmethod
def get_all_cipher():
"""
static method, returns all ciphers available
"""
return OpenSSL.cipher_algo.keys()
@staticmethod
def get_blocksize(ciphername):
cipher = OpenSSL.get_cipher(ciphername)
return cipher.get_blocksize()
@staticmethod
def gen_IV(ciphername):
cipher = OpenSSL.get_cipher(ciphername)
return OpenSSL.rand(cipher.get_blocksize())
def update(self, input):
i = OpenSSL.c_int(0)
buffer = OpenSSL.malloc(b"", len(input) + self.cipher.get_blocksize())
inp = OpenSSL.malloc(input, len(input))
if OpenSSL.EVP_CipherUpdate(self.ctx, OpenSSL.byref(buffer),
OpenSSL.byref(i), inp, len(input)) == 0:
raise Exception("[OpenSSL] EVP_CipherUpdate FAIL ...")
return buffer.raw[0:i.value]
def final(self):
i = OpenSSL.c_int(0)
buffer = OpenSSL.malloc(b"", self.cipher.get_blocksize())
if (OpenSSL.EVP_CipherFinal_ex(self.ctx, OpenSSL.byref(buffer),
OpenSSL.byref(i))) == 0:
raise Exception("[OpenSSL] EVP_CipherFinal_ex FAIL ...")
return buffer.raw[0:i.value]
def ciphering(self, input):
"""
Do update and final in one method
"""
buff = self.update(input)
return buff + self.final()
def __del__(self):
if OpenSSL._hexversion > 0x10100000 and not OpenSSL._libreSSL:
OpenSSL.EVP_CIPHER_CTX_reset(self.ctx)
else:
OpenSSL.EVP_CIPHER_CTX_cleanup(self.ctx)
OpenSSL.EVP_CIPHER_CTX_free(self.ctx)

View File

@ -1,505 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
pyelliptic/ecc.py
=====================
"""
# pylint: disable=protected-access
# Copyright (C) 2011 Yann GUIBET <yannguibet@gmail.com>
# See LICENSE for details.
from hashlib import sha512
from struct import pack, unpack
from .cipher import Cipher
from .hash import equals, hmac_sha256
from .openssl import OpenSSL
class ECC(object):
"""
Asymmetric encryption with Elliptic Curve Cryptography (ECC)
ECDH, ECDSA and ECIES
>>> import pyelliptic
>>> alice = pyelliptic.ECC() # default curve: sect283r1
>>> bob = pyelliptic.ECC(curve='sect571r1')
>>> ciphertext = alice.encrypt("Hello Bob", bob.get_pubkey())
>>> print bob.decrypt(ciphertext)
>>> signature = bob.sign("Hello Alice")
>>> # alice's job :
>>> print pyelliptic.ECC(
>>> pubkey=bob.get_pubkey()).verify(signature, "Hello Alice")
>>> # ERROR !!!
>>> try:
>>> key = alice.get_ecdh_key(bob.get_pubkey())
>>> except:
>>> print("For ECDH key agreement, the keys must be defined on the same curve !")
>>> alice = pyelliptic.ECC(curve='sect571r1')
>>> print alice.get_ecdh_key(bob.get_pubkey()).encode('hex')
>>> print bob.get_ecdh_key(alice.get_pubkey()).encode('hex')
"""
def __init__(
self,
pubkey=None,
privkey=None,
pubkey_x=None,
pubkey_y=None,
raw_privkey=None,
curve='sect283r1',
): # pylint: disable=too-many-arguments
"""
For a normal and High level use, specifie pubkey,
privkey (if you need) and the curve
"""
if isinstance(curve, str):
self.curve = OpenSSL.get_curve(curve)
else:
self.curve = curve
if pubkey_x is not None and pubkey_y is not None:
self._set_keys(pubkey_x, pubkey_y, raw_privkey)
elif pubkey is not None:
curve, pubkey_x, pubkey_y, _ = ECC._decode_pubkey(pubkey)
if privkey is not None:
curve2, raw_privkey, _ = ECC._decode_privkey(privkey)
if curve != curve2:
raise Exception("Bad ECC keys ...")
self.curve = curve
self._set_keys(pubkey_x, pubkey_y, raw_privkey)
else:
self.privkey, self.pubkey_x, self.pubkey_y = self._generate()
def _set_keys(self, pubkey_x, pubkey_y, privkey):
if self.raw_check_key(privkey, pubkey_x, pubkey_y) < 0:
self.pubkey_x = None
self.pubkey_y = None
self.privkey = None
raise Exception("Bad ECC keys ...")
else:
self.pubkey_x = pubkey_x
self.pubkey_y = pubkey_y
self.privkey = privkey
@staticmethod
def get_curves():
"""
static method, returns the list of all the curves available
"""
return OpenSSL.curves.keys()
def get_curve(self):
"""Encryption object from curve name"""
return OpenSSL.get_curve_by_id(self.curve)
def get_curve_id(self):
"""Currently used curve"""
return self.curve
def get_pubkey(self):
"""
High level function which returns :
curve(2) + len_of_pubkeyX(2) + pubkeyX + len_of_pubkeyY + pubkeyY
"""
return b''.join((
pack('!H', self.curve),
pack('!H', len(self.pubkey_x)),
self.pubkey_x,
pack('!H', len(self.pubkey_y)),
self.pubkey_y,
))
def get_privkey(self):
"""
High level function which returns
curve(2) + len_of_privkey(2) + privkey
"""
return b''.join((
pack('!H', self.curve),
pack('!H', len(self.privkey)),
self.privkey,
))
@staticmethod
def _decode_pubkey(pubkey):
i = 0
curve = unpack('!H', pubkey[i:i + 2])[0]
i += 2
tmplen = unpack('!H', pubkey[i:i + 2])[0]
i += 2
pubkey_x = pubkey[i:i + tmplen]
i += tmplen
tmplen = unpack('!H', pubkey[i:i + 2])[0]
i += 2
pubkey_y = pubkey[i:i + tmplen]
i += tmplen
return curve, pubkey_x, pubkey_y, i
@staticmethod
def _decode_privkey(privkey):
i = 0
curve = unpack('!H', privkey[i:i + 2])[0]
i += 2
tmplen = unpack('!H', privkey[i:i + 2])[0]
i += 2
privkey = privkey[i:i + tmplen]
i += tmplen
return curve, privkey, i
def _generate(self):
try:
pub_key_x = OpenSSL.BN_new()
pub_key_y = OpenSSL.BN_new()
key = OpenSSL.EC_KEY_new_by_curve_name(self.curve)
if key == 0:
raise Exception("[OpenSSL] EC_KEY_new_by_curve_name FAIL ...")
if (OpenSSL.EC_KEY_generate_key(key)) == 0:
raise Exception("[OpenSSL] EC_KEY_generate_key FAIL ...")
if (OpenSSL.EC_KEY_check_key(key)) == 0:
raise Exception("[OpenSSL] EC_KEY_check_key FAIL ...")
priv_key = OpenSSL.EC_KEY_get0_private_key(key)
group = OpenSSL.EC_KEY_get0_group(key)
pub_key = OpenSSL.EC_KEY_get0_public_key(key)
if OpenSSL.EC_POINT_get_affine_coordinates_GFp(
group, pub_key, pub_key_x, pub_key_y, 0) == 0:
raise Exception("[OpenSSL] EC_POINT_get_affine_coordinates_GFp FAIL ...")
privkey = OpenSSL.malloc(0, OpenSSL.BN_num_bytes(priv_key))
pubkeyx = OpenSSL.malloc(0, OpenSSL.BN_num_bytes(pub_key_x))
pubkeyy = OpenSSL.malloc(0, OpenSSL.BN_num_bytes(pub_key_y))
OpenSSL.BN_bn2bin(priv_key, privkey)
privkey = privkey.raw
OpenSSL.BN_bn2bin(pub_key_x, pubkeyx)
pubkeyx = pubkeyx.raw
OpenSSL.BN_bn2bin(pub_key_y, pubkeyy)
pubkeyy = pubkeyy.raw
self.raw_check_key(privkey, pubkeyx, pubkeyy)
return privkey, pubkeyx, pubkeyy
finally:
OpenSSL.EC_KEY_free(key)
OpenSSL.BN_free(pub_key_x)
OpenSSL.BN_free(pub_key_y)
def get_ecdh_key(self, pubkey):
"""
High level function. Compute public key with the local private key
and returns a 512bits shared key
"""
curve, pubkey_x, pubkey_y, _ = ECC._decode_pubkey(pubkey)
if curve != self.curve:
raise Exception("ECC keys must be from the same curve !")
return sha512(self.raw_get_ecdh_key(pubkey_x, pubkey_y)).digest()
def raw_get_ecdh_key(self, pubkey_x, pubkey_y):
"""ECDH key as binary data"""
try:
ecdh_keybuffer = OpenSSL.malloc(0, 32)
other_key = OpenSSL.EC_KEY_new_by_curve_name(self.curve)
if other_key == 0:
raise Exception("[OpenSSL] EC_KEY_new_by_curve_name FAIL ...")
other_pub_key_x = OpenSSL.BN_bin2bn(pubkey_x, len(pubkey_x), 0)
other_pub_key_y = OpenSSL.BN_bin2bn(pubkey_y, len(pubkey_y), 0)
other_group = OpenSSL.EC_KEY_get0_group(other_key)
other_pub_key = OpenSSL.EC_POINT_new(other_group)
if (OpenSSL.EC_POINT_set_affine_coordinates_GFp(other_group,
other_pub_key,
other_pub_key_x,
other_pub_key_y,
0)) == 0:
raise Exception(
"[OpenSSL] EC_POINT_set_affine_coordinates_GFp FAIL ...")
if (OpenSSL.EC_KEY_set_public_key(other_key, other_pub_key)) == 0:
raise Exception("[OpenSSL] EC_KEY_set_public_key FAIL ...")
if (OpenSSL.EC_KEY_check_key(other_key)) == 0:
raise Exception("[OpenSSL] EC_KEY_check_key FAIL ...")
own_key = OpenSSL.EC_KEY_new_by_curve_name(self.curve)
if own_key == 0:
raise Exception("[OpenSSL] EC_KEY_new_by_curve_name FAIL ...")
own_priv_key = OpenSSL.BN_bin2bn(
self.privkey, len(self.privkey), 0)
if (OpenSSL.EC_KEY_set_private_key(own_key, own_priv_key)) == 0:
raise Exception("[OpenSSL] EC_KEY_set_private_key FAIL ...")
if OpenSSL._hexversion > 0x10100000 and not OpenSSL._libreSSL:
OpenSSL.EC_KEY_set_method(own_key, OpenSSL.EC_KEY_OpenSSL())
else:
OpenSSL.ECDH_set_method(own_key, OpenSSL.ECDH_OpenSSL())
ecdh_keylen = OpenSSL.ECDH_compute_key(
ecdh_keybuffer, 32, other_pub_key, own_key, 0)
if ecdh_keylen != 32:
raise Exception("[OpenSSL] ECDH keylen FAIL ...")
return ecdh_keybuffer.raw
finally:
OpenSSL.EC_KEY_free(other_key)
OpenSSL.BN_free(other_pub_key_x)
OpenSSL.BN_free(other_pub_key_y)
OpenSSL.EC_POINT_free(other_pub_key)
OpenSSL.EC_KEY_free(own_key)
OpenSSL.BN_free(own_priv_key)
def check_key(self, privkey, pubkey):
"""
Check the public key and the private key.
The private key is optional (replace by None)
"""
curve, pubkey_x, pubkey_y, _ = ECC._decode_pubkey(pubkey)
if privkey is None:
raw_privkey = None
curve2 = curve
else:
curve2, raw_privkey, _ = ECC._decode_privkey(privkey)
if curve != curve2:
raise Exception("Bad public and private key")
return self.raw_check_key(raw_privkey, pubkey_x, pubkey_y, curve)
def raw_check_key(self, privkey, pubkey_x, pubkey_y, curve=None):
"""Check key validity, key is supplied as binary data"""
# pylint: disable=too-many-branches
if curve is None:
curve = self.curve
elif isinstance(curve, str):
curve = OpenSSL.get_curve(curve)
else:
curve = curve
try:
key = OpenSSL.EC_KEY_new_by_curve_name(curve)
if key == 0:
raise Exception("[OpenSSL] EC_KEY_new_by_curve_name FAIL ...")
if privkey is not None:
priv_key = OpenSSL.BN_bin2bn(privkey, len(privkey), 0)
pub_key_x = OpenSSL.BN_bin2bn(pubkey_x, len(pubkey_x), 0)
pub_key_y = OpenSSL.BN_bin2bn(pubkey_y, len(pubkey_y), 0)
if privkey is not None:
if (OpenSSL.EC_KEY_set_private_key(key, priv_key)) == 0:
raise Exception(
"[OpenSSL] EC_KEY_set_private_key FAIL ...")
group = OpenSSL.EC_KEY_get0_group(key)
pub_key = OpenSSL.EC_POINT_new(group)
if (OpenSSL.EC_POINT_set_affine_coordinates_GFp(group, pub_key,
pub_key_x,
pub_key_y,
0)) == 0:
raise Exception(
"[OpenSSL] EC_POINT_set_affine_coordinates_GFp FAIL ...")
if (OpenSSL.EC_KEY_set_public_key(key, pub_key)) == 0:
raise Exception("[OpenSSL] EC_KEY_set_public_key FAIL ...")
if (OpenSSL.EC_KEY_check_key(key)) == 0:
raise Exception("[OpenSSL] EC_KEY_check_key FAIL ...")
return 0
finally:
OpenSSL.EC_KEY_free(key)
OpenSSL.BN_free(pub_key_x)
OpenSSL.BN_free(pub_key_y)
OpenSSL.EC_POINT_free(pub_key)
if privkey is not None:
OpenSSL.BN_free(priv_key)
def sign(self, inputb, digest_alg=OpenSSL.digest_ecdsa_sha1):
"""
Sign the input with ECDSA method and returns the signature
"""
# pylint: disable=too-many-branches,too-many-locals
try:
size = len(inputb)
buff = OpenSSL.malloc(inputb, size)
digest = OpenSSL.malloc(0, 64)
if OpenSSL._hexversion > 0x10100000 and not OpenSSL._libreSSL:
md_ctx = OpenSSL.EVP_MD_CTX_new()
else:
md_ctx = OpenSSL.EVP_MD_CTX_create()
dgst_len = OpenSSL.pointer(OpenSSL.c_int(0))
siglen = OpenSSL.pointer(OpenSSL.c_int(0))
sig = OpenSSL.malloc(0, 151)
key = OpenSSL.EC_KEY_new_by_curve_name(self.curve)
if key == 0:
raise Exception("[OpenSSL] EC_KEY_new_by_curve_name FAIL ...")
priv_key = OpenSSL.BN_bin2bn(self.privkey, len(self.privkey), 0)
pub_key_x = OpenSSL.BN_bin2bn(self.pubkey_x, len(self.pubkey_x), 0)
pub_key_y = OpenSSL.BN_bin2bn(self.pubkey_y, len(self.pubkey_y), 0)
if (OpenSSL.EC_KEY_set_private_key(key, priv_key)) == 0:
raise Exception("[OpenSSL] EC_KEY_set_private_key FAIL ...")
group = OpenSSL.EC_KEY_get0_group(key)
pub_key = OpenSSL.EC_POINT_new(group)
if (OpenSSL.EC_POINT_set_affine_coordinates_GFp(group, pub_key,
pub_key_x,
pub_key_y,
0)) == 0:
raise Exception(
"[OpenSSL] EC_POINT_set_affine_coordinates_GFp FAIL ...")
if (OpenSSL.EC_KEY_set_public_key(key, pub_key)) == 0:
raise Exception("[OpenSSL] EC_KEY_set_public_key FAIL ...")
if (OpenSSL.EC_KEY_check_key(key)) == 0:
raise Exception("[OpenSSL] EC_KEY_check_key FAIL ...")
if OpenSSL._hexversion > 0x10100000 and not OpenSSL._libreSSL:
OpenSSL.EVP_MD_CTX_new(md_ctx)
else:
OpenSSL.EVP_MD_CTX_init(md_ctx)
OpenSSL.EVP_DigestInit_ex(md_ctx, digest_alg(), None)
if (OpenSSL.EVP_DigestUpdate(md_ctx, buff, size)) == 0:
raise Exception("[OpenSSL] EVP_DigestUpdate FAIL ...")
OpenSSL.EVP_DigestFinal_ex(md_ctx, digest, dgst_len)
OpenSSL.ECDSA_sign(0, digest, dgst_len.contents, sig, siglen, key)
if (OpenSSL.ECDSA_verify(0, digest, dgst_len.contents, sig,
siglen.contents, key)) != 1:
raise Exception("[OpenSSL] ECDSA_verify FAIL ...")
return sig.raw[:siglen.contents.value]
finally:
OpenSSL.EC_KEY_free(key)
OpenSSL.BN_free(pub_key_x)
OpenSSL.BN_free(pub_key_y)
OpenSSL.BN_free(priv_key)
OpenSSL.EC_POINT_free(pub_key)
if OpenSSL._hexversion > 0x10100000 and not OpenSSL._libreSSL:
OpenSSL.EVP_MD_CTX_free(md_ctx)
else:
OpenSSL.EVP_MD_CTX_destroy(md_ctx)
def verify(self, sig, inputb, digest_alg=OpenSSL.digest_ecdsa_sha1):
"""
Verify the signature with the input and the local public key.
Returns a boolean
"""
# pylint: disable=too-many-branches
try:
bsig = OpenSSL.malloc(sig, len(sig))
binputb = OpenSSL.malloc(inputb, len(inputb))
digest = OpenSSL.malloc(0, 64)
dgst_len = OpenSSL.pointer(OpenSSL.c_int(0))
if OpenSSL._hexversion > 0x10100000 and not OpenSSL._libreSSL:
md_ctx = OpenSSL.EVP_MD_CTX_new()
else:
md_ctx = OpenSSL.EVP_MD_CTX_create()
key = OpenSSL.EC_KEY_new_by_curve_name(self.curve)
if key == 0:
raise Exception("[OpenSSL] EC_KEY_new_by_curve_name FAIL ...")
pub_key_x = OpenSSL.BN_bin2bn(self.pubkey_x, len(self.pubkey_x), 0)
pub_key_y = OpenSSL.BN_bin2bn(self.pubkey_y, len(self.pubkey_y), 0)
group = OpenSSL.EC_KEY_get0_group(key)
pub_key = OpenSSL.EC_POINT_new(group)
if (OpenSSL.EC_POINT_set_affine_coordinates_GFp(group, pub_key,
pub_key_x,
pub_key_y,
0)) == 0:
raise Exception(
"[OpenSSL] EC_POINT_set_affine_coordinates_GFp FAIL ...")
if (OpenSSL.EC_KEY_set_public_key(key, pub_key)) == 0:
raise Exception("[OpenSSL] EC_KEY_set_public_key FAIL ...")
if (OpenSSL.EC_KEY_check_key(key)) == 0:
raise Exception("[OpenSSL] EC_KEY_check_key FAIL ...")
if OpenSSL._hexversion > 0x10100000 and not OpenSSL._libreSSL:
OpenSSL.EVP_MD_CTX_new(md_ctx)
else:
OpenSSL.EVP_MD_CTX_init(md_ctx)
OpenSSL.EVP_DigestInit_ex(md_ctx, digest_alg(), None)
if (OpenSSL.EVP_DigestUpdate(md_ctx, binputb, len(inputb))) == 0:
raise Exception("[OpenSSL] EVP_DigestUpdate FAIL ...")
OpenSSL.EVP_DigestFinal_ex(md_ctx, digest, dgst_len)
ret = OpenSSL.ECDSA_verify(
0, digest, dgst_len.contents, bsig, len(sig), key)
if ret == -1:
return False # Fail to Check
if ret == 0:
return False # Bad signature !
return True # Good
finally:
OpenSSL.EC_KEY_free(key)
OpenSSL.BN_free(pub_key_x)
OpenSSL.BN_free(pub_key_y)
OpenSSL.EC_POINT_free(pub_key)
if OpenSSL._hexversion > 0x10100000 and not OpenSSL._libreSSL:
OpenSSL.EVP_MD_CTX_free(md_ctx)
else:
OpenSSL.EVP_MD_CTX_destroy(md_ctx)
@staticmethod
def encrypt(data, pubkey, ephemcurve=None, ciphername='aes-256-cbc'):
"""
Encrypt data with ECIES method using the public key of the recipient.
"""
curve, pubkey_x, pubkey_y, _ = ECC._decode_pubkey(pubkey)
return ECC.raw_encrypt(data, pubkey_x, pubkey_y, curve=curve,
ephemcurve=ephemcurve, ciphername=ciphername)
@staticmethod
def raw_encrypt(
data,
pubkey_x,
pubkey_y,
curve='sect283r1',
ephemcurve=None,
ciphername='aes-256-cbc',
): # pylint: disable=too-many-arguments
"""ECHD encryption, keys supplied in binary data format"""
if ephemcurve is None:
ephemcurve = curve
ephem = ECC(curve=ephemcurve)
key = sha512(ephem.raw_get_ecdh_key(pubkey_x, pubkey_y)).digest()
key_e, key_m = key[:32], key[32:]
pubkey = ephem.get_pubkey()
iv = OpenSSL.rand(OpenSSL.get_cipher(ciphername).get_blocksize())
ctx = Cipher(key_e, iv, 1, ciphername)
ciphertext = iv + pubkey + ctx.ciphering(data)
mac = hmac_sha256(key_m, ciphertext)
return ciphertext + mac
def decrypt(self, data, ciphername='aes-256-cbc'):
"""
Decrypt data with ECIES method using the local private key
"""
# pylint: disable=too-many-locals
blocksize = OpenSSL.get_cipher(ciphername).get_blocksize()
iv = data[:blocksize]
i = blocksize
_, pubkey_x, pubkey_y, i2 = ECC._decode_pubkey(data[i:])
i += i2
ciphertext = data[i:len(data) - 32]
i += len(ciphertext)
mac = data[i:]
key = sha512(self.raw_get_ecdh_key(pubkey_x, pubkey_y)).digest()
key_e, key_m = key[:32], key[32:]
if not equals(hmac_sha256(key_m, data[:len(data) - 32]), mac):
raise RuntimeError("Fail to verify data")
ctx = Cipher(key_e, iv, 0, ciphername)
return ctx.ciphering(ciphertext)

View File

@ -1,69 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2011 Yann GUIBET <yannguibet@gmail.com>
# See LICENSE for details.
from .openssl import OpenSSL
# For python3
def _equals_bytes(a, b):
if len(a) != len(b):
return False
result = 0
for x, y in zip(a, b):
result |= x ^ y
return result == 0
def _equals_str(a, b):
if len(a) != len(b):
return False
result = 0
for x, y in zip(a, b):
result |= ord(x) ^ ord(y)
return result == 0
def equals(a, b):
if isinstance(a, str):
return _equals_str(a, b)
else:
return _equals_bytes(a, b)
def hmac_sha256(k, m):
"""
Compute the key and the message with HMAC SHA5256
"""
key = OpenSSL.malloc(k, len(k))
d = OpenSSL.malloc(m, len(m))
md = OpenSSL.malloc(0, 32)
i = OpenSSL.pointer(OpenSSL.c_int(0))
OpenSSL.HMAC(OpenSSL.EVP_sha256(), key, len(k), d, len(m), md, i)
return md.raw
def hmac_sha512(k, m):
"""
Compute the key and the message with HMAC SHA512
"""
key = OpenSSL.malloc(k, len(k))
d = OpenSSL.malloc(m, len(m))
md = OpenSSL.malloc(0, 64)
i = OpenSSL.pointer(OpenSSL.c_int(0))
OpenSSL.HMAC(OpenSSL.EVP_sha512(), key, len(k), d, len(m), md, i)
return md.raw
def pbkdf2(password, salt=None, i=10000, keylen=64):
if salt is None:
salt = OpenSSL.rand(8)
p_password = OpenSSL.malloc(password, len(password))
p_salt = OpenSSL.malloc(salt, len(salt))
output = OpenSSL.malloc(0, keylen)
OpenSSL.PKCS5_PBKDF2_HMAC(p_password, len(password), p_salt,
len(p_salt), i, OpenSSL.EVP_sha256(),
keylen, output)
return salt, output.raw

View File

@ -1,553 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2011 Yann GUIBET <yannguibet@gmail.com>
# See LICENSE for details.
#
# Software slightly changed by Jonathan Warren <bitmessage at-symbol jonwarren.org>
import sys
import ctypes
OpenSSL = None
class CipherName:
def __init__(self, name, pointer, blocksize):
self._name = name
self._pointer = pointer
self._blocksize = blocksize
def __str__(self):
return "Cipher : " + self._name + " | Blocksize : " + str(self._blocksize) + " | Fonction pointer : " + str(self._pointer)
def get_pointer(self):
return self._pointer()
def get_name(self):
return self._name
def get_blocksize(self):
return self._blocksize
def get_version(library):
version = None
hexversion = None
cflags = None
try:
#OpenSSL 1.1
OPENSSL_VERSION = 0
OPENSSL_CFLAGS = 1
library.OpenSSL_version.argtypes = [ctypes.c_int]
library.OpenSSL_version.restype = ctypes.c_char_p
version = library.OpenSSL_version(OPENSSL_VERSION)
cflags = library.OpenSSL_version(OPENSSL_CFLAGS)
library.OpenSSL_version_num.restype = ctypes.c_long
hexversion = library.OpenSSL_version_num()
except AttributeError:
try:
#OpenSSL 1.0
SSLEAY_VERSION = 0
SSLEAY_CFLAGS = 2
library.SSLeay.restype = ctypes.c_long
library.SSLeay_version.restype = ctypes.c_char_p
library.SSLeay_version.argtypes = [ctypes.c_int]
version = library.SSLeay_version(SSLEAY_VERSION)
cflags = library.SSLeay_version(SSLEAY_CFLAGS)
hexversion = library.SSLeay()
except AttributeError:
#raise NotImplementedError('Cannot determine version of this OpenSSL library.')
pass
return (version, hexversion, cflags)
class _OpenSSL:
"""
Wrapper for OpenSSL using ctypes
"""
def __init__(self, library):
"""
Build the wrapper
"""
self._lib = ctypes.CDLL(library)
self._version, self._hexversion, self._cflags = get_version(self._lib)
self._libreSSL = self._version.startswith(b"LibreSSL")
self.pointer = ctypes.pointer
self.c_int = ctypes.c_int
self.byref = ctypes.byref
self.create_string_buffer = ctypes.create_string_buffer
self.BN_new = self._lib.BN_new
self.BN_new.restype = ctypes.c_void_p
self.BN_new.argtypes = []
self.BN_free = self._lib.BN_free
self.BN_free.restype = None
self.BN_free.argtypes = [ctypes.c_void_p]
self.BN_num_bits = self._lib.BN_num_bits
self.BN_num_bits.restype = ctypes.c_int
self.BN_num_bits.argtypes = [ctypes.c_void_p]
self.BN_bn2bin = self._lib.BN_bn2bin
self.BN_bn2bin.restype = ctypes.c_int
self.BN_bn2bin.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
self.BN_bin2bn = self._lib.BN_bin2bn
self.BN_bin2bn.restype = ctypes.c_void_p
self.BN_bin2bn.argtypes = [ctypes.c_void_p, ctypes.c_int,
ctypes.c_void_p]
self.EC_KEY_free = self._lib.EC_KEY_free
self.EC_KEY_free.restype = None
self.EC_KEY_free.argtypes = [ctypes.c_void_p]
self.EC_KEY_new_by_curve_name = self._lib.EC_KEY_new_by_curve_name
self.EC_KEY_new_by_curve_name.restype = ctypes.c_void_p
self.EC_KEY_new_by_curve_name.argtypes = [ctypes.c_int]
self.EC_KEY_generate_key = self._lib.EC_KEY_generate_key
self.EC_KEY_generate_key.restype = ctypes.c_int
self.EC_KEY_generate_key.argtypes = [ctypes.c_void_p]
self.EC_KEY_check_key = self._lib.EC_KEY_check_key
self.EC_KEY_check_key.restype = ctypes.c_int
self.EC_KEY_check_key.argtypes = [ctypes.c_void_p]
self.EC_KEY_get0_private_key = self._lib.EC_KEY_get0_private_key
self.EC_KEY_get0_private_key.restype = ctypes.c_void_p
self.EC_KEY_get0_private_key.argtypes = [ctypes.c_void_p]
self.EC_KEY_get0_public_key = self._lib.EC_KEY_get0_public_key
self.EC_KEY_get0_public_key.restype = ctypes.c_void_p
self.EC_KEY_get0_public_key.argtypes = [ctypes.c_void_p]
self.EC_KEY_get0_group = self._lib.EC_KEY_get0_group
self.EC_KEY_get0_group.restype = ctypes.c_void_p
self.EC_KEY_get0_group.argtypes = [ctypes.c_void_p]
self.EC_POINT_get_affine_coordinates_GFp = self._lib.EC_POINT_get_affine_coordinates_GFp
self.EC_POINT_get_affine_coordinates_GFp.restype = ctypes.c_int
self.EC_POINT_get_affine_coordinates_GFp.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
self.EC_KEY_set_private_key = self._lib.EC_KEY_set_private_key
self.EC_KEY_set_private_key.restype = ctypes.c_int
self.EC_KEY_set_private_key.argtypes = [ctypes.c_void_p,
ctypes.c_void_p]
self.EC_KEY_set_public_key = self._lib.EC_KEY_set_public_key
self.EC_KEY_set_public_key.restype = ctypes.c_int
self.EC_KEY_set_public_key.argtypes = [ctypes.c_void_p,
ctypes.c_void_p]
self.EC_KEY_set_group = self._lib.EC_KEY_set_group
self.EC_KEY_set_group.restype = ctypes.c_int
self.EC_KEY_set_group.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
self.EC_POINT_set_affine_coordinates_GFp = self._lib.EC_POINT_set_affine_coordinates_GFp
self.EC_POINT_set_affine_coordinates_GFp.restype = ctypes.c_int
self.EC_POINT_set_affine_coordinates_GFp.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
self.EC_POINT_new = self._lib.EC_POINT_new
self.EC_POINT_new.restype = ctypes.c_void_p
self.EC_POINT_new.argtypes = [ctypes.c_void_p]
self.EC_POINT_free = self._lib.EC_POINT_free
self.EC_POINT_free.restype = None
self.EC_POINT_free.argtypes = [ctypes.c_void_p]
self.BN_CTX_free = self._lib.BN_CTX_free
self.BN_CTX_free.restype = None
self.BN_CTX_free.argtypes = [ctypes.c_void_p]
self.EC_POINT_mul = self._lib.EC_POINT_mul
self.EC_POINT_mul.restype = ctypes.c_int
self.EC_POINT_mul.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
self.EC_KEY_set_private_key = self._lib.EC_KEY_set_private_key
self.EC_KEY_set_private_key.restype = ctypes.c_int
self.EC_KEY_set_private_key.argtypes = [ctypes.c_void_p,
ctypes.c_void_p]
if self._hexversion >= 0x10100000 and not self._libreSSL:
self.EC_KEY_OpenSSL = self._lib.EC_KEY_OpenSSL
self._lib.EC_KEY_OpenSSL.restype = ctypes.c_void_p
self._lib.EC_KEY_OpenSSL.argtypes = []
self.EC_KEY_set_method = self._lib.EC_KEY_set_method
self._lib.EC_KEY_set_method.restype = ctypes.c_int
self._lib.EC_KEY_set_method.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
else:
self.ECDH_OpenSSL = self._lib.ECDH_OpenSSL
self._lib.ECDH_OpenSSL.restype = ctypes.c_void_p
self._lib.ECDH_OpenSSL.argtypes = []
self.ECDH_set_method = self._lib.ECDH_set_method
self._lib.ECDH_set_method.restype = ctypes.c_int
self._lib.ECDH_set_method.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
self.BN_CTX_new = self._lib.BN_CTX_new
self._lib.BN_CTX_new.restype = ctypes.c_void_p
self._lib.BN_CTX_new.argtypes = []
self.ECDH_compute_key = self._lib.ECDH_compute_key
self.ECDH_compute_key.restype = ctypes.c_int
self.ECDH_compute_key.argtypes = [ctypes.c_void_p,
ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p]
self.EVP_CipherInit_ex = self._lib.EVP_CipherInit_ex
self.EVP_CipherInit_ex.restype = ctypes.c_int
self.EVP_CipherInit_ex.argtypes = [ctypes.c_void_p,
ctypes.c_void_p, ctypes.c_void_p]
self.EVP_CIPHER_CTX_new = self._lib.EVP_CIPHER_CTX_new
self.EVP_CIPHER_CTX_new.restype = ctypes.c_void_p
self.EVP_CIPHER_CTX_new.argtypes = []
# Cipher
self.EVP_aes_128_cfb128 = self._lib.EVP_aes_128_cfb128
self.EVP_aes_128_cfb128.restype = ctypes.c_void_p
self.EVP_aes_128_cfb128.argtypes = []
self.EVP_aes_256_cfb128 = self._lib.EVP_aes_256_cfb128
self.EVP_aes_256_cfb128.restype = ctypes.c_void_p
self.EVP_aes_256_cfb128.argtypes = []
self.EVP_aes_128_cbc = self._lib.EVP_aes_128_cbc
self.EVP_aes_128_cbc.restype = ctypes.c_void_p
self.EVP_aes_128_cbc.argtypes = []
self.EVP_aes_256_cbc = self._lib.EVP_aes_256_cbc
self.EVP_aes_256_cbc.restype = ctypes.c_void_p
self.EVP_aes_256_cbc.argtypes = []
#self.EVP_aes_128_ctr = self._lib.EVP_aes_128_ctr
#self.EVP_aes_128_ctr.restype = ctypes.c_void_p
#self.EVP_aes_128_ctr.argtypes = []
#self.EVP_aes_256_ctr = self._lib.EVP_aes_256_ctr
#self.EVP_aes_256_ctr.restype = ctypes.c_void_p
#self.EVP_aes_256_ctr.argtypes = []
self.EVP_aes_128_ofb = self._lib.EVP_aes_128_ofb
self.EVP_aes_128_ofb.restype = ctypes.c_void_p
self.EVP_aes_128_ofb.argtypes = []
self.EVP_aes_256_ofb = self._lib.EVP_aes_256_ofb
self.EVP_aes_256_ofb.restype = ctypes.c_void_p
self.EVP_aes_256_ofb.argtypes = []
self.EVP_bf_cbc = self._lib.EVP_bf_cbc
self.EVP_bf_cbc.restype = ctypes.c_void_p
self.EVP_bf_cbc.argtypes = []
self.EVP_bf_cfb64 = self._lib.EVP_bf_cfb64
self.EVP_bf_cfb64.restype = ctypes.c_void_p
self.EVP_bf_cfb64.argtypes = []
self.EVP_rc4 = self._lib.EVP_rc4
self.EVP_rc4.restype = ctypes.c_void_p
self.EVP_rc4.argtypes = []
if self._hexversion >= 0x10100000 and not self._libreSSL:
self.EVP_CIPHER_CTX_reset = self._lib.EVP_CIPHER_CTX_reset
self.EVP_CIPHER_CTX_reset.restype = ctypes.c_int
self.EVP_CIPHER_CTX_reset.argtypes = [ctypes.c_void_p]
else:
self.EVP_CIPHER_CTX_cleanup = self._lib.EVP_CIPHER_CTX_cleanup
self.EVP_CIPHER_CTX_cleanup.restype = ctypes.c_int
self.EVP_CIPHER_CTX_cleanup.argtypes = [ctypes.c_void_p]
self.EVP_CIPHER_CTX_free = self._lib.EVP_CIPHER_CTX_free
self.EVP_CIPHER_CTX_free.restype = None
self.EVP_CIPHER_CTX_free.argtypes = [ctypes.c_void_p]
self.EVP_CipherUpdate = self._lib.EVP_CipherUpdate
self.EVP_CipherUpdate.restype = ctypes.c_int
self.EVP_CipherUpdate.argtypes = [ctypes.c_void_p,
ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int]
self.EVP_CipherFinal_ex = self._lib.EVP_CipherFinal_ex
self.EVP_CipherFinal_ex.restype = ctypes.c_int
self.EVP_CipherFinal_ex.argtypes = [ctypes.c_void_p,
ctypes.c_void_p, ctypes.c_void_p]
self.EVP_DigestInit = self._lib.EVP_DigestInit
self.EVP_DigestInit.restype = ctypes.c_int
self._lib.EVP_DigestInit.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
self.EVP_DigestInit_ex = self._lib.EVP_DigestInit_ex
self.EVP_DigestInit_ex.restype = ctypes.c_int
self._lib.EVP_DigestInit_ex.argtypes = 3 * [ctypes.c_void_p]
self.EVP_DigestUpdate = self._lib.EVP_DigestUpdate
self.EVP_DigestUpdate.restype = ctypes.c_int
self.EVP_DigestUpdate.argtypes = [ctypes.c_void_p,
ctypes.c_void_p, ctypes.c_int]
self.EVP_DigestFinal = self._lib.EVP_DigestFinal
self.EVP_DigestFinal.restype = ctypes.c_int
self.EVP_DigestFinal.argtypes = [ctypes.c_void_p,
ctypes.c_void_p, ctypes.c_void_p]
self.EVP_DigestFinal_ex = self._lib.EVP_DigestFinal_ex
self.EVP_DigestFinal_ex.restype = ctypes.c_int
self.EVP_DigestFinal_ex.argtypes = [ctypes.c_void_p,
ctypes.c_void_p, ctypes.c_void_p]
self.ECDSA_sign = self._lib.ECDSA_sign
self.ECDSA_sign.restype = ctypes.c_int
self.ECDSA_sign.argtypes = [ctypes.c_int, ctypes.c_void_p,
ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
self.ECDSA_verify = self._lib.ECDSA_verify
self.ECDSA_verify.restype = ctypes.c_int
self.ECDSA_verify.argtypes = [ctypes.c_int, ctypes.c_void_p,
ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p]
if self._hexversion >= 0x10100000 and not self._libreSSL:
self.EVP_MD_CTX_new = self._lib.EVP_MD_CTX_new
self.EVP_MD_CTX_new.restype = ctypes.c_void_p
self.EVP_MD_CTX_new.argtypes = []
self.EVP_MD_CTX_reset = self._lib.EVP_MD_CTX_reset
self.EVP_MD_CTX_reset.restype = None
self.EVP_MD_CTX_reset.argtypes = [ctypes.c_void_p]
self.EVP_MD_CTX_free = self._lib.EVP_MD_CTX_free
self.EVP_MD_CTX_free.restype = None
self.EVP_MD_CTX_free.argtypes = [ctypes.c_void_p]
self.EVP_sha1 = self._lib.EVP_sha1
self.EVP_sha1.restype = ctypes.c_void_p
self.EVP_sha1.argtypes = []
self.digest_ecdsa_sha1 = self.EVP_sha1
else:
self.EVP_MD_CTX_create = self._lib.EVP_MD_CTX_create
self.EVP_MD_CTX_create.restype = ctypes.c_void_p
self.EVP_MD_CTX_create.argtypes = []
self.EVP_MD_CTX_init = self._lib.EVP_MD_CTX_init
self.EVP_MD_CTX_init.restype = None
self.EVP_MD_CTX_init.argtypes = [ctypes.c_void_p]
self.EVP_MD_CTX_destroy = self._lib.EVP_MD_CTX_destroy
self.EVP_MD_CTX_destroy.restype = None
self.EVP_MD_CTX_destroy.argtypes = [ctypes.c_void_p]
self.EVP_ecdsa = self._lib.EVP_ecdsa
self._lib.EVP_ecdsa.restype = ctypes.c_void_p
self._lib.EVP_ecdsa.argtypes = []
self.digest_ecdsa_sha1 = self.EVP_ecdsa
self.RAND_bytes = self._lib.RAND_bytes
self.RAND_bytes.restype = ctypes.c_int
self.RAND_bytes.argtypes = [ctypes.c_void_p, ctypes.c_int]
self.EVP_sha256 = self._lib.EVP_sha256
self.EVP_sha256.restype = ctypes.c_void_p
self.EVP_sha256.argtypes = []
self.i2o_ECPublicKey = self._lib.i2o_ECPublicKey
self.i2o_ECPublicKey.restype = ctypes.c_int
self.i2o_ECPublicKey.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
self.EVP_sha512 = self._lib.EVP_sha512
self.EVP_sha512.restype = ctypes.c_void_p
self.EVP_sha512.argtypes = []
self.HMAC = self._lib.HMAC
self.HMAC.restype = ctypes.c_void_p
self.HMAC.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int,
ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p]
try:
self.PKCS5_PBKDF2_HMAC = self._lib.PKCS5_PBKDF2_HMAC
except:
# The above is not compatible with all versions of OSX.
self.PKCS5_PBKDF2_HMAC = self._lib.PKCS5_PBKDF2_HMAC_SHA1
self.PKCS5_PBKDF2_HMAC.restype = ctypes.c_int
self.PKCS5_PBKDF2_HMAC.argtypes = [ctypes.c_void_p, ctypes.c_int,
ctypes.c_void_p, ctypes.c_int,
ctypes.c_int, ctypes.c_void_p,
ctypes.c_int, ctypes.c_void_p]
self._set_ciphers()
self._set_curves()
def _set_ciphers(self):
self.cipher_algo = {
'aes-128-cbc': CipherName('aes-128-cbc', self.EVP_aes_128_cbc, 16),
'aes-256-cbc': CipherName('aes-256-cbc', self.EVP_aes_256_cbc, 16),
'aes-128-cfb': CipherName('aes-128-cfb', self.EVP_aes_128_cfb128, 16),
'aes-256-cfb': CipherName('aes-256-cfb', self.EVP_aes_256_cfb128, 16),
'aes-128-ofb': CipherName('aes-128-ofb', self._lib.EVP_aes_128_ofb, 16),
'aes-256-ofb': CipherName('aes-256-ofb', self._lib.EVP_aes_256_ofb, 16),
#'aes-128-ctr': CipherName('aes-128-ctr', self._lib.EVP_aes_128_ctr, 16),
#'aes-256-ctr': CipherName('aes-256-ctr', self._lib.EVP_aes_256_ctr, 16),
'bf-cfb': CipherName('bf-cfb', self.EVP_bf_cfb64, 8),
'bf-cbc': CipherName('bf-cbc', self.EVP_bf_cbc, 8),
'rc4': CipherName('rc4', self.EVP_rc4, 128), # 128 is the initialisation size not block size
}
def _set_curves(self):
self.curves = {
'secp112r1': 704,
'secp112r2': 705,
'secp128r1': 706,
'secp128r2': 707,
'secp160k1': 708,
'secp160r1': 709,
'secp160r2': 710,
'secp192k1': 711,
'secp224k1': 712,
'secp224r1': 713,
'secp256k1': 714,
'secp384r1': 715,
'secp521r1': 716,
'sect113r1': 717,
'sect113r2': 718,
'sect131r1': 719,
'sect131r2': 720,
'sect163k1': 721,
'sect163r1': 722,
'sect163r2': 723,
'sect193r1': 724,
'sect193r2': 725,
'sect233k1': 726,
'sect233r1': 727,
'sect239k1': 728,
'sect283k1': 729,
'sect283r1': 730,
'sect409k1': 731,
'sect409r1': 732,
'sect571k1': 733,
'sect571r1': 734,
}
def BN_num_bytes(self, x):
"""
returns the length of a BN (OpenSSl API)
"""
return int((self.BN_num_bits(x) + 7) / 8)
def get_cipher(self, name):
"""
returns the OpenSSL cipher instance
"""
if name not in self.cipher_algo:
raise Exception("Unknown cipher")
return self.cipher_algo[name]
def get_curve(self, name):
"""
returns the id of a elliptic curve
"""
if name not in self.curves:
raise Exception("Unknown curve")
return self.curves[name]
def get_curve_by_id(self, id):
"""
returns the name of a elliptic curve with his id
"""
res = None
for i in self.curves:
if self.curves[i] == id:
res = i
break
if res is None:
raise Exception("Unknown curve")
return res
def rand(self, size):
"""
OpenSSL random function
"""
buffer = self.malloc(0, size)
# This pyelliptic library, by default, didn't check the return value of RAND_bytes. It is
# evidently possible that it returned an error and not-actually-random data. However, in
# tests on various operating systems, while generating hundreds of gigabytes of random
# strings of various sizes I could not get an error to occur. Also Bitcoin doesn't check
# the return value of RAND_bytes either.
# Fixed in Bitmessage version 0.4.2 (in source code on 2013-10-13)
while self.RAND_bytes(buffer, size) != 1:
import time
time.sleep(1)
return buffer.raw
def malloc(self, data, size):
"""
returns a create_string_buffer (ctypes)
"""
buffer = None
if data != 0:
if sys.version_info.major == 3 and isinstance(data, type('')):
data = data.encode()
buffer = self.create_string_buffer(data, size)
else:
buffer = self.create_string_buffer(size)
return buffer
def loadOpenSSL():
global OpenSSL
from os import path, environ
from ctypes.util import find_library
libdir = []
if 'linux' in sys.platform or 'darwin' in sys.platform or 'bsd' in sys.platform:
libdir.append(find_library('ssl'))
elif 'win32' in sys.platform or 'win64' in sys.platform:
libdir.append(find_library('libeay32'))
if getattr(sys,'frozen', None):
if 'darwin' in sys.platform:
libdir.extend([
path.join(environ['RESOURCEPATH'], '..', 'Frameworks','libcrypto.dylib'),
path.join(environ['RESOURCEPATH'], '..', 'Frameworks','libcrypto.1.1.0.dylib'),
path.join(environ['RESOURCEPATH'], '..', 'Frameworks','libcrypto.1.0.2.dylib'),
path.join(environ['RESOURCEPATH'], '..', 'Frameworks','libcrypto.1.0.1.dylib'),
path.join(environ['RESOURCEPATH'], '..', 'Frameworks','libcrypto.1.0.0.dylib'),
path.join(environ['RESOURCEPATH'], '..', 'Frameworks','libcrypto.0.9.8.dylib'),
])
elif 'win32' in sys.platform or 'win64' in sys.platform:
libdir.append(path.join(sys._MEIPASS, 'libeay32.dll'))
else:
libdir.extend([
path.join(sys._MEIPASS, 'libcrypto.so'),
path.join(sys._MEIPASS, 'libssl.so'),
path.join(sys._MEIPASS, 'libcrypto.so.1.1.0'),
path.join(sys._MEIPASS, 'libssl.so.1.1.0'),
path.join(sys._MEIPASS, 'libcrypto.so.1.0.2'),
path.join(sys._MEIPASS, 'libssl.so.1.0.2'),
path.join(sys._MEIPASS, 'libcrypto.so.1.0.1'),
path.join(sys._MEIPASS, 'libssl.so.1.0.1'),
path.join(sys._MEIPASS, 'libcrypto.so.1.0.0'),
path.join(sys._MEIPASS, 'libssl.so.1.0.0'),
path.join(sys._MEIPASS, 'libcrypto.so.0.9.8'),
path.join(sys._MEIPASS, 'libssl.so.0.9.8'),
])
if 'darwin' in sys.platform:
libdir.extend(['libcrypto.dylib', '/usr/local/opt/openssl/lib/libcrypto.dylib'])
elif 'win32' in sys.platform or 'win64' in sys.platform:
libdir.append('libeay32.dll')
else:
libdir.append('libcrypto.so')
libdir.append('libssl.so')
libdir.append('libcrypto.so.1.0.0')
libdir.append('libssl.so.1.0.0')
for library in libdir:
try:
OpenSSL = _OpenSSL(library)
return
except:
pass
raise Exception("Failed to load OpenSSL library, searched for: " + " ".join(libdir))
loadOpenSSL()

View File

@ -1,23 +0,0 @@
from setuptools import setup, find_packages
setup(
name="pyelliptic",
version='2.0.1',
url='https://github.com/radfish/pyelliptic',
license='GPL',
description="Python OpenSSL wrapper for ECC (ECDSA, ECIES), AES, HMAC, Blowfish, ...",
author='Yann GUIBET',
author_email='yannguibet@gmail.com',
maintainer="redfish",
maintainer_email='redfish@galactica.pw',
packages=find_packages(),
classifiers=[
'Operating System :: Unix',
'Operating System :: Microsoft :: Windows',
'Environment :: MacOS X',
'Programming Language :: Python',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.7',
'Topic :: Security :: Cryptography',
],
)

27
src/lib/sslcrypto/LICENSE Normal file
View File

@ -0,0 +1,27 @@
MIT License
Copyright (c) 2019 Ivan Machugovskiy
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Additionally, the following licenses must be preserved:
- ripemd implementation is licensed under BSD-3 by Markus Friedl, see `_ripemd.py`;
- jacobian curve implementation is dual-licensed under MIT or public domain license, see `_jacobian.py`.

View File

@ -0,0 +1,6 @@
__all__ = ["aes", "ecc", "rsa"]
try:
from .openssl import aes, ecc, rsa
except OSError:
from .fallback import aes, ecc, rsa

53
src/lib/sslcrypto/_aes.py Normal file
View File

@ -0,0 +1,53 @@
# pylint: disable=import-outside-toplevel
class AES:
def __init__(self, backend, fallback=None):
self._backend = backend
self._fallback = fallback
def get_algo_key_length(self, algo):
if algo.count("-") != 2:
raise ValueError("Invalid algorithm name")
try:
return int(algo.split("-")[1]) // 8
except ValueError:
raise ValueError("Invalid algorithm name") from None
def new_key(self, algo="aes-256-cbc"):
if not self._backend.is_algo_supported(algo):
if self._fallback is None:
raise ValueError("This algorithm is not supported")
return self._fallback.new_key(algo)
return self._backend.random(self.get_algo_key_length(algo))
def encrypt(self, data, key, algo="aes-256-cbc"):
if not self._backend.is_algo_supported(algo):
if self._fallback is None:
raise ValueError("This algorithm is not supported")
return self._fallback.encrypt(data, key, algo)
key_length = self.get_algo_key_length(algo)
if len(key) != key_length:
raise ValueError("Expected key to be {} bytes, got {} bytes".format(key_length, len(key)))
return self._backend.encrypt(data, key, algo)
def decrypt(self, ciphertext, iv, key, algo="aes-256-cbc"):
if not self._backend.is_algo_supported(algo):
if self._fallback is None:
raise ValueError("This algorithm is not supported")
return self._fallback.decrypt(ciphertext, iv, key, algo)
key_length = self.get_algo_key_length(algo)
if len(key) != key_length:
raise ValueError("Expected key to be {} bytes, got {} bytes".format(key_length, len(key)))
return self._backend.decrypt(ciphertext, iv, key, algo)
def get_backend(self):
return self._backend.get_backend()

334
src/lib/sslcrypto/_ecc.py Normal file
View File

@ -0,0 +1,334 @@
import hashlib
import struct
import hmac
import base58
try:
hashlib.new("ripemd160")
except ValueError:
# No native implementation
from . import _ripemd
def ripemd160(*args):
return _ripemd.new(*args)
else:
# Use OpenSSL
def ripemd160(*args):
return hashlib.new("ripemd160", *args)
class ECC:
CURVES = {
"secp112r1": 704,
"secp112r2": 705,
"secp128r1": 706,
"secp128r2": 707,
"secp160k1": 708,
"secp160r1": 709,
"secp160r2": 710,
"secp192k1": 711,
"prime192v1": 409,
"secp224k1": 712,
"secp224r1": 713,
"secp256k1": 714,
"prime256v1": 415,
"secp384r1": 715,
"secp521r1": 716
}
def __init__(self, backend, aes):
self._backend = backend
self._aes = aes
def get_curve(self, name):
if name not in self.CURVES:
raise ValueError("Unknown curve {}".format(name))
nid = self.CURVES[name]
return EllipticCurve(self._backend(nid), self._aes, nid)
def get_backend(self):
return self._backend.get_backend()
class EllipticCurve:
def __init__(self, backend, aes, nid):
self._backend = backend
self._aes = aes
self.nid = nid
def _encode_public_key(self, x, y, is_compressed=True, raw=True):
if raw:
if is_compressed:
return bytes([0x02 + (y[-1] % 2)]) + x
else:
return bytes([0x04]) + x + y
else:
return struct.pack("!HH", self.nid, len(x)) + x + struct.pack("!H", len(y)) + y
def _decode_public_key(self, public_key, partial=False):
if not public_key:
raise ValueError("No public key")
if public_key[0] == 0x04:
# Uncompressed
expected_length = 1 + 2 * self._backend.public_key_length
if partial:
if len(public_key) < expected_length:
raise ValueError("Invalid uncompressed public key length")
else:
if len(public_key) != expected_length:
raise ValueError("Invalid uncompressed public key length")
x = public_key[1:1 + self._backend.public_key_length]
y = public_key[1 + self._backend.public_key_length:expected_length]
if partial:
return (x, y), expected_length
else:
return x, y
elif public_key[0] in (0x02, 0x03):
# Compressed
expected_length = 1 + self._backend.public_key_length
if partial:
if len(public_key) < expected_length:
raise ValueError("Invalid compressed public key length")
else:
if len(public_key) != expected_length:
raise ValueError("Invalid compressed public key length")
x, y = self._backend.decompress_point(public_key[:expected_length])
# Sanity check
if x != public_key[1:expected_length]:
raise ValueError("Incorrect compressed public key")
if partial:
return (x, y), expected_length
else:
return x, y
else:
raise ValueError("Invalid public key prefix")
def _decode_public_key_openssl(self, public_key, partial=False):
if not public_key:
raise ValueError("No public key")
i = 0
nid, = struct.unpack("!H", public_key[i:i + 2])
i += 2
if nid != self.nid:
raise ValueError("Wrong curve")
xlen, = struct.unpack("!H", public_key[i:i + 2])
i += 2
if len(public_key) - i < xlen:
raise ValueError("Too short public key")
x = public_key[i:i + xlen]
i += xlen
ylen, = struct.unpack("!H", public_key[i:i + 2])
i += 2
if len(public_key) - i < ylen:
raise ValueError("Too short public key")
y = public_key[i:i + ylen]
i += ylen
if partial:
return (x, y), i
else:
if i < len(public_key):
raise ValueError("Too long public key")
return x, y
def new_private_key(self):
return self._backend.new_private_key()
def private_to_public(self, private_key, is_compressed=True):
x, y = self._backend.private_to_public(private_key)
return self._encode_public_key(x, y, is_compressed=is_compressed)
def private_to_wif(self, private_key):
return base58.b58encode_check(b"\x80" + private_key)
def wif_to_private(self, wif):
dec = base58.b58decode_check(wif)
if dec[0] != 0x80:
raise ValueError("Invalid network (expected mainnet)")
return dec[1:]
def public_to_address(self, public_key):
h = hashlib.sha256(public_key).digest()
hash160 = ripemd160(h).digest()
return base58.b58encode_check(b"\x00" + hash160)
def private_to_address(self, private_key, is_compressed=True):
# Kinda useless but left for quick migration from pybitcointools
return self.public_to_address(self.private_to_public(private_key, is_compressed=is_compressed))
def derive(self, private_key, public_key):
if not isinstance(public_key, tuple):
public_key = self._decode_public_key(public_key)
return self._backend.ecdh(private_key, public_key)
def _digest(self, data, hash):
if hash is None:
return data
elif callable(hash):
return hash(data)
elif hash == "sha1":
return hashlib.sha1(data).digest()
elif hash == "sha256":
return hashlib.sha256(data).digest()
elif hash == "sha512":
return hashlib.sha512(data).digest()
else:
raise ValueError("Unknown hash/derivation method")
# High-level functions
def encrypt(self, data, public_key, algo="aes-256-cbc", derivation="sha256", mac="hmac-sha256", return_aes_key=False):
# Generate ephemeral private key
private_key = self.new_private_key()
# Derive key
ecdh = self.derive(private_key, public_key)
key = self._digest(ecdh, derivation)
k_enc_len = self._aes.get_algo_key_length(algo)
if len(key) < k_enc_len:
raise ValueError("Too short digest")
k_enc, k_mac = key[:k_enc_len], key[k_enc_len:]
# Encrypt
ciphertext, iv = self._aes.encrypt(data, k_enc, algo=algo)
ephem_public_key = self.private_to_public(private_key)
ephem_public_key = self._decode_public_key(ephem_public_key)
ephem_public_key = self._encode_public_key(*ephem_public_key, raw=False)
ciphertext = iv + ephem_public_key + ciphertext
# Add MAC tag
if callable(mac):
tag = mac(k_mac, ciphertext)
elif mac == "hmac-sha256":
h = hmac.new(k_mac, digestmod="sha256")
h.update(ciphertext)
tag = h.digest()
elif mac == "hmac-sha512":
h = hmac.new(k_mac, digestmod="sha512")
h.update(ciphertext)
tag = h.digest()
elif mac is None:
tag = b""
else:
raise ValueError("Unsupported MAC")
if return_aes_key:
return ciphertext + tag, k_enc
else:
return ciphertext + tag
def decrypt(self, ciphertext, private_key, algo="aes-256-cbc", derivation="sha256", mac="hmac-sha256"):
# Get MAC tag
if callable(mac):
tag_length = mac.digest_size
elif mac == "hmac-sha256":
tag_length = hmac.new(b"", digestmod="sha256").digest_size
elif mac == "hmac-sha512":
tag_length = hmac.new(b"", digestmod="sha512").digest_size
elif mac is None:
tag_length = 0
else:
raise ValueError("Unsupported MAC")
if len(ciphertext) < tag_length:
raise ValueError("Ciphertext is too small to contain MAC tag")
if tag_length == 0:
tag = b""
else:
ciphertext, tag = ciphertext[:-tag_length], ciphertext[-tag_length:]
orig_ciphertext = ciphertext
if len(ciphertext) < 16:
raise ValueError("Ciphertext is too small to contain IV")
iv, ciphertext = ciphertext[:16], ciphertext[16:]
public_key, pos = self._decode_public_key_openssl(ciphertext, partial=True)
ciphertext = ciphertext[pos:]
# Derive key
ecdh = self.derive(private_key, public_key)
key = self._digest(ecdh, derivation)
k_enc_len = self._aes.get_algo_key_length(algo)
if len(key) < k_enc_len:
raise ValueError("Too short digest")
k_enc, k_mac = key[:k_enc_len], key[k_enc_len:]
# Verify MAC tag
if callable(mac):
expected_tag = mac(k_mac, orig_ciphertext)
elif mac == "hmac-sha256":
h = hmac.new(k_mac, digestmod="sha256")
h.update(orig_ciphertext)
expected_tag = h.digest()
elif mac == "hmac-sha512":
h = hmac.new(k_mac, digestmod="sha512")
h.update(orig_ciphertext)
expected_tag = h.digest()
elif mac is None:
expected_tag = b""
if not hmac.compare_digest(tag, expected_tag):
raise ValueError("Invalid MAC tag")
return self._aes.decrypt(ciphertext, iv, k_enc, algo=algo)
def sign(self, data, private_key, hash="sha256", recoverable=False, is_compressed=True, entropy=None):
data = self._digest(data, hash)
if not entropy:
v = b"\x01" * len(data)
k = b"\x00" * len(data)
k = hmac.new(k, v + b"\x00" + private_key + data, "sha256").digest()
v = hmac.new(k, v, "sha256").digest()
k = hmac.new(k, v + b"\x01" + private_key + data, "sha256").digest()
v = hmac.new(k, v, "sha256").digest()
entropy = hmac.new(k, v, "sha256").digest()
return self._backend.sign(data, private_key, recoverable, is_compressed, entropy=entropy)
def recover(self, signature, data, hash="sha256"):
# Sanity check: is this signature recoverable?
if len(signature) != 1 + 2 * self._backend.public_key_length:
raise ValueError("Cannot recover an unrecoverable signature")
x, y = self._backend.recover(signature, self._digest(data, hash))
is_compressed = signature[0] >= 31
return self._encode_public_key(x, y, is_compressed=is_compressed)
def verify(self, signature, data, public_key, hash="sha256"):
if len(signature) == 1 + 2 * self._backend.public_key_length:
# Recoverable signature
signature = signature[1:]
if len(signature) != 2 * self._backend.public_key_length:
raise ValueError("Invalid signature format")
if not isinstance(public_key, tuple):
public_key = self._decode_public_key(public_key)
return self._backend.verify(signature, self._digest(data, hash), public_key)
def derive_child(self, seed, child):
# Based on BIP32
if not 0 <= child < 2 ** 31:
raise ValueError("Invalid child index")
return self._backend.derive_child(seed, child)

View File

@ -0,0 +1,375 @@
# Copyright (c) 2001 Markus Friedl. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# pylint: skip-file
import sys
digest_size = 20
digestsize = 20
class RIPEMD160:
"""
Return a new RIPEMD160 object. An optional string argument
may be provided; if present, this string will be automatically
hashed.
"""
def __init__(self, arg=None):
self.ctx = RMDContext()
if arg:
self.update(arg)
self.dig = None
def update(self, arg):
RMD160Update(self.ctx, arg, len(arg))
self.dig = None
def digest(self):
if self.dig:
return self.dig
ctx = self.ctx.copy()
self.dig = RMD160Final(self.ctx)
self.ctx = ctx
return self.dig
def hexdigest(self):
dig = self.digest()
hex_digest = ""
for d in dig:
hex_digest += "%02x" % d
return hex_digest
def copy(self):
import copy
return copy.deepcopy(self)
def new(arg=None):
"""
Return a new RIPEMD160 object. An optional string argument
may be provided; if present, this string will be automatically
hashed.
"""
return RIPEMD160(arg)
#
# Private.
#
class RMDContext:
def __init__(self):
self.state = [0x67452301, 0xEFCDAB89, 0x98BADCFE,
0x10325476, 0xC3D2E1F0] # uint32
self.count = 0 # uint64
self.buffer = [0] * 64 # uchar
def copy(self):
ctx = RMDContext()
ctx.state = self.state[:]
ctx.count = self.count
ctx.buffer = self.buffer[:]
return ctx
K0 = 0x00000000
K1 = 0x5A827999
K2 = 0x6ED9EBA1
K3 = 0x8F1BBCDC
K4 = 0xA953FD4E
KK0 = 0x50A28BE6
KK1 = 0x5C4DD124
KK2 = 0x6D703EF3
KK3 = 0x7A6D76E9
KK4 = 0x00000000
def ROL(n, x):
return ((x << n) & 0xffffffff) | (x >> (32 - n))
def F0(x, y, z):
return x ^ y ^ z
def F1(x, y, z):
return (x & y) | (((~x) % 0x100000000) & z)
def F2(x, y, z):
return (x | ((~y) % 0x100000000)) ^ z
def F3(x, y, z):
return (x & z) | (((~z) % 0x100000000) & y)
def F4(x, y, z):
return x ^ (y | ((~z) % 0x100000000))
def R(a, b, c, d, e, Fj, Kj, sj, rj, X):
a = ROL(sj, (a + Fj(b, c, d) + X[rj] + Kj) % 0x100000000) + e
c = ROL(10, c)
return a % 0x100000000, c
PADDING = [0x80] + [0] * 63
import sys
import struct
def RMD160Transform(state, block): # uint32 state[5], uchar block[64]
x = [0] * 16
if sys.byteorder == "little":
x = struct.unpack("<16L", bytes(block[0:64]))
else:
raise ValueError("Big-endian platforms are not supported")
a = state[0]
b = state[1]
c = state[2]
d = state[3]
e = state[4]
# Round 1
a, c = R(a, b, c, d, e, F0, K0, 11, 0, x)
e, b = R(e, a, b, c, d, F0, K0, 14, 1, x)
d, a = R(d, e, a, b, c, F0, K0, 15, 2, x)
c, e = R(c, d, e, a, b, F0, K0, 12, 3, x)
b, d = R(b, c, d, e, a, F0, K0, 5, 4, x)
a, c = R(a, b, c, d, e, F0, K0, 8, 5, x)
e, b = R(e, a, b, c, d, F0, K0, 7, 6, x)
d, a = R(d, e, a, b, c, F0, K0, 9, 7, x)
c, e = R(c, d, e, a, b, F0, K0, 11, 8, x)
b, d = R(b, c, d, e, a, F0, K0, 13, 9, x)
a, c = R(a, b, c, d, e, F0, K0, 14, 10, x)
e, b = R(e, a, b, c, d, F0, K0, 15, 11, x)
d, a = R(d, e, a, b, c, F0, K0, 6, 12, x)
c, e = R(c, d, e, a, b, F0, K0, 7, 13, x)
b, d = R(b, c, d, e, a, F0, K0, 9, 14, x)
a, c = R(a, b, c, d, e, F0, K0, 8, 15, x) # #15
# Round 2
e, b = R(e, a, b, c, d, F1, K1, 7, 7, x)
d, a = R(d, e, a, b, c, F1, K1, 6, 4, x)
c, e = R(c, d, e, a, b, F1, K1, 8, 13, x)
b, d = R(b, c, d, e, a, F1, K1, 13, 1, x)
a, c = R(a, b, c, d, e, F1, K1, 11, 10, x)
e, b = R(e, a, b, c, d, F1, K1, 9, 6, x)
d, a = R(d, e, a, b, c, F1, K1, 7, 15, x)
c, e = R(c, d, e, a, b, F1, K1, 15, 3, x)
b, d = R(b, c, d, e, a, F1, K1, 7, 12, x)
a, c = R(a, b, c, d, e, F1, K1, 12, 0, x)
e, b = R(e, a, b, c, d, F1, K1, 15, 9, x)
d, a = R(d, e, a, b, c, F1, K1, 9, 5, x)
c, e = R(c, d, e, a, b, F1, K1, 11, 2, x)
b, d = R(b, c, d, e, a, F1, K1, 7, 14, x)
a, c = R(a, b, c, d, e, F1, K1, 13, 11, x)
e, b = R(e, a, b, c, d, F1, K1, 12, 8, x) # #31
# Round 3
d, a = R(d, e, a, b, c, F2, K2, 11, 3, x)
c, e = R(c, d, e, a, b, F2, K2, 13, 10, x)
b, d = R(b, c, d, e, a, F2, K2, 6, 14, x)
a, c = R(a, b, c, d, e, F2, K2, 7, 4, x)
e, b = R(e, a, b, c, d, F2, K2, 14, 9, x)
d, a = R(d, e, a, b, c, F2, K2, 9, 15, x)
c, e = R(c, d, e, a, b, F2, K2, 13, 8, x)
b, d = R(b, c, d, e, a, F2, K2, 15, 1, x)
a, c = R(a, b, c, d, e, F2, K2, 14, 2, x)
e, b = R(e, a, b, c, d, F2, K2, 8, 7, x)
d, a = R(d, e, a, b, c, F2, K2, 13, 0, x)
c, e = R(c, d, e, a, b, F2, K2, 6, 6, x)
b, d = R(b, c, d, e, a, F2, K2, 5, 13, x)
a, c = R(a, b, c, d, e, F2, K2, 12, 11, x)
e, b = R(e, a, b, c, d, F2, K2, 7, 5, x)
d, a = R(d, e, a, b, c, F2, K2, 5, 12, x) # #47
# Round 4
c, e = R(c, d, e, a, b, F3, K3, 11, 1, x)
b, d = R(b, c, d, e, a, F3, K3, 12, 9, x)
a, c = R(a, b, c, d, e, F3, K3, 14, 11, x)
e, b = R(e, a, b, c, d, F3, K3, 15, 10, x)
d, a = R(d, e, a, b, c, F3, K3, 14, 0, x)
c, e = R(c, d, e, a, b, F3, K3, 15, 8, x)
b, d = R(b, c, d, e, a, F3, K3, 9, 12, x)
a, c = R(a, b, c, d, e, F3, K3, 8, 4, x)
e, b = R(e, a, b, c, d, F3, K3, 9, 13, x)
d, a = R(d, e, a, b, c, F3, K3, 14, 3, x)
c, e = R(c, d, e, a, b, F3, K3, 5, 7, x)
b, d = R(b, c, d, e, a, F3, K3, 6, 15, x)
a, c = R(a, b, c, d, e, F3, K3, 8, 14, x)
e, b = R(e, a, b, c, d, F3, K3, 6, 5, x)
d, a = R(d, e, a, b, c, F3, K3, 5, 6, x)
c, e = R(c, d, e, a, b, F3, K3, 12, 2, x) # #63
# Round 5
b, d = R(b, c, d, e, a, F4, K4, 9, 4, x)
a, c = R(a, b, c, d, e, F4, K4, 15, 0, x)
e, b = R(e, a, b, c, d, F4, K4, 5, 5, x)
d, a = R(d, e, a, b, c, F4, K4, 11, 9, x)
c, e = R(c, d, e, a, b, F4, K4, 6, 7, x)
b, d = R(b, c, d, e, a, F4, K4, 8, 12, x)
a, c = R(a, b, c, d, e, F4, K4, 13, 2, x)
e, b = R(e, a, b, c, d, F4, K4, 12, 10, x)
d, a = R(d, e, a, b, c, F4, K4, 5, 14, x)
c, e = R(c, d, e, a, b, F4, K4, 12, 1, x)
b, d = R(b, c, d, e, a, F4, K4, 13, 3, x)
a, c = R(a, b, c, d, e, F4, K4, 14, 8, x)
e, b = R(e, a, b, c, d, F4, K4, 11, 11, x)
d, a = R(d, e, a, b, c, F4, K4, 8, 6, x)
c, e = R(c, d, e, a, b, F4, K4, 5, 15, x)
b, d = R(b, c, d, e, a, F4, K4, 6, 13, x) # #79
aa = a
bb = b
cc = c
dd = d
ee = e
a = state[0]
b = state[1]
c = state[2]
d = state[3]
e = state[4]
# Parallel round 1
a, c = R(a, b, c, d, e, F4, KK0, 8, 5, x)
e, b = R(e, a, b, c, d, F4, KK0, 9, 14, x)
d, a = R(d, e, a, b, c, F4, KK0, 9, 7, x)
c, e = R(c, d, e, a, b, F4, KK0, 11, 0, x)
b, d = R(b, c, d, e, a, F4, KK0, 13, 9, x)
a, c = R(a, b, c, d, e, F4, KK0, 15, 2, x)
e, b = R(e, a, b, c, d, F4, KK0, 15, 11, x)
d, a = R(d, e, a, b, c, F4, KK0, 5, 4, x)
c, e = R(c, d, e, a, b, F4, KK0, 7, 13, x)
b, d = R(b, c, d, e, a, F4, KK0, 7, 6, x)
a, c = R(a, b, c, d, e, F4, KK0, 8, 15, x)
e, b = R(e, a, b, c, d, F4, KK0, 11, 8, x)
d, a = R(d, e, a, b, c, F4, KK0, 14, 1, x)
c, e = R(c, d, e, a, b, F4, KK0, 14, 10, x)
b, d = R(b, c, d, e, a, F4, KK0, 12, 3, x)
a, c = R(a, b, c, d, e, F4, KK0, 6, 12, x) # #15
# Parallel round 2
e, b = R(e, a, b, c, d, F3, KK1, 9, 6, x)
d, a = R(d, e, a, b, c, F3, KK1, 13, 11, x)
c, e = R(c, d, e, a, b, F3, KK1, 15, 3, x)
b, d = R(b, c, d, e, a, F3, KK1, 7, 7, x)
a, c = R(a, b, c, d, e, F3, KK1, 12, 0, x)
e, b = R(e, a, b, c, d, F3, KK1, 8, 13, x)
d, a = R(d, e, a, b, c, F3, KK1, 9, 5, x)
c, e = R(c, d, e, a, b, F3, KK1, 11, 10, x)
b, d = R(b, c, d, e, a, F3, KK1, 7, 14, x)
a, c = R(a, b, c, d, e, F3, KK1, 7, 15, x)
e, b = R(e, a, b, c, d, F3, KK1, 12, 8, x)
d, a = R(d, e, a, b, c, F3, KK1, 7, 12, x)
c, e = R(c, d, e, a, b, F3, KK1, 6, 4, x)
b, d = R(b, c, d, e, a, F3, KK1, 15, 9, x)
a, c = R(a, b, c, d, e, F3, KK1, 13, 1, x)
e, b = R(e, a, b, c, d, F3, KK1, 11, 2, x) # #31
# Parallel round 3
d, a = R(d, e, a, b, c, F2, KK2, 9, 15, x)
c, e = R(c, d, e, a, b, F2, KK2, 7, 5, x)
b, d = R(b, c, d, e, a, F2, KK2, 15, 1, x)
a, c = R(a, b, c, d, e, F2, KK2, 11, 3, x)
e, b = R(e, a, b, c, d, F2, KK2, 8, 7, x)
d, a = R(d, e, a, b, c, F2, KK2, 6, 14, x)
c, e = R(c, d, e, a, b, F2, KK2, 6, 6, x)
b, d = R(b, c, d, e, a, F2, KK2, 14, 9, x)
a, c = R(a, b, c, d, e, F2, KK2, 12, 11, x)
e, b = R(e, a, b, c, d, F2, KK2, 13, 8, x)
d, a = R(d, e, a, b, c, F2, KK2, 5, 12, x)
c, e = R(c, d, e, a, b, F2, KK2, 14, 2, x)
b, d = R(b, c, d, e, a, F2, KK2, 13, 10, x)
a, c = R(a, b, c, d, e, F2, KK2, 13, 0, x)
e, b = R(e, a, b, c, d, F2, KK2, 7, 4, x)
d, a = R(d, e, a, b, c, F2, KK2, 5, 13, x) # #47
# Parallel round 4
c, e = R(c, d, e, a, b, F1, KK3, 15, 8, x)
b, d = R(b, c, d, e, a, F1, KK3, 5, 6, x)
a, c = R(a, b, c, d, e, F1, KK3, 8, 4, x)
e, b = R(e, a, b, c, d, F1, KK3, 11, 1, x)
d, a = R(d, e, a, b, c, F1, KK3, 14, 3, x)
c, e = R(c, d, e, a, b, F1, KK3, 14, 11, x)
b, d = R(b, c, d, e, a, F1, KK3, 6, 15, x)
a, c = R(a, b, c, d, e, F1, KK3, 14, 0, x)
e, b = R(e, a, b, c, d, F1, KK3, 6, 5, x)
d, a = R(d, e, a, b, c, F1, KK3, 9, 12, x)
c, e = R(c, d, e, a, b, F1, KK3, 12, 2, x)
b, d = R(b, c, d, e, a, F1, KK3, 9, 13, x)
a, c = R(a, b, c, d, e, F1, KK3, 12, 9, x)
e, b = R(e, a, b, c, d, F1, KK3, 5, 7, x)
d, a = R(d, e, a, b, c, F1, KK3, 15, 10, x)
c, e = R(c, d, e, a, b, F1, KK3, 8, 14, x) # #63
# Parallel round 5
b, d = R(b, c, d, e, a, F0, KK4, 8, 12, x)
a, c = R(a, b, c, d, e, F0, KK4, 5, 15, x)
e, b = R(e, a, b, c, d, F0, KK4, 12, 10, x)
d, a = R(d, e, a, b, c, F0, KK4, 9, 4, x)
c, e = R(c, d, e, a, b, F0, KK4, 12, 1, x)
b, d = R(b, c, d, e, a, F0, KK4, 5, 5, x)
a, c = R(a, b, c, d, e, F0, KK4, 14, 8, x)
e, b = R(e, a, b, c, d, F0, KK4, 6, 7, x)
d, a = R(d, e, a, b, c, F0, KK4, 8, 6, x)
c, e = R(c, d, e, a, b, F0, KK4, 13, 2, x)
b, d = R(b, c, d, e, a, F0, KK4, 6, 13, x)
a, c = R(a, b, c, d, e, F0, KK4, 5, 14, x)
e, b = R(e, a, b, c, d, F0, KK4, 15, 0, x)
d, a = R(d, e, a, b, c, F0, KK4, 13, 3, x)
c, e = R(c, d, e, a, b, F0, KK4, 11, 9, x)
b, d = R(b, c, d, e, a, F0, KK4, 11, 11, x) # #79
t = (state[1] + cc + d) % 0x100000000
state[1] = (state[2] + dd + e) % 0x100000000
state[2] = (state[3] + ee + a) % 0x100000000
state[3] = (state[4] + aa + b) % 0x100000000
state[4] = (state[0] + bb + c) % 0x100000000
state[0] = t % 0x100000000
def RMD160Update(ctx, inp, inplen):
if type(inp) == str:
inp = [ord(i)&0xff for i in inp]
have = int((ctx.count // 8) % 64)
inplen = int(inplen)
need = 64 - have
ctx.count += 8 * inplen
off = 0
if inplen >= need:
if have:
for i in range(need):
ctx.buffer[have + i] = inp[i]
RMD160Transform(ctx.state, ctx.buffer)
off = need
have = 0
while off + 64 <= inplen:
RMD160Transform(ctx.state, inp[off:]) #<---
off += 64
if off < inplen:
# memcpy(ctx->buffer + have, input+off, len-off)
for i in range(inplen - off):
ctx.buffer[have + i] = inp[off + i]
def RMD160Final(ctx):
size = struct.pack("<Q", ctx.count)
padlen = 64 - ((ctx.count // 8) % 64)
if padlen < 1 + 8:
padlen += 64
RMD160Update(ctx, PADDING, padlen - 8)
RMD160Update(ctx, size, 8)
return struct.pack("<5L", *ctx.state)
assert "37f332f68db77bd9d7edd4969571ad671cf9dd3b" == new("The quick brown fox jumps over the lazy dog").hexdigest()
assert "132072df690933835eb8b6ad0b77e7b6f14acad7" == new("The quick brown fox jumps over the lazy cog").hexdigest()
assert "9c1185a5c5e9fc54612808977ee8f548b2258d31" == new("").hexdigest()

View File

@ -0,0 +1,3 @@
from .aes import aes
from .ecc import ecc
from .rsa import rsa

View File

@ -0,0 +1,159 @@
"""
This code is public domain. Everyone has the right to do whatever they want
with it for any purpose.
In case your jurisdiction does not consider the above disclaimer valid or
enforceable, here's an MIT license for you:
The MIT License (MIT)
Copyright (c) 2013 Vitalik Buterin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""
from ._util import inverse
class JacobianCurve:
def __init__(self, p, n, a, b, g):
self.p = p
self.n = n
self.a = a
self.b = b
self.g = g
self.n_length = len(bin(self.n).replace("0b", ""))
def isinf(self, p):
return p[0] == 0 and p[1] == 0
def to_jacobian(self, p):
return p[0], p[1], 1
def jacobian_double(self, p):
if not p[1]:
return 0, 0, 0
ysq = (p[1] ** 2) % self.p
s = (4 * p[0] * ysq) % self.p
m = (3 * p[0] ** 2 + self.a * p[2] ** 4) % self.p
nx = (m ** 2 - 2 * s) % self.p
ny = (m * (s - nx) - 8 * ysq ** 2) % self.p
nz = (2 * p[1] * p[2]) % self.p
return nx, ny, nz
def jacobian_add(self, p, q):
if not p[1]:
return q
if not q[1]:
return p
u1 = (p[0] * q[2] ** 2) % self.p
u2 = (q[0] * p[2] ** 2) % self.p
s1 = (p[1] * q[2] ** 3) % self.p
s2 = (q[1] * p[2] ** 3) % self.p
if u1 == u2:
if s1 != s2:
return (0, 0, 1)
return self.jacobian_double(p)
h = u2 - u1
r = s2 - s1
h2 = (h * h) % self.p
h3 = (h * h2) % self.p
u1h2 = (u1 * h2) % self.p
nx = (r ** 2 - h3 - 2 * u1h2) % self.p
ny = (r * (u1h2 - nx) - s1 * h3) % self.p
nz = (h * p[2] * q[2]) % self.p
return (nx, ny, nz)
def from_jacobian(self, p):
z = inverse(p[2], self.p)
return (p[0] * z ** 2) % self.p, (p[1] * z ** 3) % self.p
def jacobian_multiply(self, a, n, secret=False):
if a[1] == 0 or n == 0:
return 0, 0, 1
if n == 1:
return a
if n < 0 or n >= self.n:
return self.jacobian_multiply(a, n % self.n, secret)
half = self.jacobian_multiply(a, n // 2, secret)
half_sq = self.jacobian_double(half)
if secret:
# A constant-time implementation
half_sq_a = self.jacobian_add(half_sq, a)
if n % 2 == 0:
result = half_sq
if n % 2 == 1:
result = half_sq_a
return result
else:
if n % 2 == 0:
return half_sq
return self.jacobian_add(half_sq, a)
def jacobian_shamir(self, a, n, b, m):
ab = self.jacobian_add(a, b)
if n < 0 or n >= self.n:
n %= self.n
if m < 0 or m >= self.n:
m %= self.n
res = 0, 0, 1 # point on infinity
for i in range(self.n_length - 1, -1, -1):
res = self.jacobian_double(res)
has_n = n & (1 << i)
has_m = m & (1 << i)
if has_n:
if has_m == 0:
res = self.jacobian_add(res, a)
if has_m != 0:
res = self.jacobian_add(res, ab)
else:
if has_m == 0:
res = self.jacobian_add(res, (0, 0, 1)) # Try not to leak
if has_m != 0:
res = self.jacobian_add(res, b)
return res
def fast_multiply(self, a, n, secret=False):
return self.from_jacobian(self.jacobian_multiply(self.to_jacobian(a), n, secret))
def fast_add(self, a, b):
return self.from_jacobian(self.jacobian_add(self.to_jacobian(a), self.to_jacobian(b)))
def fast_shamir(self, a, n, b, m):
return self.from_jacobian(self.jacobian_shamir(self.to_jacobian(a), n, self.to_jacobian(b), m))
def is_on_curve(self, a):
x, y = a
# Simple arithmetic check
if (pow(x, 3, self.p) + self.a * x + self.b) % self.p != y * y % self.p:
return False
# nP = point-at-infinity
return self.isinf(self.jacobian_multiply(self.to_jacobian(a), self.n))

View File

@ -0,0 +1,79 @@
def int_to_bytes(raw, length):
data = []
for _ in range(length):
data.append(raw % 256)
raw //= 256
return bytes(data[::-1])
def bytes_to_int(data):
raw = 0
for byte in data:
raw = raw * 256 + byte
return raw
def legendre(a, p):
res = pow(a, (p - 1) // 2, p)
if res == p - 1:
return -1
else:
return res
def inverse(a, n):
if a == 0:
return 0
lm, hm = 1, 0
low, high = a % n, n
while low > 1:
r = high // low
nm, new = hm - lm * r, high - low * r
lm, low, hm, high = nm, new, lm, low
return lm % n
def square_root_mod_prime(n, p):
if n == 0:
return 0
if p == 2:
return n # We should never get here but it might be useful
if legendre(n, p) != 1:
raise ValueError("No square root")
# Optimizations
if p % 4 == 3:
return pow(n, (p + 1) // 4, p)
# 1. By factoring out powers of 2, find Q and S such that p - 1 =
# Q * 2 ** S with Q odd
q = p - 1
s = 0
while q % 2 == 0:
q //= 2
s += 1
# 2. Search for z in Z/pZ which is a quadratic non-residue
z = 1
while legendre(z, p) != -1:
z += 1
m, c, t, r = s, pow(z, q, p), pow(n, q, p), pow(n, (q + 1) // 2, p)
while True:
if t == 0:
return 0
elif t == 1:
return r
# Use repeated squaring to find the least i, 0 < i < M, such
# that t ** (2 ** i) = 1
t_sq = t
i = 0
for i in range(1, m):
t_sq = t_sq * t_sq % p
if t_sq == 1:
break
else:
raise ValueError("Should never get here")
# Let b = c ** (2 ** (m - i - 1))
b = pow(c, 2 ** (m - i - 1), p)
m = i
c = b * b % p
t = t * b * b % p
r = r * b % p
return r

View File

@ -0,0 +1,101 @@
import os
import pyaes
from .._aes import AES
__all__ = ["aes"]
class AESBackend:
def _get_algo_cipher_type(self, algo):
if not algo.startswith("aes-") or algo.count("-") != 2:
raise ValueError("Unknown cipher algorithm {}".format(algo))
key_length, cipher_type = algo[4:].split("-")
if key_length not in ("128", "192", "256"):
raise ValueError("Unknown cipher algorithm {}".format(algo))
if cipher_type not in ("cbc", "ctr", "cfb", "ofb"):
raise ValueError("Unknown cipher algorithm {}".format(algo))
return cipher_type
def is_algo_supported(self, algo):
try:
self._get_algo_cipher_type(algo)
return True
except ValueError:
return False
def random(self, length):
return os.urandom(length)
def encrypt(self, data, key, algo="aes-256-cbc"):
cipher_type = self._get_algo_cipher_type(algo)
# Generate random IV
iv = os.urandom(16)
if cipher_type == "cbc":
cipher = pyaes.AESModeOfOperationCBC(key, iv=iv)
elif cipher_type == "ctr":
# The IV is actually a counter, not an IV but it does almost the
# same. Notice: pyaes always uses 1 as initial counter! Make sure
# not to call pyaes directly.
# We kinda do two conversions here: from byte array to int here, and
# from int to byte array in pyaes internals. It's possible to fix that
# but I didn't notice any performance changes so I'm keeping clean code.
iv_int = 0
for byte in iv:
iv_int = (iv_int * 256) + byte
counter = pyaes.Counter(iv_int)
cipher = pyaes.AESModeOfOperationCTR(key, counter=counter)
elif cipher_type == "cfb":
# Change segment size from default 8 bytes to 16 bytes for OpenSSL
# compatibility
cipher = pyaes.AESModeOfOperationCFB(key, iv, segment_size=16)
elif cipher_type == "ofb":
cipher = pyaes.AESModeOfOperationOFB(key, iv)
encrypter = pyaes.Encrypter(cipher)
ciphertext = encrypter.feed(data)
ciphertext += encrypter.feed()
return ciphertext, iv
def decrypt(self, ciphertext, iv, key, algo="aes-256-cbc"):
cipher_type = self._get_algo_cipher_type(algo)
if cipher_type == "cbc":
cipher = pyaes.AESModeOfOperationCBC(key, iv=iv)
elif cipher_type == "ctr":
# The IV is actually a counter, not an IV but it does almost the
# same. Notice: pyaes always uses 1 as initial counter! Make sure
# not to call pyaes directly.
# We kinda do two conversions here: from byte array to int here, and
# from int to byte array in pyaes internals. It's possible to fix that
# but I didn't notice any performance changes so I'm keeping clean code.
iv_int = 0
for byte in iv:
iv_int = (iv_int * 256) + byte
counter = pyaes.Counter(iv_int)
cipher = pyaes.AESModeOfOperationCTR(key, counter=counter)
elif cipher_type == "cfb":
# Change segment size from default 8 bytes to 16 bytes for OpenSSL
# compatibility
cipher = pyaes.AESModeOfOperationCFB(key, iv, segment_size=16)
elif cipher_type == "ofb":
cipher = pyaes.AESModeOfOperationOFB(key, iv)
decrypter = pyaes.Decrypter(cipher)
data = decrypter.feed(ciphertext)
data += decrypter.feed()
return data
def get_backend(self):
return "fallback"
aes = AES(AESBackend())

View File

@ -0,0 +1,371 @@
import hmac
import os
from ._jacobian import JacobianCurve
from .._ecc import ECC
from .aes import aes
from ._util import int_to_bytes, bytes_to_int, inverse, square_root_mod_prime
# pylint: disable=line-too-long
CURVES = {
# nid: (p, n, a, b, (Gx, Gy)),
704: (
# secp112r1
0xDB7C2ABF62E35E668076BEAD208B,
0xDB7C2ABF62E35E7628DFAC6561C5,
0xDB7C2ABF62E35E668076BEAD2088,
0x659EF8BA043916EEDE8911702B22,
(
0x09487239995A5EE76B55F9C2F098,
0xA89CE5AF8724C0A23E0E0FF77500
)
),
705: (
# secp112r2
0xDB7C2ABF62E35E668076BEAD208B,
0x36DF0AAFD8B8D7597CA10520D04B,
0x6127C24C05F38A0AAAF65C0EF02C,
0x51DEF1815DB5ED74FCC34C85D709,
(
0x4BA30AB5E892B4E1649DD0928643,
0xADCD46F5882E3747DEF36E956E97
)
),
706: (
# secp128r1
0xFFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF,
0xFFFFFFFE0000000075A30D1B9038A115,
0xFFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC,
0xE87579C11079F43DD824993C2CEE5ED3,
(
0x161FF7528B899B2D0C28607CA52C5B86,
0xCF5AC8395BAFEB13C02DA292DDED7A83
)
),
707: (
# secp128r2
0xFFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF,
0x3FFFFFFF7FFFFFFFBE0024720613B5A3,
0xD6031998D1B3BBFEBF59CC9BBFF9AEE1,
0x5EEEFCA380D02919DC2C6558BB6D8A5D,
(
0x7B6AA5D85E572983E6FB32A7CDEBC140,
0x27B6916A894D3AEE7106FE805FC34B44
)
),
708: (
# secp160k1
0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73,
0x0100000000000000000001B8FA16DFAB9ACA16B6B3,
0,
7,
(
0x3B4C382CE37AA192A4019E763036F4F5DD4D7EBB,
0x938CF935318FDCED6BC28286531733C3F03C4FEE
)
),
709: (
# secp160r1
0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF,
0x0100000000000000000001F4C8F927AED3CA752257,
0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC,
0x001C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45,
(
0x4A96B5688EF573284664698968C38BB913CBFC82,
0x23A628553168947D59DCC912042351377AC5FB32
)
),
710: (
# secp160r2
0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73,
0x0100000000000000000000351EE786A818F3A1A16B,
0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC70,
0x00B4E134D3FB59EB8BAB57274904664D5AF50388BA,
(
0x52DCB034293A117E1F4FF11B30F7199D3144CE6D,
0xFEAFFEF2E331F296E071FA0DF9982CFEA7D43F2E
)
),
711: (
# secp192k1
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37,
0xFFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D,
0,
3,
(
0xDB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D,
0x9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D
)
),
409: (
# prime192v1
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC,
0x64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1,
(
0x188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012,
0x07192B95FFC8DA78631011ED6B24CDD573F977A11E794811
)
),
712: (
# secp224k1
0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D,
0x010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7,
0,
5,
(
0xA1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C,
0x7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5
)
),
713: (
# secp224r1
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE,
0xB4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4,
(
0xB70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21,
0xBD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34
)
),
714: (
# secp256k1
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,
0,
7,
(
0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,
0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8
)
),
415: (
# prime256v1
0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF,
0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551,
0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC,
0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B,
(
0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296,
0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5
)
),
715: (
# secp384r1
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC,
0xB3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF,
(
0xAA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7,
0x3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F
)
),
716: (
# secp521r1
0x01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,
0x01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409,
0x01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC,
0x0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00,
(
0x00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66,
0x011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650
)
)
}
# pylint: enable=line-too-long
class EllipticCurveBackend:
def __init__(self, nid):
self.p, self.n, self.a, self.b, self.g = CURVES[nid]
self.jacobian = JacobianCurve(*CURVES[nid])
self.public_key_length = (len(bin(self.p).replace("0b", "")) + 7) // 8
self.order_bitlength = len(bin(self.n).replace("0b", ""))
def _int_to_bytes(self, raw, len=None):
return int_to_bytes(raw, len or self.public_key_length)
def decompress_point(self, public_key):
# Parse & load data
x = bytes_to_int(public_key[1:])
# Calculate Y
y_square = (pow(x, 3, self.p) + self.a * x + self.b) % self.p
try:
y = square_root_mod_prime(y_square, self.p)
except Exception:
raise ValueError("Invalid public key") from None
if y % 2 != public_key[0] - 0x02:
y = self.p - y
return self._int_to_bytes(x), self._int_to_bytes(y)
def new_private_key(self):
while True:
private_key = os.urandom(self.public_key_length)
if bytes_to_int(private_key) >= self.n:
continue
return private_key
def private_to_public(self, private_key):
raw = bytes_to_int(private_key)
x, y = self.jacobian.fast_multiply(self.g, raw)
return self._int_to_bytes(x), self._int_to_bytes(y)
def ecdh(self, private_key, public_key):
x, y = public_key
x, y = bytes_to_int(x), bytes_to_int(y)
private_key = bytes_to_int(private_key)
x, _ = self.jacobian.fast_multiply((x, y), private_key, secret=True)
return self._int_to_bytes(x)
def _subject_to_int(self, subject):
return bytes_to_int(subject[:(self.order_bitlength + 7) // 8])
def sign(self, subject, raw_private_key, recoverable, is_compressed, entropy):
z = self._subject_to_int(subject)
private_key = bytes_to_int(raw_private_key)
k = bytes_to_int(entropy)
# Fix k length to prevent Minerva. Increasing multiplier by a
# multiple of order doesn't break anything. This fix was ported
# from python-ecdsa
ks = k + self.n
kt = ks + self.n
ks_len = len(bin(ks).replace("0b", "")) // 8
kt_len = len(bin(kt).replace("0b", "")) // 8
if ks_len == kt_len:
k = kt
else:
k = ks
px, py = self.jacobian.fast_multiply(self.g, k, secret=True)
r = px % self.n
if r == 0:
# Invalid k
raise ValueError("Invalid k")
s = (inverse(k, self.n) * (z + (private_key * r))) % self.n
if s == 0:
# Invalid k
raise ValueError("Invalid k")
inverted = False
if s * 2 >= self.n:
s = self.n - s
inverted = True
rs_buf = self._int_to_bytes(r) + self._int_to_bytes(s)
if recoverable:
recid = (py % 2) ^ inverted
recid += 2 * int(px // self.n)
if is_compressed:
return bytes([31 + recid]) + rs_buf
else:
if recid >= 4:
raise ValueError("Too big recovery ID, use compressed address instead")
return bytes([27 + recid]) + rs_buf
else:
return rs_buf
def recover(self, signature, subject):
z = self._subject_to_int(subject)
recid = signature[0] - 27 if signature[0] < 31 else signature[0] - 31
r = bytes_to_int(signature[1:self.public_key_length + 1])
s = bytes_to_int(signature[self.public_key_length + 1:])
# Verify bounds
if not 0 <= recid < 2 * (self.p // self.n + 1):
raise ValueError("Invalid recovery ID")
if r >= self.n:
raise ValueError("r is out of bounds")
if s >= self.n:
raise ValueError("s is out of bounds")
rinv = inverse(r, self.n)
u1 = (-z * rinv) % self.n
u2 = (s * rinv) % self.n
# Recover R
rx = r + (recid // 2) * self.n
if rx >= self.p:
raise ValueError("Rx is out of bounds")
# Almost copied from decompress_point
ry_square = (pow(rx, 3, self.p) + self.a * rx + self.b) % self.p
try:
ry = square_root_mod_prime(ry_square, self.p)
except Exception:
raise ValueError("Invalid recovered public key") from None
# Ensure the point is correct
if ry % 2 != recid % 2:
# Fix Ry sign
ry = self.p - ry
x, y = self.jacobian.fast_shamir(self.g, u1, (rx, ry), u2)
return self._int_to_bytes(x), self._int_to_bytes(y)
def verify(self, signature, subject, public_key):
z = self._subject_to_int(subject)
r = bytes_to_int(signature[:self.public_key_length])
s = bytes_to_int(signature[self.public_key_length:])
# Verify bounds
if r >= self.n:
raise ValueError("r is out of bounds")
if s >= self.n:
raise ValueError("s is out of bounds")
public_key = [bytes_to_int(c) for c in public_key]
# Ensure that the public key is correct
if not self.jacobian.is_on_curve(public_key):
raise ValueError("Public key is not on curve")
sinv = inverse(s, self.n)
u1 = (z * sinv) % self.n
u2 = (r * sinv) % self.n
x1, _ = self.jacobian.fast_shamir(self.g, u1, public_key, u2)
if r != x1 % self.n:
raise ValueError("Invalid signature")
return True
def derive_child(self, seed, child):
# Round 1
h = hmac.new(key=b"Bitcoin seed", msg=seed, digestmod="sha512").digest()
private_key1 = h[:32]
x, y = self.private_to_public(private_key1)
public_key1 = bytes([0x02 + (y[-1] % 2)]) + x
private_key1 = bytes_to_int(private_key1)
# Round 2
msg = public_key1 + self._int_to_bytes(child, 4)
h = hmac.new(key=h[32:], msg=msg, digestmod="sha512").digest()
private_key2 = bytes_to_int(h[:32])
return self._int_to_bytes((private_key1 + private_key2) % self.n)
@classmethod
def get_backend(cls):
return "fallback"
ecc = ECC(EllipticCurveBackend, aes)

View File

@ -0,0 +1,8 @@
# pylint: disable=too-few-public-methods
class RSA:
def get_backend(self):
return "fallback"
rsa = RSA()

View File

@ -0,0 +1,3 @@
from .aes import aes
from .ecc import ecc
from .rsa import rsa

View File

@ -0,0 +1,156 @@
import ctypes
import threading
from .._aes import AES
from ..fallback.aes import aes as fallback_aes
from .library import lib, openssl_backend
# Initialize functions
try:
lib.EVP_CIPHER_CTX_new.restype = ctypes.POINTER(ctypes.c_char)
except AttributeError:
pass
lib.EVP_get_cipherbyname.restype = ctypes.POINTER(ctypes.c_char)
thread_local = threading.local()
class Context:
def __init__(self, ptr, do_free):
self.lib = lib
self.ptr = ptr
self.do_free = do_free
def __del__(self):
if self.do_free:
self.lib.EVP_CIPHER_CTX_free(self.ptr)
class AESBackend:
ALGOS = (
"aes-128-cbc", "aes-192-cbc", "aes-256-cbc",
"aes-128-ctr", "aes-192-ctr", "aes-256-ctr",
"aes-128-cfb", "aes-192-cfb", "aes-256-cfb",
"aes-128-ofb", "aes-192-ofb", "aes-256-ofb"
)
def __init__(self):
self.is_supported_ctx_new = hasattr(lib, "EVP_CIPHER_CTX_new")
self.is_supported_ctx_reset = hasattr(lib, "EVP_CIPHER_CTX_reset")
def _get_ctx(self):
if not hasattr(thread_local, "ctx"):
if self.is_supported_ctx_new:
thread_local.ctx = Context(lib.EVP_CIPHER_CTX_new(), True)
else:
# 1 KiB ought to be enough for everybody. We don't know the real
# size of the context buffer because we are unsure about padding and
# pointer size
thread_local.ctx = Context(ctypes.create_string_buffer(1024), False)
return thread_local.ctx.ptr
def get_backend(self):
return openssl_backend
def _get_cipher(self, algo):
if algo not in self.ALGOS:
raise ValueError("Unknown cipher algorithm {}".format(algo))
cipher = lib.EVP_get_cipherbyname(algo.encode())
if not cipher:
raise ValueError("Unknown cipher algorithm {}".format(algo))
return cipher
def is_algo_supported(self, algo):
try:
self._get_cipher(algo)
return True
except ValueError:
return False
def random(self, length):
entropy = ctypes.create_string_buffer(length)
lib.RAND_bytes(entropy, length)
return bytes(entropy)
def encrypt(self, data, key, algo="aes-256-cbc"):
# Initialize context
ctx = self._get_ctx()
if not self.is_supported_ctx_new:
lib.EVP_CIPHER_CTX_init(ctx)
try:
lib.EVP_EncryptInit_ex(ctx, self._get_cipher(algo), None, None, None)
# Generate random IV
iv_length = 16
iv = self.random(iv_length)
# Set key and IV
lib.EVP_EncryptInit_ex(ctx, None, None, key, iv)
# Actually encrypt
block_size = 16
output = ctypes.create_string_buffer((len(data) // block_size + 1) * block_size)
output_len = ctypes.c_int()
if not lib.EVP_CipherUpdate(ctx, output, ctypes.byref(output_len), data, len(data)):
raise ValueError("Could not feed cipher with data")
new_output = ctypes.byref(output, output_len.value)
output_len2 = ctypes.c_int()
if not lib.EVP_CipherFinal_ex(ctx, new_output, ctypes.byref(output_len2)):
raise ValueError("Could not finalize cipher")
ciphertext = output[:output_len.value + output_len2.value]
return ciphertext, iv
finally:
if self.is_supported_ctx_reset:
lib.EVP_CIPHER_CTX_reset(ctx)
else:
lib.EVP_CIPHER_CTX_cleanup(ctx)
def decrypt(self, ciphertext, iv, key, algo="aes-256-cbc"):
# Initialize context
ctx = self._get_ctx()
if not self.is_supported_ctx_new:
lib.EVP_CIPHER_CTX_init(ctx)
try:
lib.EVP_DecryptInit_ex(ctx, self._get_cipher(algo), None, None, None)
# Make sure IV length is correct
iv_length = 16
if len(iv) != iv_length:
raise ValueError("Expected IV to be {} bytes, got {} bytes".format(iv_length, len(iv)))
# Set key and IV
lib.EVP_DecryptInit_ex(ctx, None, None, key, iv)
# Actually decrypt
output = ctypes.create_string_buffer(len(ciphertext))
output_len = ctypes.c_int()
if not lib.EVP_DecryptUpdate(ctx, output, ctypes.byref(output_len), ciphertext, len(ciphertext)):
raise ValueError("Could not feed decipher with ciphertext")
new_output = ctypes.byref(output, output_len.value)
output_len2 = ctypes.c_int()
if not lib.EVP_DecryptFinal_ex(ctx, new_output, ctypes.byref(output_len2)):
raise ValueError("Could not finalize decipher")
return output[:output_len.value + output_len2.value]
finally:
if self.is_supported_ctx_reset:
lib.EVP_CIPHER_CTX_reset(ctx)
else:
lib.EVP_CIPHER_CTX_cleanup(ctx)
aes = AES(AESBackend(), fallback_aes)

View File

@ -0,0 +1,3 @@
# Can be redefined by user
def discover():
pass

View File

@ -0,0 +1,575 @@
import ctypes
import hmac
import threading
from .._ecc import ECC
from .aes import aes
from .library import lib, openssl_backend
# Initialize functions
lib.BN_new.restype = ctypes.POINTER(ctypes.c_char)
lib.BN_bin2bn.restype = ctypes.POINTER(ctypes.c_char)
lib.BN_CTX_new.restype = ctypes.POINTER(ctypes.c_char)
lib.EC_GROUP_new_by_curve_name.restype = ctypes.POINTER(ctypes.c_char)
lib.EC_KEY_new.restype = ctypes.POINTER(ctypes.c_char)
lib.EC_POINT_new.restype = ctypes.POINTER(ctypes.c_char)
lib.EC_KEY_get0_private_key.restype = ctypes.POINTER(ctypes.c_char)
lib.EVP_PKEY_new.restype = ctypes.POINTER(ctypes.c_char)
try:
lib.EVP_PKEY_CTX_new.restype = ctypes.POINTER(ctypes.c_char)
except AttributeError:
pass
thread_local = threading.local()
# This lock is required to keep ECC thread-safe. Old OpenSSL versions (before
# 1.1.0) use global objects so they aren't thread safe. Fortunately we can check
# the code to find out which functions are thread safe.
#
# For example, EC_GROUP_new_by_curve_name checks global error code to initialize
# the group, so if two errors happen at once or two threads read the error code,
# or the codes are read in the wrong order, the group is initialized in a wrong
# way.
#
# EC_KEY_new_by_curve_name calls EC_GROUP_new_by_curve_name so it's not thread
# safe. We can't use the lock because it would be too slow; instead, we use
# EC_KEY_new and then EC_KEY_set_group which calls EC_GROUP_copy instead which
# is thread safe.
lock = threading.Lock()
class BN:
# BN_CTX
class Context:
def __init__(self):
self.ptr = lib.BN_CTX_new()
self.lib = lib # For finalizer
def __del__(self):
self.lib.BN_CTX_free(self.ptr)
@classmethod
def get(cls):
# Get thread-safe contexf
if not hasattr(thread_local, "bn_ctx"):
thread_local.bn_ctx = cls()
return thread_local.bn_ctx.ptr
def __init__(self, value=None, link_only=False):
if link_only:
self.bn = value
self._free = False
else:
if value is None:
self.bn = lib.BN_new()
self._free = True
elif isinstance(value, bytes):
self.bn = lib.BN_bin2bn(value, len(value), None)
self._free = True
else:
self.bn = lib.BN_new()
lib.BN_clear(self.bn)
lib.BN_add_word(self.bn, value)
self._free = True
def __del__(self):
if self._free:
lib.BN_free(self.bn)
def bytes(self, length=None):
buf = ctypes.create_string_buffer((len(self) + 7) // 8)
lib.BN_bn2bin(self.bn, buf)
buf = bytes(buf)
if length is None:
return buf
else:
if length < len(buf):
raise ValueError("Too little space for BN")
return b"\x00" * (length - len(buf)) + buf
def __int__(self):
value = 0
for byte in self.bytes():
value = value * 256 + byte
return value
def __len__(self):
return lib.BN_num_bits(self.bn)
def inverse(self, modulo):
result = BN()
if not lib.BN_mod_inverse(result.bn, self.bn, modulo.bn, BN.Context.get()):
raise ValueError("Could not compute inverse")
return result
def __floordiv__(self, other):
if not isinstance(other, BN):
raise TypeError("Can only divide BN by BN, not {}".format(other))
result = BN()
if not lib.BN_div(result.bn, None, self.bn, other.bn, BN.Context.get()):
raise ZeroDivisionError("Division by zero")
return result
def __mod__(self, other):
if not isinstance(other, BN):
raise TypeError("Can only divide BN by BN, not {}".format(other))
result = BN()
if not lib.BN_div(None, result.bn, self.bn, other.bn, BN.Context.get()):
raise ZeroDivisionError("Division by zero")
return result
def __add__(self, other):
if not isinstance(other, BN):
raise TypeError("Can only sum BN's, not BN and {}".format(other))
result = BN()
if not lib.BN_add(result.bn, self.bn, other.bn):
raise ValueError("Could not sum two BN's")
return result
def __sub__(self, other):
if not isinstance(other, BN):
raise TypeError("Can only subtract BN's, not BN and {}".format(other))
result = BN()
if not lib.BN_sub(result.bn, self.bn, other.bn):
raise ValueError("Could not subtract BN from BN")
return result
def __mul__(self, other):
if not isinstance(other, BN):
raise TypeError("Can only multiply BN by BN, not {}".format(other))
result = BN()
if not lib.BN_mul(result.bn, self.bn, other.bn, BN.Context.get()):
raise ValueError("Could not multiply two BN's")
return result
def __neg__(self):
return BN(0) - self
# A dirty but nice way to update current BN and free old BN at the same time
def __imod__(self, other):
res = self % other
self.bn, res.bn = res.bn, self.bn
return self
def __iadd__(self, other):
res = self + other
self.bn, res.bn = res.bn, self.bn
return self
def __isub__(self, other):
res = self - other
self.bn, res.bn = res.bn, self.bn
return self
def __imul__(self, other):
res = self * other
self.bn, res.bn = res.bn, self.bn
return self
def cmp(self, other):
if not isinstance(other, BN):
raise TypeError("Can only compare BN with BN, not {}".format(other))
return lib.BN_cmp(self.bn, other.bn)
def __eq__(self, other):
return self.cmp(other) == 0
def __lt__(self, other):
return self.cmp(other) < 0
def __gt__(self, other):
return self.cmp(other) > 0
def __ne__(self, other):
return self.cmp(other) != 0
def __le__(self, other):
return self.cmp(other) <= 0
def __ge__(self, other):
return self.cmp(other) >= 0
def __repr__(self):
return "<BN {}>".format(int(self))
def __str__(self):
return str(int(self))
class EllipticCurveBackend:
def __init__(self, nid):
self.lib = lib # For finalizer
self.nid = nid
with lock:
# Thread-safety
self.group = lib.EC_GROUP_new_by_curve_name(self.nid)
if not self.group:
raise ValueError("The curve is not supported by OpenSSL")
self.order = BN()
self.p = BN()
bn_ctx = BN.Context.get()
lib.EC_GROUP_get_order(self.group, self.order.bn, bn_ctx)
lib.EC_GROUP_get_curve_GFp(self.group, self.p.bn, None, None, bn_ctx)
self.public_key_length = (len(self.p) + 7) // 8
self.is_supported_evp_pkey_ctx = hasattr(lib, "EVP_PKEY_CTX_new")
def __del__(self):
self.lib.EC_GROUP_free(self.group)
def _private_key_to_ec_key(self, private_key):
# Thread-safety
eckey = lib.EC_KEY_new()
lib.EC_KEY_set_group(eckey, self.group)
if not eckey:
raise ValueError("Failed to allocate EC_KEY")
private_key = BN(private_key)
if not lib.EC_KEY_set_private_key(eckey, private_key.bn):
lib.EC_KEY_free(eckey)
raise ValueError("Invalid private key")
return eckey, private_key
def _public_key_to_point(self, public_key):
x = BN(public_key[0])
y = BN(public_key[1])
# EC_KEY_set_public_key_affine_coordinates is not supported by
# OpenSSL 1.0.0 so we can't use it
point = lib.EC_POINT_new(self.group)
if not lib.EC_POINT_set_affine_coordinates_GFp(self.group, point, x.bn, y.bn, BN.Context.get()):
raise ValueError("Could not set public key affine coordinates")
return point
def _public_key_to_ec_key(self, public_key):
# Thread-safety
eckey = lib.EC_KEY_new()
lib.EC_KEY_set_group(eckey, self.group)
if not eckey:
raise ValueError("Failed to allocate EC_KEY")
try:
# EC_KEY_set_public_key_affine_coordinates is not supported by
# OpenSSL 1.0.0 so we can't use it
point = self._public_key_to_point(public_key)
if not lib.EC_KEY_set_public_key(eckey, point):
raise ValueError("Could not set point")
lib.EC_POINT_free(point)
return eckey
except Exception as e:
lib.EC_KEY_free(eckey)
raise e from None
def _point_to_affine(self, point):
# Convert to affine coordinates
x = BN()
y = BN()
if lib.EC_POINT_get_affine_coordinates_GFp(self.group, point, x.bn, y.bn, BN.Context.get()) != 1:
raise ValueError("Failed to convert public key to affine coordinates")
# Convert to binary
if (len(x) + 7) // 8 > self.public_key_length:
raise ValueError("Public key X coordinate is too large")
if (len(y) + 7) // 8 > self.public_key_length:
raise ValueError("Public key Y coordinate is too large")
return x.bytes(self.public_key_length), y.bytes(self.public_key_length)
def decompress_point(self, public_key):
point = lib.EC_POINT_new(self.group)
if not point:
raise ValueError("Could not create point")
try:
if not lib.EC_POINT_oct2point(self.group, point, public_key, len(public_key), BN.Context.get()):
raise ValueError("Invalid compressed public key")
return self._point_to_affine(point)
finally:
lib.EC_POINT_free(point)
def new_private_key(self):
# Create random key
# Thread-safety
eckey = lib.EC_KEY_new()
lib.EC_KEY_set_group(eckey, self.group)
lib.EC_KEY_generate_key(eckey)
# To big integer
private_key = BN(lib.EC_KEY_get0_private_key(eckey), link_only=True)
# To binary
private_key_buf = private_key.bytes()
# Cleanup
lib.EC_KEY_free(eckey)
return private_key_buf
def private_to_public(self, private_key):
eckey, private_key = self._private_key_to_ec_key(private_key)
try:
# Derive public key
point = lib.EC_POINT_new(self.group)
try:
if not lib.EC_POINT_mul(self.group, point, private_key.bn, None, None, BN.Context.get()):
raise ValueError("Failed to derive public key")
return self._point_to_affine(point)
finally:
lib.EC_POINT_free(point)
finally:
lib.EC_KEY_free(eckey)
def ecdh(self, private_key, public_key):
if not self.is_supported_evp_pkey_ctx:
# Use ECDH_compute_key instead
# Create EC_KEY from private key
eckey, _ = self._private_key_to_ec_key(private_key)
try:
# Create EC_POINT from public key
point = self._public_key_to_point(public_key)
try:
key = ctypes.create_string_buffer(self.public_key_length)
if lib.ECDH_compute_key(key, self.public_key_length, point, eckey, None) == -1:
raise ValueError("Could not compute shared secret")
return bytes(key)
finally:
lib.EC_POINT_free(point)
finally:
lib.EC_KEY_free(eckey)
# Private key:
# Create EC_KEY
eckey, _ = self._private_key_to_ec_key(private_key)
try:
# Convert to EVP_PKEY
pkey = lib.EVP_PKEY_new()
if not pkey:
raise ValueError("Could not create private key object")
try:
lib.EVP_PKEY_set1_EC_KEY(pkey, eckey)
# Public key:
# Create EC_KEY
peer_eckey = self._public_key_to_ec_key(public_key)
try:
# Convert to EVP_PKEY
peer_pkey = lib.EVP_PKEY_new()
if not peer_pkey:
raise ValueError("Could not create public key object")
try:
lib.EVP_PKEY_set1_EC_KEY(peer_pkey, peer_eckey)
# Create context
ctx = lib.EVP_PKEY_CTX_new(pkey, None)
if not ctx:
raise ValueError("Could not create EVP context")
try:
if lib.EVP_PKEY_derive_init(ctx) != 1:
raise ValueError("Could not initialize key derivation")
if not lib.EVP_PKEY_derive_set_peer(ctx, peer_pkey):
raise ValueError("Could not set peer")
# Actually derive
key_len = ctypes.c_int(0)
lib.EVP_PKEY_derive(ctx, None, ctypes.byref(key_len))
key = ctypes.create_string_buffer(key_len.value)
lib.EVP_PKEY_derive(ctx, key, ctypes.byref(key_len))
return bytes(key)
finally:
lib.EVP_PKEY_CTX_free(ctx)
finally:
lib.EVP_PKEY_free(peer_pkey)
finally:
lib.EC_KEY_free(peer_eckey)
finally:
lib.EVP_PKEY_free(pkey)
finally:
lib.EC_KEY_free(eckey)
def _subject_to_bn(self, subject):
return BN(subject[:(len(self.order) + 7) // 8])
def sign(self, subject, private_key, recoverable, is_compressed, entropy):
z = self._subject_to_bn(subject)
private_key = BN(private_key)
k = BN(entropy)
rp = lib.EC_POINT_new(self.group)
bn_ctx = BN.Context.get()
try:
# Fix Minerva
k1 = k + self.order
k2 = k1 + self.order
if len(k1) == len(k2):
k = k2
else:
k = k1
if not lib.EC_POINT_mul(self.group, rp, k.bn, None, None, bn_ctx):
raise ValueError("Could not generate R")
# Convert to affine coordinates
rx = BN()
ry = BN()
if lib.EC_POINT_get_affine_coordinates_GFp(self.group, rp, rx.bn, ry.bn, bn_ctx) != 1:
raise ValueError("Failed to convert R to affine coordinates")
r = rx % self.order
if r == BN(0):
raise ValueError("Invalid k")
# Calculate s = k^-1 * (z + r * private_key) mod n
s = (k.inverse(self.order) * (z + r * private_key)) % self.order
if s == BN(0):
raise ValueError("Invalid k")
inverted = False
if s * BN(2) >= self.order:
s = self.order - s
inverted = True
r_buf = r.bytes(self.public_key_length)
s_buf = s.bytes(self.public_key_length)
if recoverable:
# Generate recid
recid = int(ry % BN(2)) ^ inverted
# The line below is highly unlikely to matter in case of
# secp256k1 but might make sense for other curves
recid += 2 * int(rx // self.order)
if is_compressed:
return bytes([31 + recid]) + r_buf + s_buf
else:
if recid >= 4:
raise ValueError("Too big recovery ID, use compressed address instead")
return bytes([27 + recid]) + r_buf + s_buf
else:
return r_buf + s_buf
finally:
lib.EC_POINT_free(rp)
def recover(self, signature, subject):
recid = signature[0] - 27 if signature[0] < 31 else signature[0] - 31
r = BN(signature[1:self.public_key_length + 1])
s = BN(signature[self.public_key_length + 1:])
# Verify bounds
if r >= self.order:
raise ValueError("r is out of bounds")
if s >= self.order:
raise ValueError("s is out of bounds")
bn_ctx = BN.Context.get()
z = self._subject_to_bn(subject)
rinv = r.inverse(self.order)
u1 = (-z * rinv) % self.order
u2 = (s * rinv) % self.order
# Recover R
rx = r + BN(recid // 2) * self.order
if rx >= self.p:
raise ValueError("Rx is out of bounds")
rp = lib.EC_POINT_new(self.group)
if not rp:
raise ValueError("Could not create R")
try:
init_buf = b"\x02" + rx.bytes(self.public_key_length)
if not lib.EC_POINT_oct2point(self.group, rp, init_buf, len(init_buf), bn_ctx):
raise ValueError("Could not use Rx to initialize point")
ry = BN()
if lib.EC_POINT_get_affine_coordinates_GFp(self.group, rp, None, ry.bn, bn_ctx) != 1:
raise ValueError("Failed to convert R to affine coordinates")
if int(ry % BN(2)) != recid % 2:
# Fix Ry sign
ry = self.p - ry
if lib.EC_POINT_set_affine_coordinates_GFp(self.group, rp, rx.bn, ry.bn, bn_ctx) != 1:
raise ValueError("Failed to update R coordinates")
# Recover public key
result = lib.EC_POINT_new(self.group)
if not result:
raise ValueError("Could not create point")
try:
if not lib.EC_POINT_mul(self.group, result, u1.bn, rp, u2.bn, bn_ctx):
raise ValueError("Could not recover public key")
return self._point_to_affine(result)
finally:
lib.EC_POINT_free(result)
finally:
lib.EC_POINT_free(rp)
def verify(self, signature, subject, public_key):
r_raw = signature[:self.public_key_length]
r = BN(r_raw)
s = BN(signature[self.public_key_length:])
if r >= self.order:
raise ValueError("r is out of bounds")
if s >= self.order:
raise ValueError("s is out of bounds")
bn_ctx = BN.Context.get()
z = self._subject_to_bn(subject)
pub_p = lib.EC_POINT_new(self.group)
if not pub_p:
raise ValueError("Could not create public key point")
try:
init_buf = b"\x04" + public_key[0] + public_key[1]
if not lib.EC_POINT_oct2point(self.group, pub_p, init_buf, len(init_buf), bn_ctx):
raise ValueError("Could initialize point")
sinv = s.inverse(self.order)
u1 = (z * sinv) % self.order
u2 = (r * sinv) % self.order
# Recover public key
result = lib.EC_POINT_new(self.group)
if not result:
raise ValueError("Could not create point")
try:
if not lib.EC_POINT_mul(self.group, result, u1.bn, pub_p, u2.bn, bn_ctx):
raise ValueError("Could not recover public key")
if BN(self._point_to_affine(result)[0]) % self.order != r:
raise ValueError("Invalid signature")
return True
finally:
lib.EC_POINT_free(result)
finally:
lib.EC_POINT_free(pub_p)
def derive_child(self, seed, child):
# Round 1
h = hmac.new(key=b"Bitcoin seed", msg=seed, digestmod="sha512").digest()
private_key1 = h[:32]
x, y = self.private_to_public(private_key1)
public_key1 = bytes([0x02 + (y[-1] % 2)]) + x
private_key1 = BN(private_key1)
# Round 2
child_bytes = []
for _ in range(4):
child_bytes.append(child & 255)
child >>= 8
child_bytes = bytes(child_bytes[::-1])
msg = public_key1 + child_bytes
h = hmac.new(key=h[32:], msg=msg, digestmod="sha512").digest()
private_key2 = BN(h[:32])
return ((private_key1 + private_key2) % self.order).bytes(self.public_key_length)
@classmethod
def get_backend(cls):
return openssl_backend
ecc = ECC(EllipticCurveBackend, aes)

View File

@ -0,0 +1,98 @@
import os
import sys
import ctypes
import ctypes.util
from .discovery import discover as user_discover
# Disable false-positive _MEIPASS
# pylint: disable=no-member,protected-access
# Discover OpenSSL library
def discover_paths():
# Search local files first
if "win" in sys.platform:
# Windows
names = [
"libeay32.dll"
]
openssl_paths = [os.path.abspath(path) for path in names]
if hasattr(sys, "_MEIPASS"):
openssl_paths += [os.path.join(sys._MEIPASS, path) for path in openssl_paths]
openssl_paths.append(ctypes.util.find_library("libeay32"))
elif "darwin" in sys.platform:
# Mac OS
names = [
"libcrypto.dylib",
"libcrypto.1.1.0.dylib",
"libcrypto.1.0.2.dylib",
"libcrypto.1.0.1.dylib",
"libcrypto.1.0.0.dylib",
"libcrypto.0.9.8.dylib"
]
openssl_paths = [os.path.abspath(path) for path in names]
openssl_paths += names
openssl_paths += [
"/usr/local/opt/openssl/lib/libcrypto.dylib"
]
if hasattr(sys, "_MEIPASS") and "RESOURCEPATH" in os.environ:
openssl_paths += [
os.path.join(os.environ["RESOURCEPATH"], "..", "Frameworks", name)
for name in names
]
openssl_paths.append(ctypes.util.find_library("ssl"))
else:
# Linux, BSD and such
names = [
"libcrypto.so",
"libssl.so",
"libcrypto.so.1.1.0",
"libssl.so.1.1.0",
"libcrypto.so.1.0.2",
"libssl.so.1.0.2",
"libcrypto.so.1.0.1",
"libssl.so.1.0.1",
"libcrypto.so.1.0.0",
"libssl.so.1.0.0",
"libcrypto.so.0.9.8",
"libssl.so.0.9.8"
]
openssl_paths = [os.path.abspath(path) for path in names]
openssl_paths += names
if hasattr(sys, "_MEIPASS"):
openssl_paths += [os.path.join(sys._MEIPASS, path) for path in names]
openssl_paths.append(ctypes.util.find_library("ssl"))
lst = user_discover()
if isinstance(lst, str):
lst = [lst]
elif not lst:
lst = []
return lst + openssl_paths
def discover_library():
for path in discover_paths():
if path:
try:
return ctypes.CDLL(path)
except OSError:
pass
raise OSError("OpenSSL is unavailable")
lib = discover_library()
# Initialize internal state
try:
lib.OPENSSL_add_all_algorithms_conf()
except AttributeError:
pass
try:
lib.OpenSSL_version.restype = ctypes.c_char_p
openssl_backend = lib.OpenSSL_version(0).decode()
except AttributeError:
lib.SSLeay_version.restype = ctypes.c_char_p
openssl_backend = lib.SSLeay_version(0).decode()
openssl_backend += " at " + lib._name

View File

@ -0,0 +1,11 @@
# pylint: disable=too-few-public-methods
from .library import openssl_backend
class RSA:
def get_backend(self):
return openssl_backend
rsa = RSA()

39
src/util/Electrum.py Normal file
View File

@ -0,0 +1,39 @@
import hashlib
import struct
# Electrum, the heck?!
def bchr(i):
return struct.pack("B", i)
def encode(val, base, minlen=0):
base, minlen = int(base), int(minlen)
code_string = b"".join([bchr(x) for x in range(256)])
result = b""
while val > 0:
index = val % base
result = code_string[index:index + 1] + result
val //= base
return code_string[0:1] * max(minlen - len(result), 0) + result
def insane_int(x):
x = int(x)
if x < 253:
return bchr(x)
elif x < 65536:
return bchr(253) + encode(x, 256, 2)[::-1]
elif x < 4294967296:
return bchr(254) + encode(x, 256, 4)[::-1]
else:
return bchr(255) + encode(x, 256, 8)[::-1]
def magic(message):
return b"\x18Bitcoin Signed Message:\n" + insane_int(len(message)) + message
def format(message):
return hashlib.sha256(magic(message)).digest()
def dbl_format(message):
return hashlib.sha256(format(message)).digest()

View File

@ -1,13 +1,11 @@
import logging
import os
import sys
import ctypes
import ctypes.util
from ctypes.util import find_library
from lib.sslcrypto.openssl import discovery
from Config import config
find_library_original = ctypes.util.find_library
def getOpensslPath():
if config.openssl_lib_file:
@ -49,29 +47,11 @@ def getOpensslPath():
logging.debug("OpenSSL lib not found in: %s (%s)" % (path, err))
lib_path = (
find_library_original('ssl.so') or find_library_original('ssl') or
find_library_original('crypto') or find_library_original('libcrypto') or 'libeay32'
find_library('ssl.so') or find_library('ssl') or
find_library('crypto') or find_library('libcrypto') or 'libeay32'
)
return lib_path
def patchCtypesOpensslFindLibrary():
def findLibraryPatched(name):
if name in ("ssl", "crypto", "libeay32"):
lib_path = getOpensslPath()
return lib_path
else:
return find_library_original(name)
ctypes.util.find_library = findLibraryPatched
patchCtypesOpensslFindLibrary()
def openLibrary():
lib_path = getOpensslPath()
logging.debug("Opening %s..." % lib_path)
ssl_lib = ctypes.CDLL(lib_path, ctypes.RTLD_GLOBAL)
return ssl_lib
discovery.discover = getOpensslPath