commit 5cc71ef84bec9ca39c5e13e689e5a50b92955dc4
parent 42f9d1ee6375d467fc40ee845dc6bc1a4cc6d7fd
Author: ThomasV <thomasv@electrum.org>
Date: Sun, 22 Oct 2017 08:34:29 +0200
Merge pull request #3085 from SomberNight/aes_padding_pkcs7
bitcoin.py AES: implement our own PKCS7 padding
Diffstat:
2 files changed, 36 insertions(+), 20 deletions(-)
diff --git a/lib/bitcoin.py b/lib/bitcoin.py
@@ -111,36 +111,56 @@ try:
except:
AES = None
+
+class InvalidPadding(Exception):
+ pass
+
+
+def append_PKCS7_padding(data):
+ assert_bytes(data)
+ padlen = 16 - (len(data) % 16)
+ return data + bytes([padlen]) * padlen
+
+
+def strip_PKCS7_padding(data):
+ assert_bytes(data)
+ if len(data) % 16 != 0 or len(data) == 0:
+ raise InvalidPadding("invalid length")
+ padlen = data[-1]
+ if padlen > 16:
+ raise InvalidPadding("invalid padding byte (large)")
+ for i in data[-padlen:]:
+ if i != padlen:
+ raise InvalidPadding("invalid padding byte (inconsistent)")
+ return data[0:-padlen]
+
+
def aes_encrypt_with_iv(key, iv, data):
assert_bytes(key, iv, data)
+ data = append_PKCS7_padding(data)
if AES:
- padlen = 16 - (len(data) % 16)
- if padlen == 0:
- padlen = 16
- data += bytes([padlen]) * padlen
e = AES.new(key, AES.MODE_CBC, iv).encrypt(data)
- return e
else:
aes_cbc = pyaes.AESModeOfOperationCBC(key, iv=iv)
- aes = pyaes.Encrypter(aes_cbc)
- e = aes.feed(data) + aes.feed() # empty aes.feed() appends pkcs padding
- return e
+ aes = pyaes.Encrypter(aes_cbc, padding=pyaes.PADDING_NONE)
+ e = aes.feed(data) + aes.feed() # empty aes.feed() flushes buffer
+ return e
+
def aes_decrypt_with_iv(key, iv, data):
assert_bytes(key, iv, data)
if AES:
cipher = AES.new(key, AES.MODE_CBC, iv)
data = cipher.decrypt(data)
- padlen = data[-1]
- for i in data[-padlen:]:
- if i != padlen:
- raise InvalidPassword()
- return data[0:-padlen]
else:
aes_cbc = pyaes.AESModeOfOperationCBC(key, iv=iv)
- aes = pyaes.Decrypter(aes_cbc)
- s = aes.feed(data) + aes.feed() # empty aes.feed() strips pkcs padding
- return s
+ aes = pyaes.Decrypter(aes_cbc, padding=pyaes.PADDING_NONE)
+ data = aes.feed(data) + aes.feed() # empty aes.feed() flushes buffer
+ try:
+ return strip_PKCS7_padding(data)
+ except InvalidPadding:
+ raise InvalidPassword()
+
def EncodeAES(secret, s):
assert_bytes(s)
diff --git a/lib/tests/test_bitcoin.py b/lib/tests/test_bitcoin.py
@@ -113,10 +113,6 @@ class Test_bitcoin(unittest.TestCase):
password = u"uber secret"
wrong_password = u"not the password"
enc = pw_encode(payload, password)
- # FIXME: pyaes does not check that padding is consistent
- # before removing it, wich causes this test to randomly fail.
- # Wallets are unaffected by this, because check_password
- # includes a test of the decoded public key.
self.assertRaises(Exception, pw_decode, enc, wrong_password)
def test_hash(self):