Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -582,10 +582,10 @@ jobs:
run: cargo clippy --locked -p worldscript-secure-storage --features platform-keystore --all-targets -- -D warnings
- name: Guard tests with the platform secure store
run: cargo test --locked -p worldscript-secure-storage --features platform-keystore --test platform_keystore_test
# QNBS-v3: R-15 Gate 3 slices 3A/3B staging, promotion, commit markers and reconciliation on macOS/Windows filesystems (Linux already runs them in core-rust) — CI_ONLY, not power-loss evidence.
# QNBS-v3: R-15 Gate 3 slices 3A/3B/3C staging, promotion, commit markers, reconciliation and the root commit on macOS/Windows filesystems (Linux already runs them in core-rust) — CI_ONLY, not power-loss evidence.
- name: Durable staging, promotion and commit reconciliation on this OS filesystem (macOS / Windows)
if: runner.os != 'Linux'
run: cargo test --locked -p worldscript-secure-storage --test gate3_durable_test --test gate3b_marker_test --test gate3b_commit_test
run: cargo test --locked -p worldscript-secure-storage --test gate3_durable_test --test gate3b_marker_test --test gate3b_commit_test --test gate3c_root_commit_test
Comment thread
qnbs marked this conversation as resolved.
- name: Real secure-store lifecycle (macOS / Windows)
if: runner.os != 'Linux'
run: cargo test --locked -p worldscript-secure-storage --features platform-keystore --test platform_keystore_test -- --ignored full_lifecycle
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

