Usage of EVP_MAX_BLOCK_LENGTH with OPENSSL - c

I'm working with openssl but i have some doubts..
this is the code presented in the openssl doc, to encrypt with AES 128.
int do_crypt(FILE *in, FILE *out, int do_encrypt)
{
/* Allow enough space in output buffer for additional block */
unsigned char inbuf[1024], outbuf[1024 + EVP_MAX_BLOCK_LENGTH];
int inlen, outlen;
EVP_CIPHER_CTX *ctx;
/*
* Bogus key and IV: we'd normally set these from
* another source.
*/
unsigned char key[] = "0123456789abcdeF";
unsigned char iv[] = "1234567887654321";
/* Don't set key or IV right away; we want to check lengths */
ctx = EVP_CIPHER_CTX_new();
EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, NULL, NULL,
do_encrypt);
OPENSSL_assert(EVP_CIPHER_CTX_key_length(ctx) == 16);
OPENSSL_assert(EVP_CIPHER_CTX_iv_length(ctx) == 16);
/* Now we can set key and IV */
EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, do_encrypt);
for (;;) {
inlen = fread(inbuf, 1, 1024, in);
if (inlen <= 0)
break;
if (!EVP_CipherUpdate(ctx, outbuf, &outlen, inbuf, inlen)) {
/* Error */
EVP_CIPHER_CTX_free(ctx);
return 0;
}
fwrite(outbuf, 1, outlen, out);
}
if (!EVP_CipherFinal_ex(ctx, outbuf, &outlen)) {
/* Error */
EVP_CIPHER_CTX_free(ctx);
return 0;
}
fwrite(outbuf, 1, outlen, out);
EVP_CIPHER_CTX_free(ctx);
return 1;
}
I have 2 question:
Why, for the output buffer, its allocated 1024 + EVP_MAX_BLOCK_LENGTH?
unsigned char inbuf[1024], outbuf[1024 + EVP_MAX_BLOCK_LENGTH];
Why don't we just allocate 1024 for both buffers?
what is the purpose of the EVP_CipherFinal_ex? To encrypt the last block? if so, shouldn't it already do the EVP_CipherUpdate() into the for loop?
Thank you all in advice!

Related

Openssl EVP API fails to decrypt ciphertext when it is read from file (AES 128 ecb)

