{"record":{"id":"8f0c26816b4c0c6a","repo":"siyuan-note/siyuan","slug":"conf-language-317","errorCode":null,"errorMessage":"Conf.Language(317)","messagePattern":"Conf\\.Language\\(317\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/crypto.go","lineNumber":325,"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":307,"sourceCodeEnd":343,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/afa823b6b4e4f183511e0bc0a3be93caa94c7c97/kernel/model/crypto.go#L307-L343","documentation":"ImportNotebookCryptoBackup() first json.Unmarshals the submitted bytes into a conf.NotebookCrypto; if that fails it returns Conf.Language(317): 'Invalid key backup file'. This branch means the payload is not valid JSON of the expected shape at all - structural, before any cryptographic validation happens.","triggerScenarios":"Uploading a file that is not the exported notebook-crypto-backup JSON: truncated download, an HTML error page saved as .json, a renamed unrelated file, or encoding damage (BOM/garbled bytes).","commonSituations":"User picks the wrong attachment; backup emailed/downloaded through a pipeline that mangles it; double-extension files hiding true format.","solutions":["Re-export the backup on the source machine and transfer it as-is (no re-saving through editors)","Open the file in a text editor - it must be plain JSON starting with '{' and contain masterSalt/kekVerifier-style fields","Check the file is not truncated (compare size with the source)","For API callers, run json.Valid(data) before submitting"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if !json.Valid(data) {\n    return errors.New(\"file is not valid JSON - not a key backup\")\n}\nvar probe map[string]any\nif err := json.Unmarshal(data, &probe); err != nil {\n    return errors.New(\"file is not a JSON object - not a key backup\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Transfer backups as raw files - never through editors or re-serializers","Sanity-check the file starts with '{' and mentions masterSalt/kek fields before submitting","Keep the .json extension and UTF-8 encoding intact"],"tags":["crypto","backup","import","json","validation","go","siyuan"],"backgroundTag":"json-parse-error","analyzedSha":"afa823b6b4e4f183511e0bc0a3be93caa94c7c97","analyzedAt":"2026-08-18T17:04:10.865Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}