first commit
This commit is contained in:
162
venv/lib/python3.12/site-packages/asyncssh/crypto/chacha.py
Normal file
162
venv/lib/python3.12/site-packages/asyncssh/crypto/chacha.py
Normal file
@@ -0,0 +1,162 @@
|
||||
# Copyright (c) 2015-2021 by Ron Frederick <ronf@timeheart.net> and others.
|
||||
#
|
||||
# This program and the accompanying materials are made available under
|
||||
# the terms of the Eclipse Public License v2.0 which accompanies this
|
||||
# distribution and is available at:
|
||||
#
|
||||
# http://www.eclipse.org/legal/epl-2.0/
|
||||
#
|
||||
# This program may also be made available under the following secondary
|
||||
# licenses when the conditions for such availability set forth in the
|
||||
# Eclipse Public License v2.0 are satisfied:
|
||||
#
|
||||
# GNU General Public License, Version 2.0, or any later versions of
|
||||
# that license
|
||||
#
|
||||
# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
|
||||
#
|
||||
# Contributors:
|
||||
# Ron Frederick - initial implementation, API, and documentation
|
||||
|
||||
"""Chacha20-Poly1305 symmetric encryption handler"""
|
||||
|
||||
from ctypes import c_ulonglong, create_string_buffer
|
||||
from typing import Optional, Tuple
|
||||
|
||||
from cryptography.exceptions import InvalidSignature
|
||||
from cryptography.hazmat.backends.openssl import backend
|
||||
from cryptography.hazmat.primitives.ciphers import Cipher
|
||||
from cryptography.hazmat.primitives.ciphers.algorithms import ChaCha20
|
||||
from cryptography.hazmat.primitives.poly1305 import Poly1305
|
||||
|
||||
from .cipher import register_cipher
|
||||
|
||||
|
||||
if backend.poly1305_supported():
|
||||
_CTR_0 = (0).to_bytes(8, 'little')
|
||||
_CTR_1 = (1).to_bytes(8, 'little')
|
||||
|
||||
_POLY1305_KEYBYTES = 32
|
||||
|
||||
def chacha20(key: bytes, data: bytes, nonce: bytes, ctr: int) -> bytes:
|
||||
"""Encrypt/decrypt a block of data with the ChaCha20 cipher"""
|
||||
|
||||
return Cipher(ChaCha20(key, (_CTR_1 if ctr else _CTR_0) + nonce),
|
||||
mode=None).encryptor().update(data)
|
||||
|
||||
def poly1305_key(key: bytes, nonce: bytes) -> bytes:
|
||||
"""Derive a Poly1305 key"""
|
||||
|
||||
return chacha20(key, _POLY1305_KEYBYTES * b'\0', nonce, 0)
|
||||
|
||||
def poly1305(key: bytes, data: bytes, nonce: bytes) -> bytes:
|
||||
"""Compute a Poly1305 tag for a block of data"""
|
||||
|
||||
return Poly1305.generate_tag(poly1305_key(key, nonce), data)
|
||||
|
||||
def poly1305_verify(key: bytes, data: bytes,
|
||||
nonce: bytes, tag: bytes) -> bool:
|
||||
"""Verify a Poly1305 tag for a block of data"""
|
||||
|
||||
try:
|
||||
Poly1305.verify_tag(poly1305_key(key, nonce), data, tag)
|
||||
return True
|
||||
except InvalidSignature:
|
||||
return False
|
||||
|
||||
chacha_available = True
|
||||
else: # pragma: no cover
|
||||
try:
|
||||
from libnacl import nacl
|
||||
|
||||
_chacha20 = nacl.crypto_stream_chacha20
|
||||
_chacha20_xor_ic = nacl.crypto_stream_chacha20_xor_ic
|
||||
|
||||
_POLY1305_BYTES = nacl.crypto_onetimeauth_poly1305_bytes()
|
||||
_POLY1305_KEYBYTES = nacl.crypto_onetimeauth_poly1305_keybytes()
|
||||
|
||||
_poly1305 = nacl.crypto_onetimeauth_poly1305
|
||||
_poly1305_verify = nacl.crypto_onetimeauth_poly1305_verify
|
||||
|
||||
def chacha20(key: bytes, data: bytes, nonce: bytes, ctr: int) -> bytes:
|
||||
"""Encrypt/decrypt a block of data with the ChaCha20 cipher"""
|
||||
|
||||
datalen = len(data)
|
||||
result = create_string_buffer(datalen)
|
||||
ull_datalen = c_ulonglong(datalen)
|
||||
ull_ctr = c_ulonglong(ctr)
|
||||
|
||||
_chacha20_xor_ic(result, data, ull_datalen, nonce, ull_ctr, key)
|
||||
|
||||
return result.raw
|
||||
|
||||
def poly1305_key(key: bytes, nonce: bytes) -> bytes:
|
||||
"""Derive a Poly1305 key"""
|
||||
|
||||
polykey = create_string_buffer(_POLY1305_KEYBYTES)
|
||||
ull_polykeylen = c_ulonglong(_POLY1305_KEYBYTES)
|
||||
|
||||
_chacha20(polykey, ull_polykeylen, nonce, key)
|
||||
|
||||
return polykey.raw
|
||||
|
||||
def poly1305(key: bytes, data: bytes, nonce: bytes) -> bytes:
|
||||
"""Compute a Poly1305 tag for a block of data"""
|
||||
|
||||
tag = create_string_buffer(_POLY1305_BYTES)
|
||||
ull_datalen = c_ulonglong(len(data))
|
||||
polykey = poly1305_key(key, nonce)
|
||||
|
||||
_poly1305(tag, data, ull_datalen, polykey)
|
||||
|
||||
return tag.raw
|
||||
|
||||
def poly1305_verify(key: bytes, data: bytes,
|
||||
nonce: bytes, tag: bytes) -> bool:
|
||||
"""Verify a Poly1305 tag for a block of data"""
|
||||
|
||||
ull_datalen = c_ulonglong(len(data))
|
||||
polykey = poly1305_key(key, nonce)
|
||||
|
||||
return _poly1305_verify(tag, data, ull_datalen, polykey) == 0
|
||||
|
||||
chacha_available = True
|
||||
except (ImportError, OSError, AttributeError):
|
||||
chacha_available = False
|
||||
|
||||
|
||||
class ChachaCipher:
|
||||
"""Shim for Chacha20-Poly1305 symmetric encryption"""
|
||||
|
||||
def __init__(self, key: bytes):
|
||||
keylen = len(key) // 2
|
||||
self._key = key[:keylen]
|
||||
self._adkey = key[keylen:]
|
||||
|
||||
def encrypt_and_sign(self, header: bytes, data: bytes,
|
||||
nonce: bytes) -> Tuple[bytes, bytes]:
|
||||
"""Encrypt and sign a block of data"""
|
||||
|
||||
header = chacha20(self._adkey, header, nonce, 0)
|
||||
data = chacha20(self._key, data, nonce, 1)
|
||||
tag = poly1305(self._key, header + data, nonce)
|
||||
|
||||
return header + data, tag
|
||||
|
||||
def decrypt_header(self, header: bytes, nonce: bytes) -> bytes:
|
||||
"""Decrypt header data"""
|
||||
|
||||
return chacha20(self._adkey, header, nonce, 0)
|
||||
|
||||
def verify_and_decrypt(self, header: bytes, data: bytes,
|
||||
nonce: bytes, tag: bytes) -> Optional[bytes]:
|
||||
"""Verify the signature of and decrypt a block of data"""
|
||||
|
||||
if poly1305_verify(self._key, header + data, nonce, tag):
|
||||
return chacha20(self._key, data, nonce, 1)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
if chacha_available: # pragma: no branch
|
||||
register_cipher('chacha20-poly1305', 64, 0, 1)
|
||||
Reference in New Issue
Block a user