i am trying to create a simple application that encrypts and decrypts a small file, < 10 bytes. I am using OpenSSL API of C (AES-128 ecb encryption) and i am encountering a strange "bug" when i encrypt a string and save it to a file.
unsigned char buffer[256];
unsigned char rcv[256];
char my_string[]={"123456"};
int bil = aes_encrypt(my_string, strlen(my_string), "1", NULL, buffer);
FILE* fp =fopen("encrypted_file","w");
write(fileno(fp),buffer,bil);
fclose(fp);
aes_decrypt(buffer, bil, "1", NULL, rec);
printf("%s\n",rec); /* Correct: Prints 123456 */
The problem here is that if i read the ciphertext from the file, although it is exactly the one i had previously saved, it does not seem to be decrypted correctly.
FILE* fp =fopen("encrypted_file","r");
int bil = read(fileno(fp),buffer,256); /* The buffer contains the exact cipher that was created by the aes_encrypt in the first place */
fclose(fp);
int y = aes_decrypt(buffer, bil, "1", NULL, rec);
printf("%s\n",rec); /* Emptry string */
The encryption and decryption functions are shown bellow:
Encryption:
int
aes_encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key,
unsigned char *iv, unsigned char *ciphertext){
EVP_CIPHER_CTX* ctx;
int len;
int ciphertext_len;
ctx = EVP_CIPHER_CTX_new();
EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, iv);
EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len);
ciphertext_len = len;
EVP_EncryptFinal_ex(ctx, ciphertext + len, &len);
ciphertext_len += len;
EVP_CIPHER_CTX_free(ctx);
return ciphertext_len;
}`
Decryption:
int
aes_decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key,
unsigned char *iv, unsigned char *plaintext){
EVP_CIPHER_CTX *ctx;
int len;
int plaintext_len;
ctx = EVP_CIPHER_CTX_new();
EVP_DecryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, iv);
EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len);
plaintext_len = len;
EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
plaintext_len += len;
EVP_CIPHER_CTX_free(ctx);
plaintext[plaintext_len] = '\0';
return plaintext_len;
}

C output differs from openssl output

Given the following openssl example prog
#include <openssl/evp.h>
int do_crypt(FILE* in, FILE* out, int do_encrypt)
{
/* Allow enough space in output buffer for additional block */
unsigned char inbuf[1024], outbuf[1024 + EVP_MAX_BLOCK_LENGTH];
int inlen, outlen;
EVP_CIPHER_CTX* ctx;
/* Bogus key and IV: we'd normally set these from
* another source.
*/
unsigned char key[] = "0123456789abcdeF";
unsigned char iv[] = "1234567887654321";
/* Don't set key or IV right away; we want to check lengths */
ctx = EVP_CIPHER_CTX_new();
EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, NULL, NULL, do_encrypt);
OPENSSL_assert(EVP_CIPHER_CTX_key_length(ctx) == 16);
OPENSSL_assert(EVP_CIPHER_CTX_iv_length(ctx) == 16);
/* Now we can set key and IV */
EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, do_encrypt);
for (;;) {
inlen = fread(inbuf, 1, 1024, in);
if (inlen <= 0)
break;
if (!EVP_CipherUpdate(ctx, outbuf, &outlen, inbuf, inlen)) {
/* Error */
EVP_CIPHER_CTX_free(ctx);
return 0;
}
fwrite(outbuf, 1, outlen, out);
}
if (!EVP_CipherFinal_ex(ctx, outbuf, &outlen)) {
/* Error */
EVP_CIPHER_CTX_free(ctx);
return 0;
}
fwrite(outbuf, 1, outlen, out);
EVP_CIPHER_CTX_free(ctx);
return 1;
}
int main(int argc, char* argv[])
{
FILE *fpIn;
FILE *fpOut;
fpIn = fopen("text-in.txt", "rb");
fpOut = fopen("text-out.txt", "wb");
int test = do_crypt(fpIn, fpOut, 1);
fclose(fpIn);
fclose(fpOut);
return 0;
}
I would expect that
openssl aes-128-cbc -in text-in.txt -K 0123456789abcdeF -iv 1234567887654321
would create the same output. But it doesn't. The C prog can decrypt the files it encrypted. But it can't decrypt the files encrypted with openssl.
The encryption key and IV are not actually text but binary data.
When you do the encryption on the command line, the -K and -iv arguments expect their input in hex.
So the key you're passing in is actually:
\x01\x23\x45\x67\x89\xab\xcd\xef\x00\x00\x00\x00\x00\x00\x00\x00
And the IV is:
\x12\x34\x56\x78\x87\x65\x43\x21\x00\x00\x00\x00\x00\x00\x00\x00
Both have tail padding of 0's because the key that was specified was too short.
In order to match the program's output, you need to pass in the hex codes of the characters you specified. For example, 1 is 30, 2 is 31, a is 61, F is 46, and so forth:
openssl aes-128-cbc -in text-in.txt -K 30313233343536373839616263646546 -iv 31323334353637383837363534333231

Segmentation fault in EVP_CIPHER_CTX_new() when using OpenSSL in C

I am a total beginner with the OpenSSL Library in C but was working on a code to encrypt using the libraries while taking a pass phrase as input and generating the salt,IV and key from the pass phrase. This is what I have tried till now:
int encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *ciphertext)
{
char *passphrase;
printf("\nEnter a Pass Phrase:");
scanf("%s",passphrase);
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
OPENSSL_config(NULL);
EVP_CIPHER_CTX *ctx;
const EVP_CIPHER *cipher;
const EVP_MD *dgst = NULL;
unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH];
const char *salt;
int len;
int ciphertext_len;
/* Create and initialise the context */
if(!(ctx = EVP_CIPHER_CTX_new())) //This line causes the error
handleErrors();
cipher = EVP_get_cipherbyname("aes-256-cbc");
if(!cipher) { fprintf(stderr, "no such cipher\n"); return -1; }
dgst=EVP_get_digestbyname("md5");
if(!dgst) { fprintf(stderr, "no such digest\n"); return -1; }
if(!EVP_BytesToKey(cipher, dgst, salt, (unsigned char *) passphrase, strlen(passphrase), 1, key, iv))
{
fprintf(stderr, "EVP_BytesToKey failed\n");
return -1;
}
/* Initialise the encryption operation. IMPORTANT - ensure you use a key
* and IV size appropriate for your cipher
* In this example we are using 256 bit AES (i.e. a 256 bit key). The
* IV size for *most* modes is the same as the block size. For AES this
* is 128 bits */
if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv))
handleErrors();
/* Provide the message to be encrypted, and obtain the encrypted output.
* EVP_EncryptUpdate can be called multiple times if necessary
*/
if(1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
handleErrors();
ciphertext_len = len;
/* Finalise the encryption. Further ciphertext bytes may be written at
* this stage.
*/
if(1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) handleErrors();
ciphertext_len += len;
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
EVP_cleanup();
ERR_free_strings();
return ciphertext_len;
}
I tried this out but kept getting a segmentation fault. I tried to debug and found out that the EVP_CIPHER_CTX_new() was the one cussing it. I am at my wits end right now trying to debug it and could really appreciate some help.
Thanks in advance.
This is incorrect:
char *passphrase;
printf("\nEnter a Pass Phrase:");
scanf("%s",passphrase);
char *pasphrase is just a pointer point to unknown location.
Change this to:
char passphrase[2048];
printf("\nEnter a Pass Phrase:");
scanf("%s", passphrase);

Issues with encrypting a file using openssl evp api(aes256cbc)

I am trying to read a file(.txt) in this case and encrypting/decrypting it with AES256CBC using EVP api of openssl.(read(plain.txt)->create(encrypt.txt)->create(decrypt.txt))
# include <stdio.h>
# include <stdlib.h>
# include <openssl/evp.h>
# include <openssl/aes.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <unistd.h>
# include <string.h>
# include <assert.h>
# include <error.h>
# include "debug.h"
# define SIZE 32
char buf[SIZE];
int aes_init(unsigned char* pwd, unsigned int pwd_len, unsigned char * salt, EVP_CIPHER_CTX *e_ctx, EVP_CIPHER_CTX *d_ctx)
{
int i, rounds =5; /* rounds */
unsigned char key[32], iv[32];
i = EVP_BytesToKey(EVP_aes_256_cbc(),EVP_sha1(),salt,pwd,pwd_len,rounds,key,iv);
if(i != 32)
{
printf("\n Error,Incorrect key size generated:%d:\n",i);
return -1;
}
EVP_CIPHER_CTX_init(e_ctx);
EVP_EncryptInit_ex(e_ctx, EVP_aes_256_cbc(), NULL, key, iv);
EVP_CIPHER_CTX_init(d_ctx);
EVP_DecryptInit_ex(d_ctx, EVP_aes_256_cbc(), NULL, key, iv);
return 0;
}
unsigned char* aes_encrypt(EVP_CIPHER_CTX *e,unsigned char * plain_text, unsigned int * len ) /* this function encryptes the file:fd is passed as parameter */
{
int ci_len = (*len) + AES_BLOCK_SIZE;
int flen =0;
unsigned char * cipher_text = malloc(ci_len);
EVP_EncryptInit_ex(e, NULL, NULL, NULL, NULL); /* allows reusing of e for multiple cipher cycles */
EVP_EncryptUpdate(e, cipher_text, &ci_len, plain_text, *len); /* Update cipher text */
EVP_EncryptFinal_ex(e, cipher_text+ci_len, &flen); /* updates the remaining bytes */
*len = ci_len + flen;
return cipher_text;
}
unsigned char* aes_decrypt(EVP_CIPHER_CTX *e, unsigned char * c_text, unsigned int * len)
{
int pi_len = (*len);
int flen = 0;
unsigned char * plain_text = malloc(pi_len);
EVP_DecryptInit_ex(e, NULL, NULL, NULL, NULL);
EVP_DecryptUpdate(e, plain_text, &pi_len, c_text, *len);
EVP_DecryptFinal_ex(e, plain_text+pi_len, &flen);
(*len) = pi_len + flen;
return plain_text;
}
int main(int argc,char **argv)
{
if(argc != 2)
{
perror("\n Error:\nCorrect Usage: Enter Password to be used");
exit(-1);
}
EVP_CIPHER_CTX en,de; /* The EVP structure which keeps track of all crypt operations see evp.h for details */
int in, out, fd, dec,i =0; /* fd for input and output files and random dev*/
unsigned int pwd_len = strlen((const char *)argv[1]); /* Length of the pwd supplied by the user */
unsigned char *pwd =(unsigned char*) argv[1]; /* Pointer to the pwd supplied by the user */
unsigned int rd= 0;
unsigned char salt[8];
unsigned char * encry = NULL, *decry = NULL;
i =0;
if((in = open("plain.txt",O_RDONLY)) == -1) /* Opening a plain text file for encryption */
{
perror("\n Error,Opening file for reading::");
exit(-1);
}
if((fd = open("/dev/random", O_RDONLY)) == -1)
{
perror("\n Error,Opening /dev/random::");
exit(-1);
}
else
{
if(read(fd,salt,8) == -1)
{
perror("\n Error,reading from /dev/random::");
exit(-1);
}
}
if(aes_init(pwd,pwd_len,(unsigned char*) salt,&en,&de)) /* Generating Key and IV and initializing the EVP struct */
{
perror("\n Error, Cant initialize key and IV:");
return -1;
}
if((out = open("encrypt.txt",O_RDWR|O_CREAT,0400 | 0200)) == -1)
{
dbug_p("ENC%d",out);
perror("\n Error,Opening the file to be written::");
exit(-1);
}
rd =0;
while((rd = read(in,buf,SIZE)) >0)
{
dbug_p("\nREAD::%s::%d*\n",buf,rd);
encry = aes_encrypt(&en,(unsigned char*) buf, &rd);
if((write(out,encry,rd)) != rd)
{
perror("\n Error,Required encrypted bytes not written::");
exit(-1);
}
free(encry);
}
rd =0;
if((dec = open("dec22.txt",O_RDWR|O_CREAT,0400 | 0200)) == -1)
{
dbug_p("dec%d",dec);
perror("\n Error,Opening the decrypting o/p file::");
exit(-1);
}
if((lseek(out,0,SEEK_SET)) != 0) perror("\n Error:setting lseek::");
for(i=0;i<SIZE;i++) buf[i] =0;
while((rd = read(out,dbuf,SIZE)) >0)
{
decry = aes_decrypt(&de,(unsigned char*) dbuf, &rd);
if((write(dec,decry,rd)) != rd)
{
perror("\n Error,Required decrypted bytes not written::");
exit(-1);
}
free(decry);
}
close(in);
close(fd);
EVP_CIPHER_CTX_cleanup(&en);
EVP_CIPHER_CTX_cleanup(&de);
return 0;
}
My problem was that my when i decrypt an encrypted file i get a file which is not properly decrypted (e.g. correct stringgarbagecorrect stringgarbage ...)
abhi#ubuntu:~/mpro/EFF$ cat plain.txt
Today is tuesday
tomorrow is wednesday
then thursday and friday and saturday
finally sunday
Decrypted file
cat dec22.txt
Today is tuesdayw)Q������O-%�A�8���R��.�O���and saturday
finally sunday
What can be the reason for this. Is it reading something else also or i am making any foolish error somewhere.
EDIT: If I just encrypt an array (tried with 36char long) it correctly encrypted and decrypted without printing any garbage.
I guess i am missing(not handling) some *nix file structure details ..??
Or is there any better way to do this encryption on a file.?
Many thanks
I think your analysis is wrong. This loop is problematic:
while((rd = read(in,buf,SIZE)) >0)
{
dbug_p("\nREAD::%s::\n",buf);
encry = aes_encrypt(&en,(unsigned char*) buf, &rd);
dbug_p("\n EN::%s::\n",encry);
decry = aes_decrypt(&de,(unsigned char*) encry,&rd);
dbug_p("\n DE::%s::\n",decry);
free(encry);
free(decry);
}
Firstly because you print using %s which expects a zero terminator. However, the encrypted/decrypted data is not zero terminated. Instead, you should print rd characters using a loop like for (i = 0; i < rd; i++) printf("%02x "); - this is why your analysis of the problem is likely flawed.
Secondly, I assume that in your real problem, you are reading SIZE bytes at a time and sending them to aes_decrypt() separately. This will fail because EVP_DecryptFinal_ex() is getting called too early (before all the encrypted blocks were read). You have two options. Either you send the read bytes through EVP_DecryptUpdate() in each loop iteration, and call EVP_DecryptFinal() after completing the loop (and init accordingly before the loop), or you read the whole file into a buffer first, and then send it through aes_decrypt() in one go.
Or in other words, you need to send the whole data block resulting from aes_encrypt() later to aes_decrypt(). You cannot send them in different chunks, unless you split the functions up and use the EVP "update" functions on the separate chunks.
while((rd = read(in,buf,SIZE)) >0)
{
dbug_p("\nREAD::%s::%d*\n",buf,rd);
encry = aes_encrypt(&en,(unsigned char*) buf, &rd);
and,
unsigned char* aes_encrypt(EVP_CIPHER_CTX *e,unsigned char * plain_text, unsigned int * len ) /* this function encryptes the file:fd is passed as parameter */
{
int ci_len = (*len) + AES_BLOCK_SIZE;
int flen =0;
unsigned char * cipher_text = malloc(ci_len);
EVP_EncryptInit_ex(e, NULL, NULL, NULL, NULL); /* allows reusing of e for multiple cipher cycles */
EVP_EncryptUpdate(e, cipher_text, &ci_len, plain_text, *len); /* Update cipher text */
EVP_EncryptFinal_ex(e, cipher_text+ci_len, &flen); /* updates the remaining bytes */
You are calling EVP_EncryptFinal_ex multiple times. It is supposed to be called only once at the end.
Same is true for how you are doing decryption.
Here is a simple example from the man page on how to do encrypt.
Have a similar function for decryption and it should work.
int do_crypt(char *outfile)
{
unsigned char outbuf[1024];
int outlen, tmplen;
/* Bogus key and IV: we'd normally set these from
* another source.
*/
unsigned char key[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
unsigned char iv[] = {1,2,3,4,5,6,7,8};
char intext[] = "Some Crypto Text";
EVP_CIPHER_CTX ctx;
FILE *out;
EVP_CIPHER_CTX_init(&ctx);
EVP_EncryptInit_ex(&ctx, EVP_bf_cbc(), NULL, key, iv);
if(!EVP_EncryptUpdate(&ctx, outbuf, &outlen, intext, strlen(intext)))
{
/* Error */
return 0;
}
/* Buffer passed to EVP_EncryptFinal() must be after data just
* encrypted to avoid overwriting it.
*/
if(!EVP_EncryptFinal_ex(&ctx, outbuf + outlen, &tmplen))
{
/* Error */
return 0;
}
outlen += tmplen;
EVP_CIPHER_CTX_cleanup(&ctx);
/* Need binary mode for fopen because encrypted data is
* binary data. Also cannot use strlen() on it because
* it wont be null terminated and may contain embedded
* nulls.
*/
out = fopen(outfile, "wb");
fwrite(outbuf, 1, outlen, out);
fclose(out);
return 1;
}
the following example is reading a file as your case. See how Update (called multiple times) and Final (once at the end) routines are used.
int do_crypt(FILE *in, FILE *out, int do_encrypt)
{
/* Allow enough space in output buffer for additional block */
inbuf[1024], outbuf[1024 + EVP_MAX_BLOCK_LENGTH];
int inlen, outlen;
/* Bogus key and IV: we'd normally set these from
* another source.
*/
unsigned char key[] = "0123456789";
unsigned char iv[] = "12345678";
/* Don't set key or IV because we will modify the parameters */
EVP_CIPHER_CTX_init(&ctx);
EVP_CipherInit_ex(&ctx, EVP_rc2(), NULL, NULL, NULL, do_encrypt);
EVP_CIPHER_CTX_set_key_length(&ctx, 10);
/* We finished modifying parameters so now we can set key and IV */
EVP_CipherInit_ex(&ctx, NULL, NULL, key, iv, do_encrypt);
for(;;)
{
inlen = fread(inbuf, 1, 1024, in);
if(inlen <= 0) break;
if(!EVP_CipherUpdate(&ctx, outbuf, &outlen, inbuf, inlen))
{
/* Error */
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
fwrite(outbuf, 1, outlen, out);
}
if(!EVP_CipherFinal_ex(&ctx, outbuf, &outlen))
{
/* Error */
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
fwrite(outbuf, 1, outlen, out);
EVP_CIPHER_CTX_cleanup(&ctx);
return 1;
}

OpenSSL and AES

i'm working with OpenSSL lib and get very strange effect with AES encrypt/decrypt: if i'll change some byte in encrypted message and decrypt it, i will see the part of original message, that isn't suppose to be. This is source code:
#include <openssl/evp.h>
#include <string.h>
int do_crypt(void)
{
int outlen, inlen;
FILE *in, *out;
in = fopen("in.txt", "r");
out = fopen("out.txt", "w");
unsigned char key[32];
strcpy(key, "10000000000000000000000000000002");
unsigned char iv[8];
unsigned char inbuf[BUFSIZE], outbuf[BUFSIZE];
EVP_CIPHER_CTX ctx;
const EVP_CIPHER * cipher;
EVP_CIPHER_CTX_init(&ctx);
cipher = EVP_aes_256_cfb();
EVP_EncryptInit(&ctx, cipher, key, 0);
while(1) {
inlen = fread(inbuf, 1, BUFSIZE, in);
if(inlen <= 0) break;
if(!EVP_EncryptUpdate(&ctx, outbuf, &outlen, inbuf, inlen)) return 0;
fwrite(outbuf, 1, outlen, out);
}
if(!EVP_EncryptFinal(&ctx, outbuf, &outlen)) return 0;
fwrite(outbuf, 1, outlen, out);
EVP_CIPHER_CTX_cleanup(&ctx);
return 1;
}
int do_decrypt(char *infile)
{
int outlen, inlen;
FILE *in, *out;
in = fopen("out.txt", "r");
out = fopen("out2.txt", "w");
unsigned char key[32];
strcpy(key, "10000000000000000000000000000002");
unsigned char iv[8];
unsigned char inbuf[BUFSIZE], outbuf[BUFSIZE];
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
EVP_DecryptInit(&ctx, EVP_aes_256_cfb(), key, 0);
while(1) {
inlen = fread(inbuf, 1, BUFSIZE, in);
if(inlen <= 0) break;
if(!EVP_DecryptUpdate(&ctx, outbuf, &outlen, inbuf, inlen)) return 0;
fwrite(outbuf, 1, outlen, out);
}
if(!EVP_DecryptFinal(&ctx, outbuf, &outlen)) return 0;
fwrite(outbuf, 1, outlen, out);
EVP_CIPHER_CTX_cleanup(&ctx);
return 1;
}
main(int argc, char **argv){
if(atoi(argv[1]) == 1)
do_crypt(0);
if(atoi(argv[1]) == 2)
do_decrypt(0);
}
What could be wrong?
What's wrong is your expectation that the whole message becomes unreadable because a single byte was changed.
Which parts of the message become unreadable depends on the chosen encryption mode. You're using CFB. This means if you change a single byte in the ciphertext, the corresponding byte and the block after that get corrupted, and the cipher recovers from the error afterwards.
PCBC will corrupt all output after the error. But it still does not detect the error.
I recommend adding authentication (either a MAC, or a mode with integrated authentication such as AES-GCM).
This is exactly what is supposed to be.
Greatly oversimplifying, message is encrypted and decrypted from left to right. Until decryption routine hits changed byte, decrypted text MUST match original message. What happens after changed byte is dependent on cipher mode, but decryption of the left part of the text cannot be affected by this change.

Resources