Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use standard system package for ECDSA verification and add tests #460

Merged
merged 4 commits into from
May 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packaging/aleph-vm/DEBIAN/control
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ Version: 0.1.8
Architecture: all
Maintainer: Aleph.im
Description: Aleph.im VM execution engine
Depends: python3,python3-pip,python3-aiohttp,python3-msgpack,python3-aiodns,python3-alembic,python3-sqlalchemy,python3-setproctitle,redis,python3-aioredis,python3-psutil,sudo,acl,curl,systemd-container,squashfs-tools,debootstrap,python3-packaging,python3-cpuinfo,python3-nftables,python3-jsonschema,cloud-image-utils,ndppd,python3-yaml,python3-dotenv,python3-schedule,qemu-system-x86,qemu-utils,python3-systemd,python3-dbus,btrfs-progs,nftables
Depends: python3,python3-pip,python3-aiohttp,python3-msgpack,python3-aiodns,python3-alembic,python3-sqlalchemy,python3-setproctitle,redis,python3-aioredis,python3-psutil,sudo,acl,curl,systemd-container,squashfs-tools,debootstrap,python3-packaging,python3-cpuinfo,python3-nftables,python3-jsonschema,cloud-image-utils,ndppd,python3-yaml,python3-dotenv,python3-schedule,qemu-system-x86,qemu-utils,python3-systemd,python3-dbus,btrfs-progs,nftables,python3-jwcrypto
Section: aleph-im
Priority: Extra
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ dependencies = [
"setproctitle==1.3.3",
"pyyaml==6.0.1",
"aleph-message==0.4.4",
"jwskate==0.8.0",
"eth-account~=0.10",
"sentry-sdk==1.31.0",
"aioredis==1.3.1",
Expand All @@ -51,6 +50,7 @@ dependencies = [
"alembic==1.13.1",
"aiohttp_cors~=0.7.0",
"pyroute2==0.7.12",
"jwcrypto==1.5.6",
]

[project.urls]
Expand Down
2 changes: 1 addition & 1 deletion src/aleph/vm/controllers/qemu/QEMU.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ These are installable via

This branch depends on the version 0.4.1 of `aleph-message` that add the `hypervisor` field. The easiest way is to install tha version using `pip install -e .`

To create a local venv use the `--system-site-packages` option so it can acess nftables
To create a local venv use the `--system-site-packages` option so it can access nftables

## To test launching a VM instance

Expand Down
57 changes: 34 additions & 23 deletions src/aleph/vm/orchestrator/views/authentication.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
# Keep datetime import as is as it allow patching in test
olethanh marked this conversation as resolved.
Show resolved Hide resolved
import datetime
import functools
import json
import logging
from collections.abc import Awaitable, Coroutine
from datetime import datetime, timedelta, timezone
from typing import Any, Callable, Literal, Union

import cryptography.exceptions
import pydantic
from aiohttp import web
from eth_account import Account
from eth_account.messages import encode_defunct
from jwskate import Jwk
from jwcrypto import jwk, jws
from jwcrypto.jwa import JWA
from pydantic import BaseModel, ValidationError, root_validator, validator

from aleph.vm.conf import settings

logger = logging.getLogger(__name__)


def is_token_still_valid(timestamp):
def is_token_still_valid(datestr: str):
"""
Checks if a token has expired based on its expiry timestamp
"""
current_datetime = datetime.now(tz=timezone.utc)
expiry_datetime = datetime.fromisoformat(timestamp)
current_datetime = datetime.datetime.now(tz=datetime.timezone.utc)
expiry_datetime = datetime.datetime.fromisoformat(datestr.replace("Z", "+00:00"))

return expiry_datetime > current_datetime

Expand All @@ -48,9 +51,9 @@
expires: str

@property
def json_web_key(self) -> Jwk:
def json_web_key(self) -> jwk.JWK:
"""Return the ephemeral public key as Json Web Key"""
return Jwk(self.pubkey)
return jwk.JWK(**self.pubkey)


class SignedPubKeyHeader(BaseModel):
Expand Down Expand Up @@ -95,16 +98,16 @@


class SignedOperationPayload(BaseModel):
time: datetime
time: datetime.datetime
method: Union[Literal["POST"], Literal["GET"]]
path: str
# body_sha256: str # disabled since there is no body

@validator("time")
def time_is_current(cls, v: datetime) -> datetime:
def time_is_current(cls, v: datetime.datetime) -> datetime.datetime:
"""Check that the time is current and the payload is not a replay attack."""
max_past = datetime.now(tz=timezone.utc) - timedelta(minutes=2)
max_future = datetime.now(tz=timezone.utc) + timedelta(minutes=2)
max_past = datetime.datetime.now(tz=datetime.timezone.utc) - datetime.timedelta(minutes=2)
max_future = datetime.datetime.now(tz=datetime.timezone.utc) + datetime.timedelta(minutes=2)
if v < max_past:
raise ValueError("Time is too far in the past")
if v > max_future:
Expand Down Expand Up @@ -154,13 +157,17 @@
raise web.HTTPBadRequest(reason="Invalid X-SignedPubKey fields") from error
except json.JSONDecodeError as error:
raise web.HTTPBadRequest(reason="Invalid X-SignedPubKey format") from error
except ValueError as error:
if error.args == ("Token expired",):
raise web.HTTPUnauthorized(reason="Token expired") from error
elif error.args == ("Invalid signature",):
raise web.HTTPUnauthorized(reason="Invalid signature") from error
except ValueError as errors:
olethanh marked this conversation as resolved.
Show resolved Hide resolved
logging.debug(errors)
for err in errors.args[0]:
if isinstance(err.exc, json.JSONDecodeError):
raise web.HTTPBadRequest(reason="Invalid X-SignedPubKey format") from errors
if str(err.exc) == "Token expired":
raise web.HTTPUnauthorized(reason="Token expired") from errors
if str(err.exc) == "Invalid signature":
raise web.HTTPUnauthorized(reason="Invalid signature") from errors
else:
raise error
raise errors

Check warning on line 170 in src/aleph/vm/orchestrator/views/authentication.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/orchestrator/views/authentication.py#L170

Added line #L170 was not covered by tests


def get_signed_operation(request: web.Request) -> SignedOperation:
Expand All @@ -179,14 +186,14 @@

def verify_signed_operation(signed_operation: SignedOperation, signed_pubkey: SignedPubKeyHeader) -> str:
"""Verify that the operation is signed by the ephemeral key authorized by the wallet."""
if signed_pubkey.content.json_web_key.verify(
data=signed_operation.payload,
signature=signed_operation.signature,
alg="ES256",
):
pubkey = signed_pubkey.content.json_web_key

try:
JWA.signing_alg("ES256").verify(pubkey, signed_operation.payload, signed_operation.signature)
logger.debug("Signature verified")
return signed_pubkey.content.address
else:
except cryptography.exceptions.InvalidSignature as e:
logger.debug("Failing to validate signature for operation", e)

Check warning on line 196 in src/aleph/vm/orchestrator/views/authentication.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/orchestrator/views/authentication.py#L195-L196

Added lines #L195 - L196 were not covered by tests
raise web.HTTPUnauthorized(reason="Signature could not verified")


Expand Down Expand Up @@ -225,6 +232,10 @@
authenticated_sender: str = await authenticate_jwk(request)
except web.HTTPException as e:
return web.json_response(data={"error": e.reason}, status=e.status)
except Exception as e:

Check warning on line 235 in src/aleph/vm/orchestrator/views/authentication.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/orchestrator/views/authentication.py#L235

Added line #L235 was not covered by tests
# Unexpected make sure to log it
logging.exception(e)
olethanh marked this conversation as resolved.
Show resolved Hide resolved
raise

Check warning on line 238 in src/aleph/vm/orchestrator/views/authentication.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/orchestrator/views/authentication.py#L237-L238

Added lines #L237 - L238 were not covered by tests

response = await handler(request, authenticated_sender)
return response
Expand Down
Loading