| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 | 
							- import os
 
- import hashlib
 
- import Crypto.Cipher.AES
 
- class bad_password(Exception):
 
-     """
 
-     This exception is user to mark when bad password had been used.
 
-     """
 
-     pass
 
- class secret_coder:
 
-     """
 
-     This class is required for crypting and decrypting secret by passwords.
 
-     It store crypted secret as strings in coded form, which make it easies to 
 
-     store in database, and send over REST api.
 
-     Format:
 
-     <hashed_secret>:<cipher>:<crypted_secret>
 
-      - hashed_secret: SHA256 of the secret, to validate success of the decrypt.
 
-      - cipher: Cipher from AES.
 
-      - crypted_secret: Crypted secret to encrypt with password.
 
-     """
 
-     def __init__(self, password: str) -> None:
 
-         """
 
-         This functio initialize new coder. It require password, which would
 
-         be used to encrypting and decrypting secrets.
 
-         Parameters:
 
-             password (str): Password to use as crypto key
 
-         """
 
-         password = password.encode("UTF-8")
 
-         hashed = hashlib.sha256(password)  
 
-         self.__password = hashed.digest()
 
-     @property
 
-     def password(self) -> bytes:
 
-         """ It return password as crypto key. """
 
-         return self.__password
 
-     def encrypt(self, plain: str) -> str:
 
-         """
 
-         This function encrypt new secret, and return it in specified format.
 
-         Parameters:
 
-             plain (str): Secret to encode
 
-         Returns:
 
-             (str): Encrypted secret
 
-         """
 
-         cipher = self.__iv
 
-         coder = self.__cipher(cipher)
 
-         crypted = coder.encrypt(plain.encode("UTF-8"))
 
-         hashed = self.__hash(plain)
 
-         return self.__pack(hashed, cipher, crypted)
 
-     def decrypt(self, crypted: str) -> str:
 
-         """
 
-         This function decrypt secret in given format, and return decrypted 
 
-         secret. When password which initialize coder is bad, then
 
-         bad_password Exception had been raiser.
 
-         Parameters:
 
-             crypted (str): Crypted secret in coder format
 
-         Returns:
 
-             (str): Decrypted secret
 
-         """
 
-         hashed, cipher, crypted = self.__unpack(crypted) 
 
-         coder = self.__cipher(cipher)
 
-         result = coder.decrypt(crypted)
 
-         try:
 
-             result = result.decode("UTF-8")
 
-         except:
 
-             raise bad_password("Crypto key is not correct for this secret.")
 
-         if hashed != self.__hash(result):
 
-             raise bad_password("Crypto key is not correct for this secret.")
 
-         
 
-         return result
 
-         
 
-     @property
 
-     def mode(self) -> int:
 
-         """ This return AES mode which had been used by coder. """
 
-         
 
-         return Crypto.Cipher.AES.MODE_CFB
 
-     @property
 
-     def __iv(self) -> bytes:
 
-         """ This return new random cipher for AES. """
 
-         return os.urandom(secret_coder.__get_cipher_length())
 
-     def __get_hash_length() -> int:
 
-         """ This return lenght of hash in bytes. """
 
-         return int(256 / 8)
 
-     def __get_cipher_length() -> int:
 
-         """ This return lenght of random cipher. """
 
-         return 16
 
-     def __cipher(self, iv: bytes) -> object:
 
-         """ This return new AES coder to work with coders. """
 
-         return Crypto.Cipher.AES.new(
 
-             self.password,
 
-             self.mode,
 
-             iv = iv
 
-         )
 
-     def __hash(self, secret: str) -> bytes:
 
-         """
 
-         This function hash given string, and return it as 
 
-         bytes.
 
-         Parameters:
 
-             secret (str): Content to hash
 
-         Returns:
 
-             (bytes): Hashed content
 
-         """
 
-         secret = secret.encode("UTF-8")
 
-         hashed = hashlib.sha256(secret)
 
-         
 
-         return hashed.digest()
 
-     @property
 
-     def separator(self) -> str:
 
-         """ This return separator used to packing secret. """
 
-         return secret_coder.__get_separator()
 
-     def __get_separator() -> str:
 
-         """ This return separator, which could be used in static function. """
 
-     
 
-         return ":"
 
-     def __pack(self, hashed: bytes, cipher: bytes, crypted: bytes) -> str:
 
-         """
 
-         This function pack full content required for secret, to coder format.
 
-         Parameters:
 
-             hashed (bytes): Hashed secret
 
-             cipher (bytes): Ciper used in crypter
 
-             crypted (bytes): Crypted secret
 
-         Returns:
 
-             (str): Coded crypted secret
 
-         """
 
-         hashed = hashed.hex()
 
-         cipher = cipher.hex()
 
-         crypted = crypted.hex()
 
-         return hashed + self.separator + cipher + self.separator + crypted
 
-     def __unpack(self, crypted: str) -> [bytes, bytes, bytes]:
 
-         """
 
-         This function unpack given coded secret into bytes parts. When 
 
-         secret is not valid raise TypeError.
 
-         Parameters:
 
-             crypted (str): Packed secret
 
-         Returns:
 
-             (bytes): Hashed secret before encryption to validate password
 
-             (bytes): Cipher required by AES to work
 
-             (bytes): Crypted secret to encrypt
 
-         """
 
-         splited = crypted.split(self.separator)
 
-         if len(splited) != 3:
 
-             raise TypeError("Secret is in invalid format.")
 
-         hashed = bytes.fromhex(splited[0])
 
-         cipher = bytes.fromhex(splited[1])
 
-         crypted = bytes.fromhex(splited[2])
 
-         if len(cipher) != secret_coder.__get_cipher_length():
 
-             raise TypeError("Secret is in invalid format.")
 
-         
 
-         if len(hashed) != secret_coder.__get_hash_length():
 
-             raise TypeError("Secret is in invalid format.")
 
-         
 
-         return hashed, cipher, crypted
 
-     def validate(coded: str) -> bool:
 
-         """
 
-         This validate that coded secret is correct. That check hashed lenght
 
-         and cipher lenght. This is static function.
 
-         Parameters:
 
-             coded (str): Codec secret to check correction of
 
-         Returns:
 
-             (bool): True when secret is corret, False when not.
 
-         """
 
-         
 
-         splited = coded.split(secret_coder.__get_separator())
 
-         if len(splited) != 3:
 
-             return False
 
-         hashed = bytes.fromhex(splited[0])
 
-         cipher = bytes.fromhex(splited[1])
 
-         if len(cipher) != secret_coder.__get_cipher_length():
 
-             return False
 
-         if len(hashed) != secret_coder.__get_hash_length():
 
-             return False
 
-         return True
 
 
  |