Beispiel #1
0
class BlockCipherMode(EncryptionAlg):
    SYMMETRY_TYPE = SymmetryType.SYMMETRIC
    KEY_SIZE = SizeSpec(size_type=SizeType.DEPENDENT,
                        selector=lambda mode: mode.cipher.KEY_SIZE)
    INPUT_SIZE = SizeSpec(size_type=SizeType.ARBITRARY)
    OUTPUT_SIZE = SizeSpec(size_type=SizeType.ARBITRARY)
    BLOCK_SIZE = SizeSpec(size_type=SizeType.DEPENDENT,
                          selector=lambda mode: mode.cipher.BLOCK_SIZE)
    IO_RELATION_TYPE = IORelationType.EQUAL
Beispiel #2
0
class SignatureAlg(NumberTheoreticalAlg):
    PRIMITIVE_TYPE = PrimitiveType.SIGNING
    KEY_SIZE = SizeSpec(size_type=SizeType.ARBITRARY, typical=[160, 224, 256])
    OUTPUT_SIZE = SizeSpec(size_type=SizeType.ARBITRARY,
                           typical=[320, 448, 512])
    CIPHER_TYPE = CipherType.NONE
    EPHEMERAL = EphemeralSpec(ephemeral_type=EphemeralType.KEY,
                              size=SizeSpec(
                                  size_type=SizeType.DEPENDENT,
                                  selector=lambda signer: signer.KEY_SIZE))
Beispiel #3
0
class StreamCipher(EncryptionAlg):
    SYMMETRY_TYPE = SymmetryType.SYMMETRIC
    CIPHER_TYPE = CipherType.STREAM_CIPHER
    KEY_SIZE = SizeSpec(size_type=SizeType.RANGE, sizes=[128, 256])
    INPUT_SIZE = SizeSpec(size_type=SizeType.ARBITRARY)
    OUTPUT_SIZE = SizeSpec(size_type=SizeType.ARBITRARY)
    BLOCK_SIZE = SizeSpec(size_type=SizeType.SINGLE, sizes=8)
    IO_RELATION_TYPE = IORelationType.EQUAL

    def encrypt(self, plaintext: bytes) -> Bytes:
        return self.generate(len(plaintext)) ^ plaintext

    def decrypt(self, ciphertext: bytes) -> Bytes:
        return self.encrypt(ciphertext)
Beispiel #4
0
class SHA3_224(SHA3):

    OUTPUT_SIZE = SizeSpec(size_type=SizeType.SINGLE, sizes=224)

    def __init__(self):
        super().__init__(r=1152, c=448, bits=224, padding=0x06)
        Primitive.__init__(self)
Beispiel #5
0
class SHA1(MerkleDamgardConstruction):
    """
    Cryptographic hash function considered to be broken but is still widely used.
    """

    OUTPUT_SIZE = SizeSpec(size_type=SizeType.SINGLE, sizes=160)
    USAGE_FREQUENCY = FrequencyType.PROLIFIC

    def __init__(self,
                 initial_state: bytes = state_to_bytes([
                     0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xC3D2E1F0
                 ])):
        """
        Parameters:
            initial_state (bytes): (Optional) Initial internal state.
        """
        if type(initial_state) is list:
            initial_state = state_to_bytes(initial_state)

        super().__init__(
            initial_state=initial_state,
            compression_func=compression_func,
            digest_size=20,
        )

        Primitive.__init__(self)

    def __repr__(self):
        return f"<SHA1: initial_state={self.initial_state}, block_size={self.block_size}>"

    def __str__(self):
        return self.__repr__()
Beispiel #6
0
class MD5(MerkleDamgardConstruction):
    """
    Popular but completely broken cryptographic hash function.
    """

    OUTPUT_SIZE     = SizeSpec(size_type=SizeType.SINGLE, sizes=128)
    USAGE_FREQUENCY = FrequencyType.PROLIFIC

    def __init__(self, initial_state: bytes=state_to_bytes([0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476])):
        """
        Parameters:
            initial_state (bytes): (Optional) Initial internal state.
        """
        super().__init__(
            initial_state=initial_state,
            compression_func=compression_func,
            digest_size=16,
            endianness='little'
        )

        Primitive.__init__(self)


    def __repr__(self):
        return f"<MD5: initial_state={self.initial_state}, block_size={self.block_size}>"

    def __str__(self):
        return self.__repr__()
