{"record":{"id":"18c988e14f0777f8","repo":"siyuan-note/siyuan","slug":"317","errorCode":"317","errorMessage":"Invalid key backup file","messagePattern":"Invalid key backup file","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"kernel/model/crypto.go","lineNumber":1176,"sourceCode":"\t// 调用方已持有 notebookCryptoMu，且 writeNotebookCryptoBackupData 不再申请该锁，故无死锁；\n\t// 同步写避免与 ChangeMasterPassword 的并发备份写竞争同一文件（lost update 导致 verifier 被回退）。\n\tnc := *backup\n\tif err := writeNotebookCryptoBackupData(&nc, kek); err != nil {\n\t\tlogging.LogWarnf(\"rewrite notebook crypto backup after restore failed: %s\", err)\n\t}\n\tlogging.LogInfof(\"notebook crypto restored from backup (e.g. after sync to a new device)\")\n\treturn kek, nil\n}\n\n// deriveNotebookCryptoBackupCandidate 对同步备份做无副作用验证，并确认它覆盖全部现有加密笔记本。\nfunc deriveNotebookCryptoBackupCandidate(password string) (backup *conf.NotebookCrypto, kek []byte, err error) {\n\tbackup, err = loadNotebookCryptoBackup()\n\tif err != nil || backup == nil || len(backup.MasterSalt) == 0 || len(backup.KEKVerifier) == 0 {\n\t\treturn nil, nil, errors.New(Conf.Language(310))\n\t}\n\tparams, validErr := util.ValidateArgon2Params(backup.KDFParams)\n\tif validErr != nil {\n\t\treturn nil, nil, errors.New(Conf.Language(317))\n\t}\n\tkek = util.DeriveKey(password, backup.MasterSalt, params)\n\tdecrypted, decryptErr := util.DecryptWithAAD(kek, backup.KEKVerifier, []byte(\"siyuan:kek-verifier\"))\n\tif decryptErr != nil || string(decrypted) != string(kekVerifierMagic) {\n\t\tzeroAndClear(kek)\n\t\treturn nil, nil, errors.New(Conf.Language(311))\n\t}\n\tif backup.Spec != conf.CurrentNotebookCryptoSpec || backup.Checksum == \"\" ||\n\t\tlen(backup.KEKMAC) == 0 || !verifyKEKMAC(backup, kek) {\n\t\tzeroAndClear(kek)\n\t\treturn nil, nil, errors.New(Conf.Language(316))\n\t}\n\tif !verifyKEKAgainstExistingBoxes(kek) || !verifyKEKAgainstEncryptedHistory(kek) {\n\t\tzeroAndClear(kek)\n\t\treturn nil, nil, errors.New(Conf.Language(316))\n\t}\n\tbackup.KDFParams = params\n\treturn backup, kek, nil","sourceCodeStart":1158,"sourceCodeEnd":1194,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/crypto.go#L1158-L1194","documentation":"Thrown by deriveNotebookCryptoBackupCandidate (crypto.go:1176, i18n code 317) when the backup exists and has key fields but its Argon2 KDF parameters fail validation (util.ValidateArgon2Params). Invalid KDF params mean the backup cannot safely derive the KEK, so it is rejected as an invalid key backup file rather than risking a weak or malformed key derivation.","triggerScenarios":"deriveNotebookCryptoBackupCandidate loads a backup whose KDFParams field is empty, malformed, or out of the allowed Argon2id bounds (memory, iterations, parallelism). Reached on the same recovery paths as 606: re-enable, new-device sync restore, or deriveKEK fallback.","commonSituations":"Backup written by an older/newer kernel version with different KDF param schema. Backup edited or corrupted. KDF params tampered with. Migration between Argon2 param formats.","solutions":["Restore a backup with valid KDFParams from a known-good sync snapshot or another device, then retry.","If only conf.json has valid KDFParams, prefer the local-config auth path (do not force the backup candidate path).","As a last resort, if the encrypted data is expendable, remove orphaned notebooks/history and re-enable with a fresh key domain."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Confirm backup KDF params are valid before using it for recovery.\nb, err := loadNotebookCryptoBackup()\nif err != nil || b == nil {\n    return errors.New(\"no backup\")\n}\nif _, verr := util.ValidateArgon2Params(b.KDFParams); verr != nil {\n    return fmt.Errorf(\"backup KDF params invalid; restore a compatible backup: %w\", verr)\n}","typeGuard":null,"tryCatchPattern":"if err := model.EnableEncryptedNotebook(password); err != nil {\n    if err.Error() == model.Conf.Language(317) {\n        respond(c, \"key backup has invalid KDF params; restore a spec-compatible backup\")\n        return\n    }\n    respond(c, err.Error())\n}","preventionTips":["Do not hand-edit backup KDF params.","Use a kernel version compatible with the backup's spec/KDF format.","Keep a known-good backup copy for recovery."],"tags":["encryption","notebook-crypto","kdf","argon2","backup"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}