def test_should_get_timestamp(): key = unhexlify( "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974") branca = Branca(key) token = "1jJDJOEeG2FutA8g7NAOHK4Mh5RIE8jtbXd63uYbrFDSR06dtQl9o2gZYhBa36nZHXVfiGFz" assert branca.timestamp(token) == 123206400
def test_should_throw_with_wrong_key(): key = unhexlify( "77726f6e677365637265746b6579796f7573686f756c646e6f74636f6d6d6974") branca = Branca(key) token = "870S4BYxgHw0KnP3W9fgVUHEhT5g86vJ17etaC5Kh5uIraWHCI1psNQGv298ZmjPwoYbjDQ9chy2z" with pytest.raises(RuntimeError): branca.decode(token)
def test_should_allow_hex_string_key(): branca = Branca( key="73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974") token = "875GH23U0Dr6nHFA63DhOyd9LkYudBkX8RsCTOMz5xoYAMw9sMd5QwcEqLDRnTDHPenOX7nP2trlT" assert branca.decode(token) == b"Hello world!" assert branca.timestamp(token) == 123206400
def test_decode_hello_world_with_zero_timestamp(): key = unhexlify( "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974") branca = Branca(key) token = "870S4BYxgHw0KnP3W9fgVUHEhT5g86vJ17etaC5Kh5uIraWHCI1psNQGv298ZmjPwoYbjDQ9chy2z" assert branca.decode(token) == b"Hello world!" assert branca.timestamp(token) == 0
def test_decode_hello_world_with_max_timestamp(): key = unhexlify( "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974") branca = Branca(key) token = "89i7YCwu5tWAJNHUDdmIqhzOi5hVHOd4afjZcGMcVmM4enl4yeLiDyYv41eMkNmTX6IwYEFErCSqr" assert branca.decode(token) == b"Hello world!" assert branca.timestamp(token) == 4294967295
def test_decode_empty_payload(): key = unhexlify( "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974") branca = Branca(key) token = "4sfD0vPFhIif8cy4nB3BQkHeJqkOkDvinI4zIhMjYX4YXZU5WIq9ycCVjGzB5" assert branca.decode(token) == b"" assert branca.timestamp(token) == 0
def test_decode_eight_nul_bytes_with_zero_timestamp(): key = unhexlify( "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974") branca = Branca(key) token = "1jIBheHbDdkCDFQmtgw4RUZeQoOJgGwTFJSpwOAk3XYpJJr52DEpILLmmwYl4tjdSbbNqcF1" assert branca.decode(token) == b"\x00\x00\x00\x00\x00\x00\x00\x00" assert branca.timestamp(token) == 0
def test_decode_eight_nul_bytes_with_nov27_timestamp(): key = unhexlify( "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974") branca = Branca(key) token = "1jJDJOEjuwVb9Csz1Ypw1KBWSkr0YDpeBeJN6NzJWx1VgPLmcBhu2SbkpQ9JjZ3nfUf7Aytp" assert branca.decode(token) == b"\x00\x00\x00\x00\x00\x00\x00\x00" assert branca.timestamp(token) == 123206400
def test_decode_hello_world_with_nov27_timestamp(): key = unhexlify( "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974") branca = Branca(key) token = "875GH23U0Dr6nHFA63DhOyd9LkYudBkX8RsCTOMz5xoYAMw9sMd5QwcEqLDRnTDHPenOX7nP2trlT" assert branca.decode(token) == b"Hello world!" assert branca.timestamp(token) == 123206400
def test_should_throw_with_modified_tag(): key = unhexlify( "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974") branca = Branca(key) token = "875GH23U0Dr6nHFA63DhOyd9LkYudBkX8RsCTOMz5xoYAMw9sMd5QwcEqLDRnTDHPenOX7nP2trk0" with pytest.raises(RuntimeError): branca.decode(token)
def test_should_throw_with_modified_ciphertext(): key = unhexlify( "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974") branca = Branca(key) token = "875GH23U0Dr6nHFA63DhOyd9LkYudBkX8RsCTOMz5xoYAMw9sMd5Qw6Jpo96myliI3hHD7VbKZBYh" with pytest.raises(RuntimeError): branca.decode(token)
def test_should_throw_with_modified_timestamp(): key = unhexlify( "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974") branca = Branca(key) token = "870g1RCk4lW1YInhaU3TP8u2hGtfol16ettLcTOSoA0JIpjCaQRW7tQeP6dQmTvFIB2s6wL5deMXr" with pytest.raises(RuntimeError): branca.decode(token)
def test_should_throw_with_modified_nonce(): key = unhexlify( "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974") branca = Branca(key) token = "875GH233SUysT7fQ711EWd9BXpwOjB72ng3ZLnjWFrmOqVy49Bv93b78JU5331LbcY0EEzhLfpmSx" with pytest.raises(RuntimeError): branca.decode(token)
def test_should_throw_with_modified_version(): key = unhexlify( "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974") branca = Branca(key) token = "89mvl3S0BE0UCMIY94xxIux4eg1w5oXrhvCEXrDAjusSbO0Yk7AU6FjjTnbTWTqogLfNPJLzecHVb" with pytest.raises(RuntimeError): branca.decode(token)
def test_should_throw_with_wrong_version(): key = unhexlify( "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974") branca = Branca(key) token = "89mvl3RkwXjpEj5WMxK7GUDEHEeeeZtwjMIOogTthvr44qBfYtQSIZH5MHOTC0GzoutDIeoPVZk3w" with pytest.raises(RuntimeError): branca.decode(token)
def test_decode_non_utf8_payload(): key = unhexlify( "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974") branca = Branca(key) token = "K9u6d0zjXp8RXNUGDyXAsB9AtPo60CD3xxQ2ulL8aQoTzXbvockRff0y1eXoHm" assert branca.decode(token) == b"\x80" assert branca.timestamp(token) == 123206400
def test_decode_eight_nul_bytes_with_max_timestamp(): key = unhexlify( "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974") branca = Branca(key) token = "1jrx6DUu5q06oxykef2e2ZMyTcDRTQot9ZnwgifUtzAphGtjsxfbxXNhQyBEOGtpbkBgvIQx" assert branca.decode(token) == b"\x00\x00\x00\x00\x00\x00\x00\x00" assert branca.timestamp(token) == 4294967295
def test_encode_hello_world_with_max_timestamp(): key = unhexlify( "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974") branca = Branca(key) branca._nonce = unhexlify( "beefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef") token = branca.encode("Hello world!", timestamp=4294967295) assert token == "89i7YCwu5tWAJNHUDdmIqhzOi5hVHOd4afjZcGMcVmM4enl4yeLiDyYv41eMkNmTX6IwYEFErCSqr"
def test_encode_hello_world_with_zero_timestamp(): key = unhexlify( "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974") branca = Branca(key) branca._nonce = unhexlify( "beefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef") token = branca.encode("Hello world!", timestamp=0) assert token == "870S4BYxgHw0KnP3W9fgVUHEhT5g86vJ17etaC5Kh5uIraWHCI1psNQGv298ZmjPwoYbjDQ9chy2z"
def other_impl_decode(string: bytes): with mock.branca_impl(): with mock.with_config() as config: key = config["manabi"]["key"] f = Branca(from_string(key)) ct = f.encode(string) proc = run(["cargo", "run", "decode", key, ct], stdout=PIPE, check=True) assert from_string(proc.stdout.decode("UTF-8")) == string
def test_encode_eight_nul_bytes_with_zero_timestamp(): key = unhexlify( "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974") branca = Branca(key) branca._nonce = unhexlify( "beefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef") token = branca.encode(b"\x00\x00\x00\x00\x00\x00\x00\x00", timestamp=0) assert token == "1jIBheHbDdkCDFQmtgw4RUZeQoOJgGwTFJSpwOAk3XYpJJr52DEpILLmmwYl4tjdSbbNqcF1"
def test_encode_non_utf8_payload(): key = unhexlify( "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974") branca = Branca(key) branca._nonce = unhexlify( "beefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef") token = branca.encode(b"", timestamp=0) assert token == "4sfD0vPFhIif8cy4nB3BQkHeJqkOkDvinI4zIhMjYX4YXZU5WIq9ycCVjGzB5"
def test_encode_hello_world_with_november_27_timestamp(): key = unhexlify( "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974") branca = Branca(key) branca._nonce = unhexlify( "beefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef") token = branca.encode("Hello world!", timestamp=123206400) assert token == "875GH23U0Dr6nHFA63DhOyd9LkYudBkX8RsCTOMz5xoYAMw9sMd5QwcEqLDRnTDHPenOX7nP2trlT"
def test_encode_eight_nul_bytes_with_november_27_timestamp(): key = unhexlify( "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974") branca = Branca(key) branca._nonce = unhexlify( "beefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef") token = branca.encode(b"\x00\x00\x00\x00\x00\x00\x00\x00", timestamp=123206400) assert token == "1jJDJOEjuwVb9Csz1Ypw1KBWSkr0YDpeBeJN6NzJWx1VgPLmcBhu2SbkpQ9JjZ3nfUf7Aytp"
def test_encode_eight_nul_bytes_with_zero_timestamp(): key = unhexlify( "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974") branca = Branca(key) branca._nonce = unhexlify( "beefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef") token = branca.encode(b"\x00\x00\x00\x00\x00\x00\x00\x00", timestamp=4294967295) assert token == "1jrx6DUu5q06oxykef2e2ZMyTcDRTQot9ZnwgifUtzAphGtjsxfbxXNhQyBEOGtpbkBgvIQx"
def from_ciphertext(cls, key: Key, ciphertext: str) -> "Token": assert ciphertext branca = Branca(key.data) try: timestamp = branca.timestamp(ciphertext) except (struct.error, ValueError): return cls(key, None, None) try: token_path = Path(branca.decode(ciphertext).decode("UTF-8")) except RuntimeError: return cls(key, None, timestamp) return cls(key, token_path, timestamp, ciphertext)
def __init__(self, app, secret): """Initialize the Api Token manager. Parameters ---------- app : aiohttp.web.Application The aiohttp application instance secret : str The secret key used to sign the API tokens (after hashing) """ self.app = app self.branca = Branca(key=sha3_256(secret.encode("utf-8")).digest()) logger.debug("created Branca instance")
def test_should_throw_when_expired(): key = unhexlify( "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974") branca = Branca(key) branca._nonce = unhexlify( "0102030405060708090a0b0c0102030405060708090a0b0c") token = branca.encode(b"Hello world!", timestamp=123206400) with pytest.raises(RuntimeError): branca.decode(token, 3600)
def __init__(self, app, secret): self.app = app self.branca = Branca(key=sha3_256(secret.encode("utf-8")).digest()) logger.debug("created Branca instance")
class ApiTokens: def __init__(self, app, secret): self.app = app self.branca = Branca(key=sha3_256(secret.encode("utf-8")).digest()) logger.debug("created Branca instance") async def generate_token(self, username, token_name=str(uuid4()), permissions="*"): if not token_name: token_name = str(uuid4()) logger.debug( f"generating a token for {username} with these permissions: {permissions}" ) user = await find_user_by_username( self.app, username, ["permissions", "password", "_id"] ) if permissions == "*": permissions = list(user.get("permissions", [])) else: permissions = list( filter( lambda permission: permission in user.get("permissions", []), permissions, ) ) packed = msgpack.dumps( {"username": username, "permisisons": permissions, "token_name": token_name} ) token = self.branca.encode(packed) logger.debug( f"successfuly generated a token for {username}: {token}. Adding it to the database" ) await add_token_to_user(self.app, user.get("_id", ""), token_name, token) return ( token, token_name, permissions, ) async def validate_token(self, token, permissions=[], raise_error=False): logger.debug( f"validating a token{' for permissions' + permissions if permissions != [] else ''}. Token: {token}" ) user = await find_user_by_token(self.app, token, ["permissions", "username"]) packed = self.branca.decode(token) payload = msgpack.loads(packed, raw=False) logger.debug(f"decoded token: {payload}") if user == None: if raise_error: raise HTTPUnauthorized() return False user_permission_set = set(user.get("permissions", [])) if not set(payload.get("permissions", [])).issubset(user_permission_set): payload["permissions"] = filter( lambda permission: permission in user_permission_set, payload["permissions"], ) if ( # check if token has all requested permissions not set(permissions).issubset(set(payload.get("permissions", []))) # check if user has all requested permissions or not set(permissions).issubset(user_permission_set) ): if raise_error: raise HTTPForbidden() return False logger.debug("Token is valid") return True async def get_token_info(self, token): user = await find_user_by_token(self.app, token, ["permissions", "username"]) packed = self.branca.decode(token) payload = msgpack.loads(packed, raw=False) if user == None: return payload, None user_permission_set = set(user.get("permissions", [])) if not set(payload.get("permissions", [])).issubset(user_permission_set): payload["permissions"] = filter( lambda permission: permission in user_permission_set, payload["permissions"], ) return payload, user