Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 45 additions & 14 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,24 +48,55 @@ operator. Use hosted accounts only for public demos; keep wallets, private data
and recovery material on your own device until hosted protection passes its
acceptance gate. Independent review and installed proof remain security gates.

## Capability revocation and key rotation

Runtime stores the capability revocation epoch in `capability_store/epoch`
inside its data home. A successful epoch advance commits the new value before
publishing it in memory. Capability validation reads the stored epoch and
rejects tokens with a lower epoch, including after another process advances the
same store.
Epoch writes are serialized across processes and preserve the highest stored
or observed value. A stale process that persists its state preserves a newer
epoch already committed by another process.

`elastos emergency rotate` advances that durable epoch before publishing the
replacement signing key. An epoch error stops rotation before key publication.
If key publication fails after the epoch advances, the command reports the
partial result and retains the higher epoch. The epoch and key are separate
writes.

A running Runtime loads the replacement signing key only after restart. For a
complete rotation, stop Runtime for the affected data home, run
`elastos emergency rotate`, then start Runtime with the same data home. Confirm
that Runtime loads the stored key and starts successfully before resuming use.
Command completion confirms the persisted changes; the operator owns the
restart. Runtime can use an in-memory key when it cannot load or save a signing
key, so resolve signing-key warnings before relying on authority across restarts.

An established capability store requires a readable, valid epoch at startup.
Capability validation also fails closed when that epoch is unavailable or
invalid. For recovery, stop all Runtime processes using the affected data home
and preserve the capability store. Restore a valid unsigned 64-bit epoch at or
above the latest committed epoch established from trusted, current recovery data. A higher
epoch preserves committed revocations and can invalidate additional tokens; the
maximum value leaves no room for another epoch advance. Preserve the epoch and
any existing lock file. If only `epoch.lock` is missing, stop every Runtime
process using that data home, then restart Runtime to recreate the lock without
changing the valid stored epoch. Resetting the epoch or deleting the store can
discard committed revocations. If the latest committed epoch cannot be
established, obtain recovery assistance before starting Runtime.

Upgrade note: Older Runtime versions could start at epoch zero when a stored
epoch was empty or malformed. Runtime now refuses that state; if an upgrade
exposes it, preserve the store and use the recovery procedure above before
restarting. A truncated value that still parses as a number cannot be detected
from its syntax alone, so establish the recovery lower bound from trusted,
current data.

## Open Findings

The following security-relevant findings remain open in the current runtime and are documented here for transparency.

### Capability state and key rotation are not restart safe

**Severity:** Medium
**Files:** `elastos/crates/elastos-runtime/src/capability/manager.rs`, `elastos/crates/elastos-server/src/security_cmd.rs`
**Status:** Open

Capability signing-key creation and `elastos emergency rotate` log persistence
errors but continue with in-memory state. The emergency command can therefore
report success even when the new key was not written. Rotation also advances a
fresh in-process capability store rather than a durable epoch record. Until the
key and revocation state are committed atomically, operators must verify the
persisted key and restart result instead of treating command completion as a
durable rotation receipt.

### Host-plane Carrier providers use Runtime admission

**Severity:** Medium
Expand Down
2 changes: 1 addition & 1 deletion docs/COMMAND_MATRIX.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ target.
| Configuration | `elastos config show`, `elastos config set` | Reads or writes local configuration. |
| Identity | `elastos identity show`, `elastos identity nickname get`, `elastos identity nickname set`, `elastos identity arm-owner`, `elastos identity arm-owner-recovery` | Reads or updates the local DID-backed profile. `nickname set` prompts only when no value is supplied and a TTY is available. `arm-owner` admits one first-owner passkey at an exact public HTTPS Home address. `arm-owner-recovery` prints a one-time code for recovering this Home's admin with the Recovery Kit; use the command shown on the sign-in screen. |
| TLS | `elastos tls trust`, `elastos tls regen` | Prints trust instructions or regenerates the local leaf certificate. |
| Emergency | `elastos emergency rotate` | Attempts to persist a new signing key. The active Runtime changes only after restart, and current persistence failure is not fail-closed. See the [security finding](../SECURITY.md#capability-state-and-key-rotation-are-not-restart-safe). |
| Emergency | `elastos emergency rotate` | Advances the durable capability revocation epoch, then persists a new signing key. Runtime validation reads the stored epoch; a running Runtime requires a restart to load the new key. The command reports persistence errors, including an epoch advance followed by a key publication failure. See [rotation, upgrade and recovery guidance](../SECURITY.md#capability-revocation-and-key-rotation). |
| Trusted sources | `elastos source add`, `elastos source list`, `elastos source show`, `elastos source switch-channel`, `elastos source verify` | Manages local trusted-release source state. |
| Updates | `elastos update` | Checks the trusted source over Carrier only. No local runtime is required. See [INSTALL.md](INSTALL.md#how-updates-reach-your-home). |
| Offline principal-root maintenance | hidden `elastos principal-root-migrate`, hidden `elastos principal-root-upgrade` | Operates on an explicit data directory. The Runtime must be offline and the command requires explicit backup inputs. |
Expand Down
33 changes: 33 additions & 0 deletions elastos/crates/elastos-runtime/src/bootstrap/runtime_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,39 @@ mod tests {
assert!(runtime.stop().await.is_err());
}

#[tokio::test]
async fn test_runtime_shutdown_preserves_an_externally_advanced_epoch() {
let directory = tempfile::tempdir().unwrap();
let config = RuntimeConfig {
data_dir: directory.path().to_path_buf(),
enable_audit: false,
..Default::default()
};
let mut runtime =
ElastosRuntime::build(config, Arc::new(MockComputeProvider), Arc::new(NullFetcher))
.await
.unwrap();
runtime.start().await.unwrap();
let store_path = directory.path().join("capability_store");
let external = CapabilityStore::with_persistence(&store_path)
.await
.unwrap();
assert_eq!(external.advance_epoch().unwrap(), 1);
assert_eq!(runtime.capability_store.current_epoch(), 0);

runtime.stop().await.unwrap();

let reloaded = CapabilityStore::with_persistence(&store_path)
.await
.unwrap();
assert_eq!(
reloaded.current_epoch(),
1,
"shutdown must preserve the externally advanced epoch"
);
assert!(!reloaded.is_epoch_valid(0));
}

#[tokio::test]
async fn test_runtime_accessors() {
let temp_dir = tempfile::tempdir().unwrap();
Expand Down
Loading
Loading