{"record":{"id":"f37b4cd01d320e3f","repo":"Billionmail/BillionMail","slug":"invalid-data-length","errorCode":null,"errorMessage":"invalid data length","messagePattern":"invalid data length","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/maillog_stat/encryption.go","lineNumber":55,"sourceCode":"\n\tresult := base64.URLEncoding.EncodeToString(resultBytes)\n\treturn strings.TrimRight(result, \"=\")\n}\n\nfunc Decrypt(data string, result interface{}) (err error) {\n\tdataLength := len(data)\n\tamountToPad := 4 - (dataLength % 4)\n\tif amountToPad > 0 && amountToPad < 4 {\n\t\tdata += strings.Repeat(\"=\", amountToPad)\n\t}\n\n\tdataAes, err := base64.URLEncoding.DecodeString(data)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif len(dataAes) < 32 {\n\t\terr = fmt.Errorf(\"invalid data length\")\n\t\treturn\n\t}\n\n\tdataAesLen := len(dataAes)\n\tkeyiv := make([]byte, 0, 32)\n\tkeyiv = append(keyiv, dataAes[:16]...)\n\tkeyiv = append(keyiv, dataAes[dataAesLen-16:]...)\n\n\tvar key, iv []byte\n\tfor i := 0; i < 32; i++ {\n\t\tif i%2 == 0 {\n\t\t\tkey = append(key, keyiv[i])\n\t\t} else {\n\t\t\tiv = append(iv, keyiv[i])\n\t\t}\n\t}\n\n\tblock, err := aes.NewCipher(key)","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/maillog_stat/encryption.go#L37-L73","documentation":"Decrypt in the maillog_stat package base64-url-decodes an encrypted blob and requires at least 32 bytes (16-byte key + 16-byte IV material). If the decoded data is shorter than 32 bytes it returns 'invalid data length' without attempting AES decryption, indicating the ciphertext is truncated or was never produced by the matching Encrypt function.","triggerScenarios":"Decrypt(data) is called with a string that decodes to fewer than 32 bytes — e.g. an empty string, invalid base64 that decoded to partial garbage, a manually truncated value, or a value encrypted/shortened by a different scheme.","commonSituations":"Stored database values corrupted or written by an older/other code path; passing plaintext instead of encrypted data; passing the base64 of a short value from tests; manual copy-paste truncation of tokens.","solutions":["Confirm the value was produced by the matching Encrypt function in this package (key+IV+AES format)","Validate the input is non-empty, valid base64 URL encoding, and decodes to >=32 bytes before calling Decrypt","Check the DB column/data source for truncation (column length limits, manual edits)","Re-encrypt the affected data since the original key material cannot be recovered from a truncated blob"],"exampleFix":"// before\nplain, err := Decrypt(storedValue)\n// after\nraw, err := base64.URLEncoding.DecodeString(storedValue)\nif err != nil || len(raw) < 32 {\n    // handle: value is missing/corrupt; re-encrypt and store anew\n    return fallbackValue\n}\nplain, err := Decrypt(storedValue)","handlingStrategy":"type-guard","validationCode":"func isDecryptable(data string) bool {\n    raw, err := base64.URLEncoding.DecodeString(data)\n    return err == nil && len(raw) >= 32\n}\nif !isDecryptable(stored) { /* re-encrypt or use fallback */ }","typeGuard":"func validCiphertext(data string) bool {\n    raw, err := base64.URLEncoding.DecodeString(data)\n    return err == nil && len(raw) >= 32\n}","tryCatchPattern":"plain, err := Decrypt(stored)\nif err != nil {\n    if err.Error() == \"invalid data length\" || errors.Is(err, base64.CorruptInputError(0)) {\n        // treat as lost data: log, re-encrypt source, return zero value\n        return zeroValue, nil\n    }\n    return nil, err\n}","preventionTips":["Always round-trip through Encrypt/Decrypt in tests for stored values","Check DB column lengths don't truncate base64 blobs","Never store plaintext or third-party-encoded values in fields Decrypt reads","Validate base64-decoded length >= 32 before decrypting legacy data"],"tags":["encryption","aes","data-corruption"],"backgroundTag":"invalid-ciphertext-length","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}