use ed25519_dalek::SigningKey;
use serde::{Deserialize, Serialize};
use crate::{
BINDING_DOMAIN, LicenceError, MAX_UNIX_TIME, SignedEnvelope, envelope_key, parse_canonical, sign_envelope,
valid_hex32, valid_token, verify_envelope,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum BindState {
Bound,
Unbound,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BindingRecord {
pub v: u32,
pub device_id: String,
pub device_pubkey: String,
pub mesh_id: String,
pub grant_pubkey: String,
pub steward_node_id: String,
pub steward_pubkey: String,
pub state: BindState,
pub seq: u64,
pub bound_at: u64,
}
pub type SignedBinding = SignedEnvelope;
pub fn sign_binding(
record: &BindingRecord,
operator: &SigningKey,
) -> Result<SignedBinding, LicenceError> {
sign_envelope(BINDING_DOMAIN, record, operator)
}
pub fn verify_binding_signature(
signed: &SignedBinding,
operator_ok: &dyn Fn(&[u8; 32]) -> bool,
) -> Result<BindingRecord, LicenceError> {
let pk = envelope_key(signed)?;
if !operator_ok(&pk) {
return Err(LicenceError::UntrustedKey);
}
verify_envelope(BINDING_DOMAIN, signed, &pk)?;
let rec: BindingRecord = parse_canonical(&signed.payload)?;
if rec.v != 2 {
return Err(LicenceError::Malformed("binding version".into()));
}
let hex_ok = [&rec.device_pubkey, &rec.mesh_id, &rec.grant_pubkey, &rec.steward_pubkey]
.iter()
.all(|s| valid_hex32(s));
if !hex_ok || !valid_token(&rec.device_id) || !valid_token(&rec.steward_node_id) {
return Err(LicenceError::Malformed("binding field".into()));
}
if rec.bound_at > MAX_UNIX_TIME {
return Err(LicenceError::Malformed("binding bounds".into()));
}
Ok(rec)
}