secret_crypto.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import Crypto.Cipher.AES
  2. import hashlib
  3. import os
  4. class secret_crypto:
  5. """
  6. This class is responsible for creation and manage of the secrets crypto.
  7. All of the secrets would have own IV. When new secret_crypto is creating,
  8. IV is random. It could be change by function set_iv. Set IV is necessary
  9. to decrypt secret.
  10. Password HAVE NOT being stored in database. It must be given from API,
  11. to protect database. Password from the user is hashed by SHA256 to use it
  12. as key. IV is random, and other for all of the keys.
  13. If database leaks, secrets are save, because keys is provided from user
  14. every request for the secret.
  15. """
  16. def __init__(
  17. self,
  18. password: str,
  19. __iv: bytes | None = None,
  20. __key: bytes | None = None
  21. ) -> None:
  22. """
  23. This function create new secret_crypto. It could be used encrypt new
  24. secret. To decrypt secret, app must set IV.
  25. password: str - Password to encrypt secrets
  26. __iv: bytes | None - IV, used to clone (default: None)
  27. __key: bytes | None - Key, used to clone (default: None)
  28. """
  29. if __key is None:
  30. encoded = password.encode("UTF-8")
  31. hashed = hashlib.sha256(encoded)
  32. __key = hashed.digest()
  33. if __iv is None:
  34. __iv = os.urandom(16)
  35. self.__iv = __iv
  36. self.__key = __key
  37. self.__password = password
  38. def encrypt(self, secret: str) -> bytes:
  39. """
  40. This function encrypt secret, using previously loaded password as key
  41. and IV. IV would be random, and own for all secrets.
  42. secret: str - Secret to encode
  43. return: bytes - Encrypted secret
  44. """
  45. return self.__cipher.encrypt(secret.encode("UTF-8"))
  46. def decrypt(self, crypted: bytes) -> str:
  47. """
  48. This function decrypt previously crypted secret using given password
  49. as key. IV must be restore. When random IV is used, then cipher would
  50. not decrypt secret.
  51. crypted: bytes - Crypted secret to decrypt
  52. return: str - Decrypted secret
  53. """
  54. return self.__cipher.decrypt(crypted).decode("UTF-8")
  55. def crypted(self, secret: str) -> [bytes, bytes]:
  56. """
  57. This function is complex version of encrypt. It do exacly the same,
  58. but it return also IV, which could make code look better.
  59. secret: str - Secret to encrypt
  60. return: [bytes, bytes] - [Encrypted secret, IV]
  61. """
  62. return self.encrypt(secret), self.iv
  63. @property
  64. def password(self) -> str:
  65. """
  66. return: str - Password to encrypt
  67. """
  68. return self.__password
  69. @property
  70. def key(self) -> bytes:
  71. """
  72. return: bytes - Password as 256 bit binary key
  73. """
  74. return self.__key
  75. @property
  76. def iv(self) -> bytes:
  77. """
  78. return: bytes - IV of the crypto, get it to save in database
  79. """
  80. return self.__iv
  81. def set_iv(self, iv: bytes) -> object:
  82. """
  83. This function set IV. IV must be save in database, to decrypt secret
  84. later. This function is used to restore IV, previously read from
  85. database.
  86. iv: bytes - IV read from database
  87. return: secret_crypto - Clone of crypto with set given iv
  88. """
  89. return secret_crypto(self.password, iv, self.key)
  90. @property
  91. def mode(self) -> int:
  92. """
  93. return: int - Mode of the AES used to crypt
  94. """
  95. return Crypto.Cipher.AES.MODE_CFB
  96. @property
  97. def __cipher(self) -> object:
  98. """
  99. return: object - New clean cipher, which could be used to work
  100. """
  101. return Crypto.Cipher.AES.new(
  102. self.key,
  103. self.mode,
  104. iv = self.iv
  105. )