Add encryption key fetch from bucket (#2159)

This commit is contained in:
Leendert de Borst
2026-07-23 09:05:13 +02:00
parent 1588f7a56d
commit 2e0e2611af
5 changed files with 36 additions and 2 deletions
+8 -1
View File
@@ -259,8 +259,15 @@ export async function vaultCodecCanonicalizeFromSqlite(input: CodecCanonicalizeI
* emitting it, so unknown newer-client data survives the round trip inside the vault DB itself.
*/
export async function vaultCodecMaterializeAsSqlite(manifest: CodecManifest, dataBuckets: CodecDataBucket[], schemaColumns?: Record<string, string[]>): Promise<CodecMaterialized> {
/**
* Extract the primary encryption-key row (the user's asymmetric keypair) from the decrypted
* `EncryptionKeys` data bucket — the small, independently-decryptable bucket the keypair now lives in.
* Used to unwrap shared-folder VEKs during pull without materializing the full root manifest.
* Returns null when the bucket carries no primary key.
*/
export async function vaultCodecExtractPrimaryEncryptionKeyFromBucket(bucket: CodecDataBucket): Promise<Record<string, unknown> | null> {
await initRustCore();
return core.vaultCodecMaterializeAsSqlite({ manifest, dataBuckets, schemaColumns }) as CodecMaterialized;
return (core.vaultCodecExtractPrimaryEncryptionKeyFromBucket(bucket) ?? null) as Record<string, unknown> | null;
}
/**
+10
View File
@@ -271,6 +271,16 @@ pub unsafe extern "C" fn vault_codec_validate_data_bucket_ffi(input_json: *const
codec_json_ffi(input_json, "input_json", crate::vault_codec::validate_data_bucket_json)
}
/// Extract the primary encryption-key row from the decrypted `EncryptionKeys` data bucket.
/// Input: `DataBucket` JSON. Output: the `EncryptionKeys` row JSON object, or `null` when absent.
///
/// # Safety
/// `input_json` must be a valid null-terminated C string; free the result with `free_string`.
#[no_mangle]
pub unsafe extern "C" fn vault_codec_extract_primary_encryption_key_from_bucket_ffi(input_json: *const c_char) -> *mut c_char {
codec_json_ffi(input_json, "input_json", crate::vault_codec::extract_primary_encryption_key_from_bucket_json)
}
/// The bucket layout: `[{ category, tables: [<name>] }]` JSON.
///
/// # Safety
+1 -1
View File
@@ -28,7 +28,7 @@ pub use vault_codec::{
extract_bucket, generate_user_salt, unpack_payload, materialize_as_sqlite, pack_payload,
validate_manifest, validate_data_bucket, BlobEntry,
CanonicalizeInput, CanonicalizedVault, CodecRecord, CodecTableData, DataBucket, Manifest,
MaterializeInput, MaterializedTables, ValidationResult,
MaterializeInput, MaterializedTables, SharedFolderSpec, SharedVault, ValidationResult,
};
pub use vault_pruner::{
prune_vault, PruneInput, PruneOutput, PruneStats,
+8
View File
@@ -135,6 +135,14 @@ pub fn validate_data_bucket(b: &DataBucket) -> ValidationResult {
validate::validate_data_bucket(b)
}
/// Extract the primary encryption-key row from the decrypted `EncryptionKeys` data bucket — used to
/// unwrap shared-folder VEKs during pull, decrypting only the small keypair bucket rather than the
/// full root manifest.
pub fn extract_primary_encryption_key_from_bucket_json(bucket_json: &str) -> VaultResult<String> {
let b: DataBucket = serde_json::from_str(bucket_json)?;
Ok(serde_json::to_string(&extract_primary_encryption_key_from_bucket(&b))?)
}
/// SHA-256 (lowercase hex) of a base64 ciphertext string — storage-layer integrity.
pub fn compute_ciphertext_hash(base64_ciphertext: &str) -> String {
match BASE64.decode(base64_ciphertext) {
+9
View File
@@ -214,6 +214,15 @@ pub fn vault_codec_compute_ciphertext_hash_js(base64_ciphertext: &str) -> String
vault_codec::compute_ciphertext_hash(base64_ciphertext)
}
/// Extract the primary encryption-key row from the decrypted `EncryptionKeys` data bucket.
/// Input: `DataBucket`. Output: the `EncryptionKeys` row object, or null when the bucket has none.
#[wasm_bindgen(js_name = vaultCodecExtractPrimaryEncryptionKeyFromBucket)]
pub fn vault_codec_extract_primary_encryption_key_from_bucket_js(bucket: JsValue) -> Result<JsValue, JsValue> {
let b: DataBucket = serde_wasm_bindgen::from_value(bucket)
.map_err(|e| JsValue::from_str(&format!("Failed to parse data bucket: {}", e)))?;
codec_to_js(&vault_codec::extract_primary_encryption_key_from_bucket(&b))
}
// ═══════════════════════════════════════════════════════════════════════════════
// Credential Matcher WASM Bindings
// ═══════════════════════════════════════════════════════════════════════════════