{"record":{"id":"51af4a06f71abb3b","repo":"siyuan-note/siyuan","slug":"requires-a-32-byte-aes-256-key","errorCode":null,"errorMessage":" requires a 32-byte (AES-256) key","messagePattern":" requires a 32-byte \\(AES-256\\) key","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/kdf.go","lineNumber":151,"sourceCode":"\treturn out\n}\n\n// EncryptWithAAD 用 AES-256-GCM 加密并绑定 AAD（附加认证数据）。\n// AAD 不被加密，但参与 GCM 认证——解密时必须提供相同 AAD，否则认证失败。\n// 把用途/boxID/路径等元数据放入 AAD，可防止同 box 内密文被替换用途或路径（bind 到上下文）。\n// 返回格式与 Encrypt 一致，但 AAD 参与校验。\nfunc EncryptWithAAD(key, plaintext, aad []byte) ([]byte, error) {\n\treturn encryptGCM(key, plaintext, aad, \"EncryptWithAAD\")\n}\n\n// DecryptWithAAD 对应 EncryptWithAAD 的解密。格式无效、AAD 不匹配或密文被篡改时返回错误。\nfunc DecryptWithAAD(key, ciphertext, aad []byte) ([]byte, error) {\n\treturn decryptGCM(key, ciphertext, aad, \"DecryptWithAAD\")\n}\n\nfunc encryptGCM(key, plaintext, aad []byte, operation string) ([]byte, error) {\n\tif len(key) != 32 {\n\t\treturn nil, errors.New(operation + \" requires a 32-byte (AES-256) key\")\n\t}\n\tblock, err := aes.NewCipher(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tgcm, err := cipher.NewGCM(block)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tnonceSize := gcm.NonceSize()\n\tnonce := make([]byte, nonceSize)\n\tif _, err = rand.Read(nonce); err != nil {\n\t\treturn nil, err\n\t}\n\tenvelope := make([]byte, encryptionEnvelopeHeaderSize, encryptionEnvelopeHeaderSize+nonceSize+len(plaintext)+gcm.Overhead())\n\tcopy(envelope, encryptionMagic[:])\n\tenvelope[len(encryptionMagic)] = EncryptionSpec\n\tenvelope[len(encryptionMagic)+1] = encryptionAlgorithmAES256GCM","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/util/kdf.go#L133-L169","documentation":"encryptGCM (backing Encrypt and EncryptWithAAD) accepts only exactly 32-byte keys because it performs AES-256-GCM. A shorter or longer key is rejected up front with a message prefixed by the operation name (e.g. \"Encrypt requires a 32-byte (AES-256) key\") instead of letting aes.NewCipher produce a vaguer failure or silently weakening the cipher.","triggerScenarios":"Calling util.Encrypt or util.EncryptWithAAD with a key that is not 32 bytes — e.g. a raw password string, a 16-byte AES-128 key, a hex/base64 string passed instead of decoded bytes, or a sub-key derived with a non-32 output length.","commonSituations":"Passing the user's password directly instead of running it through DeriveKey; decoding a base64 DEK into a string rather than []byte; truncating keys with [:16] to 'save space'; using salt (16 bytes) as the key.","solutions":["Derive the key with util.DeriveKey(password, salt, params) which returns KeyLength bytes (set KeyLength: 32)","Use util.GenerateDEK() to create a proper 32-byte random data key","If the key is encoded, decode it first (hex.DecodeString/base64.StdEncoding.DecodeString) and confirm len == 32 before encrypting"],"exampleFix":"// before\nkey := []byte(password)\nciphertext, err := util.Encrypt(key, plaintext) // wrong length\n// after\ndeveloperKey := util.DeriveKey(password, salt, util.Argon2Params{Memory: 64 * 1024, Iterations: 3, Parallelism: 4, KeyLength: 32})\nciphertext, err := util.Encrypt(developerKey, plaintext)","handlingStrategy":"validation","validationCode":"if len(key) != 32 { return fmt.Errorf(\"encryption key must be 32 bytes, got %d\", len(key)) }","typeGuard":"func isAES256Key(key []byte) bool { return len(key) == 32 }","tryCatchPattern":"ciphertext, err := util.Encrypt(key, plaintext)\nif err != nil && strings.Contains(err.Error(), \"32-byte\") {\n    return nil, fmt.Errorf(\"bad key material: %w\", err)\n}","preventionTips":["Obtain keys only from util.DeriveKey (KeyLength: 32) or util.GenerateDEK()","Never encrypt with a raw password, salt, or encoded string","Decode hex/base64 key material and assert length before every encrypt call"],"tags":["crypto","aes-gcm","key-size","validation"],"backgroundTag":"invalid-argument-value","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"}