- **R-15 Gate 3, slice 3C part 3b — committing the root of trust (#445):** the protected-storage
core can now commit a new root of trust in the order the storage contract requires, finish or
cleanly abandon a commit that was interrupted at any step, and on startup trust only the root the
secure key store names — repairing a stale pointer instead of following it. Nothing reads or
writes user data through it yet. PR #944.
- **R-15 Gate 3, slice 3C part 3a — root and key-epoch records (#445):** the storage contract now
fixes the exact byte formats for the stored root of trust, the small pointer to it, and the key
epoch records. The protected-storage core can seal, strictly check and open the root and key-epoch
Expand Down
2 changes: 1 addition & 1 deletion crates/worldscript-secure-storage/src/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,7 @@ fn promote_staged<F: DurableFs>(
/// Moves rejected bytes to `<name>.rejected-<tag>` without ever losing them: the new name is linked
/// and made durable before the old one is removed. `tag` is a digest of the marker's operation ID,
/// so marker text never shapes a path.
fn relocate<F: DurableFs>(
pub(crate) fn relocate<F: DurableFs>(
fs: &mut F,
dir: &Path,
path: &Path,
Expand Down
10 changes: 10 additions & 0 deletions crates/worldscript-secure-storage/src/durable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ pub trait DurableFs {
fn sync_dir(&mut self, dir: &Path) -> io::Result<DirectoryDurability>;
/// The names of the entries directly inside `dir`, in no particular order.
fn list_dir(&mut self, dir: &Path) -> io::Result<Vec<OsString>>;
/// Atomically replaces `to` with `from` within one directory (the §5.3 pointer's
/// atomic-rename-and-fsync mechanism). Used only for the recoverable root pointer, never for a
/// generation file.
fn rename_replace(&mut self, from: &Path, to: &Path) -> io::Result<()>;
}

/// The real filesystem. On Apple platforms `File::sync_all` issues `F_FULLFSYNC`; on Windows it is
Expand Down Expand Up @@ -101,6 +105,12 @@ impl DurableFs for StdFs {
.map(|entry| entry.map(|entry| entry.file_name()))
.collect()
}

/// `std::fs::rename`: `rename(2)` on Unix and `MoveFileExW(MOVEFILE_REPLACE_EXISTING)` on
/// Windows, both of which replace an existing destination.
fn rename_replace(&mut self, from: &Path, to: &Path) -> io::Result<()> {
fs::rename(from, to)
}
}

/// A Core-generated write operation identity: 128 random bits as 32 lowercase hex characters, so it
Expand Down
9 changes: 7 additions & 2 deletions crates/worldscript-secure-storage/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
//! ([`record`]), the §10.4.1 record-class disposition ([`mod@disposition`]), the Gate 3
//! slice 3A durable staging and promotion of one generation ([`durable`]), and slice 3B's
//! `record-commit` marker codec ([`marker`]) and marker commit protocol with startup reconciliation
//! ([`commit`]), and slice 3C's authority-root digests ([`root`]), record catalog ([`catalog`])
//! and persisted root records ([`root_record`]).
//! ([`commit`]), and slice 3C's authority-root digests ([`root`]), record catalog ([`catalog`]),
//! persisted root records ([`root_record`]) and the two-phase root commit ([`root_store`]).
Comment thread
qnbs marked this conversation as resolved.
//! It changes no current
//! TypeScript/Tauri storage authority and holds no journal or authority-root commit yet.

Expand All @@ -33,6 +33,7 @@ pub mod record_class;
pub mod recovery;
pub mod root;
pub mod root_record;
pub mod root_store;
pub mod seal;
pub mod secure_store;
pub mod store_authority;
Expand Down Expand Up @@ -79,6 +80,10 @@ pub use root_record::{
open_root_slot, seal_root_slot, KeyEpochAddress, KeyEpochRead, KeyEpochRecord, KeyEpochStatus,
KeyEpochWrite, RootPointer, RootRecordError, RootSlotRead,
};
pub use root_store::{
commit_root, load_committed_root, recover_root, CommittedRootView, RootCommitRequest,
RootCommitted, RootLayout, RootRecovery, RootRecoveryReason, RootStep, RootStoreError,
};
#[cfg(feature = "test-randomness")]
pub use seal::seal_with_random;
pub use seal::{open, seal, Key, RecordMeta, SealTarget};
Expand Down
15 changes: 13 additions & 2 deletions crates/worldscript-secure-storage/src/root_record.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,23 @@ pub fn seal_root_slot(
scope: &InstallationScopeId,
root: &RootBody,
) -> Result<Vec<u8>, RootRecordError> {
let (meta, payload) = root_slot_plaintext(root)?;
seal_record(key, &root_identity(scope)?, meta, &payload).map_err(RootRecordError::Seal)
}

/// The envelope metadata and plaintext payload of `root`'s slot, for callers that seal through the
/// durable staging path (slice 3A) instead of [`seal_root_slot`].
pub(crate) fn root_slot_plaintext(
root: &RootBody,
) -> Result<(RecordMeta, Vec<u8>), RootRecordError> {
let mut payload = ROOT_SLOT_FORMAT_VERSION.to_be_bytes().to_vec();
payload.extend_from_slice(&encode_root_body(root)?);
let meta = RecordMeta {
key_epoch: root.active_key_epoch,
record_generation: root.root_generation,
record_schema: CONTROL_RECORD_SCHEMA,
};
seal_record(key, &root_identity(scope)?, meta, &payload).map_err(RootRecordError::Seal)
Ok((meta, payload))
}

/// A root slot to open: the sealed bytes of generation `root_generation` of
Expand Down Expand Up @@ -348,7 +357,9 @@ fn key_route(rest: &[u8]) -> Result<RootKeyRefV1, RootRecordError> {
RootKeyRefV1::new(route.to_vec()).map_err(|_| RootRecordError::Corrupt("invalid key route"))
}

fn root_identity(scope: &InstallationScopeId) -> Result<RecordIdentity, RootRecordError> {
pub(crate) fn root_identity(
scope: &InstallationScopeId,
) -> Result<RecordIdentity, RootRecordError> {
RecordIdentity::new(RecordClass::AuthorityRoot, &[scope.as_str()])
.map_err(|_| RootRecordError::InvalidIdentity)
}
Expand Down
Loading
Loading