To circular shift an 8-bit object with large values like 112 ('p'), mod the shift by 8u. % with a negative char and 8 is not mod so use unsigned math.
Access plaintext[i] as an unsigned char [] to avoid sign extension on right shifts.
Use size_t to index string to handle even very long strings.
Sample fix:
char *shift_encrypt2(char *plaintext, const char *password) {
unsigned char *uplaintext = (unsigned char *) plaintext;
for (size_t i = 0; uplaintext[i]; i++) {
unsigned shift = password[i] % 8u;
uplaintext[i] = (uplaintext[i] << shift) | (uplaintext[i] >> (8u - shift));
}
return plaintext;
}
Note: if the password string is shorter than than plaintext string, we have trouble. A possible fix would re-cycle through the password[].
Advanced: use restrict to allow the compiler to assume plaintext[] and password[] do not overlap and emit potentially faster code.
char *shift_encrypt2(char * restrict plaintext, const char * restrict password) {
Advanced: Code really should access password[] as an unsigned char array too, yet with common and ubiquitous 2's compliment, password[i] % 8u makes no difference.
char *shift_encrypt3(char * restrict plaintext, const char * restrict password) {
if (password[0]) {
unsigned char *uplaintext = (unsigned char *) plaintext;
const unsigned char *upassword = (const unsigned char *) password;
for (size_t i = 0; uplaintext[i]; i++) {
if (*upassword == 0) {
upassword = (const unsigned char *) password;
}
unsigned shift = *upassword++ % 8u;
uplaintext[i] = (uplaintext[i] << shift) | (uplaintext[i] >> (8u - shift));
}
}
return plaintext;
}