mirror of
https://gitlab.com/veilid/veilid.git
synced 2025-10-14 19:40:38 -04:00
Implement DHT record encryption
This commit is contained in:
parent
848da0ae4e
commit
285d98a185
84 changed files with 2353 additions and 1077 deletions
|
@ -11,7 +11,7 @@ from veilid.types import ValueSeqNum, VeilidJSONEncoder
|
|||
|
||||
##################################################################
|
||||
BOGUS_KEY = veilid.RecordKey.from_value(
|
||||
veilid.CryptoKind.CRYPTO_KIND_VLD0, veilid.BareRecordKey.from_bytes(b' '))
|
||||
veilid.CryptoKind.CRYPTO_KIND_VLD0, veilid.BareRecordKey.from_parts(veilid.BareOpaqueRecordKey.from_bytes(b' '), None))
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
@ -105,10 +105,11 @@ async def test_set_get_dht_value_with_owner(api_connection: veilid.VeilidAPI):
|
|||
async with cs:
|
||||
owner = await cs.generate_key_pair()
|
||||
|
||||
record_key = await rc.get_dht_record_key(veilid.DHTSchema.dflt(2), owner = owner.key())
|
||||
|
||||
rec = await rc.create_dht_record(kind, veilid.DHTSchema.dflt(2), owner=owner)
|
||||
|
||||
record_key = await rc.get_dht_record_key(veilid.DHTSchema.dflt(2), owner = owner.key(), encryption_key=rec.key.encryption_key())
|
||||
|
||||
assert rec.key == record_key
|
||||
assert rec.owner == owner.key()
|
||||
assert rec.owner_secret is not None
|
||||
|
|
|
@ -58,7 +58,7 @@ class RoutingContext(ABC):
|
|||
|
||||
@abstractmethod
|
||||
async def get_dht_record_key(
|
||||
self, schema: types.DHTSchema, owner: types.PublicKey) -> types.RecordKey:
|
||||
self, schema: types.DHTSchema, owner: types.PublicKey, encryption_key: Optional[types.SharedSecret]) -> types.RecordKey:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
|
|
|
@ -654,9 +654,11 @@ class _JsonRoutingContext(RoutingContext):
|
|||
|
||||
|
||||
async def get_dht_record_key(
|
||||
self, schema: DHTSchema, owner: PublicKey) -> RecordKey:
|
||||
self, schema: DHTSchema, owner: PublicKey, encryption_key: Optional[SharedSecret]) -> RecordKey:
|
||||
assert isinstance(schema, DHTSchema)
|
||||
assert isinstance(owner, PublicKey)
|
||||
if encryption_key is not None:
|
||||
assert isinstance(encryption_key, SharedSecret)
|
||||
|
||||
return raise_api_result(
|
||||
await self.api.send_ndjson_request(
|
||||
|
@ -666,6 +668,7 @@ class _JsonRoutingContext(RoutingContext):
|
|||
rc_op=RoutingContextOperation.GET_DHT_RECORD_KEY,
|
||||
schema=schema,
|
||||
owner=owner,
|
||||
encryption_key=encryption_key,
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
@ -1306,6 +1306,12 @@
|
|||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"encryption_key": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"owner": {
|
||||
"type": "string"
|
||||
},
|
||||
|
|
|
@ -8,6 +8,7 @@ from .types import (
|
|||
Timestamp,
|
||||
TimestampDuration,
|
||||
PublicKey,
|
||||
RecordKey,
|
||||
ValueData,
|
||||
ValueSubkey,
|
||||
VeilidLogLevel,
|
||||
|
@ -563,12 +564,12 @@ class VeilidRouteChange:
|
|||
|
||||
|
||||
class VeilidValueChange:
|
||||
key: PublicKey
|
||||
key: RecordKey
|
||||
subkeys: list[tuple[ValueSubkey, ValueSubkey]]
|
||||
count: int
|
||||
value: Optional[ValueData]
|
||||
|
||||
def __init__(self, key: PublicKey, subkeys: list[tuple[ValueSubkey, ValueSubkey]], count: int, value: Optional[ValueData]):
|
||||
def __init__(self, key: RecordKey, subkeys: list[tuple[ValueSubkey, ValueSubkey]], count: int, value: Optional[ValueData]):
|
||||
self.key = key
|
||||
self.subkeys = subkeys
|
||||
self.count = count
|
||||
|
@ -578,7 +579,7 @@ class VeilidValueChange:
|
|||
def from_json(cls, j: dict) -> Self:
|
||||
"""JSON object hook"""
|
||||
return cls(
|
||||
PublicKey(j["key"]),
|
||||
RecordKey(j["key"]),
|
||||
[(p[0], p[1]) for p in j["subkeys"]],
|
||||
j["count"],
|
||||
None if j["value"] is None else ValueData.from_json(j["value"]),
|
||||
|
|
|
@ -124,9 +124,6 @@ class EncodedString(str):
|
|||
assert isinstance(b, bytes)
|
||||
return cls(urlsafe_b64encode_no_pad(b))
|
||||
|
||||
class BareRecordKey(EncodedString):
|
||||
pass
|
||||
|
||||
class BarePublicKey(EncodedString):
|
||||
pass
|
||||
|
||||
|
@ -145,7 +142,6 @@ class BareSignature(EncodedString):
|
|||
class Nonce(EncodedString):
|
||||
pass
|
||||
|
||||
|
||||
class BareRouteId(EncodedString):
|
||||
pass
|
||||
|
||||
|
@ -155,6 +151,27 @@ class BareNodeId(EncodedString):
|
|||
class BareMemberId(EncodedString):
|
||||
pass
|
||||
|
||||
class BareOpaqueRecordKey(EncodedString):
|
||||
pass
|
||||
|
||||
class BareRecordKey(str):
|
||||
@classmethod
|
||||
def from_parts(cls, key: BareOpaqueRecordKey, encryption_key: Optional[BareSharedSecret]) -> Self:
|
||||
assert isinstance(key, BareOpaqueRecordKey)
|
||||
if encryption_key is not None:
|
||||
assert isinstance(encryption_key, BareSharedSecret)
|
||||
return cls(f"{key}:{encryption_key}")
|
||||
return cls(f"{key}")
|
||||
|
||||
def key(self) -> BareOpaqueRecordKey:
|
||||
parts = self.split(":", 1)
|
||||
return BareOpaqueRecordKey(parts[0])
|
||||
|
||||
def encryption_key(self) -> Optional[BareSharedSecret]:
|
||||
parts = self.split(":", 1)
|
||||
if len(parts) == 2:
|
||||
return BareSharedSecret(self.split(":", 1)[1])
|
||||
return None
|
||||
|
||||
class BareKeyPair(str):
|
||||
@classmethod
|
||||
|
@ -180,6 +197,15 @@ class CryptoTyped(str):
|
|||
raise ValueError("Not CryptoTyped")
|
||||
return self[5:]
|
||||
|
||||
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 RecordKey(CryptoTyped):
|
||||
@classmethod
|
||||
|
@ -191,15 +217,9 @@ 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())
|
||||
def encryption_key(self) -> Optional[SharedSecret]:
|
||||
ek = self.value().encryption_key()
|
||||
return None if ek == None else SharedSecret.from_value(self.kind(), ek)
|
||||
|
||||
class HashDigest(CryptoTyped):
|
||||
@classmethod
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue