{"record":{"id":"925a3129b68aab07","repo":"siyuan-note/siyuan","slug":"invalid-key-backup-file","errorCode":null,"errorMessage":"Invalid key backup file","messagePattern":"Invalid key backup file","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/crypto.go","lineNumber":324,"sourceCode":"// 安全：备份文件不含主密码（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) {\n\t\treturn errors.New(Conf.Language(317))\n\t}\n\tif !verifyKEKMAC(nc, kek) {\n\t\treturn errors.New(Conf.Language(317))\n\t}","sourceCodeStart":306,"sourceCodeEnd":342,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/crypto.go#L306-L342","documentation":"ImportNotebookCryptoBackup returns Conf.Language(317) ('invalid key backup file') at line 324 when json.Unmarshal of the provided bytes into a NotebookCrypto struct fails. The file is not valid JSON or its top-level structure does not match the NotebookCrypto schema. This is the first structural gate in the import validation chain.","triggerScenarios":"ImportNotebookCryptoBackup is called with bytes that are not valid JSON, are truncated, or do not deserialize into conf.NotebookCrypto (missing required JSON fields, wrong types). User selects a non-backup file, a partially downloaded/corrupted backup, or a file from an incompatible version.","commonSituations":"User picks the wrong file (e.g., conf.json, a regular notebook .sy); backup file truncated during copy/download; backup produced by a future/newer spec the current build cannot parse; file edited externally and broken.","solutions":["Re-export a fresh backup from the source device's encryption settings and import that file.","Verify the file is the notebook-crypto-backup-*.json produced by ExportNotebookCryptoBackup, not another JSON file.","If migrating across versions, ensure both source and target run compatible SiYuan versions sharing the same NotebookCrypto schema."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Reject non-JSON or non-NotebookCrypto content before importing.\nvar probe map[string]any\nif err := json.Unmarshal(data, &probe); err != nil {\n    return errors.New(\"file is not valid JSON\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Only import files produced by ExportNotebookCryptoBackup (named notebook-crypto-backup-*.json).","Do not feed arbitrary JSON or conf.json into the import path.","Verify the file transferred completely (not truncated) before importing."],"tags":["crypto","backup","import","validation","json"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}