weft_licence_wire/
binding.rs1use ed25519_dalek::SigningKey;
4use serde::{Deserialize, Serialize};
5
6use crate::{
7 BINDING_DOMAIN, LicenceError, MAX_UNIX_TIME, SignedEnvelope, envelope_key, parse_canonical, sign_envelope,
8 valid_hex32, valid_token, verify_envelope,
9};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
13#[serde(rename_all = "snake_case")]
14pub enum BindState {
15 Bound,
17 Unbound,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
23#[serde(deny_unknown_fields)]
24pub struct BindingRecord {
25 pub v: u32,
27 pub device_id: String,
29 pub device_pubkey: String,
31 pub mesh_id: String,
33 pub grant_pubkey: String,
35 pub steward_node_id: String,
37 pub steward_pubkey: String,
39 pub state: BindState,
41 pub seq: u64,
43 pub bound_at: u64,
45}
46
47pub type SignedBinding = SignedEnvelope;
49
50pub fn sign_binding(
52 record: &BindingRecord,
53 operator: &SigningKey,
54) -> Result<SignedBinding, LicenceError> {
55 sign_envelope(BINDING_DOMAIN, record, operator)
56}
57
58pub fn verify_binding_signature(
61 signed: &SignedBinding,
62 operator_ok: &dyn Fn(&[u8; 32]) -> bool,
63) -> Result<BindingRecord, LicenceError> {
64 let pk = envelope_key(signed)?;
65 if !operator_ok(&pk) {
66 return Err(LicenceError::UntrustedKey);
67 }
68 verify_envelope(BINDING_DOMAIN, signed, &pk)?;
69 let rec: BindingRecord = parse_canonical(&signed.payload)?;
70 if rec.v != 2 {
71 return Err(LicenceError::Malformed("binding version".into()));
72 }
73 let hex_ok = [&rec.device_pubkey, &rec.mesh_id, &rec.grant_pubkey, &rec.steward_pubkey]
74 .iter()
75 .all(|s| valid_hex32(s));
76 if !hex_ok || !valid_token(&rec.device_id) || !valid_token(&rec.steward_node_id) {
77 return Err(LicenceError::Malformed("binding field".into()));
78 }
79 if rec.bound_at > MAX_UNIX_TIME {
80 return Err(LicenceError::Malformed("binding bounds".into()));
81 }
82 Ok(rec)
83}