/* crypt.c -- symmetric encryption/decryption */
/* to encrypt: crypt ciphertext */
/* to decrypt: crypt plaintext */
/* the program prompts for a key on /dev/tty */
/* ciphering algorithm is RC4-drop512 */
/* https://en.wikipedia.org/wiki/RC4 */
/* compile with: gcc -o crypt crypt.c */
#include /* getpass DEPRECATED */
#include /* getchar, putchar, EOF, fprintf */
#include /* strdup, strlen, strcmp */
#include /* exit */
#define N 256 /* size of random number state */
void swap(unsigned char *a, unsigned char *b) {
int tmp = *a; *a = *b; *b = tmp; }
int main(int argc, char *argv[]) {
char *key, *saved_key; /* key entered twice by user */
int len; /* length of key */
unsigned char state[N]; /* state of random number generator */
int i = 0; int j = 0; /* indices into random number state */
int c, k; /* current input and key characters */
int n = 512; /* drop counter */
/* request key */
key = getpass("Enter key: "); saved_key = strdup(key);
key = getpass("Repeat key: "); len = strlen(key);
/* ensure keys match */
if (strcmp(saved_key, key) != 0) {
fprintf(stderr, "ERROR: %s: Mis-matched keys", argv[0]);
exit(1); } /* error return */
/* ensure key non-null */
if (len == 0) {
fprintf(stderr, "ERROR: %s: Null key", argv[0]);
exit(1); } /* error return */
/* key scheduling algorithm */
for (int i = 0; i < N; i++) state[i] = i;
for (int i = 0; i < N; i++) { j = (j + state[i] + key[i % len]) % N; swap(&state[i], &state[j]); } /* drop beginning of keystream */ while (n-- > 0) {
i = (i + 1) % N; j = (j + state[i]) % N;
swap(&state[i], &state[j]); }
/* perform encryption */
while ((c = getchar()) != EOF) {
i = (i + 1) % N; j = (j + state[i]) % N;
swap(&state[i], &state[j]);
k = state[(state[i] + state[j]) % N];
putchar(c ^ k); }
exit(0); /* normal return */
}
Like this:
Like Loading...
Related
Pages: 1 2
I’m sure no-one was suggesting you personally were unaware of these things, I certainly wasn’t.
It occurs to me that both your solution and mine are deeply flawed – the keystream depends only on key so, as is well known, if two messages are encrypted with the same key, xoring them together gives the xor of the two plaintexts, with the two identical keystreams cancelling each other out, and a skilled cryptographer will be able to split out the two messages and reconstruct the keystream (this is how the brilliant John Tiltman made the first inroad into the Lorenz cipher at Bletchley Park).
Usual solution is to use a salt or nonce value to perturb the encryption, here’s Speck again, with a randomly generated salt, written as the first 8 bytes of the ciphertext. Unfortunately, this means we have to distinguish between encryption and decryption modes:
There’s a serious flaw in that solution as well – the read at line 15 isn’t guaranteed to read N bytes, even if we aren’t at EOF (eg. if reading from the terminal). Better to use fread (and fwrite), which may also be more efficient since the internal buffering mean fewer syscalls are made. open and read are the right thing for /dev/urandom, which we are told doesn’t block, but in fact on Linux we can use “getrandom(2)” to make life even easier:
Updated my Python solution to eliminate the issue raised by matthew.
Here’s a solution in C. The code depends on wraparound for some of the modular arithmetic.
Example usage (the key is “praxis”):