URI: 
       tcrypto: add chacha20_decrypt; unused for now - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 9740744d70461d9b0f02a78f836784bc91ff2d1a
   DIR parent 549b9a95df6fedbf0880df139a99f419931150e1
  HTML Author: SomberNight <somber.night@protonmail.com>
       Date:   Wed, 10 Mar 2021 17:13:42 +0100
       
       crypto: add chacha20_decrypt; unused for now
       
       Diffstat:
         M electrum/crypto.py                  |      27 +++++++++++++++++++++++++--
         M electrum/tests/test_bitcoin.py      |      16 +++++++++++++---
       
       2 files changed, 38 insertions(+), 5 deletions(-)
       ---
   DIR diff --git a/electrum/crypto.py b/electrum/crypto.py
       t@@ -338,6 +338,8 @@ def chacha20_poly1305_encrypt(
            assert isinstance(nonce, (bytes, bytearray))
            assert isinstance(associated_data, (bytes, bytearray, type(None)))
            assert isinstance(data, (bytes, bytearray))
       +    assert len(key) == 32, f"unexpected key size: {len(nonce)} (expected: 32)"
       +    assert len(nonce) == 12, f"unexpected nonce size: {len(nonce)} (expected: 12)"
            if HAS_CRYPTODOME:
                cipher = CD_ChaCha20_Poly1305.new(key=key, nonce=nonce)
                if associated_data is not None:
       t@@ -361,6 +363,8 @@ def chacha20_poly1305_decrypt(
            assert isinstance(nonce, (bytes, bytearray))
            assert isinstance(associated_data, (bytes, bytearray, type(None)))
            assert isinstance(data, (bytes, bytearray))
       +    assert len(key) == 32, f"unexpected key size: {len(nonce)} (expected: 32)"
       +    assert len(nonce) == 12, f"unexpected nonce size: {len(nonce)} (expected: 12)"
            if HAS_CRYPTODOME:
                cipher = CD_ChaCha20_Poly1305.new(key=key, nonce=nonce)
                if associated_data is not None:
       t@@ -380,14 +384,33 @@ def chacha20_encrypt(*, key: bytes, nonce: bytes, data: bytes) -> bytes:
            assert isinstance(key, (bytes, bytearray))
            assert isinstance(nonce, (bytes, bytearray))
            assert isinstance(data, (bytes, bytearray))
       -    assert len(nonce) == 8, f"unexpected nonce size: {len(nonce)} (expected: 8)"
       +    assert len(key) == 32, f"unexpected key size: {len(nonce)} (expected: 32)"
       +    assert len(nonce) in (8, 12), f"unexpected nonce size: {len(nonce)} (expected: 8 or 12)"
            if HAS_CRYPTODOME:
                cipher = CD_ChaCha20.new(key=key, nonce=nonce)
                return cipher.encrypt(data)
            if HAS_CRYPTOGRAPHY:
       -        nonce = bytes(8) + nonce  # cryptography wants 16 byte nonces
       +        nonce = bytes(16 - len(nonce)) + nonce  # cryptography wants 16 byte nonces
                algo = CG_algorithms.ChaCha20(key=key, nonce=nonce)
                cipher = CG_Cipher(algo, mode=None, backend=CG_default_backend())
                encryptor = cipher.encryptor()
                return encryptor.update(data)
            raise Exception("no chacha20 backend found")
       +
       +
       +def chacha20_decrypt(*, key: bytes, nonce: bytes, data: bytes) -> bytes:
       +    assert isinstance(key, (bytes, bytearray))
       +    assert isinstance(nonce, (bytes, bytearray))
       +    assert isinstance(data, (bytes, bytearray))
       +    assert len(key) == 32, f"unexpected key size: {len(nonce)} (expected: 32)"
       +    assert len(nonce) in (8, 12), f"unexpected nonce size: {len(nonce)} (expected: 8 or 12)"
       +    if HAS_CRYPTODOME:
       +        cipher = CD_ChaCha20.new(key=key, nonce=nonce)
       +        return cipher.decrypt(data)
       +    if HAS_CRYPTOGRAPHY:
       +        nonce = bytes(16 - len(nonce)) + nonce  # cryptography wants 16 byte nonces
       +        algo = CG_algorithms.ChaCha20(key=key, nonce=nonce)
       +        cipher = CG_Cipher(algo, mode=None, backend=CG_default_backend())
       +        decryptor = cipher.decryptor()
       +        return decryptor.update(data)
       +    raise Exception("no chacha20 backend found")
   DIR diff --git a/electrum/tests/test_bitcoin.py b/electrum/tests/test_bitcoin.py
       t@@ -302,12 +302,22 @@ class Test_bitcoin(ElectrumTestCase):
                                 crypto.chacha20_poly1305_decrypt(key=key, nonce=nonce, data=data, associated_data=b''))
        
            @needs_test_with_all_chacha20_implementations
       -    def test_chacha20_encrypt(self):
       +    def test_chacha20_encrypt__8_byte_nonce(self):
                key = bytes.fromhex('37326d9d69a83b815ddfd947d21b0dd39111e5b6a5a44042c44d570ea03e3179')
                nonce = bytes.fromhex('0102030405060708')
                data = bytes.fromhex('38a0e0a7c865fe9ca31f0730cfcab610f18e6da88dc3790f1d243f711a257c78')
       -        self.assertEqual(bytes.fromhex('f62fbd74d197323c7c3d5658476a884d38ee6f4b5500add1e8dc80dcd9c15dff'),
       -                         crypto.chacha20_encrypt(key=key, nonce=nonce, data=data))
       +        ciphertext = crypto.chacha20_encrypt(key=key, nonce=nonce, data=data)
       +        self.assertEqual(bytes.fromhex('f62fbd74d197323c7c3d5658476a884d38ee6f4b5500add1e8dc80dcd9c15dff'), ciphertext)
       +        self.assertEqual(data, crypto.chacha20_decrypt(key=key, nonce=nonce, data=ciphertext))
       +
       +    @needs_test_with_all_chacha20_implementations
       +    def test_chacha20_encrypt__12_byte_nonce(self):
       +        key = bytes.fromhex('37326d9d69a83b815ddfd947d21b0dd39111e5b6a5a44042c44d570ea03e3179')
       +        nonce = bytes.fromhex('010203040506070809101112')
       +        data = bytes.fromhex('38a0e0a7c865fe9ca31f0730cfcab610f18e6da88dc3790f1d243f711a257c78')
       +        ciphertext = crypto.chacha20_encrypt(key=key, nonce=nonce, data=data)
       +        self.assertEqual(bytes.fromhex('c0b1cb75c3c23c13f47dab393add738c92c62c4e2546cb3bf2b48269a4184028'), ciphertext)
       +        self.assertEqual(data, crypto.chacha20_decrypt(key=key, nonce=nonce, data=ciphertext))
        
            def test_sha256d(self):
                self.assertEqual(b'\x95MZI\xfdp\xd9\xb8\xbc\xdb5\xd2R&x)\x95\x7f~\xf7\xfalt\xf8\x84\x19\xbd\xc5\xe8"\t\xf4',