-3

I had an idea, to encrypt a string with bytes generated by python's pseudo random generator, to create essentially a one time pad, which should be secure, the question is in relation to the seeding of the random generator to create the pad.

Assuming you dont know the seed_key, How likely is a collision of seeds for the random generator?.

#!/usr/bin/env python3

### IMPORTS ###
import random
from base91 import encode
from os import urandom

### CLASS DEFINITITON ###
class SeedPad:

    """ Encrypts a string with a one time pad generated by the pseudo random generator using a seed """

    def __init__(self, plain_text=None, cipher_text=None, seed_key=None):
        self.__plain_text = plain_text
        self.__cipher_text = cipher_text
        self.__seed_key = seed_key

    ### PROPERTY GETTERS ###
    @property
    def plain_text(self):
        return self.__plain_text

    @property
    def cipher_text(self):
        return self.__cipher_text

    @property
    def seed_key(self):
        return self.__seed_key

    ### PROPERTY SETTERS ###
    @plain_text.setter
    def plain_text(self, plain_text):
        self.__plain_text = plain_text

    @cipher_text.setter
    def cipher_text(self, cipher_text):
        self.__cipher_text = cipher_text
    
    @seed_key.setter
    def seed_key(self, seed_key):
        self.__seed_key = seed_key

    ### METHODS ###
    def encrypt(self):

        """ 
        takes a plain text string and returns a cipher_text as a hex string

        seed_key and plain_text must be set to use this method
        """

        if self.__plain_text is None:
            raise ValueError("plain_text is not set.")

        if self.__seed_key is None:
            self.__keygen()

        if type(self.__plain_text) != bytes:
            self.__plain_text = self.__plain_text.encode()

        random.seed(self.__seed_key)

        self.__cipher_text = ""

        for i in self.__plain_text:
            self.__cipher_text += f"{i ^ random.randint(0,255):02x}"

        return self.__cipher_text

    def decrypt(self):
        if self.__cipher_text is None:
            raise ValueError("cipher_text is not set.")

        if self.__seed_key is None:
            raise ValueError("seed_key is not set.")

        random.seed(self.__seed_key)

        # Convert hex string to bytes
        cipher_bytes = bytes.fromhex(self.cipher_text)


        self.__plain_text = ""

        for byte in cipher_bytes:
            self.__plain_text += chr(byte ^ random.randint(0, 255))

        return self.__plain_text

    def __keygen(self):
        self.__seed_key = encode(urandom(16))
        print("Random seed: ", self.__seed_key)


if __name__ == "__main__":
    
    from flag import flag, secret_key
    
    crypter = SeedPad(plain_text=flag, seed_key=secret_key)
    print(crypter.encrypt())
lonny
  • 135
  • 1
  • 4
  • 4
    Stack Overflow is not a code review or critique site. You *may* be able to get more help at either [codereview.se] or [security.se], but please make sure you take their tours and read through their help centers ***first*** to ensure that your questions are on-topic. – MattDMo Nov 25 '21 at 01:25
  • Thanks, I should have known that, probably should pull this post before I go into negative reputation – lonny Nov 25 '21 at 01:32
  • When you create the cipher text, you're using random integers in the process, but I don't see where you store those random integers or I don't see those random integers being derived from the key, so I'm confused at how you are even able to decipher the data after its been created as its being created using random numbers that are then thrown away? – hostingutilities.com Nov 25 '21 at 01:34
  • @hostingutilities.com, you can either specify a seed_key, or if none is specified a random one will be generated and it is printed to the screen – lonny Nov 25 '21 at 01:36
  • 1
    @hostingutilities.com They're using the seed to generate the same values. – Carcigenicate Nov 25 '21 at 01:36
  • Ahh, that makes sense. – hostingutilities.com Nov 25 '21 at 01:43
  • So while the seed is completely random, you are relying on `random.random`'s Mersenne Twister algorithm to generate additional random numbers based off that seed. If you called `os.urandom` each time you needed more random numbers, then that would be more cryptographically secure. – hostingutilities.com Nov 25 '21 at 01:56
  • 1
    At the end of your decryption algorithm you should set `self.__seed_key` back to `None`. With one-time pad encryption algorithms, if a person gets ahold of two messages that were encrypted with the same key, they will be able to decrypt the contents of those messages. – hostingutilities.com Nov 25 '21 at 02:02

1 Answers1

1

I believe everything you need to know is in the documentation of the Random class:

Warning The pseudo-random generators of this module should not be used for security purposes. For security or cryptographic uses, see the secrets module.

Also:

Python uses the Mersenne Twister as the core generator. . . . However, being completely deterministic, it is not suitable for all purposes, and is completely unsuitable for cryptographic purposes.

Frank Yellin
  • 9,127
  • 1
  • 12
  • 22
  • thanks, the pseudo random generator can be seeded, an I am using that ability to generate a one time pad, I dont beleve the secrets module can be seeded, this is just a way to turn a relatively short secret into a long string of non repeating bytes. – lonny Nov 25 '21 at 01:41
  • If you're just trying to stop your roommate from reading your stuff, then go ahead and use this. If you're seriously concerned about security, use well known cryptographic methods. Inventing one's own cryptography has repeatedly led to failure. https://resources.infosecinstitute.com/topic/the-dangers-of-rolling-your-own-encryption/ – Frank Yellin Nov 25 '21 at 02:03