From 3df4aa716786f06e26ef4132380ac6a8abe29203 Mon Sep 17 00:00:00 2001 From: jml Date: Mon, 16 Feb 2026 04:46:53 +0000 Subject: [PATCH] Signed assertions --- crates/stemedb-api/Cargo.toml | 1 + .../src/handlers/stemedb_claims.rs | 43 ++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/crates/stemedb-api/Cargo.toml b/crates/stemedb-api/Cargo.toml index 89e9947..73d79af 100644 --- a/crates/stemedb-api/Cargo.toml +++ b/crates/stemedb-api/Cargo.toml @@ -46,6 +46,7 @@ getrandom = "0.2" metrics = "0.23" metrics-exporter-prometheus = "0.15" dashmap = "6.0" +ed25519-dalek = { version = "2.1", features = ["rand_core"] } [dev-dependencies] tempfile = "3" diff --git a/crates/stemedb-api/src/handlers/stemedb_claims.rs b/crates/stemedb-api/src/handlers/stemedb_claims.rs index 3427f17..6e8b88a 100644 --- a/crates/stemedb-api/src/handlers/stemedb_claims.rs +++ b/crates/stemedb-api/src/handlers/stemedb_claims.rs @@ -5,8 +5,10 @@ use axum::{extract::{Path, State}, http::StatusCode, Json}; use tracing::info; +use ed25519_dalek::{SigningKey, Signer, VerifyingKey}; -use stemedb_core::types::{Assertion, LifecycleStage, ObjectValue}; +use stemedb_core::types::{Assertion, LifecycleStage, ObjectValue, SignatureEntry}; +use stemedb_core::signing::compute_content_hash_v2; use stemedb_ingest::worker::serialize_assertion; use stemedb_storage::{key_codec, KVStore}; @@ -16,6 +18,40 @@ use crate::{ AppState, }; +// ============================================================================ +// Server Signing Key Generation + +/// Generate a deterministic server signing key from a seed. +/// This ensures the same key is used across server restarts. +fn get_server_signing_key() -> SigningKey { + // Use a fixed seed for deterministic key generation + // In production, this should be loaded from a config file or environment + let seed = [0x42u8; 32]; // Placeholder seed - replace with actual key management + SigningKey::from_bytes(&seed) +} + +/// Sign an assertion with the server's key and add the signature. +fn add_server_signature(assertion: &mut Assertion) { + let signing_key = get_server_signing_key(); + let verifying_key: VerifyingKey = (&signing_key).into(); + + // Compute content hash for v2 signature + let content_hash = compute_content_hash_v2(assertion); + + // Sign the content hash + let signature_bytes = signing_key.sign(&content_hash); + + // Create signature entry + let sig_entry = SignatureEntry { + agent_id: verifying_key.to_bytes(), + signature: signature_bytes.to_bytes(), + timestamp: assertion.timestamp, + version: 2, // Use v2 (enterprise) signing + }; + + assertion.signatures.push(sig_entry); +} + // ============================================================================ // Handlers // ============================================================================ @@ -39,7 +75,10 @@ pub async fn create_claim( info!(claim_id = %req.claim.id, concept_path = %req.claim.concept_path, "Creating claim in StemeDB"); // Convert DTO to Assertion - let assertion = dto_to_assertion(&req.claim)?; + let mut assertion = dto_to_assertion(&req.claim)?; + + // Add server signature to the assertion + add_server_signature(&mut assertion); // Serialize and append to WAL let payload = serialize_assertion(&assertion)