initial commit
Some checks failed
CI / lint (push) Successful in 1m37s
CI / test-python (push) Successful in 1m49s
CI / test-zig (push) Successful in 1m39s
CI / test-wasm (push) Successful in 1m54s
CI / test (push) Successful in 14m44s
CI / miri (push) Successful in 14m18s
CI / build (push) Successful in 1m9s
CI / fuzz-regression (push) Successful in 9m9s
CI / publish (push) Failing after 1m10s
CI / publish-python (push) Failing after 1m46s
CI / publish-wasm (push) Has been cancelled
Some checks failed
CI / lint (push) Successful in 1m37s
CI / test-python (push) Successful in 1m49s
CI / test-zig (push) Successful in 1m39s
CI / test-wasm (push) Successful in 1m54s
CI / test (push) Successful in 14m44s
CI / miri (push) Successful in 14m18s
CI / build (push) Successful in 1m9s
CI / fuzz-regression (push) Successful in 9m9s
CI / publish (push) Failing after 1m10s
CI / publish-python (push) Failing after 1m46s
CI / publish-wasm (push) Has been cancelled
Signed-off-by: Kamal Tufekcic <kamal@lo.sh>
This commit is contained in:
commit
1d99048c95
165830 changed files with 79062 additions and 0 deletions
84
soliton_py/src/call.rs
Normal file
84
soliton_py/src/call.rs
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
//! Call key derivation for encrypted voice/video.
|
||||
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyBytes;
|
||||
|
||||
use crate::errors::to_py_err;
|
||||
|
||||
/// Call keys for encrypted voice/video media.
|
||||
///
|
||||
/// Derived from a ratchet session's root key and an ephemeral KEM shared secret.
|
||||
/// Keys are role-assigned by fingerprint order — both parties get the same
|
||||
/// send/recv assignment without coordination.
|
||||
///
|
||||
/// Use as a context manager for automatic zeroization::
|
||||
///
|
||||
/// with ratchet.derive_call_keys(kem_ss, call_id) as keys:
|
||||
/// send = keys.send_key()
|
||||
/// recv = keys.recv_key()
|
||||
/// keys.advance() # derive next round
|
||||
#[pyclass]
|
||||
pub struct CallKeys {
|
||||
inner: Option<soliton::call::CallKeys>,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl CallKeys {
|
||||
/// Current send key (32 bytes).
|
||||
fn send_key<'py>(&self, py: Python<'py>) -> PyResult<Py<PyBytes>> {
|
||||
let inner = self
|
||||
.inner
|
||||
.as_ref()
|
||||
.ok_or_else(|| crate::errors::InvalidDataError::new_err("call keys consumed"))?;
|
||||
Ok(PyBytes::new(py, inner.send_key()).into())
|
||||
}
|
||||
|
||||
/// Current recv key (32 bytes).
|
||||
fn recv_key<'py>(&self, py: Python<'py>) -> PyResult<Py<PyBytes>> {
|
||||
let inner = self
|
||||
.inner
|
||||
.as_ref()
|
||||
.ok_or_else(|| crate::errors::InvalidDataError::new_err("call keys consumed"))?;
|
||||
Ok(PyBytes::new(py, inner.recv_key()).into())
|
||||
}
|
||||
|
||||
/// Advance the call chain — derives fresh keys, zeroizes old ones.
|
||||
///
|
||||
/// Raises ``ChainExhaustedError`` after 2^24 advances.
|
||||
fn advance(&mut self) -> PyResult<()> {
|
||||
let inner = self
|
||||
.inner
|
||||
.as_mut()
|
||||
.ok_or_else(|| crate::errors::InvalidDataError::new_err("call keys consumed"))?;
|
||||
inner.advance().map_err(to_py_err)
|
||||
}
|
||||
|
||||
/// Zeroize all key material.
|
||||
fn close(&mut self) {
|
||||
self.inner = None;
|
||||
}
|
||||
|
||||
fn __enter__(slf: Py<Self>) -> Py<Self> {
|
||||
slf
|
||||
}
|
||||
|
||||
fn __exit__(
|
||||
&mut self,
|
||||
_exc_type: Option<&Bound<'_, PyAny>>,
|
||||
_exc_val: Option<&Bound<'_, PyAny>>,
|
||||
_exc_tb: Option<&Bound<'_, PyAny>>,
|
||||
) {
|
||||
self.close();
|
||||
}
|
||||
}
|
||||
|
||||
impl CallKeys {
|
||||
pub fn from_inner(inner: soliton::call::CallKeys) -> Self {
|
||||
Self { inner: Some(inner) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
m.add_class::<CallKeys>()?;
|
||||
Ok(())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue