synology_apm_repo.sdk.dedup.keys module

Key material: parsing an administrator-provided key string, and resolving the wrapped VaultKey from whichever of the two on-disk locations this repository’s layout uses.

“Is this key correct” is answered by ``KeyMaterial.verify`` alone, via AES-256-GCM’s own authentication tag — never by also decrypting a real chunk. GCM tag success is already cryptographic proof the (userKeyID, userKey) pair correctly unwraps the stored wrapped-VaultKey record (FORMAT-SPEC.md: vaultkey-custody); given the repository-wide invariant that the DEK (vaultKey) itself never changes after first initialization (also vaultkey-custody), that same VaultKey is, by construction, the one used for every real chunk this repository has — not merely “probably” so. Per-chunk fingerprint verification (decrypting a real chunk and comparing its SHA-256 against its stored .fgp) is a separate, data-integrity concern this module has no part in — see Pool.read_chunk’s own verify_fingerprint option for that.

class synology_apm_repo.sdk.dedup.keys.KeyVerification(gcm_ok, vault_key)

Bases: object

Result of KeyMaterial.verify. vault_key holds the resolved DEK only when gcm_ok and encryption is actually in use; it’s None both for an unencrypted repository and for a failed unwrap.

gcm_ok: bool
vault_key: bytes | None
property ok: bool
class synology_apm_repo.sdk.dedup.keys.KeyMaterial(user_key_id, user_key)

Bases: object

A parsed "<userKeyID>@<base64(userKey)>" key string.

user_key_id == "NoEncryption" (NO_ENCRYPTION_USER_KEY_ID) marks an unencrypted repository — no VaultKey exists to resolve.

user_key_id: str
user_key: bytes
classmethod from_key_string(key_string)
property is_no_encryption: bool
async resolve_vault_key(store, layout)

Fetch this repository’s wrapped VaultKey (from whichever of the two on-disk locations layout.kind implies) and AES-256-GCM unwrap it.

Returns None if no wrapped key is on record at all (e.g. the lookup row/file is simply absent) — as distinct from KeyMismatchError, which means a wrapped key was found but this (userKeyID, userKey) pair does not open it.

async verify(store, layout)

The whole answer to “is this key correct” — no separate per-chunk check follows this. Never raises for an ordinary “wrong key” outcome — that is reported via the returned KeyVerification, not an exception; the caller decides what a failed verification means for its flow (CLI/TUI report it, Repository.set_key may choose to reject it). Touches only the wrapped-VaultKey record itself (one sqlite row or one small key file) — never the Pool.

async synology_apm_repo.sdk.dedup.keys.probe_encrypted(store, layout)

Cheaply determine whether this repository is vault-encrypted by reading its own encryption-key record directly — VAULT layout reads db/vault_encryption_key’s latest row, OBJECT_STORE layout reads the key-dir marker under <key_root>/userKey/. No bucket file opened, no Pool scan, no key required — the repository’s own encryption-key record is trusted as the single source of truth here, the same way KeyMaterial.resolve_vault_key trusts it to find one specific candidate key’s wrapped VaultKey, just reading the record’s latest entry instead of looking one up by id.

VAULT layout: db/vault_encryption_key is an append-only key-rotation log, never updated in place. Its last-inserted row’s user_key_uuid is the currently-active key, "NoEncryption" iff this vault has never been encrypted (Encryption↔NoEncryption cannot toggle after first initialization — the DEK/vaultKey itself never changes once set, per FORMAT-SPEC.md: vaultkey-custody).

OBJECT_STORE layout: the equivalent record lives as individual objects named by userKeyID under <key_root>/userKey/ rather than db rows, with the same "NoEncryption" sentinel written at bucket-creation time for an unencrypted bucket.

Returns None only if the record itself is entirely absent — should not happen for a properly initialized repository — as distinct from a confirmed-unencrypted repository (False).