Switch to crypto to typed keys everywhere

This commit is contained in:
Christien Rioux 2025-08-22 16:53:37 -04:00
parent 88f69ce237
commit 848da0ae4e
180 changed files with 8532 additions and 8488 deletions

View file

@ -1,4 +0,0 @@
#!/bin/bash
poetry config --local virtualenvs.in-project true
export PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring
poetry install

View file

@ -6,17 +6,17 @@ from veilid.api import CryptoSystem
@pytest.mark.asyncio
async def test_best_crypto_system(api_connection: veilid.VeilidAPI):
cs: CryptoSystem = await api_connection.best_crypto_system()
async with cs:
assert await cs.default_salt_length() == 16
async def test_valid_crypto_kinds(api_connection: veilid.VeilidAPI):
kinds = await api_connection.valid_crypto_kinds()
assert len(kinds) > 0
@pytest.mark.asyncio
async def test_get_crypto_system(api_connection: veilid.VeilidAPI):
cs: CryptoSystem = await api_connection.get_crypto_system(veilid.CryptoKind.CRYPTO_KIND_VLD0)
async with cs:
assert await cs.default_salt_length() == 16
for kind in await api_connection.valid_crypto_kinds():
cs = await api_connection.get_crypto_system(kind)
async with cs:
assert await cs.kind() == kind
@pytest.mark.asyncio
@ -31,72 +31,76 @@ async def test_get_crypto_system_invalid(api_connection: veilid.VeilidAPI):
@pytest.mark.asyncio
async def test_hash_and_verify_password(api_connection: veilid.VeilidAPI):
cs = await api_connection.best_crypto_system()
async with cs:
nonce = await cs.random_nonce()
salt = nonce.to_bytes()
for kind in await api_connection.valid_crypto_kinds():
cs = await api_connection.get_crypto_system(kind)
async with cs:
nonce = await cs.random_nonce()
salt = nonce.to_bytes()
# Password match
phash = await cs.hash_password(b"abc123", salt)
assert await cs.verify_password(b"abc123", phash)
# Password match
phash = await cs.hash_password(b"abc123", salt)
assert await cs.verify_password(b"abc123", phash)
# Password mismatch
await cs.hash_password(b"abc1234", salt)
assert not await cs.verify_password(b"abc12345", phash)
# Password mismatch
await cs.hash_password(b"abc1234", salt)
assert not await cs.verify_password(b"abc12345", phash)
@pytest.mark.asyncio
async def test_sign_and_verify_signature(api_connection: veilid.VeilidAPI):
cs = await api_connection.best_crypto_system()
async with cs:
kp1 = await cs.generate_key_pair()
kp2 = await cs.generate_key_pair()
# BareSignature match
sig = await cs.sign(kp1.key(), kp1.secret(), b"abc123")
assert await cs.verify(kp1.key(), b"abc123", sig)
for kind in await api_connection.valid_crypto_kinds():
cs = await api_connection.get_crypto_system(kind)
async with cs:
kp1 = await cs.generate_key_pair()
kp2 = await cs.generate_key_pair()
# BareSignature mismatch
sig2 = await cs.sign(kp1.key(), kp1.secret(), b"abc1234")
assert await cs.verify(kp1.key(), b"abc1234", sig2)
assert not await cs.verify(kp1.key(), b"abc12345", sig2)
assert not await cs.verify(kp2.key(), b"abc1234", sig2)
# Signature match
sig = await cs.sign(kp1.key(), kp1.secret(), b"abc123")
assert await cs.verify(kp1.key(), b"abc123", sig)
# Signature mismatch
sig2 = await cs.sign(kp1.key(), kp1.secret(), b"abc1234")
assert await cs.verify(kp1.key(), b"abc1234", sig2)
assert not await cs.verify(kp1.key(), b"abc12345", sig2)
assert not await cs.verify(kp2.key(), b"abc1234", sig2)
@pytest.mark.asyncio
async def test_sign_and_verify_signatures(api_connection: veilid.VeilidAPI):
cs = await api_connection.best_crypto_system()
async with cs:
kind = await cs.kind()
kp1 = await cs.generate_key_pair()
# BareSignature match
sigs = await api_connection.generate_signatures(b"abc123", [veilid.KeyPair.from_value(kind, kp1)])
keys = [veilid.PublicKey.from_value(kind,kp1.key())]
assert (await api_connection.verify_signatures(keys, b"abc123", sigs)) == keys
for kind in await api_connection.valid_crypto_kinds():
cs = await api_connection.get_crypto_system(kind)
async with cs:
kind = await cs.kind()
kp1 = await cs.generate_key_pair()
# BareSignature mismatch
assert (await api_connection.verify_signatures([veilid.PublicKey.from_value(kind,kp1.key())], b"abc1234", sigs)) is None
# BareSignature match
sigs = await api_connection.generate_signatures(b"abc123", [kp1])
keys = [kp1.key()]
assert (await api_connection.verify_signatures(keys, b"abc123", sigs)) == keys
# BareSignature mismatch
assert (await api_connection.verify_signatures([kp1.key()], b"abc1234", sigs)) is None
@pytest.mark.asyncio
async def test_generate_shared_secret(api_connection: veilid.VeilidAPI):
cs = await api_connection.best_crypto_system()
async with cs:
kp1 = await cs.generate_key_pair()
kp2 = await cs.generate_key_pair()
kp3 = await cs.generate_key_pair()
for kind in await api_connection.valid_crypto_kinds():
cs = await api_connection.get_crypto_system(kind)
async with cs:
kp1 = await cs.generate_key_pair()
kp2 = await cs.generate_key_pair()
kp3 = await cs.generate_key_pair()
ssA = await cs.generate_shared_secret(kp1.key(), kp2.secret(), b"abc123")
ssB = await cs.generate_shared_secret(kp2.key(), kp1.secret(), b"abc123")
ssA = await cs.generate_shared_secret(kp1.key(), kp2.secret(), b"abc123")
ssB = await cs.generate_shared_secret(kp2.key(), kp1.secret(), b"abc123")
assert ssA == ssB
assert ssA == ssB
ssC = await cs.generate_shared_secret(kp2.key(), kp1.secret(), b"abc1234")
ssC = await cs.generate_shared_secret(kp2.key(), kp1.secret(), b"abc1234")
assert ssA != ssC
assert ssA != ssC
ssD = await cs.generate_shared_secret(kp3.key(), kp1.secret(), b"abc123")
ssD = await cs.generate_shared_secret(kp3.key(), kp1.secret(), b"abc123")
assert ssA != ssD
assert ssA != ssD

