#![cfg(feature = "rhai-runtime")]
use std::fmt::Write as _;
use std::sync::Arc;
use rhai::Engine;
use uni_plugin::{Capability, CapabilitySet, KmsProvider};
use crate::host_fn_impls::{require_allowed, require_service, rt_err};
use crate::host_fns::RhaiHostFnSpec;
use crate::loader::RhaiLoader;
pub fn register(loader: &mut RhaiLoader) {
let kms = loader.kms();
let placeholder = Capability::Kms {
key_ids: vec!["*".into()],
};
let sign_kms = kms.clone();
loader.host_fns_mut().register(RhaiHostFnSpec::gated(
"uni.kms.sign",
placeholder.clone(),
"Sign bytes with a host-managed key (returns hex signature).",
move |engine: &mut Engine, caps: &CapabilitySet| {
register_sign(engine, caps.clone(), sign_kms.clone());
},
));
loader.host_fns_mut().register(RhaiHostFnSpec::gated(
"uni.kms.verify",
placeholder,
"Verify a hex signature against a host-managed key.",
move |engine: &mut Engine, caps: &CapabilitySet| {
register_verify(engine, caps.clone(), kms.clone());
},
));
}
fn register_sign(engine: &mut Engine, caps: CapabilitySet, kms: Option<Arc<dyn KmsProvider>>) {
engine.register_fn(
"uni_kms_sign",
move |key_id: &str, data: &str| -> Result<String, Box<rhai::EvalAltResult>> {
require_allowed(
&caps,
|c| c.kms_allows(key_id),
format!("uni.kms.sign: key `{key_id}` not in granted Kms allow-list"),
)?;
let kms = require_service(&kms, "uni.kms.sign: no KMS provider configured")?;
let sig = kms
.sign(key_id, data.as_bytes())
.map_err(|e| rt_err(format!("uni.kms.sign(`{key_id}`): {e}")))?;
Ok(to_hex(&sig))
},
);
}
fn register_verify(engine: &mut Engine, caps: CapabilitySet, kms: Option<Arc<dyn KmsProvider>>) {
engine.register_fn(
"uni_kms_verify",
move |key_id: &str, data: &str, sig: &str| -> Result<bool, Box<rhai::EvalAltResult>> {
require_allowed(
&caps,
|c| c.kms_allows(key_id),
format!("uni.kms.verify: key `{key_id}` not in granted Kms allow-list"),
)?;
let kms = require_service(&kms, "uni.kms.verify: no KMS provider configured")?;
let sig_bytes =
from_hex(sig).map_err(|e| rt_err(format!("uni.kms.verify: signature hex: {e}")))?;
kms.verify(key_id, data.as_bytes(), &sig_bytes)
.map_err(|e| rt_err(format!("uni.kms.verify(`{key_id}`): {e}")))
},
);
}
fn to_hex(bytes: &[u8]) -> String {
let mut s = String::with_capacity(bytes.len() * 2);
for b in bytes {
let _ = write!(s, "{b:02x}");
}
s
}
fn from_hex(s: &str) -> Result<Vec<u8>, String> {
if !s.len().is_multiple_of(2) {
return Err("odd-length hex string".to_owned());
}
(0..s.len())
.step_by(2)
.map(|i| u8::from_str_radix(&s[i..i + 2], 16).map_err(|e| e.to_string()))
.collect()
}