1# This file is dual licensed under the terms of the Apache License, Version 
    2# 2.0, and the BSD License. See the LICENSE file in the root of this repository 
    3# for complete details. 
    4 
    5from __future__ import annotations 
    6 
    7import abc 
    8 
    9from cryptography import utils 
    10from cryptography.exceptions import UnsupportedAlgorithm, _Reasons 
    11from cryptography.hazmat.primitives._cipheralgorithm import ( 
    12    BlockCipherAlgorithm, 
    13    CipherAlgorithm, 
    14) 
    15from cryptography.hazmat.primitives.ciphers import algorithms 
    16 
    17 
    18class Mode(metaclass=abc.ABCMeta): 
    19    @property 
    20    @abc.abstractmethod 
    21    def name(self) -> str: 
    22        """ 
    23        A string naming this mode (e.g. "ECB", "CBC"). 
    24        """ 
    25 
    26    @abc.abstractmethod 
    27    def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: 
    28        """ 
    29        Checks that all the necessary invariants of this (mode, algorithm) 
    30        combination are met. 
    31        """ 
    32 
    33 
    34class ModeWithInitializationVector(Mode, metaclass=abc.ABCMeta): 
    35    @property 
    36    @abc.abstractmethod 
    37    def initialization_vector(self) -> utils.Buffer: 
    38        """ 
    39        The value of the initialization vector for this mode as bytes. 
    40        """ 
    41 
    42 
    43class ModeWithTweak(Mode, metaclass=abc.ABCMeta): 
    44    @property 
    45    @abc.abstractmethod 
    46    def tweak(self) -> utils.Buffer: 
    47        """ 
    48        The value of the tweak for this mode as bytes. 
    49        """ 
    50 
    51 
    52class ModeWithNonce(Mode, metaclass=abc.ABCMeta): 
    53    @property 
    54    @abc.abstractmethod 
    55    def nonce(self) -> utils.Buffer: 
    56        """ 
    57        The value of the nonce for this mode as bytes. 
    58        """ 
    59 
    60 
    61class ModeWithAuthenticationTag(Mode, metaclass=abc.ABCMeta): 
    62    @property 
    63    @abc.abstractmethod 
    64    def tag(self) -> bytes | None: 
    65        """ 
    66        The value of the tag supplied to the constructor of this mode. 
    67        """ 
    68 
    69 
    70def _check_aes_key_length(self: Mode, algorithm: CipherAlgorithm) -> None: 
    71    if algorithm.key_size > 256 and algorithm.name == "AES": 
    72        raise ValueError( 
    73            "Only 128, 192, and 256 bit keys are allowed for this AES mode" 
    74        ) 
    75 
    76 
    77def _check_iv_length( 
    78    self: ModeWithInitializationVector, algorithm: BlockCipherAlgorithm 
    79) -> None: 
    80    iv_len = len(self.initialization_vector) 
    81    if iv_len * 8 != algorithm.block_size: 
    82        raise ValueError(f"Invalid IV size ({iv_len}) for {self.name}.") 
    83 
    84 
    85def _check_nonce_length( 
    86    nonce: utils.Buffer, name: str, algorithm: CipherAlgorithm 
    87) -> None: 
    88    if not isinstance(algorithm, BlockCipherAlgorithm): 
    89        raise UnsupportedAlgorithm( 
    90            f"{name} requires a block cipher algorithm", 
    91            _Reasons.UNSUPPORTED_CIPHER, 
    92        ) 
    93    if len(nonce) * 8 != algorithm.block_size: 
    94        raise ValueError(f"Invalid nonce size ({len(nonce)}) for {name}.") 
    95 
    96 
    97def _check_iv_and_key_length( 
    98    self: ModeWithInitializationVector, algorithm: CipherAlgorithm 
    99) -> None: 
    100    if not isinstance(algorithm, BlockCipherAlgorithm): 
    101        raise UnsupportedAlgorithm( 
    102            f"{self} requires a block cipher algorithm", 
    103            _Reasons.UNSUPPORTED_CIPHER, 
    104        ) 
    105    _check_aes_key_length(self, algorithm) 
    106    _check_iv_length(self, algorithm) 
    107 
    108 
    109class CBC(ModeWithInitializationVector): 
    110    name = "CBC" 
    111 
    112    def __init__(self, initialization_vector: utils.Buffer): 
    113        utils._check_byteslike("initialization_vector", initialization_vector) 
    114        self._initialization_vector = initialization_vector 
    115 
    116    @property 
    117    def initialization_vector(self) -> utils.Buffer: 
    118        return self._initialization_vector 
    119 
    120    validate_for_algorithm = _check_iv_and_key_length 
    121 
    122 
    123class XTS(ModeWithTweak): 
    124    name = "XTS" 
    125 
    126    def __init__(self, tweak: utils.Buffer): 
    127        utils._check_byteslike("tweak", tweak) 
    128 
    129        if len(tweak) != 16: 
    130            raise ValueError("tweak must be 128-bits (16 bytes)") 
    131 
    132        self._tweak = tweak 
    133 
    134    @property 
    135    def tweak(self) -> utils.Buffer: 
    136        return self._tweak 
    137 
    138    def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: 
    139        if isinstance(algorithm, (algorithms.AES128, algorithms.AES256)): 
    140            raise TypeError( 
    141                "The AES128 and AES256 classes do not support XTS, please use " 
    142                "the standard AES class instead." 
    143            ) 
    144 
    145        if algorithm.key_size not in (256, 512): 
    146            raise ValueError( 
    147                "The XTS specification requires a 256-bit key for AES-128-XTS" 
    148                " and 512-bit key for AES-256-XTS" 
    149            ) 
    150 
    151 
    152class ECB(Mode): 
    153    name = "ECB" 
    154 
    155    validate_for_algorithm = _check_aes_key_length 
    156 
    157 
    158class OFB(ModeWithInitializationVector): 
    159    name = "OFB" 
    160 
    161    def __init__(self, initialization_vector: utils.Buffer): 
    162        utils._check_byteslike("initialization_vector", initialization_vector) 
    163        self._initialization_vector = initialization_vector 
    164 
    165    @property 
    166    def initialization_vector(self) -> utils.Buffer: 
    167        return self._initialization_vector 
    168 
    169    validate_for_algorithm = _check_iv_and_key_length 
    170 
    171 
    172class CFB(ModeWithInitializationVector): 
    173    name = "CFB" 
    174 
    175    def __init__(self, initialization_vector: utils.Buffer): 
    176        utils._check_byteslike("initialization_vector", initialization_vector) 
    177        self._initialization_vector = initialization_vector 
    178 
    179    @property 
    180    def initialization_vector(self) -> utils.Buffer: 
    181        return self._initialization_vector 
    182 
    183    validate_for_algorithm = _check_iv_and_key_length 
    184 
    185 
    186class CFB8(ModeWithInitializationVector): 
    187    name = "CFB8" 
    188 
    189    def __init__(self, initialization_vector: utils.Buffer): 
    190        utils._check_byteslike("initialization_vector", initialization_vector) 
    191        self._initialization_vector = initialization_vector 
    192 
    193    @property 
    194    def initialization_vector(self) -> utils.Buffer: 
    195        return self._initialization_vector 
    196 
    197    validate_for_algorithm = _check_iv_and_key_length 
    198 
    199 
    200class CTR(ModeWithNonce): 
    201    name = "CTR" 
    202 
    203    def __init__(self, nonce: utils.Buffer): 
    204        utils._check_byteslike("nonce", nonce) 
    205        self._nonce = nonce 
    206 
    207    @property 
    208    def nonce(self) -> utils.Buffer: 
    209        return self._nonce 
    210 
    211    def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: 
    212        _check_aes_key_length(self, algorithm) 
    213        _check_nonce_length(self.nonce, self.name, algorithm) 
    214 
    215 
    216class GCM(ModeWithInitializationVector, ModeWithAuthenticationTag): 
    217    name = "GCM" 
    218    _MAX_ENCRYPTED_BYTES = (2**39 - 256) // 8 
    219    _MAX_AAD_BYTES = (2**64) // 8 
    220 
    221    def __init__( 
    222        self, 
    223        initialization_vector: utils.Buffer, 
    224        tag: bytes | None = None, 
    225        min_tag_length: int = 16, 
    226    ): 
    227        # OpenSSL 3.0.0 constrains GCM IVs to [64, 1024] bits inclusive 
    228        # This is a sane limit anyway so we'll enforce it here. 
    229        utils._check_byteslike("initialization_vector", initialization_vector) 
    230        if len(initialization_vector) < 8 or len(initialization_vector) > 128: 
    231            raise ValueError( 
    232                "initialization_vector must be between 8 and 128 bytes (64 " 
    233                "and 1024 bits)." 
    234            ) 
    235        self._initialization_vector = initialization_vector 
    236        if tag is not None: 
    237            utils._check_bytes("tag", tag) 
    238            if min_tag_length < 4: 
    239                raise ValueError("min_tag_length must be >= 4") 
    240            if len(tag) < min_tag_length: 
    241                raise ValueError( 
    242                    f"Authentication tag must be {min_tag_length} bytes or " 
    243                    "longer." 
    244                ) 
    245        self._tag = tag 
    246        self._min_tag_length = min_tag_length 
    247 
    248    @property 
    249    def tag(self) -> bytes | None: 
    250        return self._tag 
    251 
    252    @property 
    253    def initialization_vector(self) -> utils.Buffer: 
    254        return self._initialization_vector 
    255 
    256    def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: 
    257        _check_aes_key_length(self, algorithm) 
    258        if not isinstance(algorithm, BlockCipherAlgorithm): 
    259            raise UnsupportedAlgorithm( 
    260                "GCM requires a block cipher algorithm", 
    261                _Reasons.UNSUPPORTED_CIPHER, 
    262            ) 
    263        block_size_bytes = algorithm.block_size // 8 
    264        if self._tag is not None and len(self._tag) > block_size_bytes: 
    265            raise ValueError( 
    266                f"Authentication tag cannot be more than {block_size_bytes} " 
    267                "bytes." 
    268            )