pubfn reconcile_storage_with_gsc(&self) -> binder::Result<bool> { // Get storage root let resp = self.storage.request(&StorageRequest::GetStatus).map_err(binder_error::from_tipc)?; let storage_root = iflet StorageResponse::GetStatus(GetStatusResponse { root_hash, .. }) = resp {
root_hash
} else { // Don't bother to reconcile if the storage is inaccessible or uninitialized. return Ok(true);
};
// Get GSC root let (gsc_root, _) = self.gsc.sys_info().map_err(|err| {
error!("gsc.sys_info() failed: {err:?}");
binder_error::gsc()
})?; if storage_root.0 == gsc_root.0 { return Ok(true);
}
warn!("GSC and storage out of sync; attempting log replay");
// Get the GSC log. letmut buffer = Vec::new(); let entries = self.gsc.get_log(Some(Hash(storage_root.0)), &mut buffer).map_err(|err| {
error!("gsc.get_log() failed: {err:?}");
binder_error::gsc()
})?;
// Replay the log letmut to_replay = entries.iter().rev().peekable(); // We need to skip the first operation if we have already applied it to storage. if matches!(to_replay.peek(), Some(entry) if entry.root.0 == storage_root.0) { let _skip = to_replay.next();
} letmut to_remove = Map::new(); letmut new_root: TreeHash = storage_root; whilelet Some(entry) = to_replay.next() { match entry.data() {
LogEntryData::ResetTree => { return Ok(false);
}
LogEntryData::InsertLeaf(hmac) => { // This is somewhat precarious because we don't have the data to recover this leaf. // The best we can do is try to remove it. iflet Ok((root, leaf_id)) = self.replay_without_gsc(entry.label, TreeHash(hmac.0))
{
new_root = root;
to_remove.insert(entry.label.get(), leaf_id);
} else { return Ok(false);
}
}
LogEntryData::TryAuth00(_) | LogEntryData::TryAuth02(_) => { // Skip try-auth for an inserted leaf since it will fail, and if it gets removed // before it is needed, replay may still succeed. if to_remove.contains_key(&entry.label.get()) { continue;
} // Skip non-remove operations on the same leaf. if matches!(to_replay.peek(), Some(next_entry) if next_entry.label == entry.label)
{ continue;
} iflet Ok(root) = self.replay_try_auth(entry.root, entry.label) {
new_root = root;
} else { return Ok(false);
}
}
LogEntryData::RemoveLeaf => { iflet Ok((root, _)) = self.replay_without_gsc(entry.label, EMPTY_TREE_HASH) {
new_root = root;
} else { return Ok(false);
};
to_remove.remove(&entry.label.get());
}
LogEntryData::UnrecognizedOperation => {
error!("Got unrecognized option: {:02x}", entry.operation.0); continue;
}
}
} // If there are leaves to remove skip the root check since, self.remove_leaf(..) will only // succeed if the storage and gsc roots are in sync after the operation. if to_remove.is_empty() { if new_root.0 != gsc_root.0 { return Ok(false);
}
} else { // For now there should only be at most one leaf to remove.
warn!("GSC and storage out of sync; removing {} leaf", to_remove.len()); for leaf_id in to_remove.into_values() { self.remove_leaf(leaf_id)?;
}
}
info!("log replay successful");
Ok(true)
}
letmut key = Secret([0u8; 32]);
rand_bytes(&mut key.0).map_err(|err| {
error!("failed to generate key: {err:?}");
binder_error::pinweaver(c"failed to generate key")
})?;
letmut iv = LargeSecretIv([0u8; 12]);
rand_bytes(&mut iv.0).map_err(|err| {
error!("failed to generate iv: {err:?}");
binder_error::pinweaver(c"failed to generate iv")
})?;
// Use the leaf_id as additional auth data to prevent the secret // from being moved between leaf_id's letmut auth_tag = LargeSecretAuthTag(0.into()); let secret_ciphertext = SerdeVec::from_vec(
encrypt_aead(
Cipher::aes_256_gcm(),
&key.0,
Some(&iv.0),
leaf_id.as_bytes(),
secret,
auth_tag.0.deref_mut().as_mut(),
)
.map_err(|err| {
error!("failed to encrypt large secret: {err:?}");
binder_error::pinweaver(c"failed to generate key")
})?,
);
Ok((key, SerdeOption::Some(LargeSecretInfo { iv, auth_tag, secret_ciphertext })))
}
fn reset_tree(&self) -> binder::Result<()> { // Reset from the GSC first to make sure it succeeds before clearing the storage. let root_hash = self
.gsc
.reset_tree(GscTreeParams {
bits_per_level: DEFAULT_TREE_BITS_PER_LEVEL,
height: DEFAULT_TREE_HEIGHT,
})
.map_err(|err| {
error!("gsc reset tree failed: {err:?}");
binder_error::gsc()
})?;
// The tree is reset on the GSC, so continue with the leaf storage. let ret = self
.storage
.request(&StorageRequest::ResetTree(ResetTreeRequest {
tree_params: TreeParams {
height: DEFAULT_TREE_HEIGHT,
bits_per_level: DEFAULT_TREE_BITS_PER_LEVEL,
},
}))
.map_err(|err| {
error!("storage reset tree failed: {err:?}");
binder_error::from_tipc(err)
})?; iflet StorageResponse::ResetTree(resp) = ret { if *resp.root_hash == root_hash.0 {
Ok(())
} else { // This should never happen.
error!("root_hash mismatch during reset; please file a bug");
Err(binder_error::pinweaver(c"reset_tree: unexpected response"))
}
} else {
error!("reset_tree: got unexpected response: {ret:?}");
Err(binder_error::pinweaver(c"reset_tree: unexpected response"))
}
}
fn commit_remove_leaf(
&self,
leaf_id: StorageLeafId,
path: Path,
leaf_hmac: TreeHash,
path_hashes: TreeHashes,
) -> binder::Result<()> { // Convert parameters. let gsc_path = storage_to_gsc::path(path); let gsc_leaf_hmac = storage_to_gsc::hash(leaf_hmac); let gsc_path_hashes = storage_to_gsc::path_hashes(path_hashes);
fn handle_pending_operation(&self) -> binder::Result<()> { let resp = self.storage.request(&StorageRequest::GetStatus).map_err(binder_error::from_tipc)?; iflet StorageResponse::GetStatus(GetStatusResponse {
pending_op: SerdeOption::Some(StartOpRequest::Remove(leaf_id)),
..
}) = resp
{ self.remove_leaf(leaf_id)
} else { // We should not get here.
error!("handle_pending_operation: unexpected response: {:?}", &resp);
Err(binder_error::unsupported_operation())
}
}
// Handle errors expected during the first storage request such as ResponseError::NotInitialized // // This will retry the command as appropriate after initializing the tree. fn first_request_helper(&self, request: &StorageRequest) -> binder::Result<StorageResponse> { letmut ret = self.storage.request(request); iflet Ok(StorageResponse::Error(err)) = &ret { match err {
ResponseError::NotInitialized => { self.reset_tree()?;
ret = self.storage.request(request);
}
ResponseError::OperationPending => { self.handle_pending_operation()?;
ret = self.storage.request(request);
}
_ => {}
}
}
ret.map_err(binder_error::from_tipc)
}
}
impl<Deps: PinWeaverServiceDeps> Interface for PinWeaverService<Deps> {}
// Convert parameters from IPinweaver to storage and GSC types. let leaf_id = api_to_storage::leaf_id(api_leaf_id); let low_entropy_secret = api_to_gsc::secret(api_low_entropy_secret)?; let (high_entropy_secret, large_key_info) = self.handle_secret_size(leaf_id, api_high_entropy_secret)?; let reset_secret = api_to_gsc::secret(api_reset_secret)?; let delay_schedule = api_to_gsc::delay_schedule(api_delay_schedule)?; let insert_mode = api_to_storage::insertmode(api_insert_mode)?;
// Start the operation with Storage to find an empty leaf and whether a replacement will occur. let start_request =
StartOpRequest::Insert(InsertOpRequest { leaf_id, mode: insert_mode, large_key_info }); let ret = self.first_request_helper(&StorageRequest::StartOp(start_request.clone()))?; // Extract returned values and convert from Storage to GSC. let (path, tree_hashes, replacing_path) = match ret {
StorageResponse::StartOp(StartOpResponse {
path,
tree_hashes,
op_data: StartOpResponseData::Insert(StartInsertResponseData { replacing_path }),
}) => (path, tree_hashes, replacing_path),
StorageResponse::Error(ResponseError::WouldReplace) => { return Err(binder_error::leaf_exists());
}
_ => {
error!("start_insert: unexpected response: {ret:?}"); return Err(binder_error::pinweaver(c"unexpected response"));
}
}; if replacing_path.as_ref().is_some() && matches!(api_insert_mode, InsertMode::NEW_ONLY) { // This should never happen.
error!("unexpected replacing_path; please file a bug"); return Err(binder_error::pinweaver(c"unexpected replacing_path; please file a bug"));
} let path_hashes: Vec<Hash> = storage_to_gsc::path_hashes(tree_hashes);
// Convert parameters from IPinweaver to storage and GSC types. let leaf_id = api_to_storage::leaf_id(api_leaf_id); let low_entropy_secret = api_to_gsc::secret(api_low_entropy_secret)?;
// Start try auth let start_request = StartOpRequest::TryAuth(leaf_id); let ret = self.first_request_helper(&StorageRequest::StartOp(start_request.clone()))?; // Extract returned values and convert from Storage to GSC. let (tree_hashes, leaf_contents, large_key_info) = match ret {
StorageResponse::StartOp(StartOpResponse {
tree_hashes,
op_data:
StartOpResponseData::TryAuth(StartTryAuthResponseData {
leaf_contents,
large_key_info,
}),
..
}) => (tree_hashes, leaf_contents, large_key_info),
StorageResponse::Error(ResponseError::DoesNotExist) => { return Err(binder_error::leaf_not_found());
}
_ => {
error!("start_try_auth: unexpected response: {ret:?}"); return Err(binder_error::pinweaver(c"unexpected response"));
}
}; let path_hashes: Vec<Hash> = storage_to_gsc::path_hashes(tree_hashes);
// Convert parameters from IPinweaver to storage and GSC types. let leaf_id = api_to_storage::leaf_id(api_leaf_id); let reset_secret = api_to_gsc::secret(api_reset_secret)?;
// Start try auth let start_request = StartOpRequest::TryAuth(leaf_id); let ret = self.first_request_helper(&StorageRequest::StartOp(start_request.clone()))?; // Extract returned values and convert from Storage to GSC. let (tree_hashes, leaf_contents) = match ret {
StorageResponse::StartOp(StartOpResponse {
tree_hashes,
op_data:
StartOpResponseData::TryAuth(StartTryAuthResponseData { leaf_contents, .. }),
..
}) => (tree_hashes, leaf_contents),
StorageResponse::Error(ResponseError::DoesNotExist) => { return Err(binder_error::leaf_not_found());
}
_ => {
error!("start_try_auth: unexpected response: {ret:?}"); return Err(binder_error::pinweaver(c"unexpected response"));
}
}; let path_hashes: Vec<Hash> = storage_to_gsc::path_hashes(tree_hashes);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.