Skip to main content

clawft_kernel/workload_pkg/
sign.rs

1//! Ed25519 signing of manifest envelopes (ADR-025 key type).
2
3use ed25519_dalek::{Signer, SigningKey};
4
5use super::codec::{hex_decode_exact, hex_encode};
6use super::manifest::{ManifestEnvelope, ManifestError, SignatureEntry, valid_token};
7
8/// Default key id for a public key: `ed25519:` plus the first 16 hex chars
9/// of its BLAKE3 hash.
10pub fn key_id_for(public_key: &[u8; 32]) -> String {
11    let h = blake3::hash(public_key).to_hex();
12    format!("ed25519:{}", &h[..16])
13}
14
15/// Load a signing key from a 64-char lower-case hex seed (surrounding
16/// whitespace allowed, as written by `weaver workload keygen`).
17pub fn signing_key_from_hex(s: &str) -> Result<SigningKey, String> {
18    let seed = hex_decode_exact::<32>(s.trim())
19        .ok_or_else(|| "signing key must be 64 lower-case hex chars".to_string())?;
20    Ok(SigningKey::from_bytes(&seed))
21}
22
23/// Append a signature by `key` under `key_id`. Re-signing with a key that
24/// already signed replaces that entry, so the operation is idempotent.
25pub fn sign_envelope(
26    envelope: &mut ManifestEnvelope,
27    key: &SigningKey,
28    key_id: &str,
29) -> Result<(), ManifestError> {
30    if !valid_token(key_id, 128) {
31        return Err(ManifestError::Invalid(format!("bad key id {key_id:?}")));
32    }
33    let msg = envelope.signed_statement()?;
34    let sig = key.sign(&msg);
35    let public_key = hex_encode(key.verifying_key().as_bytes());
36    envelope.signatures.retain(|s| s.public_key != public_key);
37    envelope.signatures.push(SignatureEntry {
38        algorithm: "ed25519".to_string(),
39        key_id: key_id.to_string(),
40        public_key,
41        signature: hex_encode(&sig.to_bytes()),
42    });
43    Ok(())
44}