{"record":{"id":"1225675c6c6405ea","repo":"siyuan-note/siyuan","slug":"encrypted-notebook-key-envelope-nonce-mismatch","errorCode":null,"errorMessage":"encrypted notebook key envelope nonce mismatch","messagePattern":"encrypted notebook key envelope nonce mismatch","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/crypto.go","lineNumber":1647,"sourceCode":"\tif err := validateWrappedDEKEnvelope(enc); err != nil {\n\t\treturn nil, err\n\t}\n\treturn util.DecryptWithAAD(kek, enc.WrappedDEK, wrappedDEKAAD(boxID))\n}\n\nfunc validateWrappedDEKEnvelope(enc *conf.BoxEncryption) error {\n\tif enc == nil || enc.Spec != boxEncryptionSpec {\n\t\treturn errors.New(\"unsupported encrypted notebook key envelope\")\n\t}\n\tif enc.CreatedAt <= 0 {\n\t\treturn errors.New(\"encrypted notebook key envelope creation time is missing\")\n\t}\n\tnonce, err := util.EncryptionNonce(enc.WrappedDEK)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid encrypted notebook key envelope: %w\", err)\n\t}\n\tif !bytes.Equal(nonce, enc.WrapNonce) {\n\t\treturn errors.New(\"encrypted notebook key envelope nonce mismatch\")\n\t}\n\treturn nil\n}\n\nfunc validateBoxEncryption(enc *conf.BoxEncryption) error {\n\tif err := validateWrappedDEKEnvelope(enc); err != nil {\n\t\treturn err\n\t}\n\tif _, err := util.EncryptionNonce(enc.Metadata); err != nil {\n\t\treturn fmt.Errorf(\"invalid encrypted notebook metadata envelope: %w\", err)\n\t}\n\treturn nil\n}\n\n// mustEncryptionNonce 从刚刚成功生成的密文中提取 nonce。生成密文格式错误属于内部不变量被破坏，直接终止执行。\nfunc mustEncryptionNonce(ciphertext []byte) []byte {\n\tnonce, err := util.EncryptionNonce(ciphertext)\n\tif err != nil {","sourceCodeStart":1629,"sourceCodeEnd":1665,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/crypto.go#L1629-L1665","documentation":"The nonce embedded in the WrappedDEK ciphertext does not equal the separately stored WrapNonce field. The two must match for the envelope to be internally consistent; a mismatch means the stored pieces were mixed across generations or tampered with.","triggerScenarios":"During unlock, validateWrappedDEKEnvelope compares util.EncryptionNonce(enc.WrappedDEK) with enc.WrapNonce using bytes.Equal; any re-wrap (password change, rotation) that updated one field but not the other triggers this.","commonSituations":"A partially applied master-password change that rewrote WrappedDEK but left the old WrapNonce (or vice versa); restoring conf fields from different backups; concurrent writers to the same box conf.","solutions":["Identify which operation last rewrapped the DEK (e.g. ChangeMasterPassword) and restore a conf backup from before that operation","Ensure password-change/rotation flows complete atomically across all boxes; re-run the change from a consistent state","Do not hand-edit WrapNonce; it must be derived from the actual WrappedDEK ciphertext"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"n, err := util.EncryptionNonce(enc.WrappedDEK); if err == nil && !bytes.Equal(n, enc.WrapNonce) { return errors.New(\"envelope fields inconsistent; restore pre-rotation backup\") }","typeGuard":null,"tryCatchPattern":"if err := unlockBox(boxID); err != nil { if strings.Contains(err.Error(), \"nonce mismatch\") { /* restore conf backup from before the last rewrap */ } }","preventionTips":["Ensure master-password changes complete atomically and are not interrupted","Keep a conf backup immediately before any password/rotation operation","Never mix envelope fields from different backups or notebooks"],"tags":["encryption","key-envelope","integrity","nonce"],"backgroundTag":"checksum-mismatch","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}