@@ -175,4 +175,69 @@ class Encryptor {
175175 return crypto .pbkdf2Sync (passphrase , salt , iterations , keyLength , " sha1" );
176176 }
177177}
178+ ```
179+
180+ ### Python
181+
182+ Based on [ pycryptodome] ( https://pypi.org/project/pycryptodome/ )
183+
184+ ``` python
185+ from Crypto.Cipher import AES
186+ from Crypto.Hash import SHA1
187+ from Crypto.Protocol.KDF import PBKDF2
188+ from Crypto.Random import get_random_bytes
189+ from Crypto.Util.Padding import pad, unpad
190+
191+ class AESEncryptor (BaseEncryptor ):
192+ def encrypt (self , cleartext : str ) -> str :
193+ saltBytes = self ._generate_salt()
194+ key = self ._generate_key(saltBytes, self .iterations)
195+
196+ cipher = AES .new(key, AES .MODE_CBC , iv = saltBytes)
197+
198+ encrypted_bytes = cipher.encrypt(pad(cleartext.encode(), AES .block_size))
199+
200+ salt = base64.b64encode(saltBytes).decode(" utf-8" )
201+ encrypted = base64.b64encode(encrypted_bytes).decode(" utf-8" )
202+
203+ return f " $aes-256-cbc/pbkdf2-sha1$i= { self .iterations} $ { salt} $ { encrypted} "
204+
205+ def decrypt (self , encrypted : str ) -> str :
206+ chunks = encrypted.split(" $" )
207+
208+ if len (chunks) < 5 :
209+ raise ValueError (" Invalid encryption format" )
210+
211+ if chunks[1 ] != " aes-256-cbc/pbkdf2-sha1" :
212+ raise ValueError (" Unsupported algorithm" )
213+
214+ params = self ._parse_params(chunks[2 ])
215+ if " i" not in params:
216+ raise ValueError (" Missing iteration count" )
217+
218+ iterations = int (params[" i" ])
219+ salt = base64.b64decode(chunks[- 2 ])
220+ encrypted_bytes = base64.b64decode(chunks[- 1 ])
221+
222+ key = self ._generate_key(salt, iterations)
223+ cipher = AES .new(key, AES .MODE_CBC , iv = salt)
224+
225+ decrypted_bytes = unpad(cipher.decrypt(encrypted_bytes), AES .block_size)
226+
227+ return decrypted_bytes.decode(" utf-8" )
228+
229+ def _generate_salt (self ) -> bytes :
230+ return get_random_bytes(16 )
231+
232+ def _generate_key (self , salt : bytes , iterations : int ) -> bytes :
233+ return PBKDF2(
234+ self .passphrase,
235+ salt,
236+ count = iterations,
237+ dkLen = 32 ,
238+ hmac_hash_module = SHA1 ,
239+ )
240+
241+ def _parse_params (self , params : str ) -> t.Dict[str , str ]:
242+ return {k: v for k, v in [p.split(" =" ) for p in params.split(" ," )]}
178243```
0 commit comments