1

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?

  • 1
    `sizeof(plaintext)` is the size of a pointer, not the length of the plaintext string. Just use `; plaintext[i] != '\0';` to run the loop until reaching the end of the string... That's just a beginning... – Fe2O3 Aug 06 '22 at 09:19
  • ohh okay thanks, any idea on how I would fix the shifting? – FreeAntiVirus Aug 06 '22 at 09:23
  • What do you expect it to shift by if not ascii value? – Shawn Aug 06 '22 at 09:34
  • Please [edit] your question and show us for some example input the expected output. – the busybee Aug 06 '22 at 09:42
  • 1
    For example I want to encrypt the word "APPLE" using the password "password", The binary representation for 'A' is 0100 0001 and I want to left shift that by 'p' which is 0x70 in HEX. so 0100 0001 << 0x70?? – FreeAntiVirus Aug 06 '22 at 09:43
  • Right now I am getting the error "shift exponent 112 is too large for 32-bit type 'int'" – FreeAntiVirus Aug 06 '22 at 09:46
  • 70 in hex is 112 in decimal, btw. – Shawn Aug 06 '22 at 09:52
  • "Shift" here is a confusing terminology. In English we say that the cipher shifts letters by a fixed amount, but the real operation is a sum. If a is the first letter and we shift by 3, that "a" becomes "d" meaning we move (shift) 3 to the right. Mathematical shift is a multiplication by power of 2 but it does not make any sense in this context. – Fra93 Aug 06 '22 at 09:52
  • As busybee suggests, what do you expect as output? What do you hope 'A' might become? Remember that a `char` is only 8 bits wide, and ASCII uses only the lower 7 bits of each byte... – Fe2O3 Aug 06 '22 at 09:52
  • Also it's not clear, even after your explanation, how you would perform encryption. "Password" is longer than "APPLE", how would you use any character of the password? And what if the word to encrypt is longer than the password? – Fra93 Aug 06 '22 at 09:54
  • @Fra93 "circle shift" here is clear given OP's [1 ... 112] table. A nice _string_ property of a circular shift is that non-_null characters_ will not become _null characters_, thus keeping the encoded text a _string_ of the same length. Alternate approaches need this property without assuming all the plaintext is ASCII. – chux - Reinstate Monica Aug 06 '22 at 13:07

2 Answers2

1

Disclaimer: as pointed out in the comments and explained here the C standard does not guarantee that letters are contiguous. However the idea behind this answer still holds.

Characters are defined as linear entries in an ASCII table. This means that each character is represented by a number. Adding 1 to a character brings you to the next one and so on.

You should also be familiar with modular arithmetic. What is "Z" +1? It goes back to "a".

Putting together these information you can see how the first representable character in a string in an ASCII table is represented by the number 33 decimal and the last one is represented by 126.

You can then make a shift function to shift a letter by n:

shift_letter(L,n)
    ret 33 + (((L-33)+n)%(126-33))
  • The L-33 is done to start from 0.
  • Then we add n.
  • We cycle back in case the result is grater than the number of possible letters. %(126-33)
  • We add offset again

PS:

As I said in the comments, your are shifting in the mathematical sense which not only makes no sense for the operation you want to do, but it also throws an error because shifting by 112 means multiplying by 2^112 which is just a bit too much.

Fra93
  • 1,992
  • 1
  • 9
  • 18
  • 1
    The C standard does not require ASCII. 33 is not the first character in ASCII. It is not even the first non-null character in ASCII. Nor is it the first printable character in ASCII. It is the first printable non-space character in ASCII. But OP probably seeks to implement a Caesar cipher rotating letters through the alphabet, not using a larger subset of characters. – Eric Postpischil Aug 06 '22 at 10:19
  • If I print 'a' it always print 97. Also I selected the first printable characters, that's why I started from 33. – Fra93 Aug 06 '22 at 10:23
  • I have updated the answer, thanks for pointing it out. – Fra93 Aug 06 '22 at 10:35
  • I have updated the question, sorry for being unclear – FreeAntiVirus Aug 06 '22 at 10:51
  • I think I have answered, does this help you? You don't shift, you add. Does this clear things up? – Fra93 Aug 06 '22 at 11:57
  • It does help but I am wondering, is it not possible to do the same thing by shifting? – FreeAntiVirus Aug 06 '22 at 12:00
  • Why do you want to shift? What advantage does it bring that addition doesn't? Also, addition has the modulo operation to stay within limits, the shift can be a rotation, but I don't see how it helps. – Fra93 Aug 06 '22 at 12:30
  • 1
    @Fra93 Given your goal, `%(126-33)` should be `%(126-33 + 1)` as `shift_letter(126, 0)` should return 126, not 33. – chux - Reinstate Monica Aug 06 '22 at 12:51
1

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;
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Hey, I just tried this out and am now getting a stuck buffer overflow error using the exact suggested code – FreeAntiVirus Aug 06 '22 at 13:30
  • @FreeAntiVirus Your comments lacks a [mcve]. Take some time to form a simple - codeable example of the failure. `char plain[] = "A"; puts(shift_encrypt3(plain, "p"));` works OK. – chux - Reinstate Monica Aug 06 '22 at 13:34
  • Sorry it may be an issue with other parts of my code, you have seen my other question already. I used shift_encrypt2 btw. – FreeAntiVirus Aug 06 '22 at 14:03