Beispiel #7
0
class DH25519(KeyExchangeAlg):
    """
    Elliptical curve Diffie-Hellman using Montgomery curves.
    """

    KEY_SIZE = SizeSpec(size_type=SizeType.ARBITRARY, typical=[255, 448])
    USAGE_FREQUENCY = FrequencyType.OFTEN

    def __init__(self,
                 d: int = None,
                 pub: int = None,
                 base: int = None,
                 curve: MontgomeryCurve = Curve25519):
        """
        Parameters:
            d                 (int): Secret key that will be clamped to the curve.
            base              (int): Base multiplier used in generating the challenge.
            curve (MontgomeryCurve): The curve used.
        """
        Primitive.__init__(self)
        self.d = Bytes.wrap(d or random_int_between(1, curve.ring.order)).int()
        self.curve = curve
        self.key = curve.clamp_to_curve(self.d)
        self.base = base or curve.U

        self.pub = pub

        if not pub:
            self.recompute_public()

    def __repr__(self):
        return f"<DH25519: d={self.d}, key={self.key}, pub={self.pub}, base={self.base}>"

    def __str__(self):
        return self.__repr__()

    def recompute_public(self) -> int:
        """
        Gets the challenge.

        Returns:
            int: The integer challenge representing an `x` value on the curve.
        """
        self.pub = self.key * self.base

    def get_pub_bytes(self) -> Bytes:
        return Bytes(self.pub, 'little')

    def derive_key(self, challenge: int) -> Bytes:
        """
        Derives the shared key from the other instance's challenge.

        Parameters:
            challenge (int): The other instance's challenge.
        
        Returns:
            int: Shared key.
        """
        return Bytes(self.key * challenge).zfill(
            (self.curve.p.bit_length() + 7) // 8)
Beispiel #8
0
class SHA3_512(SHA3):

    OUTPUT_SIZE = SizeSpec(size_type=SizeType.SINGLE, sizes=512)

    def __init__(self):
        super().__init__(r=576, c=1024, bits=512, padding=0x06)
        Primitive.__init__(self)
Beispiel #9
0
class SHA3_256(SHA3):

    OUTPUT_SIZE = SizeSpec(size_type=SizeType.SINGLE, sizes=256)

    def __init__(self):
        super().__init__(r=1088, c=512, bits=256, padding=0x06)
        Primitive.__init__(self)
Beispiel #10
0
class SHA3_384(SHA3):

    OUTPUT_SIZE = SizeSpec(size_type=SizeType.SINGLE, sizes=384)

    def __init__(self):
        super().__init__(r=832, c=768, bits=384, padding=0x06)
        Primitive.__init__(self)
Beispiel #11
0
class RIPEMD160(MerkleDamgardConstruction):
    """
    Stands for RACE Integrity Primitives Evaluation Message Digest (RIPEMD). While there exist other
    versions of RIPEMD (128, 256, and 320), 160 is the most popular.
    """

    OUTPUT_SIZE = SizeSpec(size_type=SizeType.SINGLE, sizes=160)

    def __init__(self, initial_state: bytes = INIT_STATE):
        """
        Parameters:
            initial_state (bytes): (Optional) Initial internal state.
        """
        super().__init__(initial_state=initial_state,
                         compression_func=COMPRESS,
                         digest_size=20,
                         endianness='little')

        Primitive.__init__(self)

    def __repr__(self):
        return f"<RIPEMD160: initial_state={self.initial_state}, block_size={self.block_size}>"

    def __str__(self):
        return self.__repr__()
Beispiel #12
0
class MAC(Primitive):
    PRIMITIVE_TYPE = PrimitiveType.MAC
    KEY_SIZE = SizeSpec(size_type=SizeType.DEPENDENT,
                        selector=lambda cipher: cipher.KEY_SIZE)
    SYMMETRY_TYPE = SymmetryType.SYMMETRIC
    INPUT_SIZE = SizeSpec(size_type=SizeType.ARBITRARY)
    BLOCK_SIZE = SizeSpec(size_type=SizeType.DEPENDENT,
                          selector=lambda cipher: cipher.BLOCK_SIZE)
    OUTPUT_SIZE = SizeSpec(size_type=SizeType.DEPENDENT,
                           selector=lambda cipher: cipher.OUTPUT_SIZE)

    @abstractmethod
    def generate(self, *args, **kwargs):
        pass

    def verify(self, message: bytes, signature: bytes) -> bool:
        return self.generate(message) == signature
Beispiel #13
0
class BlockCipher(EncryptionAlg):
    SYMMETRY_TYPE = SymmetryType.SYMMETRIC
    CIPHER_TYPE = CipherType.BLOCK_CIPHER
    CONSTRUCTION_TYPES = [ConstructionType.FEISTEL_NETWORK]
    KEY_SIZE = SizeSpec(size_type=SizeType.RANGE,
                        sizes=[128, 192, 256],
                        typical=[128, 256])
    BLOCK_SIZE = SizeSpec(size_type=SizeType.SINGLE, sizes=128)
    IO_RELATION_TYPE = IORelationType.EQUAL

    @classproperty
    def INPUT_SIZE(cls):
        return cls.BLOCK_SIZE

    @classproperty
    def OUTPUT_SIZE(cls):
        return cls.BLOCK_SIZE
Beispiel #14
0
class OFB(StreamingBlockCipherMode):
    """Output feedback block cipher mode."""

    EPHEMERAL = EphemeralSpec(
        ephemeral_type=EphemeralType.NONCE,
        size=SizeSpec(
            size_type=SizeType.DEPENDENT,
            selector=lambda block_mode: block_mode.cipher.BLOCK_SIZE))

    def __init__(self, cipher: EncryptionAlg, iv: bytes):
        """
        Parameters:
            cipher (EncryptionAlg): Instantiated encryption algorithm.
            iv             (bytes): Bytes-like initialization vector.
        """
        Primitive.__init__(self)
        self.cipher = cipher
        self.iv = iv
        self.cbc = CBC(cipher, iv)

    def __repr__(self):
        return f"<OFB: cipher={self.cipher}, iv={self.iv}>"

    def __str__(self):
        return self.__repr__()

    def encrypt(self, plaintext: bytes) -> Bytes:
        """
        Encrypts `plaintext`.

        Parameters:
            plaintext (bytes): Bytes-like object to be encrypted.
        
        Returns:
            Bytes: Resulting ciphertext.
        """
        plaintext = Bytes.wrap(plaintext)

        num_blocks = ceil(len(plaintext) / self.cipher.block_size)
        keystream = self.cbc.encrypt(
            b'\x00' * self.cipher.block_size * num_blocks, False)

        return keystream[:len(plaintext)] ^ plaintext

    def decrypt(self, ciphertext: bytes) -> Bytes:
        """
        Decrypts `ciphertext`.

        Parameters:
            ciphertext (bytes): Bytes-like object to be decrypted.
        
        Returns:
            Bytes: Resulting plaintext.
        """
        return self.encrypt(ciphertext)
Beispiel #15
0
class SHAKE256(SHA3):

    OUTPUT_SIZE = SizeSpec(size_type=SizeType.ARBITRARY, typical=[256])

    def __init__(self, digest_bit_length: int):
        """
        Parameters:
            digest_bit_length (int): Desired digest length in bits.
        """
        super().__init__(r=1088, c=512, bits=digest_bit_length, padding=0x1F)
        Primitive.__init__(self)
Beispiel #16
0
class Hash(Primitive):
    PRIMITIVE_TYPE = PrimitiveType.HASH
    INPUT_SIZE = SizeSpec(size_type=SizeType.ARBITRARY)
    CONSTRUCTION_TYPES = [ConstructionType.MERKLE_DAMGARD]

    @classproperty
    def BLOCK_SIZE(cls):
        return cls.OUTPUT_SIZE

    @abstractmethod
    def hash(self, *args, **kwargs):
        pass
Beispiel #17
0
class BLAKE2s(BLAKE2):
    BLOCK_SIZE = SizeSpec(size_type=SizeType.SINGLE, sizes=64)
    OUTPUT_SIZE = SizeSpec(size_type=SizeType.ARBITRARY, typical=[256])

    WORD_SIZE = 32
    MASKBITS = 0xFFFFFFFF
    ROUNDS = 10
    IMPL_BLOCK_SIZE = 64
    IV = H_256
    ROTATIONS = [16, 12, 8, 7]

    def __init__(self, desired_hash_len=32, key=b''):
        """
        Parameters:
            key            (bytes): (Optional) Bytes-like object to key the hash.
            desired_hash_len (int): Desired output length.
        """
        super().__init__(key, desired_hash_len)

    def __repr__(self):
        return f"<BLAKE2s: iv={self.IV}, digest_size={self.digest_size}, key={self.key}>"

    def __str__(self):
        return self.__repr__()
Beispiel #18
0
class SHA384(SHA2):

    OUTPUT_SIZE = SizeSpec(size_type=SizeType.SINGLE, sizes=384)

    def __init__(self, h: list=None):
        """
        Parameters:
            h (list): Initial state as list of integers.
        """
        super().__init__(
            initial_state=h or H_384,
            digest_size=384 // 8,
            state_size=8,
            block_size=128,
            rounds=80,
            rot=ROT_512,
            k=K_512
        )
Beispiel #19
0
class KeyExchangeAlg(Primitive):
    PRIMITIVE_TYPE = PrimitiveType.KEY_EXCHANGE
    SYMMETRY_TYPE = SymmetryType.ASYMMETRIC
    SECURITY_PROOF = SecurityProofType.DISCRETE_LOGARITHM
    KEY_SIZE = SizeSpec(size_type=SizeType.ARBITRARY,
                        typical=[1024, 2048, 4096])

    @classproperty
    def INPUT_SIZE(cls):
        return cls.KEY_SIZE

    @classproperty
    def OUTPUT_SIZE(cls):
        return cls.KEY_SIZE

    @classproperty
    def BLOCK_SIZE(cls):
        return cls.KEY_SIZE
Beispiel #20
0
class SHA256(SHA2):

    OUTPUT_SIZE = SizeSpec(size_type=SizeType.SINGLE, sizes=256)

    def __init__(self, h: list=None):
        """
        Parameters:
            h (list): Initial state as list of integers.
        """
        super().__init__(
            initial_state=h or H_256,
            digest_size=256 // 8,
            state_size=4,
            block_size=64,
            rounds=64,
            rot=ROT_256,
            k=K_256
        )
Beispiel #21
0
class NumberTheoreticalAlg(Primitive):
    SYMMETRY_TYPE = SymmetryType.ASYMMETRIC
    PRIMITIVE_TYPE = PrimitiveType.CIPHER
    CIPHER_TYPE = CipherType.NUMBER_THEORETICAL_CIPHER
    KEY_SIZE = SizeSpec(size_type=SizeType.ARBITRARY,
                        typical=[1024, 2048, 4096])
    SECURITY_PROOF = SecurityProofType.DISCRETE_LOGARITHM

    @classproperty
    def INPUT_SIZE(cls):
        return cls.KEY_SIZE

    @classproperty
    def OUTPUT_SIZE(cls):
        return cls.KEY_SIZE

    @classproperty
    def BLOCK_SIZE(cls):
        return cls.OUTPUT_SIZE
Beispiel #22
0
class SHA512(SHA2):

    OUTPUT_SIZE = SizeSpec(size_type=SizeType.RANGE, sizes=range(513), typical=[512])

    def __init__(self, h: list=None, trunc: int=None):
        """
        Parameters:
            h    (list): Initial state as list of integers.
            trunc (int): Truncation length for SHA-512/t.
        """
        # FIPS 180-4
        if trunc:
            h = h or H_512
            h_doubleprime = [h ^ 0xa5a5a5a5a5a5a5a5 for h in H_512]
            h = [chunk.int() for chunk in SHA512(h=h_doubleprime).hash(f'SHA-512/{trunc}'.encode('utf-8')).chunk(8)]

        super().__init__(
            initial_state=h or H_512,
            digest_size=512 // 8,
            state_size=8,
            block_size=128,
            rounds=80,
            rot=ROT_512,
            k=K_512
        )

        self.trunc = trunc or 0


    def hash(self, message: bytes) -> Bytes:
        """
        Yields the final, hashed state of the `message`.

        Parameters:
            message (bytes): Message to be hashed.
        
        Returns:
            Bytes: Fully-hashed state.
        """
        final_state = super().hash(message)
        return final_state[:math.ceil((self.trunc or 512) / 8)]
Beispiel #23
0
class MD4(MerkleDamgardConstruction):
    """
    Obsolete cryptographic hash function and predecessor to MD5.
    """

    OUTPUT_SIZE = SizeSpec(size_type=SizeType.SINGLE, sizes=128)

    def __init__(self, initial_state: bytes = state_to_bytes(iv)):
        """
        Parameters:
            initial_state (bytes): (Optional) Initial internal state.
        """
        super().__init__(initial_state=initial_state,
                         compression_func=compression_func,
                         digest_size=16,
                         endianness='little')

        Primitive.__init__(self)

    def __repr__(self):
        return f"<MD4: initial_state={self.initial_state}, block_size={self.block_size}>"

    def __str__(self):
        return self.__repr__()
Beispiel #24
0
class DSA(EncodablePKI, SignatureAlg):
    """
    Digital Signature Algorithm
    """

    PRIV_ENCODINGS = {
        PKIEncoding.OpenSSH: OpenSSHDSAPrivateKey,
        PKIEncoding.PKCS1: PKCS1DSAPrivateKey,
        PKIEncoding.PKCS8: PKCS8DSAPrivateKey
    }


    PUB_ENCODINGS = {
        PKIEncoding.OpenSSH: OpenSSHDSAPublicKey,
        PKIEncoding.SSH2: SSH2DSAPublicKey,
        PKIEncoding.X509_CERT: X509DSACertificate,
        PKIEncoding.X509: X509DSAPublicKey
    }

    X509_SIGNING_ALGORITHMS = X509DSASigningAlgorithms
    X509_SIGNING_DEFAULT    = X509DSASigningAlgorithms.id_dsa_with_sha256

    EPHEMERAL       = EphemeralSpec(ephemeral_type=EphemeralType.KEY, size=SizeSpec(size_type=SizeType.DEPENDENT, selector=lambda dsa: dsa.q.bit_length()))
    USAGE_FREQUENCY = FrequencyType.OFTEN

    def __init__(self, hash_obj: object=SHA256(), p: int=None, q: int=None, g: int=None, x: int=None, L: int=2048, N: int=256):
        """
        Parameters:
            hash_obj (object): Instantiated object with compatible hash interface.
            p           (int): (Optional) Prime modulus.
            q           (int): (Optional) Prime modulus.
            g           (int): (Optional) Generator.
            x           (int): (Optional) Private key.
            L           (int): (Optional) Bit length of `p`.
            N           (int): (Optional) Bit length of `q`.
        """
        Primitive.__init__(self)
        # Parameter generation
        # https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
        if not q:
            q = find_prime(N)

            # Start somewhere in 2**(L-1); ensure it's even
            i = Bytes.random((L-1) // 8).int() // 2 * 2

            # Construct the base as an even multiple of `q`
            base = 2**(L-1) // (2*q) * 2
            while not is_prime((base + i) * q + 1):
                i += 2

            p = (base + i) * q + 1
            assert (p-1) % q == 0

            # Construct `g`
            while True:
                h = Bytes.random(N // 8).int() % (p-1)
                g = pow(h, (p-1) // q, p)

                if h > 1 and h < (p-1) and g > 1:
                    break

        self.p = p
        self.q = q
        self.g = g

        self.x = x or random_int_between(1, self.q)
        self.y = pow(self.g, self.x, self.p)
        self.hash_obj = hash_obj



    def __repr__(self):
        return f"<DSA: hash_obj={self.hash_obj}, p={self.p}, q={self.q}, g={self.g}, x={self.x}, y={self.y}>"

    def __str__(self):
        return self.__repr__()



    def sign(self, message: bytes, k: int=None) -> (int, int):
        """
        Signs a `message`.

        Parameters:
            message (bytes): Message to sign.
            k         (int): (Optional) Ephemeral key.
        
        Returns:
            (int, int): Signature formatted as (r, s).
        """
        k = k or random_int_between(1, self.q)
        inv_k = mod_inv(k, self.q)
        r = pow(self.g, k, self.p) % self.q
        s = (inv_k * (self.hash_obj.hash(message).int() + self.x * r)) % self.q
        return (r, s)



    def verify(self, message: bytes, sig: (int, int)) -> bool:
        """
        Verifies a `message` against a `sig`.

        Parameters:
            message  (bytes): Message.
            sig ((int, int)): Signature of `message`.
        
        Returns:
            bool: Whether the signature is valid or not.
        """
        (r, s) = sig
        w = mod_inv(s, self.q)
        u_1 = (self.hash_obj.hash(message).int() * w) % self.q
        u_2 = (r * w) % self.q
        v = (pow(self.g, u_1, self.p) * pow(self.y, u_2, self.p) % self.p) % self.q
        return v == r



    # Confirmed works on ECDSA as well
    def derive_k_from_sigs(self, msg_a: bytes, sig_a: (int, int), msg_b: bytes, sig_b: (int, int)) -> int:
        """
        Derives `k` from signatures that share an `r` value.

        Parameters:
            msg_a      (bytes): Message A.
            msg_b      (bytes): Message B.
            sig_a ((int, int)): Signature of `msg_a`.
            sig_b ((int, int)): Signature of `msg_b`.

        Returns:
            int: Derived `k`.
        """
        (r_a, s_a) = sig_a
        (r_b, s_b) = sig_b
        assert r_a == r_b

        s = (s_a - s_b) % self.q
        m = (self.hash_obj.hash(msg_a).int() - self.hash_obj.hash(msg_b).int()) % self.q
        return mod_inv(s, self.q) * m % self.q



    # Confirmed works on ECDSA as well
    def derive_x_from_k(self, message: bytes, k: int, sig: (int, int)) -> int:
        """
        Derives `x` from a known `k`.

        Parameters:
            message  (bytes): Message.
            k          (int): `k` used in `message`'s signature.
            sig ((int, int)): Signature of `message`.
        
        Returns:
            int: Derived `x`.
        """
        (r, s) = sig
        return ((s * k) - self.hash_obj.hash(message).int()) * mod_inv(r, self.q) % self.q
Beispiel #25
0
class DES(FeistelNetwork, BlockCipher):
    """
    Structure: Feistel Network
    Key size: 64 (56, actually)
    Block size: 64
    """

    KEY_SIZE = SizeSpec(size_type=SizeType.SINGLE, sizes=64)
    BLOCK_SIZE = SizeSpec(size_type=SizeType.SINGLE, sizes=64)
    USAGE_FREQUENCY = FrequencyType.UNUSUAL

    def __init__(self, key: bytes):
        """
        Parameters:
            key (bytes): Bytes-like object to key the cipher.
        """
        super().__init__(round_func, key_schedule)
        PrimitiveType.__init__(self)

        self.key = Bytes.wrap(key).zfill(8)
        self.block_size = 8

    def __repr__(self):
        return f"<DES: key={self.key}>"

    def __str__(self):
        return self.__repr__()

    def process_plaintext(self, plaintext):
        plaintext_bitstring = bytes_to_bitstring(plaintext)
        return int.to_bytes(
            int(''.join([plaintext_bitstring[IP[i] - 1] for i in range(64)]),
                2), 8, 'big')

    def process_ciphertext(self, ciphertext):
        ciphertext_bitstring = bytes_to_bitstring(ciphertext)
        return int.to_bytes(
            int(''.join([ciphertext_bitstring[FP[i] - 1] for i in range(64)]),
                2), 8, 'big')

    def encrypt(self, plaintext: bytes) -> Bytes:
        """
        Encrypts `plaintext`.

        Parameters:
            plaintext (bytes): Bytes-like object to be encrypted.
        
        Returns:
            Bytes: Resulting ciphertext.
        """
        permuted_plaintext = self.process_plaintext(plaintext)
        result = FeistelNetwork.encrypt(self, self.key, permuted_plaintext)
        return Bytes(self.process_ciphertext(result))

    def decrypt(self, ciphertext: bytes) -> Bytes:
        """
        Decrypts `ciphertext`.

        Parameters:
            ciphertext (bytes): Bytes-like object to be decrypted.
        
        Returns:
            Bytes: Resulting plaintext.
        """
        permuted_ciphertext = self.process_plaintext(ciphertext)
        result = FeistelNetwork.decrypt(self, self.key, permuted_ciphertext)
        return Bytes(self.process_ciphertext(result))
Beispiel #26
0
class RC5(BlockCipher):
    """
    Structure: Feistel Network
    Key size: 0-2040 bits
    Block size: 32, 64, 128 bits
    """

    KEY_SIZE = SizeSpec(size_type=SizeType.RANGE, sizes=range(0, 2041))
    BLOCK_SIZE = SizeSpec(size_type=SizeType.RANGE, sizes=[32, 64, 128])

    def __init__(self,
                 key: bytes,
                 num_rounds: int = 12,
                 block_size: int = 128):
        """
        Parameters:
            key      (bytes): Bytes-like object to key the cipher.
            num_rounds (int): Number of rounds to perform.
            block_size (int): The desired block size in bits.
        """
        Primitive.__init__(self)

        if not block_size in [32, 64, 128]:
            raise Exception("Invalid block size: must be 32, 64, or 128 bits")

        self.key = Bytes.wrap(key)
        self.num_rounds = num_rounds
        self.block_size = block_size // 2
        self.mod = 2**self.block_size
        self.S = self._key_expansion()

    def __repr__(self):
        return f"<RC5: key={self.key}, num_rounds={self.num_rounds}, block_size={self.block_size}, S={self.S}>"

    def __str__(self):
        return self.__repr__()

    def _key_expansion(self):
        b = len(self.key)
        u = self.block_size // 8
        t = 2 * (self.num_rounds + 1)

        if b == 0:
            c = 1
        elif b % u:
            self.key = self.key.zfill(u - b % u + b)
            b = len(self.key)
            c = b // u
        else:
            c = b // u

        const_idx = int(math.log(self.block_size, 2) - 4)

        if b == 0:
            L = [0]
        else:
            L = self.key.chunk(b // c)

        for i in range(b - 1, -1, -1):
            L[i // u] = Bytes.wrap(L[i // u] << 8).int() + self.key[i]

        S = [(P_w[const_idx] + (Q_w[const_idx] * i)) % self.mod
             for i in range(t)]

        i = j = 0
        A = B = 0

        for _ in range(3 * max(t, c)):
            A = S[i] = left_rotate((S[i] + A + B), 3, bits=self.block_size)
            B = L[j] = left_rotate((L[j] + A + B), (A + B) % self.block_size,
                                   bits=self.block_size)
            i = (i + 1) % t
            j = (j + 1) % c

        return S

    def encrypt(self, plaintext: bytes) -> Bytes:
        """
        Encrypts `plaintext`.

        Parameters:
            plaintext (bytes): Bytes-like object to be encrypted.
        
        Returns:
            Bytes: Resulting ciphertext.
        """
        plaintext = Bytes.wrap(plaintext).zfill(self.block_size // 4)

        A = plaintext[self.block_size // 8:].int()
        B = plaintext[:self.block_size // 8].int()

        A = (A + self.S[0]) % self.mod
        B = (B + self.S[1]) % self.mod

        for i in range(1, self.num_rounds + 1):
            A = (left_rotate(A ^ B, B % self.block_size, bits=self.block_size)
                 + self.S[2 * i]) % self.mod
            B = (left_rotate(B ^ A, A % self.block_size, bits=self.block_size)
                 + self.S[2 * i + 1]) % self.mod

        return (Bytes(A, 'little').zfill(self.block_size // 8) +
                Bytes(B, 'little').zfill(self.block_size // 8))

    def decrypt(self, ciphertext: bytes) -> Bytes:
        """
        Decrypts `ciphertext`.

        Parameters:
            ciphertext (bytes): Bytes-like object to be decrypted.
        
        Returns:
            Bytes: Resulting plaintext.
        """
        ciphertext = Bytes.wrap(ciphertext).zfill(self.block_size // 4)

        A = ciphertext[:self.block_size // 8].int()
        B = ciphertext[self.block_size // 8:].int()

        for i in range(self.num_rounds, 0, -1):
            B = right_rotate((B - self.S[2 * i + 1]) % self.mod,
                             A % self.block_size,
                             bits=self.block_size) ^ A
            A = right_rotate((A - self.S[2 * i]) % self.mod,
                             B % self.block_size,
                             bits=self.block_size) ^ B

        A = (A - self.S[0]) % self.mod
        B = (B - self.S[1]) % self.mod

        return Bytes(
            (Bytes(A, 'little').zfill(self.block_size // 8) +
             Bytes(B, 'little').zfill(self.block_size // 8)).int()).zfill(
                 self.block_size // 4)
Beispiel #27
0
class CFB(StreamingBlockCipherMode):
    """Cipher feedback block cipher mode."""

    EPHEMERAL = EphemeralSpec(
        ephemeral_type=EphemeralType.NONCE,
        size=SizeSpec(size_type=SizeType.DEPENDENT,
                      selector=lambda block_mode: block_mode.cipher.BLOCK_SIZE,
                      typical=[128]))

    def __init__(self, cipher: EncryptionAlg, iv: bytes):
        """
        Parameters:
            cipher (EncryptionAlg): Instantiated encryption algorithm.
            iv             (bytes): Bytes-like initialization vector.
        """
        Primitive.__init__(self)
        self.cipher = cipher
        self.iv = iv

    def __repr__(self):
        return f"<CFB: cipher={self.cipher}, iv={self.iv}>"

    def __str__(self):
        return self.__repr__()

    def encrypt(self, plaintext: bytes) -> Bytes:
        """
        Encrypts `plaintext`.

        Parameters:
            plaintext (bytes): Bytes-like object to be encrypted.
        
        Returns:
            Bytes: Resulting ciphertext.
        """
        ciphertext = b''
        plaintext = Bytes.wrap(plaintext)

        last_block = self.iv

        for block in get_blocks(plaintext,
                                self.cipher.block_size,
                                allow_partials=True):
            enc_block = self.cipher.encrypt(
                bytes(last_block))[:len(block)] ^ block
            ciphertext += enc_block
            last_block = enc_block

        return ciphertext

    def decrypt(self, ciphertext: bytes) -> Bytes:
        """
        Decrypts `ciphertext`.

        Parameters:
            ciphertext (bytes): Bytes-like object to be decrypted.
        
        Returns:
            Bytes: Resulting plaintext.
        """
        plaintext = b''
        ciphertext = Bytes.wrap(ciphertext)

        last_block = self.iv

        for block in get_blocks(ciphertext,
                                self.cipher.block_size,
                                allow_partials=True):
            enc_block = self.cipher.encrypt(
                bytes(last_block))[:len(block)] ^ block
            plaintext += enc_block
            last_block = block

        return plaintext
Beispiel #28
0
class SNOW3G(StreamCipher):
    """
    SNOW3G stream cipher

    Used in 4G LTE encryption.
    """

    CONSTRUCTION_TYPES = [ConstructionType.LFSR]
    USAGE_TYPE         = UsageType.CELLULAR
    USAGE_FREQUENCY    = FrequencyType.OFTEN
    EPHEMERAL          = EphemeralSpec(ephemeral_type=EphemeralType.NONCE, size=SizeSpec(size_type=SizeType.SINGLE, sizes=128))

    def __init__(self, key: bytes, iv: bytes):
        """
        Parameters:
            key (bytes): Key (128 or 256 bits).
            iv  (bytes): Initialization vector (16 bytes).
        """
        Primitive.__init__(self)

        self.key = Bytes.wrap(key)
        self.iv  = Bytes.wrap(iv)

        k0, k1, k2, k3 = [chunk.to_int() for chunk in self.key.chunk(4)]
        iv0, iv1, iv2, iv3 = [chunk.to_int() for chunk in self.iv.chunk(4)]

        s = [None] * 16
        s[15] = k3 ^ iv0
        s[14] = k2
        s[13] = k1
        s[12] = k0 ^ iv1
        s[11] = k3 ^ 0xFFFFFFFF
        s[10] = k2 ^ 0xFFFFFFFF ^ iv2
        s[9] = k1 ^ 0xFFFFFFFF ^ iv3
        s[8] = k0 ^ 0xFFFFFFFF
        s[7] = k3
        s[6] = k2
        s[5] = k1
        s[4] = k0
        s[3] = k3 ^ 0xFFFFFFFF
        s[2] = k2 ^ 0xFFFFFFFF
        s[1] = k1 ^ 0xFFFFFFFF
        s[0] = k0 ^ 0xFFFFFFFF

        self.s = s

        self.R1 = 0
        self.R2 = 0
        self.R3 = 0

        for _ in range(32):
            F = self.clock_FSM()
            self.clock_lfsr(F)



    def __repr__(self):
        return f"<SNOW3G: key={self.key}, iv={self.iv}, s={self.s}, R1={self.self.R1}, R2={self.self.R2}, R3={self.self.R3}>"

    def __str__(self):
        return self.__repr__()



    def MULx(self, V: int, c: int) -> int:
        if V >> 7:
            return ((V << 1) % 256) ^ c
        else:
            return V << 1


    def MULa(self, c: int) -> int:
        return (self.MULxPOW(c, 23, 0xA9) << 24) + (self.MULxPOW(c, 245, 0xA9) << 16) + (self.MULxPOW(c, 48, 0xA9) << 8) + self.MULxPOW(c, 239, 0xA9)


    def DIVa(self, c: int) -> int:
        return (self.MULxPOW(c, 16, 0xA9) << 24) + (self.MULxPOW(c, 39, 0xA9) << 16) + (self.MULxPOW(c, 6, 0xA9) << 8) + self.MULxPOW(c, 64, 0xA9)


    def MULxPOW(self, V: int, i: int, c: int) -> int:
        if i == 0:
            return V
        else:
            return self.MULx(self.MULxPOW(V, i - 1, c), c)



    def S1(self, w: int) -> Bytes:
        return self._perform_sbox_transform(Bytes.wrap(w).zfill(4), RIJ_SBOX, 0x1B, True)


    def S2(self, w: int) -> Bytes:
        return self._perform_sbox_transform(Bytes.wrap(w).zfill(4), SQ, 0x69)


    def _perform_sbox_transform(self, w: bytes, sbox: list, val: int, s2=False) -> Bytes:
        sqw0, sqw1, sqw2, sqw3 = [sbox[w_i] for w_i in w]

        r0 = self.MULx(sqw0, val) ^ sqw1 ^ sqw2 ^ self.MULx(sqw3, val) ^ sqw3
        r1 = self.MULx(sqw0, val) ^ sqw0 ^ self.MULx(sqw1, val)  ^ sqw2 ^ sqw3
        r2 = sqw0 ^ self.MULx(sqw1, val) ^ sqw1  ^ self.MULx(sqw2, val) ^ sqw3
        r3 = sqw0 ^ sqw1 ^ self.MULx(sqw2, val) ^ sqw2 ^ self.MULx(sqw3, val)

        return Bytes([r0, r1, r2, r3]).to_int()


    def clock_FSM(self) -> int:
        """
        Used internally. Clocks the internal FSM.

        Returns:
            int: Next value of `F`.
        """
        F = ((self.s[15] + self.R1) % (2 ** 32)) ^ self.R2
        r = (self.R2 + (self.R3 ^ self.s[5])) % (2 ** 32)
        self.R3 = self.S2(self.R2)
        self.R2 = self.S1(self.R1)
        self.R1 = r

        return F


    def clock_lfsr(self, F: int=None):
        """
        Used internally. Clocks the LFSR and possibly combines with `F`.

        Parameters:
            F (int): Value of `F` to be clocked with.
        """
        v = ((self.s[0] << 8) & 0xFFFFFF00) ^ self.MULa((self.s[0] >> 24) & 0xFF) ^ self.s[2] ^ ((self.s[11] >> 8) & 0x00FFFFFF) ^ self.DIVa(self.s[11] & 0xFF)
        if F:
            v^= F

        for i in range(15):
            self.s[i] = self.s[i + 1]

        self.s[15] = v


    def generate(self, length: int) -> Bytes:
        """
        Generates `length` of keystream.

        Parameters:
            length (int): Desired length of keystream in bytes.
        
        Returns:
            Bytes: Keystream.
        """
        _F = self.clock_FSM()
        self.clock_lfsr()

        ks = []

        for _ in range(length):
            F = self.clock_FSM()
            ks.append(F ^ self.s[0])
            self.clock_lfsr()

        return sum([Bytes(i).zfill(4) for i in ks])
Beispiel #29
0
class Blowfish(BlockCipher):
    """
    Structure: Feistel Network
    Key size: 32-448 bits
    Block size: 64 bits
    """

    KEY_SIZE   = SizeSpec(size_type=SizeType.RANGE, sizes=range(32, 449))
    BLOCK_SIZE = SizeSpec(size_type=SizeType.SINGLE, sizes=64)

    def __init__(self, key: bytes, run_key_schedule: bool=True):
        """
        Parameters:
            key             (bytes): Bytes-like object to key the cipher.
            run_key_schedule (bool): Whether or not to run the key schedule. Useful when extending Blowfish (i.e. bcrypt).
        """
        Primitive.__init__(self)

        self.key = Bytes.wrap(key)
        self.P = deepcopy(P)
        self.S = [deepcopy(S1), deepcopy(S2), deepcopy(S3), deepcopy(S4)]
        self.block_size = 8

        if run_key_schedule:
            self.key_schedule()


    def __repr__(self):
        return f"<Blowfish: key={self.key}, P={self.P}>"

    def __str__(self):
        return self.__repr__()


    def key_schedule(self):
        self.P = [(subkey ^ P[i]).to_int() for i, subkey in enumerate(self.key.stretch(len(P) * 4).chunk(4))]
        L = R = 0

        for i in range(0, 18, 2):
            R, L = self.enc_L_R(L, R)
            self.P[i] = L
            self.P[i + 1] = R


        for i in range(4):
            for j in range(0, 256, 2):
                R, L = self.enc_L_R(L, R)
                self.S[i][j] = L
                self.S[i][j +1] = R


    def enc_L_R(self, L: int, R: int) -> (int, int):
        """
        Internal function. Used to encrypt integers directly.

        Parameters:
            L (int): Left side integer.
            R (int): Right side integer.
        
        Returns:
            (int, int): The encrypted versions of `L` and `R`.
        """
        L_i, R_i = L, R

        for i in range(16):
            L_i ^= self.P[i]
            L_i &= 0xFFFFFFFF

            R_i ^= round_func(
                    self.S[0][(L_i >> 24) & 0xFF],
                    self.S[1][(L_i >> 16) & 0xFF],
                    self.S[2][(L_i >> 8) & 0xFF],
                    self.S[3][L_i & 0xFF]
                ) & 0xFFFFFFFF

            R_i, L_i = L_i, R_i

        L_i ^= self.P[16]
        R_i ^= self.P[17]

        R_i, L_i = L_i, R_i

        return R_i, L_i


    def encrypt(self, plaintext: bytes) -> Bytes:
        """
        Encrypts `plaintext`.

        Parameters:
            plaintext (bytes): Bytes-like object to be encrypted.
        
        Returns:
            Bytes: Resulting ciphertext.
        """
        plaintext = Bytes.wrap(plaintext)

        half = len(plaintext) // 2
        L_i, R_i = plaintext[:half].to_int(), plaintext[half:].to_int()

        R_i, L_i = self.enc_L_R(L_i, R_i)

        return Bytes(L_i).zfill(4) + Bytes(R_i).zfill(4)



    def decrypt(self, ciphertext: bytes) -> Bytes:
        """
        Decrypts `ciphertext`.

        Parameters:
            ciphertext (bytes): Bytes-like object to be decrypted.
        
        Returns:
            Bytes: Resulting plaintext.
        """
        ciphertext = Bytes.wrap(ciphertext)

        half = len(ciphertext) // 2
        L_i, R_i = ciphertext[:half].to_int(), ciphertext[half:].to_int()

        L_i ^= self.P[17]
        R_i ^= self.P[16]

        R_i, L_i = L_i, R_i

        for i in range(15, -1, -1):
            R_i, L_i = L_i, R_i

            R_i ^= round_func(
                    self.S[0][(L_i >> 24) & 0xFF],
                    self.S[1][(L_i >> 16) & 0xFF],
                    self.S[2][(L_i >> 8) & 0xFF],
                    self.S[3][L_i & 0xFF]
                ) & 0xFFFFFFFF

            L_i ^= self.P[i]
            L_i &= 0xFFFFFFFF

        return Bytes(L_i).zfill(4) + Bytes(R_i).zfill(4)
Beispiel #30
0
class GCM(StreamingBlockCipherMode):
    """Galois counter mode (GCM) block cipher mode"""

    EPHEMERAL = EphemeralSpec(ephemeral_type=EphemeralType.NONCE,
                              size=SizeSpec(size_type=SizeType.SINGLE,
                                            sizes=96))
    AUTH_TAG_SIZE = SizeSpec(size_type=SizeType.SINGLE, sizes=128)
    USAGE_FREQUENCY = FrequencyType.PROLIFIC

    def __init__(self, cipher: EncryptionAlg):
        """
        Parameters:
            cipher (EncryptionAlg): Instantiated encryption algorithm.
        """
        Primitive.__init__(self)
        self.cipher = cipher
        self.H = self.cipher.encrypt(b'\x00' * 16).int()
        self.ctr = CTR(self.cipher, b'\x00' * 8)

        # Precompute the product table
        self.product_table = [0] * 16
        self.product_table[reverse_bits(1)] = self.H

        for i in range(2, 16, 2):
            self.product_table[reverse_bits(i)] = self.gcm_shift(
                self.product_table[reverse_bits(i // 2)])
            self.product_table[reverse_bits(
                i + 1)] = self.product_table[reverse_bits(i)] ^ self.H

    def __repr__(self):
        return f"<GCM: cipher={self.cipher}, H={self.H}, ctr={self.ctr}>"

    def __str__(self):
        return self.__repr__()

    def clock_ctr(self, nonce: bytes) -> Bytes:
        nonce = Bytes.wrap(nonce)
        if len(nonce) == 12:
            self.ctr.nonce = nonce
            self.ctr.counter = 1
        else:
            payload = nonce.pad_congruent_right(16) + (b'\x00' * 8) + Bytes(
                len(nonce) * 8).zfill(8)
            J_0 = Bytes(self.update(0, payload)).zfill(16)
            self.ctr.nonce = J_0[:15]
            self.ctr.counter = J_0[-1]

        return self.ctr.encrypt(Bytes(b'').zfill(16))

    def encrypt(self,
                nonce: bytes,
                plaintext: bytes,
                data: bytes = b'') -> Bytes:
        """
        Encrypts `plaintext`.

        Parameters:
            nonce     (bytes): Bytes-like nonce.
            plaintext (bytes): Bytes-like object to be encrypted.
            data      (bytes): Bytes-like additional data to be authenticated but not encrypted.
        
        Returns:
            Bytes: Resulting ciphertext.
        """
        tag_mask = self.clock_ctr(nonce)
        data = Bytes.wrap(data)

        ciphertext = self.ctr.encrypt(plaintext)
        tag = self.auth(ciphertext, data, tag_mask)

        return ciphertext + tag

    def decrypt(self,
                nonce: bytes,
                authed_ciphertext: bytes,
                data: bytes = b'') -> Bytes:
        """
        Decrypts `ciphertext`.

        Parameters:
            nonce             (bytes): Bytes-like nonce.
            authed_ciphertext (bytes): Bytes-like object to be decrypted.
            data              (bytes): Bytes-like additional data to be authenticated.
        
        Returns:
            Bytes: Resulting plaintext.
        """
        from samson.utilities.runtime import RUNTIME

        authed_ciphertext = Bytes.wrap(authed_ciphertext)
        ciphertext, orig_tag = authed_ciphertext[:-16], authed_ciphertext[-16:]

        tag_mask = self.clock_ctr(nonce)
        data = Bytes.wrap(data)
        tag = self.auth(ciphertext, data, tag_mask)

        if not RUNTIME.compare_bytes(tag, orig_tag):
            raise Exception('Tag mismatch: authentication failed!')

        return self.ctr.decrypt(ciphertext)

    def gcm_shift(self, x: int) -> int:
        high_bit_set = x & 1
        x >>= 1

        if high_bit_set:
            x ^= 0xe1 << (128 - 8)

        return x

    def mul(self, y):
        ret = 0

        for _ in range(0, 128, 4):
            high_bit = ret & 0xF
            ret >>= 4
            ret ^= GCM_REDUCTION_TABLE[high_bit] << (128 - 16)
            ret ^= self.product_table[y & 0xF]
            y >>= 4

        return ret

    def auth(self, ciphertext: Bytes, ad: Bytes, tag_mask: Bytes) -> Bytes:
        y = 0
        y = self.update(y, ad)
        y = self.update(y, ciphertext)
        y ^= (len(ad) << (3 + 64)) | (len(ciphertext) << 3)
        y = self.mul(y)
        y ^= tag_mask.int()
        return Bytes(int.to_bytes(y, 16, 'big'))

    def update(self, y: int, data: Bytes) -> int:
        for chunk in data.chunk(16):
            y ^= chunk.int()
            y = self.mul(y)

        extra = len(data) % 16

        if extra != 0:
            block = bytearray(16)
            block[:extra] = data[-extra:]
            y ^= int.from_bytes(block, 'big')
            y = self.mul(y)
        return y