{"record":{"id":"6fe9c7495c5bda53","repo":"siyuan-note/siyuan","slug":"invalid-encrypted-envelope-magic","errorCode":null,"errorMessage":"invalid encrypted envelope magic","messagePattern":"invalid encrypted envelope magic","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/kdf.go","lineNumber":104,"sourceCode":"func DeriveKey(password string, salt []byte, p Argon2Params) []byte {\n\treturn argon2.IDKey([]byte(password), salt, p.Iterations, p.Memory, p.Parallelism, p.KeyLength)\n}\n\n// Encrypt 用 AES-256-GCM 加密。每次调用生成随机 nonce，因此同一明文多次加密结果不同。\n// 返回格式：magic(4B) || spec(1B) || algorithm(1B) || nonceLength(1B) || nonce || ciphertext || GCM tag(16B)。\nfunc Encrypt(key, plaintext []byte) ([]byte, error) {\n\treturn encryptGCM(key, plaintext, nil, \"Encrypt\")\n}\n\n// Decrypt 对应 Encrypt 的解密。密钥错误、格式无效或密文被篡改时返回错误。\nfunc Decrypt(key, ciphertext []byte) ([]byte, error) {\n\treturn decryptGCM(key, ciphertext, nil, \"Decrypt\")\n}\n\n// EncryptionNonce 从 AES-GCM 密文信封中提取 nonce。\nfunc EncryptionNonce(ciphertext []byte) ([]byte, error) {\n\tif !hasEncryptionMagic(ciphertext) {\n\t\treturn nil, errors.New(\"invalid encrypted envelope magic\")\n\t}\n\tif len(ciphertext) < encryptionEnvelopeHeaderSize {\n\t\treturn nil, errors.New(\"encrypted envelope too short\")\n\t}\n\tif ciphertext[len(encryptionMagic)] != EncryptionSpec {\n\t\treturn nil, errors.New(\"unsupported encrypted envelope spec\")\n\t}\n\tif ciphertext[len(encryptionMagic)+1] != encryptionAlgorithmAES256GCM {\n\t\treturn nil, errors.New(\"unsupported encrypted envelope algorithm\")\n\t}\n\tnonceLength := int(ciphertext[len(encryptionMagic)+2])\n\tif nonceLength == 0 || len(ciphertext) < encryptionEnvelopeHeaderSize+nonceLength {\n\t\treturn nil, errors.New(\"invalid encrypted envelope nonce length\")\n\t}\n\treturn append([]byte(nil), ciphertext[encryptionEnvelopeHeaderSize:encryptionEnvelopeHeaderSize+nonceLength]...), nil\n}\n\n// DeriveSubKey 用 HKDF-SHA256 从主 DEK 派生用途隔离的子密钥。","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/util/kdf.go#L86-L122","documentation":"Raised by EncryptionNonce when the ciphertext does not begin with the 4-byte magic 'SENC' (checked by hasEncryptionMagic). EncryptionNonce extracts the nonce from a SiYuan AES-GCM envelope; input that is not an SESC envelope (plain JSON, random bytes, a truncated blob) is rejected at the first guard.","triggerScenarios":"EncryptionNonce is called on KEKVerifier / WrappedDEK / Metadata blobs during notebook crypto operations (crypto_lifecycle.go, crypto.go). If the stored blob is not an SESC envelope (e.g. an unencrypted legacy value, corrupted bytes, or a different format), the magic check fails.","commonSituations":"Reading a notebook that was never encrypted (plain JSON where a ciphertext was expected); partial corruption of the conf; mixing encrypted and unencrypted notebooks; a path migration that placed a non-ciphertext object where ciphertext is expected (the IsCiphertext guard exists exactly to pre-filter these).","solutions":["Pre-check with util.IsCiphertext(blob) (which is hasEncryptionMagic) before calling EncryptionNonce, and skip non-ciphertext blobs.","Ensure you are reading from an encrypted notebook whose metadata was written by Encrypt/EncryptWithAAD.","If the conf is corrupted, restore from a known-good backup rather than forcing decryption."],"exampleFix":"// before\nnonce, err := util.EncryptionNonce(meta)\nif err != nil { return err }\n\n// after: guard with the magic check first\nif !util.IsCiphertext(meta) {\n    return fmt.Errorf(\"metadata is not an encrypted envelope\")\n}\nnonce, err := util.EncryptionNonce(meta)\nif err != nil { return err }","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"// util.IsCiphertext already exposes this check.\nif !util.IsCiphertext(blob) {\n    // not an SESC envelope: skip nonce extraction / treat as plain\n    return\n}","tryCatchPattern":null,"preventionTips":["Always gate EncryptionNonce with util.IsCiphertext.","Do not call EncryptionNonce on blobs from unencrypted notebooks.","Use mustEncryptionNonce only on ciphertext you just produced with Encrypt."],"tags":["crypto","aes-gcm","encryption","validation","notebook-crypto"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}