File diff suppressed because it is too large Load diff

View file

@ -68,10 +68,10 @@ async def test_routing_context_app_message_loopback():
rc = await api.new_routing_context()
async with rc:
# make a new local private route
prl, blob = await api.new_private_route()
route_blob = await api.new_private_route()
try:
# import it as a remote route as well so we can send to it
prr = await api.import_remote_private_route(blob)
prr = await api.import_remote_private_route(route_blob.blob)
try:
# send an app message to our own private route
message = b"abcd1234"
@ -91,7 +91,7 @@ async def test_routing_context_app_message_loopback():
await api.release_private_route(prr)
finally:
# release local private route
await api.release_private_route(prl)
await api.release_private_route(route_blob.route_id)
@pytest.mark.asyncio
@ -115,10 +115,10 @@ async def test_routing_context_app_call_loopback():
rc = await api.new_routing_context()
async with rc:
# make a new local private route
prl, blob = await api.new_private_route()
route_blob = await api.new_private_route()
try:
# import it as a remote route as well so we can send to it
prr = await api.import_remote_private_route(blob)
prr = await api.import_remote_private_route(route_blob.blob)
try:
# send an app message to our own private route
@ -146,7 +146,7 @@ async def test_routing_context_app_call_loopback():
await api.release_private_route(prr)
finally:
# release local private route
await api.release_private_route(prl)
await api.release_private_route(route_blob.route_id)
@pytest.mark.asyncio
async def test_routing_context_app_message_loopback_big_packets():
@ -175,10 +175,10 @@ async def test_routing_context_app_message_loopback_big_packets():
rc = await api.new_routing_context()
async with rc:
# make a new local private route
prl, blob = await api.new_private_route()
route_blob = await api.new_private_route()
try:
# import it as a remote route as well so we can send to it
prr = await api.import_remote_private_route(blob)
prr = await api.import_remote_private_route(route_blob.blob)
try:
# do this test 5 times
for _ in range(5):
@ -203,7 +203,7 @@ async def test_routing_context_app_message_loopback_big_packets():
await api.release_private_route(prr)
finally:
# release local private route
await api.release_private_route(prl)
await api.release_private_route(route_blob.route_id)
@pytest.mark.asyncio
@ -241,13 +241,13 @@ async def test_routing_context_app_call_loopback_big_packets():
rc = await api.new_routing_context()
async with rc:
# make a new local private route
prl, blob = await api.new_custom_private_route(
route_blob = await api.new_custom_private_route(
[veilid.CryptoKind.CRYPTO_KIND_VLD0],
veilid.Stability.RELIABLE,
veilid.Sequencing.ENSURE_ORDERED)
try:
# import it as a remote route as well so we can send to it
prr = await api.import_remote_private_route(blob)
prr = await api.import_remote_private_route(route_blob.blob)
try:
# do this test 5 times
for _ in range(5):
@ -261,7 +261,7 @@ async def test_routing_context_app_call_loopback_big_packets():
await api.release_private_route(prr)
finally:
# release local private route
await api.release_private_route(prl)
await api.release_private_route(route_blob.route_id)
finally:
app_call_task.cancel()
@ -288,10 +288,10 @@ async def test_routing_context_app_message_loopback_bandwidth():
rc = await api.new_routing_context()
async with rc:
# make a new local private route
prl, blob = await api.new_private_route()
route_blob = await api.new_private_route()
try:
# import it as a remote route as well so we can send to it
prr = await api.import_remote_private_route(blob)
prr = await api.import_remote_private_route(route_blob.blob)
try:
# do this test 10000 times
message = random.randbytes(16384)
@ -307,4 +307,4 @@ async def test_routing_context_app_message_loopback_bandwidth():
await api.release_private_route(prr)
finally:
# release local private route
await api.release_private_route(prl)
await api.release_private_route(route_blob.route_id)

View file

@ -63,7 +63,7 @@ class RoutingContext(ABC):
@abstractmethod
async def create_dht_record(
self, schema: types.DHTSchema, owner: Optional[types.KeyPair] = None, kind: Optional[types.CryptoKind] = None
self, kind: types.CryptoKind, schema: types.DHTSchema, owner: Optional[types.KeyPair] = None
) -> types.DHTRecordDescriptor:
pass
@ -240,19 +240,19 @@ class CryptoSystem(ABC):
pass
@abstractmethod
async def cached_dh(self, key: types.BarePublicKey, secret: types.BareSecretKey) -> types.BareSharedSecret:
async def cached_dh(self, key: types.PublicKey, secret: types.SecretKey) -> types.SharedSecret:
pass
@abstractmethod
async def compute_dh(
self, key: types.BarePublicKey, secret: types.BareSecretKey
) -> types.BareSharedSecret:
self, key: types.PublicKey, secret: types.SecretKey
) -> types.SharedSecret:
pass
@abstractmethod
async def generate_shared_secret(
self, key: types.BarePublicKey, secret: types.BareSecretKey, domain: bytes
) -> types.BareSharedSecret:
self, key: types.PublicKey, secret: types.SecretKey, domain: bytes
) -> types.SharedSecret:
pass
@abstractmethod
@ -292,27 +292,27 @@ class CryptoSystem(ABC):
pass
@abstractmethod
async def check_shared_secret(self, secret: types.BareSharedSecret):
async def check_shared_secret(self, secret: types.SharedSecret):
pass
@abstractmethod
async def check_nonce(self, nonce: types.BareNonce):
async def check_nonce(self, nonce: types.Nonce):
pass
@abstractmethod
async def check_hash_digest(self, digest: types.BareHashDigest):
async def check_hash_digest(self, digest: types.HashDigest):
pass
@abstractmethod
async def check_public_key(self, key: types.BarePublicKey):
async def check_public_key(self, key: types.PublicKey):
pass
@abstractmethod
async def check_secret_key(self, key: types.BareSecretKey):
async def check_secret_key(self, key: types.SecretKey):
pass
@abstractmethod
async def check_signature(self, signature: types.BareSignature):
async def check_signature(self, signature: types.Signature):
pass
@abstractmethod
@ -324,55 +324,49 @@ class CryptoSystem(ABC):
pass
@abstractmethod
async def derive_shared_secret(self, password: bytes, salt: bytes) -> types.BareSharedSecret:
async def derive_shared_secret(self, password: bytes, salt: bytes) -> types.SharedSecret:
pass
@abstractmethod
async def random_nonce(self) -> types.BareNonce:
async def random_nonce(self) -> types.Nonce:
pass
@abstractmethod
async def random_shared_secret(self) -> types.BareSharedSecret:
async def random_shared_secret(self) -> types.SharedSecret:
pass
@abstractmethod
async def generate_key_pair(self) -> types.BareKeyPair:
async def generate_key_pair(self) -> types.KeyPair:
pass
@abstractmethod
async def generate_hash(self, data: bytes) -> types.BareHashDigest:
async def generate_hash(self, data: bytes) -> types.HashDigest:
pass
@abstractmethod
async def validate_key_pair(self, key: types.BarePublicKey, secret: types.BareSecretKey) -> bool:
async def validate_key_pair(self, key: types.PublicKey, secret: types.SecretKey) -> bool:
pass
@abstractmethod
async def validate_hash(self, data: bytes, hash_digest: types.BareHashDigest) -> bool:
pass
@abstractmethod
async def distance(
self, key1: types.BareHashDigest, key2: types.BareHashDigest
) -> types.BareHashDistance:
async def validate_hash(self, data: bytes, hash_digest: types.HashDigest) -> bool:
pass
@abstractmethod
async def sign(
self, key: types.BarePublicKey, secret: types.BareSecretKey, data: bytes
) -> types.BareSignature:
self, key: types.PublicKey, secret: types.SecretKey, data: bytes
) -> types.Signature:
pass
@abstractmethod
async def verify(self, key: types.BarePublicKey, data: bytes, signature: types.BareSignature) -> bool:
async def verify(self, key: types.PublicKey, data: bytes, signature: types.Signature) -> bool:
pass
@abstractmethod
async def decrypt_aead(
self,
body: bytes,
nonce: types.BareNonce,
shared_secret: types.BareSharedSecret,
nonce: types.Nonce,
shared_secret: types.SharedSecret,
associated_data: Optional[bytes],
) -> bytes:
pass
@ -381,15 +375,15 @@ class CryptoSystem(ABC):
async def encrypt_aead(
self,
body: bytes,
nonce: types.BareNonce,
shared_secret: types.BareSharedSecret,
nonce: types.Nonce,
shared_secret: types.SharedSecret,
associated_data: Optional[bytes],
) -> bytes:
pass
@abstractmethod
async def crypt_no_auth(
self, body: bytes, nonce: types.BareNonce, shared_secret: types.BareSharedSecret
self, body: bytes, nonce: types.Nonce, shared_secret: types.SharedSecret
) -> bytes:
pass
@ -440,7 +434,7 @@ class VeilidAPI(ABC):
pass
@abstractmethod
async def new_private_route(self) -> tuple[types.RouteId, bytes]:
async def new_private_route(self) -> types.RouteBlob:
pass
@abstractmethod
@ -449,7 +443,7 @@ class VeilidAPI(ABC):
kinds: list[types.CryptoKind],
stability: types.Stability,
sequencing: types.Sequencing,
) -> tuple[types.RouteId, bytes]:
) -> types.RouteBlob:
pass
@abstractmethod
@ -480,10 +474,6 @@ class VeilidAPI(ABC):
async def get_crypto_system(self, kind: types.CryptoKind) -> CryptoSystem:
pass
@abstractmethod
async def best_crypto_system(self) -> CryptoSystem:
pass
@abstractmethod
async def verify_signatures(
self,
@ -530,3 +520,7 @@ class VeilidAPI(ABC):
@abstractmethod
async def default_veilid_config(self) -> str:
pass
@abstractmethod
async def valid_crypto_kinds(self) -> list[types.CryptoKind]:
pass

View file

@ -2,7 +2,6 @@ import asyncio
import importlib.resources as importlib_resources
import json
import os
import traceback
from typing import Awaitable, Callable, Optional, Self
@ -20,25 +19,24 @@ from .operations import (
)
from .state import VeilidState, VeilidUpdate
from .types import (
BareHashDistance,
CryptoKind,
DHTRecordDescriptor,
DHTRecordReport,
DHTReportScope,
DHTSchema,
BareHashDigest,
BareKeyPair,
NewPrivateRouteResult,
BareNonce,
HashDigest,
KeyPair,
RouteBlob,
Nonce,
OperationId,
BarePublicKey,
PublicKey,
RouteId,
SafetySelection,
BareSecretKey,
SecretKey,
Sequencing,
SetDHTValueOptions,
BareSharedSecret,
BareSignature,
SharedSecret,
Signature,
MemberId,
Stability,
Timestamp,
@ -62,7 +60,7 @@ _STREAM_LIMIT = (65536 * 4)
def _get_schema_validator(schema):
cls = validators.validator_for(schema)
cls.check_schema(schema)
validator = cls(schema)
validator = cls(schema) # type: ignore
return validator
@ -345,21 +343,21 @@ class _JsonVeilidAPI(VeilidAPI):
)
)
async def new_private_route(self) -> tuple[RouteId, bytes]:
return NewPrivateRouteResult.from_json(
async def new_private_route(self) -> RouteBlob:
return RouteBlob.from_json(
raise_api_result(await self.send_ndjson_request(Operation.NEW_PRIVATE_ROUTE))
).to_tuple()
)
async def new_custom_private_route(
self, kinds: list[CryptoKind], stability: Stability, sequencing: Sequencing
) -> tuple[RouteId, bytes]:
) -> RouteBlob:
assert isinstance(kinds, list)
for k in kinds:
assert isinstance(k, CryptoKind)
assert isinstance(stability, Stability)
assert isinstance(sequencing, Sequencing)
return NewPrivateRouteResult.from_json(
return RouteBlob.from_json(
raise_api_result(
await self.send_ndjson_request(
Operation.NEW_CUSTOM_PRIVATE_ROUTE,
@ -368,7 +366,7 @@ class _JsonVeilidAPI(VeilidAPI):
sequencing=sequencing,
)
)
).to_tuple()
)
async def import_remote_private_route(self, blob: bytes) -> RouteId:
assert isinstance(blob, bytes)
@ -426,10 +424,6 @@ class _JsonVeilidAPI(VeilidAPI):
)
return _JsonCryptoSystem(self, cs_id)
async def best_crypto_system(self) -> CryptoSystem:
cs_id = raise_api_result(await self.send_ndjson_request(Operation.BEST_CRYPTO_SYSTEM))
return _JsonCryptoSystem(self, cs_id)
async def verify_signatures(
self, node_ids: list[PublicKey], data: bytes, signatures: list[Signature]
) -> Optional[list[PublicKey]]:
@ -508,6 +502,17 @@ class _JsonVeilidAPI(VeilidAPI):
async def default_veilid_config(self) -> str:
return raise_api_result(await self.send_ndjson_request(Operation.DEFAULT_VEILID_CONFIG))
async def valid_crypto_kinds(self) -> list[CryptoKind]:
return list(
map(
lambda x: CryptoKind(x),
raise_api_result(
await self.send_ndjson_request(Operation.VALID_CRYPTO_KINDS)
)
)
)
######################################################
@ -665,11 +670,11 @@ class _JsonRoutingContext(RoutingContext):
)
async def create_dht_record(
self, schema: DHTSchema, owner: Optional[KeyPair] = None, kind: Optional[CryptoKind] = None
self, kind: CryptoKind, schema: DHTSchema, owner: Optional[KeyPair] = None
) -> DHTRecordDescriptor:
assert isinstance(kind, CryptoKind)
assert isinstance(schema, DHTSchema)
assert owner is None or isinstance(owner, KeyPair)
assert kind is None or isinstance(kind, CryptoKind)
return DHTRecordDescriptor.from_json(
raise_api_result(
@ -1134,11 +1139,11 @@ class _JsonCryptoSystem(CryptoSystem):
)
self.done = True
async def cached_dh(self, key: BarePublicKey, secret: BareSecretKey) -> BareSharedSecret:
assert isinstance(key, BarePublicKey)
assert isinstance(secret, BareSecretKey)
async def cached_dh(self, key: PublicKey, secret: SecretKey) -> SharedSecret:
assert isinstance(key, PublicKey)
assert isinstance(secret, SecretKey)
return BareSharedSecret(
return SharedSecret(
raise_api_result(
await self.api.send_ndjson_request(
Operation.CRYPTO_SYSTEM,
@ -1151,11 +1156,11 @@ class _JsonCryptoSystem(CryptoSystem):
)
)
async def compute_dh(self, key: BarePublicKey, secret: BareSecretKey) -> BareSharedSecret:
assert isinstance(key, BarePublicKey)
assert isinstance(secret, BareSecretKey)
async def compute_dh(self, key: PublicKey, secret: SecretKey) -> SharedSecret:
assert isinstance(key, PublicKey)
assert isinstance(secret, SecretKey)
return BareSharedSecret(
return SharedSecret(
raise_api_result(
await self.api.send_ndjson_request(
Operation.CRYPTO_SYSTEM,
@ -1168,12 +1173,12 @@ class _JsonCryptoSystem(CryptoSystem):
)
)
async def generate_shared_secret(self, key: BarePublicKey, secret: BareSecretKey, domain: bytes) -> BareSharedSecret:
assert isinstance(key, BarePublicKey)
assert isinstance(secret, BareSecretKey)
async def generate_shared_secret(self, key: PublicKey, secret: SecretKey, domain: bytes) -> SharedSecret:
assert isinstance(key, PublicKey)
assert isinstance(secret, SecretKey)
assert isinstance(domain, bytes)
return BareSharedSecret(
return SharedSecret(
raise_api_result(
await self.api.send_ndjson_request(
Operation.CRYPTO_SYSTEM,
@ -1282,8 +1287,8 @@ class _JsonCryptoSystem(CryptoSystem):
)
)
async def check_shared_secret(self, secret: BareSharedSecret):
assert isinstance(secret, BareSharedSecret)
async def check_shared_secret(self, secret: SharedSecret):
assert isinstance(secret, SharedSecret)
return raise_api_result(
await self.api.send_ndjson_request(
@ -1295,8 +1300,8 @@ class _JsonCryptoSystem(CryptoSystem):
)
)
async def check_nonce(self, nonce: BareNonce):
assert isinstance(nonce, BareNonce)
async def check_nonce(self, nonce: Nonce):
assert isinstance(nonce, Nonce)
return raise_api_result(
await self.api.send_ndjson_request(
@ -1308,8 +1313,8 @@ class _JsonCryptoSystem(CryptoSystem):
)
)
async def check_hash_digest(self, digest: BareHashDigest):
assert isinstance(digest, BareHashDigest)
async def check_hash_digest(self, digest: HashDigest):
assert isinstance(digest, HashDigest)
return raise_api_result(
await self.api.send_ndjson_request(
@ -1321,8 +1326,8 @@ class _JsonCryptoSystem(CryptoSystem):
)
)
async def check_public_key(self, key: BarePublicKey):
assert isinstance(key, BarePublicKey)
async def check_public_key(self, key: PublicKey):
assert isinstance(key, PublicKey)
return raise_api_result(
await self.api.send_ndjson_request(
@ -1334,8 +1339,8 @@ class _JsonCryptoSystem(CryptoSystem):
)
)
async def check_secret_key(self, key: BareSecretKey):
assert isinstance(key, BareSecretKey)
async def check_secret_key(self, key: SecretKey):
assert isinstance(key, SecretKey)
return raise_api_result(
await self.api.send_ndjson_request(
@ -1347,8 +1352,8 @@ class _JsonCryptoSystem(CryptoSystem):
)
)
async def check_signature(self, signature: BareSignature):
assert isinstance(signature, BareSignature)
async def check_signature(self, signature: Signature):
assert isinstance(signature, Signature)
return raise_api_result(
await self.api.send_ndjson_request(
@ -1390,11 +1395,11 @@ class _JsonCryptoSystem(CryptoSystem):
)
)
async def derive_shared_secret(self, password: bytes, salt: bytes) -> BareSharedSecret:
async def derive_shared_secret(self, password: bytes, salt: bytes) -> SharedSecret:
assert isinstance(password, bytes)
assert isinstance(salt, bytes)
return BareSharedSecret(
return SharedSecret(
raise_api_result(
await self.api.send_ndjson_request(
Operation.CRYPTO_SYSTEM,
@ -1407,8 +1412,8 @@ class _JsonCryptoSystem(CryptoSystem):
)
)
async def random_nonce(self) -> BareNonce:
return BareNonce(
async def random_nonce(self) -> Nonce:
return Nonce(
raise_api_result(
await self.api.send_ndjson_request(
Operation.CRYPTO_SYSTEM,
@ -1419,8 +1424,8 @@ class _JsonCryptoSystem(CryptoSystem):
)
)
async def random_shared_secret(self) -> BareSharedSecret:
return BareSharedSecret(
async def random_shared_secret(self) -> SharedSecret:
return SharedSecret(
raise_api_result(
await self.api.send_ndjson_request(
Operation.CRYPTO_SYSTEM,
@ -1431,8 +1436,8 @@ class _JsonCryptoSystem(CryptoSystem):
)
)
async def generate_key_pair(self) -> BareKeyPair:
return BareKeyPair(
async def generate_key_pair(self) -> KeyPair:
return KeyPair(
raise_api_result(
await self.api.send_ndjson_request(
Operation.CRYPTO_SYSTEM,
@ -1443,10 +1448,10 @@ class _JsonCryptoSystem(CryptoSystem):
)
)
async def generate_hash(self, data: bytes) -> BareHashDigest:
async def generate_hash(self, data: bytes) -> HashDigest:
assert isinstance(data, bytes)
return BareHashDigest(
return HashDigest(
raise_api_result(
await self.api.send_ndjson_request(
Operation.CRYPTO_SYSTEM,
@ -1458,9 +1463,9 @@ class _JsonCryptoSystem(CryptoSystem):
)
)
async def validate_key_pair(self, key: BarePublicKey, secret: BareSecretKey) -> bool:
assert isinstance(key, BarePublicKey)
assert isinstance(secret, BareSecretKey)
async def validate_key_pair(self, key: PublicKey, secret: SecretKey) -> bool:
assert isinstance(key, PublicKey)
assert isinstance(secret, SecretKey)
return raise_api_result(
await self.api.send_ndjson_request(
@ -1473,9 +1478,9 @@ class _JsonCryptoSystem(CryptoSystem):
)
)
async def validate_hash(self, data: bytes, hash_digest: BareHashDigest) -> bool:
async def validate_hash(self, data: bytes, hash_digest: HashDigest) -> bool:
assert isinstance(data, bytes)
assert isinstance(hash_digest, BareHashDigest)
assert isinstance(hash_digest, HashDigest)
return raise_api_result(
await self.api.send_ndjson_request(
@ -1488,29 +1493,12 @@ class _JsonCryptoSystem(CryptoSystem):
)
)
async def distance(self, key1: BareHashDigest, key2: BareHashDigest) -> BareHashDistance:
assert isinstance(key1, BareHashDigest)
assert isinstance(key2, BareHashDigest)
return BareHashDistance(
raise_api_result(
await self.api.send_ndjson_request(
Operation.CRYPTO_SYSTEM,
validate=validate_cs_op,
cs_id=self.cs_id,
cs_op=CryptoSystemOperation.DISTANCE,
key1=key1,
key2=key2,
)
)
)
async def sign(self, key: BarePublicKey, secret: BareSecretKey, data: bytes) -> BareSignature:
assert isinstance(key, BarePublicKey)
assert isinstance(secret, BareSecretKey)
async def sign(self, key: PublicKey, secret: SecretKey, data: bytes) -> Signature:
assert isinstance(key, PublicKey)
assert isinstance(secret, SecretKey)
assert isinstance(data, bytes)
return BareSignature(
return Signature(
raise_api_result(
await self.api.send_ndjson_request(
Operation.CRYPTO_SYSTEM,
@ -1524,10 +1512,10 @@ class _JsonCryptoSystem(CryptoSystem):
)
)
async def verify(self, key: BarePublicKey, data: bytes, signature: BareSignature):
assert isinstance(key, BarePublicKey)
async def verify(self, key: PublicKey, data: bytes, signature: Signature):
assert isinstance(key, PublicKey)
assert isinstance(data, bytes)
assert isinstance(signature, BareSignature)
assert isinstance(signature, Signature)
return raise_api_result(
await self.api.send_ndjson_request(
@ -1544,13 +1532,13 @@ class _JsonCryptoSystem(CryptoSystem):
async def decrypt_aead(
self,
body: bytes,
nonce: BareNonce,
shared_secret: BareSharedSecret,
nonce: Nonce,
shared_secret: SharedSecret,
associated_data: Optional[bytes],
) -> bytes:
assert isinstance(body, bytes)
assert isinstance(nonce, BareNonce)
assert isinstance(shared_secret, BareSharedSecret)
assert isinstance(nonce, Nonce)
assert isinstance(shared_secret, SharedSecret)
assert associated_data is None or isinstance(associated_data, bytes)
return urlsafe_b64decode_no_pad(
@ -1571,13 +1559,13 @@ class _JsonCryptoSystem(CryptoSystem):
async def encrypt_aead(
self,
body: bytes,
nonce: BareNonce,
shared_secret: BareSharedSecret,
nonce: Nonce,
shared_secret: SharedSecret,
associated_data: Optional[bytes],
) -> bytes:
assert isinstance(body, bytes)
assert isinstance(nonce, BareNonce)
assert isinstance(shared_secret, BareSharedSecret)
assert isinstance(nonce, Nonce)
assert isinstance(shared_secret, SharedSecret)
assert associated_data is None or isinstance(associated_data, bytes)
return urlsafe_b64decode_no_pad(
@ -1595,10 +1583,10 @@ class _JsonCryptoSystem(CryptoSystem):
)
)
async def crypt_no_auth(self, body: bytes, nonce: BareNonce, shared_secret: BareSharedSecret) -> bytes:
async def crypt_no_auth(self, body: bytes, nonce: Nonce, shared_secret: SharedSecret) -> bytes:
assert isinstance(body, bytes)
assert isinstance(nonce, BareNonce)
assert isinstance(shared_secret, BareSharedSecret)
assert isinstance(nonce, Nonce)
assert isinstance(shared_secret, SharedSecret)
return urlsafe_b64decode_no_pad(
raise_api_result(
await self.api.send_ndjson_request(

View file

@ -21,7 +21,6 @@ class Operation(StrEnum):
TABLE_DB = "TableDb"
TABLE_DB_TRANSACTION = "TableDbTransaction"
GET_CRYPTO_SYSTEM = "GetCryptoSystem"
BEST_CRYPTO_SYSTEM = "BestCryptoSystem"
CRYPTO_SYSTEM = "CryptoSystem"
VERIFY_SIGNATURES = "VerifySignatures"
GENERATE_SIGNATURES = "GenerateSignatures"
@ -32,6 +31,7 @@ class Operation(StrEnum):
VEILID_VERSION = "VeilidVersion"
VEILID_FEATURES = "VeilidFeatures"
DEFAULT_VEILID_CONFIG = "DefaultVeilidConfig"
VALID_CRYPTO_KINDS = "ValidCryptoKinds"
class RoutingContextOperation(StrEnum):

View file

@ -835,14 +835,34 @@
"cs_op": {
"type": "string",
"const": "ValidateKeyPair"
},
"value": {
"type": "boolean"
}
},
"anyOf": [
{
"type": "object",
"properties": {
"value": {
"type": "boolean"
}
},
"required": [
"value"
]
},
{
"type": "object",
"properties": {
"error": {
"$ref": "#/$defs/VeilidAPIError"
}
},
"required": [
"error"
]
}
],
"required": [
"cs_op",
"value"
"cs_op"
]
},
{
@ -851,30 +871,34 @@
"cs_op": {
"type": "string",
"const": "ValidateHash"
},
"value": {
"type": "boolean"
}
},
"required": [
"cs_op",
"value"
]
},
{
"type": "object",
"properties": {
"cs_op": {
"type": "string",
"const": "Distance"
"anyOf": [
{
"type": "object",
"properties": {
"value": {
"type": "boolean"
}
},
"required": [
"value"
]
},
"value": {
"type": "string"
{
"type": "object",
"properties": {
"error": {
"$ref": "#/$defs/VeilidAPIError"
}
},
"required": [
"error"
]
}
},
],
"required": [
"cs_op",
"value"
"cs_op"
]
},
{
@ -1271,21 +1295,6 @@
"slowest"
]
},
"NewPrivateRouteResult": {
"type": "object",
"properties": {
"blob": {
"type": "string"
},
"route_id": {
"type": "string"
}
},
"required": [
"route_id",
"blob"
]
},
"PeerStats": {
"description": "Statistics for a peer in the routing table",
"type": "object",
@ -1750,7 +1759,7 @@
"type": "object",
"properties": {
"value": {
"$ref": "#/$defs/NewPrivateRouteResult"
"$ref": "#/$defs/RouteBlob"
}
},
"required": [
@ -1786,7 +1795,7 @@
"type": "object",
"properties": {
"value": {
"$ref": "#/$defs/NewPrivateRouteResult"
"$ref": "#/$defs/RouteBlob"
}
},
"required": [
@ -2403,9 +2412,43 @@
"op",
"value"
]
},
{
"type": "object",
"properties": {
"op": {
"type": "string",
"const": "ValidCryptoKinds"
},
"value": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"op",
"value"
]
}
]
},
"RouteBlob": {
"type": "object",
"properties": {
"blob": {
"type": "string"
},
"route_id": {
"type": "string"
}
},
"required": [
"route_id",
"blob"
]
},
"RoutingContextResponse": {
"type": "object",
"properties": {

View file

@ -291,18 +291,6 @@
"kind"
]
},
{
"type": "object",
"properties": {
"op": {
"type": "string",
"const": "BestCryptoSystem"
}
},
"required": [
"op"
]
},
{
"type": "object",
"properties": {
@ -460,6 +448,18 @@
"required": [
"op"
]
},
{
"type": "object",
"properties": {
"op": {
"type": "string",
"const": "ValidCryptoKinds"
}
},
"required": [
"op"
]
}
],
"$defs": {
@ -926,26 +926,6 @@
"hash_digest"
]
},
{
"type": "object",
"properties": {
"cs_op": {
"type": "string",
"const": "Distance"
},
"key1": {
"type": "string"
},
"key2": {
"type": "string"
}
},
"required": [
"cs_op",
"key1",
"key2"
]
},
{
"type": "object",
"properties": {
@ -1347,10 +1327,7 @@
"type": "object",
"properties": {
"kind": {
"type": [
"string",
"null"
]
"type": "string"
},
"owner": {
"type": [
@ -1368,6 +1345,7 @@
},
"required": [
"rc_op",
"kind",
"schema"
]
},

View file

@ -51,6 +51,7 @@ class VeilidLogLevel(StrEnum):
class CryptoKind(StrEnum):
CRYPTO_KIND_NONE = "NONE"
CRYPTO_KIND_VLD0 = "VLD0"
CRYPTO_KIND_VLD1 = "VLD1"
class VeilidCapability(StrEnum):
@ -123,9 +124,6 @@ class EncodedString(str):
assert isinstance(b, bytes)
return cls(urlsafe_b64encode_no_pad(b))
class BareHashDistance(EncodedString):
pass
class BareRecordKey(EncodedString):
pass
@ -144,7 +142,7 @@ class BareHashDigest(EncodedString):
class BareSignature(EncodedString):
pass
class BareNonce(EncodedString):
class Nonce(EncodedString):
pass
@ -193,6 +191,26 @@ class RecordKey(CryptoTyped):
def value(self) -> BareRecordKey:
return BareRecordKey(self._value())
class SharedSecret(CryptoTyped):
@classmethod
def from_value(cls, kind: CryptoKind, value: BareSharedSecret) -> Self:
assert isinstance(kind, CryptoKind)
assert isinstance(value, BareSharedSecret)
return cls(f"{kind}:{value}")
def value(self) -> BareSharedSecret:
return BareSharedSecret(self._value())
class HashDigest(CryptoTyped):
@classmethod
def from_value(cls, kind: CryptoKind, value: BareHashDigest) -> Self:
assert isinstance(kind, CryptoKind)
assert isinstance(value, BareHashDigest)
return cls(f"{kind}:{value}")
def value(self) -> BareHashDigest:
return BareHashDigest(self._value())
class PublicKey(CryptoTyped):
@classmethod
def from_value(cls, kind: CryptoKind, value: BarePublicKey) -> Self:
@ -328,7 +346,7 @@ class VeilidVersion:
return self._patch
class NewPrivateRouteResult:
class RouteBlob:
route_id: RouteId
blob: bytes
@ -339,13 +357,13 @@ class NewPrivateRouteResult:
self.route_id = route_id
self.blob = blob
def to_tuple(self) -> tuple[RouteId, bytes]:
return (self.route_id, self.blob)
@classmethod
def from_json(cls, j: dict) -> Self:
return cls(RouteId(j["route_id"]), urlsafe_b64decode_no_pad(j["blob"]))
def to_json(self) -> dict:
return self.__dict__
class DHTSchemaSMPLMember:
m_key: BareMemberId
@ -445,14 +463,14 @@ class DHTSchemaSMPL(DHTSchema):
class DHTRecordDescriptor:
key: RecordKey
owner: PublicKey
owner_secret: Optional[BareSecretKey]
owner_secret: Optional[SecretKey]
schema: DHTSchema
def __init__(
self,
key: RecordKey,
owner: PublicKey,
owner_secret: Optional[BareSecretKey],
owner_secret: Optional[SecretKey],
schema: DHTSchema,
):
self.key = key
@ -466,19 +484,19 @@ class DHTRecordDescriptor:
def owner_bare_key_pair(self) -> Optional[BareKeyPair]:
if self.owner_secret is None:
return None
return BareKeyPair.from_parts(self.owner.value(), self.owner_secret)
return BareKeyPair.from_parts(self.owner.value(), self.owner_secret.value())
def owner_key_pair(self) -> Optional[KeyPair]:
if self.owner_secret is None:
return None
return KeyPair.from_value(self.owner.kind(), BareKeyPair.from_parts(self.owner.value(), self.owner_secret))
return KeyPair.from_value(self.owner.kind(), BareKeyPair.from_parts(self.owner.value(), self.owner_secret.value()))
@classmethod
def from_json(cls, j: dict) -> Self:
return cls(
RecordKey(j["key"]),
PublicKey(j["owner"]),
None if j["owner_secret"] is None else BareSecretKey(j["owner_secret"]),
None if j["owner_secret"] is None else SecretKey(j["owner_secret"]),
DHTSchema.from_json(j["schema"]),
)
@ -595,14 +613,14 @@ class ValueData:
class SafetySpec:
preferred_route: Optional[BareRouteId]
preferred_route: Optional[RouteId]
hop_count: int
stability: Stability
sequencing: Sequencing
def __init__(
self,
preferred_route: Optional[BareRouteId],
preferred_route: Optional[RouteId],
hop_count: int,
stability: Stability,
sequencing: Sequencing,
@ -615,7 +633,7 @@ class SafetySpec:
@classmethod
def from_json(cls, j: dict) -> Self:
return cls(
BareRouteId(j["preferred_route"]) if "preferred_route" in j else None,
RouteId(j["preferred_route"]) if "preferred_route" in j else None,
j["hop_count"],
Stability(j["stability"]),
Sequencing(j["sequencing"]),