-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: response_code in redirect_uri (#257)
* feat: stub of resp_code in callback uri * fix: interfaces and unit test for resp code * fix: url > uri * fix: redirect -> 200 * feat: code from session storage to hmac(state) * feat: helper class to simply method injection * chore: rename and cleanup * chore: cleaned outdated comments * feat: response_code: hmac -> aead * chore: clean docs * chore: cleanup * fix: status endpoint with code * Apply suggestions from code review * Apply suggestions from code review --------- Co-authored-by: Giuseppe De Marco <[email protected]>
- Loading branch information
Showing
12 changed files
with
171 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from pydantic import BaseModel | ||
|
||
|
||
class ResponseConfig(BaseModel): | ||
sym_key: str |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import base64 | ||
from dataclasses import dataclass, field | ||
from cryptography.hazmat.primitives.ciphers.aead import AESGCM | ||
import secrets | ||
import string | ||
|
||
CODE_SYM_KEY_LEN = 32 # in bytes (256 bits) | ||
|
||
|
||
@dataclass | ||
class ResponseCodeSource: | ||
"""ResponseCodeSource is a utility box that wraps a secreet key and | ||
exposes utility methods that define the relationship between request | ||
status and response code. | ||
The class assumes that the response status is a string with UTF-8 | ||
encoding. When this is not true, the resulting chipertext might | ||
be longer than necessary. | ||
Constructor arguments: | ||
:param key: encryption/decryption key, represented as a hex string | ||
:type key: str | ||
""" | ||
|
||
key: str = field(repr=False) # repr=False as we do not want to accidentally expose a secret key in a log file | ||
|
||
def __post_init__(self): | ||
# Validate input(s) | ||
_ = decode_key(self.key) | ||
|
||
def create_code(self, state: str) -> str: | ||
return create_code(state, self.key) | ||
|
||
def recover_state(self, code: str) -> str: | ||
return recover_state(code, self.key) | ||
|
||
|
||
def decode_key(key: str) -> bytes: | ||
if not set(key) <= set(string.hexdigits): | ||
raise ValueError("key in format different than hex currently not supported") | ||
key_len = len(key) | ||
if key_len != 2*CODE_SYM_KEY_LEN: | ||
raise ValueError(f"invalid key: key should be {CODE_SYM_KEY_LEN} bytes, obtained instead: {key_len//2}") | ||
return bytes.fromhex(key) | ||
|
||
|
||
def _base64_encode_no_pad(b: bytes) -> str: | ||
return base64.urlsafe_b64encode(b).decode().rstrip('=') | ||
|
||
|
||
def _base64_decode_no_pad(s: str) -> bytes: | ||
padded = s + "="*((4 - len(s) % 4) % 4) | ||
return base64.urlsafe_b64decode(padded) | ||
|
||
|
||
def _encrypt_state(msg: bytes, key: bytes) -> bytes: | ||
nonce = secrets.token_bytes(12) | ||
ciphertext = AESGCM(key).encrypt(nonce, msg, b'') | ||
return nonce + ciphertext | ||
|
||
|
||
def _decrypt_code(encrypted_token: bytes, key: bytes) -> bytes: | ||
nonce = encrypted_token[:12] | ||
ciphertext = encrypted_token[12:] | ||
dec = AESGCM(key).decrypt(nonce, ciphertext, b'') | ||
return dec | ||
|
||
|
||
def create_code(state: str, key: str) -> str: | ||
bkey = decode_key(key) | ||
msg = bytes(state, encoding='utf-8') | ||
code = _encrypt_state(msg, bkey) | ||
return _base64_encode_no_pad(code) | ||
|
||
|
||
def recover_state(code: str, key: str) -> str: | ||
bkey = decode_key(key) | ||
enc = _base64_decode_no_pad(code) | ||
state = _decrypt_code(enc, bkey) | ||
return state.decode(encoding='utf-8') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import pytest | ||
|
||
from pyeudiw.satosa.utils.respcode import ResponseCodeSource, create_code, recover_state | ||
|
||
|
||
def test_valid_resp_code(): | ||
state = "state" | ||
key = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" | ||
code = create_code(state, key) | ||
assert recover_state(code, key) == state | ||
|
||
|
||
def test_invalid_resp_code(): | ||
key = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" | ||
try: | ||
recover_state("this_is_an_invalid_response_code", key) | ||
assert False | ||
except Exception: | ||
assert True | ||
|
||
|
||
def test_bad_key(): | ||
key = "" | ||
try: | ||
create_code("state", key) | ||
assert False | ||
except ValueError: | ||
assert True | ||
|
||
|
||
class TestResponseCodeHelper: | ||
|
||
@pytest.fixture(autouse=True) | ||
def setup(self): | ||
key = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" | ||
self.respose_code_helper = ResponseCodeSource(key) | ||
|
||
def test_valid_code(self): | ||
state = "state" | ||
code = self.respose_code_helper.create_code(state) | ||
assert self.respose_code_helper.recover_state(code) == state | ||
|
||
def test_invalid_code(self): | ||
try: | ||
self.respose_code_helper.create_code("this_is_an_invalid_response_code") | ||
assert False | ||
except Exception: | ||
assert True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters