Files
quicproquo/crates/quicprochat-sdk/src/keys.rs
Christian Nennemann a710037dde chore: rename quicproquo → quicprochat in Rust workspace
Rename all crate directories, package names, binary names, proto
package/module paths, ALPN strings, env var prefixes, config filenames,
mDNS service names, and plugin ABI symbols from quicproquo/qpq to
quicprochat/qpc.
2026-03-21 19:14:06 +01:00

110 lines
3.7 KiB
Rust

//! Key management — upload/fetch KeyPackages and hybrid public keys.
use quicprochat_proto::bytes::Bytes;
use quicprochat_proto::prost::Message;
use quicprochat_proto::{method_ids, qpc::v1};
use quicprochat_rpc::client::RpcClient;
use crate::error::SdkError;
/// Upload a KeyPackage for pre-key distribution.
/// Returns the SHA-256 fingerprint echoed by the server.
pub async fn upload_key_package(
rpc: &RpcClient,
identity_key: &[u8],
package: &[u8],
) -> Result<Vec<u8>, SdkError> {
let req = v1::UploadKeyPackageRequest {
identity_key: identity_key.to_vec(),
package: package.to_vec(),
};
let resp_bytes = rpc
.call(method_ids::UPLOAD_KEY_PACKAGE, Bytes::from(req.encode_to_vec()))
.await?;
let resp = v1::UploadKeyPackageResponse::decode(resp_bytes)
.map_err(|e| SdkError::Other(anyhow::anyhow!("decode UploadKeyPackageResponse: {e}")))?;
Ok(resp.fingerprint)
}
/// Fetch a KeyPackage for a peer (consumed: single-use).
/// Returns `None` if the peer has no available key packages.
pub async fn fetch_key_package(
rpc: &RpcClient,
identity_key: &[u8],
) -> Result<Option<Vec<u8>>, SdkError> {
let req = v1::FetchKeyPackageRequest {
identity_key: identity_key.to_vec(),
};
let resp_bytes = rpc
.call(method_ids::FETCH_KEY_PACKAGE, Bytes::from(req.encode_to_vec()))
.await?;
let resp = v1::FetchKeyPackageResponse::decode(resp_bytes)
.map_err(|e| SdkError::Other(anyhow::anyhow!("decode FetchKeyPackageResponse: {e}")))?;
if resp.package.is_empty() {
Ok(None)
} else {
Ok(Some(resp.package))
}
}
/// Upload hybrid public key (X25519 + ML-KEM-768).
pub async fn upload_hybrid_key(
rpc: &RpcClient,
identity_key: &[u8],
hybrid_public_key: &[u8],
) -> Result<(), SdkError> {
let req = v1::UploadHybridKeyRequest {
identity_key: identity_key.to_vec(),
hybrid_public_key: hybrid_public_key.to_vec(),
};
let resp_bytes = rpc
.call(method_ids::UPLOAD_HYBRID_KEY, Bytes::from(req.encode_to_vec()))
.await?;
let _resp = v1::UploadHybridKeyResponse::decode(resp_bytes)
.map_err(|e| SdkError::Other(anyhow::anyhow!("decode UploadHybridKeyResponse: {e}")))?;
Ok(())
}
/// Fetch a peer's hybrid public key.
/// Returns `None` if the peer has not uploaded a hybrid key.
pub async fn fetch_hybrid_key(
rpc: &RpcClient,
identity_key: &[u8],
) -> Result<Option<Vec<u8>>, SdkError> {
let req = v1::FetchHybridKeyRequest {
identity_key: identity_key.to_vec(),
};
let resp_bytes = rpc
.call(method_ids::FETCH_HYBRID_KEY, Bytes::from(req.encode_to_vec()))
.await?;
let resp = v1::FetchHybridKeyResponse::decode(resp_bytes)
.map_err(|e| SdkError::Other(anyhow::anyhow!("decode FetchHybridKeyResponse: {e}")))?;
if resp.hybrid_public_key.is_empty() {
Ok(None)
} else {
Ok(Some(resp.hybrid_public_key))
}
}
/// Batch fetch hybrid keys for multiple identities.
/// Returns one `Option<Vec<u8>>` per requested identity, in the same order.
pub async fn fetch_hybrid_keys(
rpc: &RpcClient,
identity_keys: &[Vec<u8>],
) -> Result<Vec<Option<Vec<u8>>>, SdkError> {
let req = v1::FetchHybridKeysRequest {
identity_keys: identity_keys.to_vec(),
};
let resp_bytes = rpc
.call(method_ids::FETCH_HYBRID_KEYS, Bytes::from(req.encode_to_vec()))
.await?;
let resp = v1::FetchHybridKeysResponse::decode(resp_bytes)
.map_err(|e| SdkError::Other(anyhow::anyhow!("decode FetchHybridKeysResponse: {e}")))?;
let result = resp
.keys
.into_iter()
.map(|k| if k.is_empty() { None } else { Some(k) })
.collect();
Ok(result)
}