use super::{
CreateKeyRequest, CreateKeyResponse, GetKeySecretResponse, ImportKeyRequest, ImportKeyResponse,
InvalidateKeyResponse, ListKeysResponse, ListSeedsResponse, RenameKeyRequest,
RenameKeyResponse, RotateSeedRequest, RotateSeedResponse, SignResponse, Transport, VtaClient,
WrappingKeyResponse, encode_path_segment,
};
use crate::error::VtaError;
use crate::keys::{KeyRecord, KeyType};
use crate::protocols::key_management::derive_and_sign::DeriveAndSignResultBody;
use crate::protocols::key_management::derive_and_sign_document::DeriveAndSignDocumentResultBody;
use crate::protocols::key_management::sign::SignAlgorithm;
use crate::trust_tasks;
#[cfg(feature = "client")]
use crate::protocols::key_management;
#[cfg(feature = "client")]
impl VtaClient {
pub async fn create_key(&self, req: CreateKeyRequest) -> Result<CreateKeyResponse, VtaError> {
let body = crate::protocols::key_management::create::CreateKeyBody {
key_type: req.key_type.clone(),
derivation_path: req.derivation_path.clone().unwrap_or_default(),
mnemonic: req.mnemonic.clone(),
label: req.label.clone(),
context_id: req.context_id.clone(),
};
let wrapped: crate::protocols::key_management::create::CreateKeyResponseBody = self
.rpc_tt(
trust_tasks::TASK_KEYS_CREATE_0_1,
serde_json::to_value(&body)?,
30,
|c, url| c.post(format!("{url}/keys")).json(&req),
)
.await?;
let key = wrapped.key;
Ok(CreateKeyResponse {
key_id: key.key_id,
key_type: key.key_type,
derivation_path: key.derivation_path,
public_key: key.public_key,
status: key.status,
label: key.label,
created_at: key.created_at,
})
}
pub async fn import_key(&self, req: ImportKeyRequest) -> Result<ImportKeyResponse, VtaError> {
if req.private_key_multibase.is_some() {
return self
.rpc(
key_management::IMPORT_KEY,
serde_json::to_value(&req)?,
key_management::IMPORT_KEY_RESULT,
30,
|c, url| c.post(format!("{url}/keys/import")).json(&req),
)
.await;
}
let body = crate::protocols::key_management::import::ImportKeyBody {
key_type: req.key_type.clone(),
private_key_sealed: req.private_key_sealed.clone(),
private_key_jwe: req.private_key_jwe.clone(),
private_key_multibase: None,
label: req.label.clone(),
context_id: req.context_id.clone(),
};
let wrapped: crate::protocols::key_management::create::CreateKeyResponseBody = self
.rpc_tt(
trust_tasks::TASK_KEYS_IMPORT_0_1,
serde_json::to_value(&body)?,
30,
|c, url| c.post(format!("{url}/keys/import")).json(&req),
)
.await?;
Ok(ImportKeyResponse {
key_id: wrapped.key.key_id,
key_type: wrapped.key.key_type,
public_key: wrapped.key.public_key,
status: wrapped.key.status,
label: wrapped.key.label,
origin: wrapped.key.origin,
created_at: wrapped.key.created_at,
})
}
pub async fn list_keys(
&self,
offset: u64,
limit: u64,
status: Option<&str>,
context_id: Option<&str>,
) -> Result<ListKeysResponse, VtaError> {
self.rpc_tt(
trust_tasks::TASK_KEYS_LIST_0_1,
serde_json::to_value(crate::protocols::key_management::list::ListKeysBody {
offset: Some(offset),
limit: Some(limit),
status: status
.map(str::to_string)
.and_then(|s| serde_json::from_value(serde_json::Value::String(s)).ok()),
context_id: context_id.map(str::to_string),
})?,
30,
|c, url| {
let mut u = format!("{url}/keys?offset={offset}&limit={limit}");
if let Some(s) = status {
u.push_str(&format!("&status={s}"));
}
if let Some(ctx) = context_id {
u.push_str(&format!("&context_id={ctx}"));
}
c.get(u)
},
)
.await
}
pub async fn get_key(&self, key_id: &str) -> Result<KeyRecord, VtaError> {
let wrapped: crate::protocols::key_management::get::GetKeyResponseBody = self
.rpc_tt(
trust_tasks::TASK_KEYS_SHOW_0_1,
serde_json::json!({ "keyId": key_id }),
30,
|c, url| c.get(format!("{url}/keys/{}", encode_path_segment(key_id))),
)
.await?;
wrapped
.key
.ok_or_else(|| VtaError::NotFound(format!("no key record for `{key_id}`")))
}
pub async fn get_key_secret(&self, key_id: &str) -> Result<GetKeySecretResponse, VtaError> {
self.rpc_tt(
trust_tasks::TASK_SEEDS_EXPORT_MNEMONIC_1_0,
serde_json::json!({ "key_id": key_id }),
30,
|c, url| c.get(format!("{url}/keys/{}/secret", encode_path_segment(key_id))),
)
.await
}
pub async fn sign(
&self,
key_id: &str,
payload: &[u8],
algorithm: SignAlgorithm,
) -> Result<SignResponse, VtaError> {
use base64::Engine;
let payload_b64 = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(payload);
self.rpc_tt(
trust_tasks::TASK_KEYS_SIGN_0_1,
serde_json::json!({
"keyId": key_id,
"payload": payload_b64,
"algorithm": algorithm,
}),
30,
|c, url| {
c.post(format!("{url}/keys/{}/sign", encode_path_segment(key_id)))
.json(&serde_json::json!({
"payload": payload_b64,
"algorithm": algorithm,
}))
},
)
.await
}
pub async fn derive_and_sign(
&self,
key_type: KeyType,
derivation_path: &str,
payload: &[u8],
algorithm: SignAlgorithm,
) -> Result<DeriveAndSignResultBody, VtaError> {
use base64::Engine;
let payload_b64 = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(payload);
let body = serde_json::json!({
"keyType": serde_json::to_value(&key_type)?,
"derivationPath": derivation_path,
"payload": payload_b64,
"algorithm": algorithm,
});
self.rpc_tt(
trust_tasks::TASK_KEYS_DERIVE_AND_SIGN_0_1,
body.clone(),
30,
move |c, url| c.post(format!("{url}/keys/derive-and-sign")).json(&body),
)
.await
}
pub async fn derive_and_sign_document(
&self,
key_type: KeyType,
derivation_path: &str,
document: serde_json::Value,
proof_purpose: Option<&str>,
) -> Result<DeriveAndSignDocumentResultBody, VtaError> {
let body = serde_json::json!({
"keyType": serde_json::to_value(&key_type)?,
"derivationPath": derivation_path,
"document": document,
"proofPurpose": proof_purpose,
});
self.rpc_tt(
trust_tasks::TASK_KEYS_DERIVE_AND_SIGN_DOCUMENT_0_1,
body.clone(),
30,
move |c, url| {
c.post(format!("{url}/keys/derive-and-sign-document"))
.json(&body)
},
)
.await
}
pub async fn invalidate_key(&self, key_id: &str) -> Result<InvalidateKeyResponse, VtaError> {
self.rpc_tt(
trust_tasks::TASK_KEYS_REVOKE_0_1,
serde_json::json!({ "keyId": key_id }),
30,
|c, url| c.delete(format!("{url}/keys/{}", encode_path_segment(key_id))),
)
.await
}
pub async fn rename_key(
&self,
key_id: &str,
new_key_id: &str,
) -> Result<RenameKeyResponse, VtaError> {
self.rpc_tt(
trust_tasks::TASK_KEYS_RENAME_0_1,
serde_json::json!({ "keyId": key_id, "newKeyId": new_key_id }),
30,
|c, url| {
c.patch(format!("{url}/keys/{}", encode_path_segment(key_id)))
.json(&RenameKeyRequest {
key_id: new_key_id.to_string(),
})
},
)
.await
}
pub async fn get_wrapping_key(&self) -> Result<WrappingKeyResponse, VtaError> {
match &self.transport {
Transport::Rest {
client,
base_url,
auth,
} => {
Self::ensure_token_valid(client, base_url, auth).await?;
let token = auth.lock().await.token.clone();
let req = client.get(format!("{base_url}/keys/import/wrapping-key"));
let resp = Self::with_auth_token(req, &token).send().await?;
Self::handle_response(resp).await
}
#[cfg(feature = "session")]
Transport::DIDComm { .. } => Err(VtaError::UnsupportedTransport(
"wrapping key not needed for DIDComm transport".into(),
)),
#[cfg(feature = "tsp")]
Transport::Tsp { .. } => Err(VtaError::UnsupportedTransport(
"wrapping key not needed for TSP transport".into(),
)),
}
}
pub async fn list_seeds(&self) -> Result<ListSeedsResponse, VtaError> {
self.rpc_tt(
trust_tasks::TASK_SEEDS_LIST_1_0,
serde_json::json!({}),
30,
|c, url| c.get(format!("{url}/keys/seeds")),
)
.await
}
pub async fn rotate_seed(
&self,
mnemonic: Option<String>,
) -> Result<RotateSeedResponse, VtaError> {
let body = RotateSeedRequest {
mnemonic: mnemonic.clone(),
};
self.rpc_tt(
trust_tasks::TASK_SEEDS_ROTATE_1_0,
serde_json::json!({ "mnemonic": mnemonic }),
30,
|c, url| c.post(format!("{url}/keys/seeds/rotate")).json(&body),
)
.await
}
}