use affinidi_secrets_resolver::secrets::Secret;
use ed25519_dalek::{Signer as Ed25519Signer, SigningKey};
use crate::error::AppError;
use crate::operations::internal_authority::InternalAuthority;
use vta_sdk::did_key::decode_private_key_multibase;
use vta_sdk::sealed_transfer::{
AssertionProof, DidSignedAssertion, ProducerAssertion, template_bootstrap::VtaTrustBundle,
};
use super::ProvisionIntegrationDeps;
async fn load_vta_key_as_secret(
state: &ProvisionIntegrationDeps,
key_id: String,
) -> Result<Secret, AppError> {
let authority = InternalAuthority::new("provision-integration");
let resp = crate::operations::keys::get_key_secret_internal(
&state.keys_ks,
&state.imported_ks,
&*state.seed_store,
&state.audit_ks,
authority,
&key_id,
"provision-integration-internal",
)
.await?;
let _seed: [u8; 32] = decode_private_key_multibase(&resp.private_key_multibase)
.map_err(|e| AppError::Internal(format!("decode VTA key secret for {key_id}: {e}")))?;
let mut secret = Secret::from_multibase(&resp.private_key_multibase, None)
.map_err(|e| AppError::Internal(format!("construct Secret for {key_id}: {e}")))?;
secret.id = key_id;
Ok(secret)
}
pub(super) async fn load_vta_vc_issuance_secret(
state: &ProvisionIntegrationDeps,
vta_did: &str,
) -> Result<Secret, AppError> {
load_vta_key_as_secret(state, format!("{vta_did}#key-0")).await
}
pub(super) async fn load_vta_sealed_transfer_secret(
state: &ProvisionIntegrationDeps,
vta_did: &str,
) -> Result<Secret, AppError> {
load_vta_key_as_secret(state, format!("{vta_did}#sealed-transfer-0"))
.await
.map_err(|e| match e {
AppError::NotFound(_) => AppError::Internal(format!(
"VTA missing '{vta_did}#sealed-transfer-0' — re-bootstrap required (this VTA was \
provisioned before key-use split, see review item 12)"
)),
other => other,
})
}
pub(super) async fn load_vta_trust_bundle(
state: &ProvisionIntegrationDeps,
vta_did: &str,
) -> Result<VtaTrustBundle, AppError> {
#[cfg(feature = "webvh")]
let vta_did_log = crate::webvh_store::get_did_log(&state.webvh_ks, vta_did).await?;
#[cfg(not(feature = "webvh"))]
let vta_did_log: Option<String> = None;
let vta_did_document = match &vta_did_log {
#[cfg(feature = "webvh")]
Some(log) => {
let mut webvh_state = didwebvh_rs::DIDWebVHState::default();
let (log_entry, _meta) =
webvh_state
.resolve_log(vta_did, log, None)
.await
.map_err(|e| {
AppError::Internal(format!(
"resolve VTA DID '{vta_did}' from local webvh log: {e}"
))
})?;
use didwebvh_rs::log_entry::LogEntryMethods;
log_entry.get_did_document().map_err(|e| {
AppError::Internal(format!(
"render VTA DID doc from local webvh log for '{vta_did}': {e}"
))
})?
}
_ => {
let resolver = state
.did_resolver
.as_ref()
.ok_or_else(|| AppError::Internal("DID resolver not initialized".into()))?;
let resolved = resolver
.resolve(vta_did)
.await
.map_err(|e| AppError::Internal(format!("resolve VTA DID '{vta_did}': {e}")))?;
serde_json::to_value(&resolved.doc)
.map_err(|e| AppError::Internal(format!("serialize VTA DID doc: {e}")))?
}
};
Ok(VtaTrustBundle {
vta_did: vta_did.to_string(),
vta_did_document,
vta_did_log,
})
}
pub(super) fn build_did_signed_assertion(
vta_signing_secret: &Secret,
client_x25519_pub: &[u8; 32],
bundle_id: [u8; 16],
) -> Result<ProducerAssertion, AppError> {
use base64::Engine;
use base64::engine::general_purpose::URL_SAFE_NO_PAD as B64URL;
let (vta_did_frag, _) = vta_signing_secret
.id
.split_once('#')
.ok_or_else(|| AppError::Internal("VTA signing secret id missing fragment".into()))?;
let vta_did = vta_did_frag.to_string();
let priv_mb = vta_signing_secret
.get_private_keymultibase()
.map_err(|e| AppError::Internal(format!("get VTA private key multibase: {e}")))?;
let seed: [u8; 32] = decode_private_key_multibase(&priv_mb)
.map_err(|e| AppError::Internal(format!("decode VTA signing seed: {e}")))?;
let signing_key = SigningKey::from_bytes(&seed);
let mut to_sign = Vec::with_capacity(64);
to_sign.extend_from_slice(b"vta-sealed-transfer/v1\0");
to_sign.extend_from_slice(client_x25519_pub);
to_sign.extend_from_slice(&bundle_id);
let signature = signing_key.sign(&to_sign);
let signature_b64 = B64URL.encode(signature.to_bytes());
Ok(ProducerAssertion {
producer_did: vta_did.clone(),
proof: AssertionProof::DidSigned(DidSignedAssertion {
did: vta_did,
signature_b64,
verification_method: vta_signing_secret.id.clone(),
}),
})
}