python: expose key persistence and value metadata

This commit is contained in:
Adrien Beraud committed 2026-10-26 00:51:27 -04:00
1 parent 91c3f62c0c
commit b6cf2fd246
3 files changed
+129 -1

No files matched your search

+53
View File
@@ -78,6 +78,59 @@ async def dht_async_demo(key_str: str):
asyncio.run(dht_async_demo("unique_key"))
```
## Identity Persistence
Keys can be imported from PEM or DER bytes. Private keys export PKCS#8 PEM;
public keys export DER. Certificates export their complete PEM chain.
```python
import opendht as dht
identity = dht.Identity.generate("my-node")
private_key_pem = identity.key.serialize(password)
certificate_pem = bytes(identity.certificate)
public_key_der = bytes(identity.publickey)
restored = dht.Identity(
dht.PrivateKey(private_key_pem, password),
dht.Certificate(certificate_pem),
)
public_key = dht.PublicKey(public_key_der)
node = dht.DhtRunner()
node.run(id=restored)
```
Here `password` is an application-supplied string. Save the exported bytes using
your application's storage, protect private key files, and keep the password
separate from the key. `bytes(private_key)` and `private_key.serialize()` without
a password export **unencrypted** private keys. Empty or malformed key data,
incorrect passwords, and passwords containing NUL characters raise exceptions.
Exporting an uninitialized key raises `ValueError`.
## Value Metadata
The following properties are readable and writable on `Value`, including values
returned by `get()` and `listen()`:
| Property | Type | Meaning |
|----------|------|---------|
| `type` | unsigned 16-bit integer | Native value type identifier |
| `seq` | unsigned 16-bit integer | Sequence number for signed value updates |
| `priority` | unsigned integer | Push priority: `0` high, `1` normal |
| `pushType` | string | Push notification type, such as `audioCall` |
```python
value = dht.Value(b"some binary data")
value.type = 42
value.seq = 1
value.priority = 1
value.pushType = "audioCall"
```
Set metadata before publishing the value. Numeric values outside the native
integer range raise `OverflowError`.
## Documentation
For more information, examples and documentation, or to repport issues, visit: https://opendht.net
+68 -1
View File
@@ -324,6 +324,30 @@ cdef class Value(object):
def id(self, cpp.uint64_t value):
self._value.get().id = value
@property
def type(self) -> int:
return self._value.get().type
@type.setter
def type(self, cpp.uint16_t value):
self._value.get().type = value
@property
def seq(self) -> int:
return self._value.get().seq
@seq.setter
def seq(self, cpp.uint16_t value):
self._value.get().seq = value
@property
def priority(self) -> int:
return self._value.get().priority
@priority.setter
def priority(self, unsigned value):
self._value.get().priority = value
@property
def pushType(self) -> str:
return self._value.get().pushType.decode()
@pushType.setter
def pushType(self, str value):
self._value.get().pushType = value.encode()
@property
def size(self) -> int:
return self._value.get().size()
@@ -383,6 +407,30 @@ cdef class NodeSet(object):
cdef class PrivateKey(_WithPk):
cdef shared_ptr[cpp.PrivateKey] _key
def __init__(self, bytes data=None, str password=""):
cdef cpp.Blob encoded
if '\0' in password:
raise ValueError("Password must not contain NUL characters")
if data is not None:
if not data:
raise ValueError("Private key data must not be empty")
encoded = data
self._key = cpp.make_shared[cpp.PrivateKey](encoded, <cpp.string>password.encode())
def serialize(self, str password="") -> bytes:
"""Export PKCS#8 PEM, encrypted when a nonempty password is supplied."""
if not self._key:
raise ValueError("PrivateKey is not initialized")
if '\0' in password:
raise ValueError("Password must not contain NUL characters")
cdef cpp.Blob encoded = self._key.get().serialize(password.encode())
if encoded.empty():
raise RuntimeError("Could not export private key")
return (<char*>encoded.data())[:encoded.size()]
def __bytes__(self):
return self.serialize()
def getId(self) -> InfoHash:
if not self._key:
raise ValueError("PrivateKey is not initialized")
@@ -441,6 +489,25 @@ cdef class PrivateKey(_WithPk):
cdef class PublicKey(_WithPk):
cdef cpp.shared_ptr[cpp.PublicKey] _key
def __init__(self, bytes data=None):
cdef cpp.Blob encoded
if data is not None:
if not data:
raise ValueError("Public key data must not be empty")
encoded = data
self._key = cpp.make_shared[cpp.PublicKey](encoded)
def serialize(self) -> bytes:
"""Export the public key as DER bytes."""
if not self._key:
raise ValueError("PublicKey is not initialized")
cdef cpp.Blob encoded
self._key.get().pack(encoded)
return (<char*>encoded.data())[:encoded.size()]
def __bytes__(self):
return self.serialize()
def getId(self) -> InfoHash:
if not self._key:
raise ValueError("PublicKey is not initialized")
@@ -512,7 +579,7 @@ cdef class Certificate(_WithPk):
raise ValueError("PrivateKey is not initialized")
self._cert.get().revoke(deref(k._key.get()), deref(c._cert.get()));
def __bytes__(self):
return self._cert.get().toString().encode() if self._cert else b''
return self._cert.get().toString() if self._cert else b''
@property
def issuer(self) -> Certificate|None:
if self._cert and self._cert.get().issuer:
+8
View File
@@ -104,7 +104,9 @@ cdef extern from "opendht/crypto.h" namespace "dht::crypto":
cdef cppclass PrivateKey:
PrivateKey() except +
PrivateKey(Blob data, string password) except +
shared_ptr[PublicKey] getSharedPublicKey() except +
Blob serialize(string password) except +
Blob decrypt(Blob data) except +
Blob sign(Blob data) except +
@staticmethod
@@ -114,8 +116,10 @@ cdef extern from "opendht/crypto.h" namespace "dht::crypto":
cdef cppclass PublicKey:
PublicKey() except +
PublicKey(Blob data) except +
InfoHash getId() except +
PkId getLongId() except +
void pack(Blob& data) except +
Blob encrypt(Blob data) except +
bool checkSignature(Blob data, Blob signature) except +
bool operator==(PublicKey) const
@@ -168,6 +172,10 @@ cdef extern from "opendht/value.h" namespace "dht":
InfoHash recipient
vector[uint8_t] data
string user_type
uint16_t type
uint16_t seq
unsigned priority
string pushType
cdef cppclass ValueType:
ValueType(uint16_t id, string name, seconds expiration) except +