{"record":{"id":"e4c65a0c9e1cefe3","repo":"juicedata/juicefs","slug":"malformed-ciphertext-d-d","errorCode":null,"errorMessage":"malformed ciphertext: %d %d","messagePattern":"malformed ciphertext: (.+?) (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/object/encrypt.go","lineNumber":265,"sourceCode":"\tbuf[1] = byte(len(cipherkey) & 0xFF)\n\tbuf[2] = byte(len(nonce))\n\tp := buf[3:]\n\tcopy(p, cipherkey)\n\tp = p[len(cipherkey):]\n\tcopy(p, nonce)\n\tp = p[len(nonce):]\n\tciphertext := aead.Seal(p[:0], nonce, plaintext, nil)\n\treturn buf[:headerSize+len(ciphertext)], nil\n}\n\nfunc (e *dataEncryptor) Decrypt(ciphertext []byte) ([]byte, error) {\n\tif len(ciphertext) < 3 {\n\t\treturn nil, fmt.Errorf(\"received encrypted text length is less than 3, the object is corrupted\")\n\t}\n\tkeyLen := int(ciphertext[0])<<8 + int(ciphertext[1])\n\tnonceLen := int(ciphertext[2])\n\tif 3+keyLen+nonceLen >= len(ciphertext) {\n\t\treturn nil, fmt.Errorf(\"malformed ciphertext: %d %d\", keyLen, nonceLen)\n\t}\n\tciphertext = ciphertext[3:]\n\tcipherkey := ciphertext[:keyLen]\n\tnonce := ciphertext[keyLen : keyLen+nonceLen]\n\tciphertext = ciphertext[keyLen+nonceLen:]\n\n\tkey, err := e.keyEncryptor.Decrypt(cipherkey)\n\tif err != nil {\n\t\treturn nil, errors.New(\"decryt key: \" + err.Error())\n\t}\n\taead, err := e.aead(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn aead.Open(ciphertext[:0], nonce, ciphertext, nil)\n}\n\n// MaxOverhead returns the maximum number of extra bytes that Encrypt can add.","sourceCodeStart":247,"sourceCodeEnd":283,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/pkg/object/encrypt.go#L247-L283","documentation":"Inside dataEncryptor.Decrypt, the 3-byte header encodes the encrypted data-key length and nonce length. If 3+keyLen+nonceLen is not strictly less than the total length, there is no room left for actual ciphertext, so the buffer is declared malformed.","triggerScenarios":"Calling Read on an object whose bytes were truncated right after (or inside) the encrypted-key/nonce header, or whose header bytes were corrupted to encode absurd lengths.","commonSituations":"Partial object upload or truncated download; bit rot or manual edit of the object in object storage; attempting to decrypt data encrypted with a different format/version; endianness/layout mismatch from another tool.","solutions":["Restore the object from a backup or re-upload it; the stored bytes are structurally invalid","Compare the object size and first bytes against a known-good object to confirm truncation","Verify the encryption key file is the same one used at write time (a mismatched RSA key decrypts the data key to garbage lengths)","Check network/proxy completeness (Content-Length) if the object is streamed through a gateway"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if len(ciphertext) >= 3 {\n\tk := int(ciphertext[0])<<8 | int(ciphertext[1]); n := int(ciphertext[2])\n\tif 3+k+n >= len(ciphertext) { /* malformed: restore object */ }\n}","typeGuard":null,"tryCatchPattern":"plain, err := enc.Decrypt(ciphertext)\nif err != nil && strings.Contains(err.Error(), \"malformed ciphertext\") {\n\t// header corrupt or truncated object: re-sync from source\n}","preventionTips":["Verify the RSA key file matches the format-time key before mounting","Guard against partial uploads; re-run interrupted syncs","Enable checksum verification on backup/restore pipelines","Avoid manual edits of objects in the storage console"],"tags":["crypto","data-corruption","decrypt","malformed-data"],"backgroundTag":"checksum-mismatch","analyzedSha":"c9a67b23e8e08ec23ec331aa6f1675e2319e921c","analyzedAt":"2026-09-06T17:55:48.476Z","contentChangedAt":"2026-09-06T17:55:48.476Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}