{"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":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/crypto.go","lineNumber":1615,"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":1597,"sourceCodeEnd":1633,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/crypto.go#L1597-L1633","documentation":"Thrown by validateWrappedDEKEnvelope when enc.CreatedAt <= 0. CreatedAt is a Unix-millisecond timestamp set at key-wrap time; it must be positive. A zero or negative value indicates the field was never set, was lost during serialization, or was corrupted.","triggerScenarios":"Reached via decryptWrappedDEK during unlock or ChangeMasterPassword. Fires when the BoxEncryption struct's CreatedAt field is 0 or negative — e.g., the JSON field was omitted during manual editing, a sync conflict dropped it, or the struct was constructed without setting CreatedAt.","commonSituations":"Manual editing of conf.json removed the CreatedAt field. A sync-conflict resolution script or third-party merge tool dropped the field. A bug in a custom import/migration tool that constructs BoxEncryption without setting CreatedAt.","solutions":["Restore conf.json from a backup that has a valid CreatedAt timestamp.","Restore the per-notebook crypt backup if it has a valid CreatedAt.","If you have access to the source code and are migrating notebooks programmatically, ensure CreatedAt is set to time.Now().UnixMilli() when constructing BoxEncryption."],"exampleFix":"// before: BoxEncryption missing CreatedAt\nboxConf.BoxCrypt = &conf.BoxEncryption{\n    WrappedDEK: wrapped,\n    WrapNonce:  nonce,\n    Spec:       boxEncryptionSpec,\n    // CreatedAt missing\n}\n// after\nboxConf.BoxCrypt = &conf.BoxEncryption{\n    WrappedDEK: wrapped,\n    WrapNonce:  nonce,\n    Spec:       boxEncryptionSpec,\n    CreatedAt:  time.Now().UnixMilli(),\n}","handlingStrategy":"validation","validationCode":"// Validate the envelope before attempting unlock:\nboxCrypt, err := model.GetBoxEncryption(boxID)\nif err != nil {\n    return\n}\nif boxCrypt.CreatedAt <= 0 {\n    // timestamp missing — restore conf from backup\n    return\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never omit or zero the CreatedAt field when editing conf.json.","Use SiYuan's API to create/modify encrypted notebooks — it always sets CreatedAt.","Keep backups of valid conf.json files."],"tags":["encryption","config-validation","timestamp","go"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}