Skip to content

Commit b39cc50

Browse files
feat(core): add _warning field and save_meta doc comment (#161)
Add a _warning field to KeyMeta that serializes at the top of .meta JSON files, warning users and agents not to modify these files directly since they are HMAC-verified. Add a doc comment to save_meta documenting the meta-tag invariant: every call must be followed by a meta-tag re-stamp, or ensure_meta_integrity will reject the key on next load. Co-authored-by: Jay Gowdy <jay@gowdy.me>
1 parent b08880c commit b39cc50

1 file changed

Lines changed: 19 additions & 0 deletions

File tree

crates/enclaveapp-core/src/metadata.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,17 @@ use std::collections::BTreeSet;
1010
use std::io::Write;
1111
use std::path::{Path, PathBuf};
1212

13+
pub fn meta_warning_default() -> String {
14+
"HMAC-verified — do not modify this file directly. Use CLI tools (e.g. sshenc identity)."
15+
.to_string()
16+
}
17+
1318
/// Metadata stored alongside a hardware-bound key.
1419
#[derive(Debug, Clone, Serialize, Deserialize)]
1520
pub struct KeyMeta {
21+
/// Tamper warning rendered at the top of the JSON.
22+
#[serde(default = "meta_warning_default", rename = "_warning")]
23+
pub warning: String,
1624
/// Key label (unique identifier within the app).
1725
pub label: String,
1826
/// Type of key (signing or encryption). Defaults to Signing for backward
@@ -40,6 +48,7 @@ impl KeyMeta {
4048
.as_secs()
4149
.to_string();
4250
KeyMeta {
51+
warning: meta_warning_default(),
4352
label: label.to_string(),
4453
key_type,
4554
access_policy,
@@ -233,6 +242,15 @@ pub fn restrict_file_permissions(path: &Path) -> Result<()> {
233242
}
234243

235244
/// Save key metadata to a JSON file.
245+
///
246+
/// # Meta-tag invariant
247+
///
248+
/// On platforms with meta-integrity tags (macOS, Windows, Linux),
249+
/// every call to `save_meta` MUST be followed by a meta-tag re-stamp.
250+
/// Callers that skip the re-stamp will break `ensure_meta_integrity`
251+
/// verification on the next load. The canonical way to guarantee this
252+
/// is to route meta mutations through the agent process, which stamps
253+
/// the tag atomically. See sshenc-agent's `SetIdentity` handler.
236254
pub fn save_meta(dir: &Path, label: &str, meta: &KeyMeta) -> Result<()> {
237255
crate::types::validate_label(label)?;
238256
let meta_path = dir.join(format!("{label}.meta"));
@@ -304,6 +322,7 @@ pub fn load_meta(dir: &Path, label: &str) -> Result<KeyMeta> {
304322
let meta_path = dir.join(format!("{label}.meta"));
305323
if !meta_path.exists() {
306324
return Ok(KeyMeta {
325+
warning: meta_warning_default(),
307326
label: label.to_string(),
308327
key_type: crate::KeyType::Signing,
309328
access_policy: crate::AccessPolicy::None,

0 commit comments

Comments
 (0)