Search code examples
cencryptionbitwise-operatorsshift

How to shift a character by another character in C


How would I go about circle left shifting every character in a string by the corresponding character in a 'password' string.

char *shift_encrypt(char *plaintext, char *password) {
    for (int i = 0; plaintext[i] != '\0'; i++) {
        plaintext[i] = (plaintext[i] << password[i]) | (plaintext[i] >> (8 - password[i]));
    }
   return plaintext;
}

EDIT: To clarify what I am asking, if I wanted to circle shift for example the character 'A' by the character 'p', I mean something along the lines of:

0100 0001 ('A') << 0x70 ('p') shown bit-shifted left bit by bit

 1. 1000 0010
 2. 0000 0101
      .
      .
      .
 110. 0101 0000
 111. 1010 0000
 112. 0100 0001

So basically shifting by 1, 126 times?


Solution

  • 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;
    }