Skip to content

Commit 1ad806d

Browse files
author
boraxpr
committed
Cryptography fernet
1 parent 258f121 commit 1ad806d

File tree

3 files changed

+177
-0
lines changed

3 files changed

+177
-0
lines changed

264/clamy_fernet.py

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import base64
2+
from cryptography.fernet import Fernet # type: ignore
3+
from cryptography.hazmat.backends import default_backend # type: ignore
4+
from cryptography.hazmat.primitives import hashes # type: ignore
5+
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC # type: ignore
6+
from dataclasses import dataclass
7+
from os import urandom
8+
from typing import ByteString, Tuple, Optional
9+
10+
11+
@dataclass
12+
class ClamyFernet:
13+
"""Fernet implementation by clamytoe
14+
15+
Takes a bytestring as a password and derives a Fernet
16+
key from it. If a key is provided, that key will be used.
17+
"""
18+
password: ByteString = b"pybites"
19+
key: Optional[ByteString] = None
20+
21+
def __post_init__(self):
22+
"""Initializes the class"""
23+
self.salt = urandom(16)
24+
self.algorithm = hashes.SHA512()
25+
self.length = 32
26+
self.iterations = 100_000
27+
self.backend = default_backend()
28+
29+
if self.key is None:
30+
self.key = base64.urlsafe_b64encode(self.kdf.derive(self.password))
31+
32+
@property
33+
def kdf(self) -> PBKDF2HMAC:
34+
"""Derives the key from the password
35+
36+
Uses PBKDF2HMAC to generate a secure key.
37+
"""
38+
return PBKDF2HMAC(
39+
algorithm=self.algorithm,
40+
length=self.length,
41+
salt=self.salt,
42+
iterations=self.iterations,
43+
backend=self.backend,
44+
)
45+
46+
@property
47+
def clf(self):
48+
"""Generates a Fernet object"""
49+
return Fernet(self.key)
50+
51+
def encrypt(self, message: str) -> ByteString:
52+
"""Encrypts the message passed to it"""
53+
return self.clf.encrypt(str.encode(message))
54+
55+
def decrypt(self, token: ByteString) -> str:
56+
"""Decrypts the encrypted message passed to it"""
57+
return self.clf.decrypt(token).decode("utf-8")

264/submissions/save1_passed.py

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import base64
2+
from cryptography.fernet import Fernet # type: ignore
3+
from cryptography.hazmat.backends import default_backend # type: ignore
4+
from cryptography.hazmat.primitives import hashes # type: ignore
5+
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC # type: ignore
6+
from dataclasses import dataclass
7+
from os import urandom
8+
from typing import ByteString, Tuple, Optional
9+
10+
11+
@dataclass
12+
class ClamyFernet:
13+
"""Fernet implementation by clamytoe
14+
15+
Takes a bytestring as a password and derives a Fernet
16+
key from it. If a key is provided, that key will be used.
17+
"""
18+
password: ByteString = b"pybites"
19+
key: Optional[ByteString] = None
20+
21+
def __post_init__(self):
22+
"""Initializes the class"""
23+
self.salt = urandom(16)
24+
self.algorithm = hashes.SHA512()
25+
self.length = 32
26+
self.iterations = 100_000
27+
self.backend = default_backend()
28+
29+
if self.key is None:
30+
self.key = base64.urlsafe_b64encode(self.kdf.derive(self.password))
31+
32+
@property
33+
def kdf(self) -> PBKDF2HMAC:
34+
"""Derives the key from the password
35+
36+
Uses PBKDF2HMAC to generate a secure key.
37+
"""
38+
return PBKDF2HMAC(
39+
algorithm=self.algorithm,
40+
length=self.length,
41+
salt=self.salt,
42+
iterations=self.iterations,
43+
backend=self.backend,
44+
)
45+
46+
@property
47+
def clf(self):
48+
"""Generates a Fernet object"""
49+
return Fernet(self.key)
50+
51+
def encrypt(self, message: str) -> ByteString:
52+
"""Encrypts the message passed to it"""
53+
return self.clf.encrypt(str.encode(message))
54+
55+
def decrypt(self, token: ByteString) -> str:
56+
"""Decrypts the encrypted message passed to it"""
57+
return self.clf.decrypt(token).decode("utf-8")

264/test_clamy_fernet.py

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from datetime import datetime
2+
from random import choice
3+
from tempfile import NamedTemporaryFile
4+
5+
import pytest
6+
7+
from clamy_fernet import PBKDF2HMAC, ByteString, ClamyFernet, Fernet
8+
9+
KEYS = (
10+
b"rvxePMSDUcZFowEaNxnFb8Pifn1KmhkF70Mz1ZQe2Bw=",
11+
b"2gODW4C4Lc7H9bjuuhPyn48HkVHriqa96P8lmstABo8=",
12+
b"mAbAfF5CW3EGlngOEEroDqtxlxVlJILzoUE4TJScMIw=",
13+
)
14+
MESSAGE = "This is my secret message"
15+
TMP_FILE = NamedTemporaryFile(delete=False)
16+
FILE = TMP_FILE.name
17+
18+
19+
@pytest.fixture(scope="function")
20+
def rcf():
21+
password = b"#clamybite"
22+
key = choice(KEYS)
23+
return ClamyFernet(password, key)
24+
25+
26+
@pytest.fixture(scope="module")
27+
def cf():
28+
return ClamyFernet(key=KEYS[0])
29+
30+
31+
def test_clamyfernet_no_args(rcf):
32+
tmp_cf = ClamyFernet()
33+
assert tmp_cf.key is not None
34+
assert tmp_cf.password == b"pybites"
35+
36+
37+
def test_clamyfernet_random_key(rcf):
38+
token = rcf.encrypt("secret msg")
39+
ts = rcf.clf.extract_timestamp(token)
40+
dt = datetime.fromtimestamp(ts)
41+
assert isinstance(rcf, ClamyFernet)
42+
assert isinstance(rcf.clf, Fernet)
43+
assert isinstance(rcf.key, ByteString)
44+
assert isinstance(rcf.kdf, PBKDF2HMAC)
45+
assert dt.year == datetime.now().year
46+
47+
48+
def test_clamyfernet(cf):
49+
token = cf.encrypt(MESSAGE)
50+
og_message = cf.decrypt(token)
51+
assert len(token) == 120
52+
assert isinstance(token, bytes)
53+
assert cf.key == KEYS[0]
54+
assert og_message == MESSAGE
55+
56+
57+
def test_clamyfernet_random(rcf):
58+
token = rcf.encrypt(MESSAGE)
59+
og_message = rcf.decrypt(token)
60+
assert len(token) == 120
61+
assert isinstance(token, bytes)
62+
assert rcf.key in KEYS
63+
assert og_message == MESSAGE

0 commit comments

Comments
 (0)