{"record":{"id":"ec69951a0c43006e","repo":"siyuan-note/siyuan","slug":"invalid-historical-notebook-encryption-key","errorCode":null,"errorMessage":"invalid historical notebook encryption key","messagePattern":"invalid historical notebook encryption key","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/crypto_history_keys.go","lineNumber":44,"sourceCode":"\nconst historyKEKAAD = \"siyuan:history-kek:v1\"\n\n// decryptHistoryKEKs 只在当前配置已经完成主密码认证后使用，返回值由调用方在使用结束时清零。\nfunc decryptHistoryKEKs(kek []byte, wrappedKeys [][]byte) (keys [][]byte, err error) {\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tclearHistoryKEKs(keys)\n\t\t\tkeys = nil\n\t\t}\n\t}()\n\tfor _, wrapped := range wrappedKeys {\n\t\tkey, decryptErr := util.DecryptWithAAD(kek, wrapped, []byte(historyKEKAAD))\n\t\tif decryptErr != nil {\n\t\t\treturn keys, decryptErr\n\t\t}\n\t\tif len(key) != 32 {\n\t\t\tzeroAndClear(key)\n\t\t\treturn keys, errors.New(\"invalid historical notebook encryption key\")\n\t\t}\n\t\tkeys = append(keys, key)\n\t}\n\treturn keys, nil\n}\n\nfunc clearHistoryKEKs(keys [][]byte) {\n\tfor _, key := range keys {\n\t\tzeroAndClear(key)\n\t}\n}\n\n// rewrapHistoryKEKs 将历次 KEK 和本次旧 KEK 一起封装到新 KEK 下，恢复不依赖历史所在设备。\nfunc rewrapHistoryKEKs(oldKEK, newKEK []byte, wrappedKeys [][]byte) (ret [][]byte, err error) {\n\tkeys, err := decryptHistoryKEKs(oldKEK, wrappedKeys)\n\tif err != nil {\n\t\treturn nil, err\n\t}","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/crypto_history_keys.go#L26-L62","documentation":"During decryption of historical key-encryption-keys (KEKs), a KEK successfully authenticated via AAD but the resulting plaintext key was not exactly 32 bytes. This indicates the stored wrapped history key blob is malformed or was produced by an incompatible format, so the derived historical key is rejected instead of being used.","triggerScenarios":"decryptHistoryKEKs decrypts a wrapped history KEK with util.DecryptWithAAD and the decrypted length differs from 32 bytes; reached via deriveKEK, rewrapHistoryKEKs, decryptWrappedDEKWithHistory, or the history-key tests.","commonSituations":"Hand-edited or partially truncated key envelope files; data restored from an old backup produced by an earlier envelope format; corruption during sync merging key metadata.","solutions":["Restore the affected encrypted notebook's key/metadata files from a known-good backup or sync snapshot.","Verify the master password is correct — a wrong password should fail AAD authentication before this check, so if you get here the blob itself is likely truncated.","Re-wrap history keys by re-entering the master password through the official migration flow rather than editing key files manually.","If reproducible, report it as data corruption; the 32-byte length invariant must always hold for valid envelopes."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"keys, err := decryptHistoryKEKs(masterKey, wrappedList)\nif err != nil {\n    return fmt.Errorf(\"history keys unusable, restore from backup: %w\", err)\n}","preventionTips":["Never hand-edit key envelope files; use the official password migration flow.","Keep backups/sync snapshots of the workspace including key metadata.","After restoring from old backups, verify history key access before deleting anything."],"tags":["go","encryption","history","key-management"],"backgroundTag":"checksum-mismatch","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}