Skip to content

Commit 419957f

Browse files
committed
[docs] Python encryption example
1 parent 6215080 commit 419957f

File tree

1 file changed

+65
-0
lines changed

1 file changed

+65
-0
lines changed

web/mkdocs/docs/privacy/encryption.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)