mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-03-06 06:01:48 +00:00
Merge pull request #1370 from tahoe-lafs/fix-ci
Fixes ticket:4100 Fixes ticket:4101
This commit is contained in:
commit
15d6de68ba
1
newsfragments/4100.bugfix
Normal file
1
newsfragments/4100.bugfix
Normal file
@ -0,0 +1 @@
|
||||
Fix incompatibility with cryptography 43.
|
1
newsfragments/4101.bugfix
Normal file
1
newsfragments/4101.bugfix
Normal file
@ -0,0 +1 @@
|
||||
Fix incompatibility with attrs 24.1.
|
3
setup.py
3
setup.py
@ -62,8 +62,7 @@ install_requires = [
|
||||
# Twisted[conch] also depends on cryptography and Twisted[tls]
|
||||
# transitively depends on cryptography. So it's anyone's guess what
|
||||
# version of cryptography will *really* be installed.
|
||||
# * cryptography 43.0.0 makes __provides__ read-only; see ticket 4300
|
||||
"cryptography >= 2.6, < 43.0.0",
|
||||
"cryptography >= 2.6",
|
||||
|
||||
# * Used for custom HTTPS validation
|
||||
"pyOpenSSL >= 23.2.0",
|
||||
|
@ -10,6 +10,9 @@ objects that `cryptography` documents.
|
||||
Ported to Python 3.
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives.ciphers import (
|
||||
Cipher,
|
||||
@ -17,34 +20,34 @@ from cryptography.hazmat.primitives.ciphers import (
|
||||
modes,
|
||||
CipherContext,
|
||||
)
|
||||
from zope.interface import (
|
||||
Interface,
|
||||
directlyProvides,
|
||||
)
|
||||
|
||||
|
||||
DEFAULT_IV = b'\x00' * 16
|
||||
|
||||
|
||||
class IEncryptor(Interface):
|
||||
@dataclass
|
||||
class Encryptor:
|
||||
"""
|
||||
An object which can encrypt data.
|
||||
|
||||
Create one using :func:`create_encryptor` and use it with
|
||||
:func:`encrypt_data`
|
||||
"""
|
||||
encrypt_context: CipherContext
|
||||
|
||||
|
||||
class IDecryptor(Interface):
|
||||
@dataclass
|
||||
class Decryptor:
|
||||
"""
|
||||
An object which can decrypt data.
|
||||
|
||||
Create one using :func:`create_decryptor` and use it with
|
||||
:func:`decrypt_data`
|
||||
"""
|
||||
decrypt_context: CipherContext
|
||||
|
||||
|
||||
def create_encryptor(key, iv=None):
|
||||
def create_encryptor(key: bytes, iv: Optional[bytes]=None) -> Encryptor:
|
||||
"""
|
||||
Create and return a new object which can do AES encryptions with
|
||||
the given key and initialization vector (IV). The default IV is 16
|
||||
@ -57,33 +60,30 @@ def create_encryptor(key, iv=None):
|
||||
or None for the default (which is 16 zero bytes)
|
||||
|
||||
:returns: an object suitable for use with :func:`encrypt_data` (an
|
||||
:class:`IEncryptor`)
|
||||
:class:`Encryptor`)
|
||||
"""
|
||||
cryptor = _create_cryptor(key, iv)
|
||||
directlyProvides(cryptor, IEncryptor)
|
||||
return cryptor
|
||||
return Encryptor(cryptor)
|
||||
|
||||
|
||||
def encrypt_data(encryptor, plaintext):
|
||||
def encrypt_data(encryptor: Encryptor, plaintext: bytes) -> bytes:
|
||||
"""
|
||||
AES-encrypt `plaintext` with the given `encryptor`.
|
||||
|
||||
:param encryptor: an instance of :class:`IEncryptor` previously
|
||||
:param encryptor: an instance of :class:`Encryptor` previously
|
||||
returned from `create_encryptor`
|
||||
|
||||
:param bytes plaintext: the data to encrypt
|
||||
|
||||
:returns: bytes of ciphertext
|
||||
"""
|
||||
|
||||
_validate_cryptor(encryptor, encrypt=True)
|
||||
if not isinstance(plaintext, (bytes, memoryview)):
|
||||
raise ValueError(f'Plaintext must be bytes or memoryview: {type(plaintext)}')
|
||||
|
||||
return encryptor.update(plaintext)
|
||||
return encryptor.encrypt_context.update(plaintext)
|
||||
|
||||
|
||||
def create_decryptor(key, iv=None):
|
||||
def create_decryptor(key: bytes, iv: Optional[bytes]=None) -> Decryptor:
|
||||
"""
|
||||
Create and return a new object which can do AES decryptions with
|
||||
the given key and initialization vector (IV). The default IV is 16
|
||||
@ -96,33 +96,30 @@ def create_decryptor(key, iv=None):
|
||||
or None for the default (which is 16 zero bytes)
|
||||
|
||||
:returns: an object suitable for use with :func:`decrypt_data` (an
|
||||
:class:`IDecryptor` instance)
|
||||
:class:`Decryptor` instance)
|
||||
"""
|
||||
cryptor = _create_cryptor(key, iv)
|
||||
directlyProvides(cryptor, IDecryptor)
|
||||
return cryptor
|
||||
return Decryptor(cryptor)
|
||||
|
||||
|
||||
def decrypt_data(decryptor, plaintext):
|
||||
def decrypt_data(decryptor: Decryptor, plaintext: bytes) -> bytes:
|
||||
"""
|
||||
AES-decrypt `plaintext` with the given `decryptor`.
|
||||
|
||||
:param decryptor: an instance of :class:`IDecryptor` previously
|
||||
:param decryptor: an instance of :class:`Decryptor` previously
|
||||
returned from `create_decryptor`
|
||||
|
||||
:param bytes plaintext: the data to decrypt
|
||||
|
||||
:returns: bytes of ciphertext
|
||||
"""
|
||||
|
||||
_validate_cryptor(decryptor, encrypt=False)
|
||||
if not isinstance(plaintext, (bytes, memoryview)):
|
||||
raise ValueError(f'Plaintext must be bytes or memoryview: {type(plaintext)}')
|
||||
|
||||
return decryptor.update(plaintext)
|
||||
return decryptor.decrypt_context.update(plaintext)
|
||||
|
||||
|
||||
def _create_cryptor(key, iv):
|
||||
def _create_cryptor(key: bytes, iv: Optional[bytes]) -> CipherContext:
|
||||
"""
|
||||
Internal helper.
|
||||
|
||||
@ -135,26 +132,10 @@ def _create_cryptor(key, iv):
|
||||
modes.CTR(iv),
|
||||
backend=default_backend()
|
||||
)
|
||||
return cipher.encryptor()
|
||||
return cipher.encryptor() # type: ignore[return-type]
|
||||
|
||||
|
||||
def _validate_cryptor(cryptor, encrypt=True):
|
||||
"""
|
||||
raise ValueError if `cryptor` is not a valid object
|
||||
"""
|
||||
klass = IEncryptor if encrypt else IDecryptor
|
||||
name = "encryptor" if encrypt else "decryptor"
|
||||
if not isinstance(cryptor, CipherContext):
|
||||
raise ValueError(
|
||||
"'{}' must be a CipherContext".format(name)
|
||||
)
|
||||
if not klass.providedBy(cryptor):
|
||||
raise ValueError(
|
||||
"'{}' must be created with create_{}()".format(name, name)
|
||||
)
|
||||
|
||||
|
||||
def _validate_key(key):
|
||||
def _validate_key(key: bytes) -> bytes:
|
||||
"""
|
||||
confirm `key` is suitable for AES encryption, or raise ValueError
|
||||
"""
|
||||
@ -165,7 +146,7 @@ def _validate_key(key):
|
||||
return key
|
||||
|
||||
|
||||
def _validate_iv(iv):
|
||||
def _validate_iv(iv: Optional[bytes]) -> bytes:
|
||||
"""
|
||||
Returns a suitable initialiation vector. If `iv` is `None`, a
|
||||
default is returned. If `iv` is not a suitable initialization
|
||||
|
@ -74,7 +74,7 @@ def encrypt_privkey(writekey: bytes, privkey: bytes) -> bytes:
|
||||
crypttext = aes.encrypt_data(encryptor, privkey)
|
||||
return crypttext
|
||||
|
||||
def decrypt_privkey(writekey: bytes, enc_privkey: bytes) -> rsa.PrivateKey:
|
||||
def decrypt_privkey(writekey: bytes, enc_privkey: bytes) -> bytes:
|
||||
"""
|
||||
The inverse of ``encrypt_privkey``.
|
||||
"""
|
||||
|
@ -33,7 +33,6 @@ from zope.interface import (
|
||||
|
||||
import attr
|
||||
from attr.validators import optional
|
||||
from allmydata.util.attrs_provides import provides
|
||||
from twisted.internet import reactor
|
||||
from eliot import (
|
||||
ILogger,
|
||||
@ -74,6 +73,7 @@ from twisted.internet.defer import (
|
||||
)
|
||||
from twisted.application.service import MultiService
|
||||
|
||||
from .attrs_provides import provides
|
||||
from .jsonbytes import AnyBytesJSONEncoder
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user