{"record":{"id":"d7373ee56986d80a","repo":"siyuan-note/siyuan","slug":"conf-language-324","errorCode":null,"errorMessage":"Conf.Language(324)","messagePattern":"Conf\\.Language\\(324\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/model/crypto.go","lineNumber":320,"sourceCode":"}\n\n// ImportNotebookCryptoBackup 接收用户导入的密钥备份文件内容（JSON 字节），\n// 校验为合法 NotebookCrypto 后写回 <DataDir>/.siyuan/data-crypto-backup.json 并装回本机 Conf。\n// 用于新设备/重装后不依赖同步、手动恢复加密配置（详见设计文档 §4.1）。\n// 安全：备份文件不含主密码（salt 不保密、verifier 是密文），导入只恢复配置，解锁仍需主密码。\n// 防呆：本机已有完整且已启用的加密配置时拒绝导入，避免覆盖现有 salt/verifier 孤立现有 WrappedDEK。\n// ImportNotebookCryptoBackup 接收用户导入的密钥备份文件内容（JSON 字节）+ 主密码，\n// 校验主密码能解开备份里的 verifier 后才写回配置。防止 crafted 备份设置弱 KDFParams 等攻击。\n// RecoveryRequired 状态允许导入，但候选 KEK 必须能解开所有现存笔记本和已删除笔记本历史。\nfunc ImportNotebookCryptoBackup(data []byte, password string) error {\n\tnotebookCryptoMu.Lock()\n\tdefer notebookCryptoMu.Unlock()\n\n\tConf.m.RLock()\n\tcurrent := *Conf.NotebookCrypto\n\tConf.m.RUnlock()\n\tif current.Enabled && notebookCryptoConfigurationComplete(&current) {\n\t\treturn errors.New(Conf.Language(324))\n\t}\n\n\tnc := &conf.NotebookCrypto{}\n\tif err := json.Unmarshal(data, nc); err != nil {\n\t\treturn errors.New(Conf.Language(317))\n\t}\n\tif !notebookCryptoConfigurationComplete(nc) {\n\t\treturn errors.New(Conf.Language(317))\n\t}\n\n\t// 用导入的 salt + 用户输入的主密码派生 KEK，校验能否解开备份里的 verifier\n\tparams, validErr := util.ValidateArgon2Params(nc.KDFParams)\n\tif validErr != nil {\n\t\treturn errors.New(Conf.Language(317))\n\t}\n\tkek := util.DeriveKey(password, nc.MasterSalt, params)\n\tdefer zeroAndClear(kek)\n\tif nc.Checksum != computeBackupChecksum(nc) {","sourceCodeStart":302,"sourceCodeEnd":338,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/afa823b6b4e4f183511e0bc0a3be93caa94c7c97/kernel/model/crypto.go#L302-L338","documentation":"ImportNotebookCryptoBackup() refuses to import when the local NotebookCrypto config is enabled AND complete (notebookCryptoConfigurationComplete), returning Conf.Language(324): 'Cannot import a key backup while encrypted notebooks are enabled. Disable it first'. This is a deliberate guard: importing would overwrite the existing salt/verifier and orphan every existing WrappedDEK, making current encrypted notebooks permanently undecryptable.","triggerScenarios":"Calling the import API/flow on a machine that already has working notebook encryption configured, e.g. when attempting to migrate keys from another device into an already-encrypted setup.","commonSituations":"Trying to restore keys onto a reinstalled machine where encryption was re-enabled first; copying keys between devices in the wrong order; misunderstanding the flow as additive rather than destructive.","solutions":["Disable notebook encryption first (Settings flow with the current master password), then import the backup","Or import into a fresh workspace where encryption was never enabled","If encryption cannot be disabled because the password is lost, import is not the remedy - restore via conf.json/backup files instead","After importing, re-enable encryption and verify all notebooks unlock before adding data"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// kernel-side guard mirrors the check: only import into a disabled or incomplete config\nstate := model.GetNotebookCryptoState() // conceptually: Enabled && complete must be false\nif state == NotebookCryptoStateEnabled {\n    return errors.New(\"disable notebook encryption before importing a key backup\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Import keys BEFORE enabling encryption on a new setup - order matters","If encryption is already enabled and healthy, importing is never the right move; export instead","Keep exactly one authoritative backup per master password generation"],"tags":["crypto","master-password","backup","import","notebook-encryption","go","siyuan"],"backgroundTag":"existing-config-conflict","analyzedSha":"afa823b6b4e4f183511e0bc0a3be93caa94c7c97","analyzedAt":"2026-08-18T17:04:10.865Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}