{"record":{"id":"943b7f35cd761f88","repo":"siyuan-note/siyuan","slug":"encrypted-notebook-key-envelope-creation-time-is-m","errorCode":null,"errorMessage":"encrypted notebook key envelope creation time is missing","messagePattern":"encrypted notebook key envelope creation time is missing","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/crypto.go","lineNumber":1640,"sourceCode":"}\n\nfunc wrappedDEKAAD(boxID string) []byte {\n\treturn []byte(\"siyuan:wrapped-dek:\" + boxID)\n}\n\nfunc decryptWrappedDEK(boxID string, enc *conf.BoxEncryption, kek []byte) ([]byte, error) {\n\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}","sourceCodeStart":1622,"sourceCodeEnd":1658,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/crypto.go#L1622-L1658","documentation":"The key envelope for the encrypted notebook is structurally valid (spec matches) but its CreatedAt timestamp is missing (<= 0). Envelope creation time is required metadata for format versioning and key rotation auditing, so validation fails before unwrapping the DEK.","triggerScenarios":"Unlocking an encrypted notebook whose conf.BoxEncryption was created/written without a CreatedAt value, or whose value was zeroed by corruption, manual editing, or an older writing tool.","commonSituations":"Hand-edited notebook config JSON; partial write of the conf; restoring data through a tool that did not copy the envelope metadata.","solutions":["Inspect the notebook's box conf and confirm BoxEncryption.CreatedAt is present and > 0","Restore the conf from a backup taken before the metadata was lost","Re-run the notebook's setup/lock flow on a build that writes full envelope metadata; if key material is intact it will regenerate consistent metadata only via documented migration paths"],"exampleFix":"// before (hand-edited conf.json snippet)\n\"encryption\": {\"spec\": \"v1\", \"wrappedDEK\": \"...\"}\n// after\n\"encryption\": {\"spec\": \"v1\", \"createdAt\": 1726600000, \"wrappedDEK\": \"...\", \"wrapNonce\": \"...\"}","handlingStrategy":"validation","validationCode":"if enc == nil || enc.CreatedAt <= 0 { return errors.New(\"envelope metadata incomplete; restore conf from backup\") }","typeGuard":"func hasEnvelopeTimestamp(enc *conf.BoxEncryption) bool { return enc != nil && enc.CreatedAt > 0 }","tryCatchPattern":"if err := unlockBox(boxID); err != nil { if strings.Contains(err.Error(), \"creation time is missing\") { /* restore conf from backup before retrying */ } }","preventionTips":["Back up the notebook conf before any manual config work","Restore whole conf files, not individual fields, when recovering","Verify envelope completeness after migrations between workspaces"],"tags":["encryption","metadata","key-envelope"],"backgroundTag":"missing-required-config-field","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"}