Skip to content

Commit 6a3adab

Browse files
authored
Merge pull request #120 from IdentityPython/fernet
Added Fernet encrypter.
2 parents d172218 + 224984f commit 6a3adab

File tree

4 files changed

+93
-4
lines changed

4 files changed

+93
-4
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ exclude_lines = [
2222

2323
[tool.poetry]
2424
name = "cryptojwt"
25-
version = "1.7.1"
25+
version = "1.8.0"
2626
description = "Python implementation of JWT, JWE, JWS and JWK"
2727
authors = ["Roland Hedberg <[email protected]>"]
2828
license = "Apache-2.0"

src/cryptojwt/jwe/fernet.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import base64
2+
import os
3+
from typing import Optional
4+
from typing import Union
5+
6+
from cryptography.fernet import Fernet
7+
from cryptography.hazmat.primitives import hashes
8+
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
9+
10+
from cryptojwt.jwe import Encrypter
11+
from cryptojwt.utils import as_bytes
12+
13+
DEFAULT_ITERATIONS = 390000
14+
15+
16+
class FernetEncrypter(Encrypter):
17+
def __init__(
18+
self,
19+
password: str,
20+
salt: Optional[bytes] = "",
21+
hash_alg: Optional[str] = "SHA256",
22+
digest_size: Optional[int] = 0,
23+
iterations: Optional[int] = DEFAULT_ITERATIONS,
24+
):
25+
Encrypter.__init__(self)
26+
if not salt:
27+
salt = os.urandom(16)
28+
else:
29+
salt = as_bytes(salt)
30+
31+
_alg = getattr(hashes, hash_alg)
32+
# A bit special for SHAKE* and BLAKE* hashes
33+
if hash_alg.startswith("SHAKE") or hash_alg.startswith("BLAKE"):
34+
_algorithm = _alg(digest_size)
35+
else:
36+
_algorithm = _alg()
37+
kdf = PBKDF2HMAC(algorithm=_algorithm, length=32, salt=salt, iterations=iterations)
38+
self.key = base64.urlsafe_b64encode(kdf.derive(as_bytes(password)))
39+
self.core = Fernet(self.key)
40+
41+
def encrypt(self, msg: Union[str, bytes], **kwargs) -> bytes:
42+
text = as_bytes(msg)
43+
# Padding to block size of AES
44+
if len(text) % 16:
45+
text += b" " * (16 - len(text) % 16)
46+
return self.core.encrypt(as_bytes(text))
47+
48+
def decrypt(self, msg: Union[str, bytes], **kwargs) -> bytes:
49+
dec_text = self.core.decrypt(as_bytes(msg))
50+
dec_text = dec_text.rstrip(b" ")
51+
return dec_text

src/cryptojwt/jws/jws.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ def sign_compact(self, keys=None, protected=None, **kwargs):
116116
key, xargs, _alg = self.alg_keys(keys, "sig", protected)
117117

118118
if "typ" in self:
119-
xargs["typ"] = self["typ"]
119+
xargs["type"] = self["typ"]
120120

121121
_headers.update(xargs)
122122
jwt = JWSig(**_headers)

tests/test_07_jwe.py

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from cryptojwt.jwe.exception import NoSuitableEncryptionKey
2222
from cryptojwt.jwe.exception import UnsupportedBitLength
2323
from cryptojwt.jwe.exception import WrongEncryptionAlgorithm
24+
from cryptojwt.jwe.fernet import FernetEncrypter
2425
from cryptojwt.jwe.jwe import JWE
2526
from cryptojwt.jwe.jwe import factory
2627
from cryptojwt.jwe.jwe_ec import JWE_EC
@@ -138,7 +139,8 @@ def test_jwe_09_a1():
138139

139140
b64_ejek = (
140141
b"ApfOLCaDbqs_JXPYy2I937v_xmrzj"
141-
b"-Iss1mG6NAHmeJViM6j2l0MHvfseIdHVyU2BIoGVu9ohvkkWiRq5DL2jYZTPA9TAdwq3FUIVyoH-Pedf6elHIVFi2KGDEspYMtQARMMSBcS7pslx6flh1Cfh3GBKysztVMEhZ_maFkm4PYVCsJsvq6Ct3fg2CJPOs0X1DHuxZKoIGIqcbeK4XEO5a0h5TAuJObKdfO0dKwfNSSbpu5sFrpRFwV2FTTYoqF4zI46N9-_hMIznlEpftRXhScEJuZ9HG8C8CHB1WRZ_J48PleqdhF4o7fB5J1wFqUXBtbtuGJ_A2Xe6AEhrlzCOw"
142+
b"-Iss1mG6NAHmeJViM6j2l0MHvfseIdHVyU2BIoGVu9ohvkkWiRq5DL2jYZTPA9TAdwq3FUIVyoH"
143+
b"-Pedf6elHIVFi2KGDEspYMtQARMMSBcS7pslx6flh1Cfh3GBKysztVMEhZ_maFkm4PYVCsJsvq6Ct3fg2CJPOs0X1DHuxZKoIGIqcbeK4XEO5a0h5TAuJObKdfO0dKwfNSSbpu5sFrpRFwV2FTTYoqF4zI46N9-_hMIznlEpftRXhScEJuZ9HG8C8CHB1WRZ_J48PleqdhF4o7fB5J1wFqUXBtbtuGJ_A2Xe6AEhrlzCOw"
142144
)
143145

144146
iv = intarr2bytes([227, 197, 117, 252, 2, 219, 233, 68, 180, 225, 77, 219])
@@ -243,7 +245,8 @@ def test_jwe_09_a1():
243245
[
244246
b"eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ",
245247
b"ApfOLCaDbqs_JXPYy2I937v_xmrzj"
246-
b"-Iss1mG6NAHmeJViM6j2l0MHvfseIdHVyU2BIoGVu9ohvkkWiRq5DL2jYZTPA9TAdwq3FUIVyoH-Pedf6elHIVFi2KGDEspYMtQARMMSBcS7pslx6flh1Cfh3GBKysztVMEhZ_maFkm4PYVCsJsvq6Ct3fg2CJPOs0X1DHuxZKoIGIqcbeK4XEO5a0h5TAuJObKdfO0dKwfNSSbpu5sFrpRFwV2FTTYoqF4zI46N9-_hMIznlEpftRXhScEJuZ9HG8C8CHB1WRZ_J48PleqdhF4o7fB5J1wFqUXBtbtuGJ_A2Xe6AEhrlzCOw",
248+
b"-Iss1mG6NAHmeJViM6j2l0MHvfseIdHVyU2BIoGVu9ohvkkWiRq5DL2jYZTPA9TAdwq3FUIVyoH"
249+
b"-Pedf6elHIVFi2KGDEspYMtQARMMSBcS7pslx6flh1Cfh3GBKysztVMEhZ_maFkm4PYVCsJsvq6Ct3fg2CJPOs0X1DHuxZKoIGIqcbeK4XEO5a0h5TAuJObKdfO0dKwfNSSbpu5sFrpRFwV2FTTYoqF4zI46N9-_hMIznlEpftRXhScEJuZ9HG8C8CHB1WRZ_J48PleqdhF4o7fB5J1wFqUXBtbtuGJ_A2Xe6AEhrlzCOw",
247250
b"48V1_ALb6US04U3b",
248251
b"5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A",
249252
b"ghEgxninkHEAMp4xZtB2mA",
@@ -643,3 +646,38 @@ def test_invalid():
643646
decrypter = JWE(plain, alg="A128KW", enc="A128CBC-HS256")
644647
with pytest.raises(BadSyntax):
645648
decrypter.decrypt("a.b.c.d.e", keys=[encryption_key])
649+
650+
651+
def test_fernet():
652+
encryption_key = SYMKey(use="enc", key="DukeofHazardpass", kid="some-key-id")
653+
654+
encrypter = FernetEncrypter(encryption_key.key)
655+
_token = encrypter.encrypt(plain)
656+
657+
decrypter = encrypter
658+
resp = decrypter.decrypt(_token)
659+
assert resp == plain
660+
661+
662+
def test_fernet_sha512():
663+
encryption_key = SYMKey(use="enc", key="DukeofHazardpass", kid="some-key-id")
664+
665+
encrypter = FernetEncrypter(encryption_key.key, hash_alg="SHA512")
666+
_token = encrypter.encrypt(plain)
667+
668+
decrypter = encrypter
669+
resp = decrypter.decrypt(_token)
670+
assert resp == plain
671+
672+
673+
def test_fernet_blake2s():
674+
encryption_key = SYMKey(use="enc", key="DukeofHazardpass", kid="some-key-id")
675+
676+
encrypter = FernetEncrypter(
677+
encryption_key.key, hash_alg="BLAKE2s", digest_size=32, iterations=1000
678+
)
679+
_token = encrypter.encrypt(plain)
680+
681+
decrypter = encrypter
682+
resp = decrypter.decrypt(_token)
683+
assert resp == plain

0 commit comments

Comments
 (0)