├── Makefile ├── README.md ├── bin ├── decrypt ├── encrypt ├── random └── xor ├── decrypt.c ├── encrypt.c ├── huffman.c ├── random.c └── xor.c /Makefile: -------------------------------------------------------------------------------- 1 | all: xor random encrypt decrypt zip 2 | 3 | xor: 4 | gcc xor.c -o bin/xor 5 | 6 | random: 7 | gcc random.c -o bin/random 8 | 9 | encrypt: 10 | gcc encrypt.c -o bin/encrypt 11 | 12 | decrypt: 13 | gcc decrypt.c -o bin/decrypt 14 | 15 | zip: 16 | # gcc huffman.c -o bin/huffman #-Wno_incompatible-pointer-types-discards-qualifiers 17 | 18 | .PHONY: all xor random encrypt decrypt zip -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xipher 🔐 2 | A very simple yet powerful [XOR cipher](https://en.wikipedia.org/wiki/XOR_cipher) scheme 3 | 4 | With a 'key' file containing truly random noise, this encryption is unbreakable even in theory. 5 | 6 | The [XOR cipher](https://en.wikipedia.org/wiki/XOR_cipher) is so easy, every computer science student should be able to implement this cipher by heart in a couple of lines! If it is 100% secure and incredibly easy to implement, why isn't the whole world using it? 7 | 8 | It is in fact used all over the world albeit as part of more complicated cypher schemes. 9 | The problem with the naïve application of xor is that you lose perfect security if you apply the key too often (more than once). 10 | Also for big files you need to have big keys, to avoid or minimize repetition. 11 | 12 | This project contains a very simple Algorithm to mitigate the above limitations. 13 | 14 | In the age of the slow Internet connections, exchanging big keys with your trustees was impractical, 15 | unless you provided your friends with the key on physical devices. These days you can just create a key which is a couple of megabytes in size and give it to your communication partners, preferably/necessarily using a secure channel/medium like a CD**. 16 | 17 | 18 | 19 | Here is how it works: 20 | 21 | In your Mac, Linux or Windows shell type: 22 | 23 | `git clone https://github.com/pannous/xipher.git` 24 | 25 | `cd xipher` 26 | 27 | `make` 28 | 29 | `./bin/random > key` 30 | 31 | `./bin/encrypt README.md key > encrypted` 32 | 33 | `./bin/decrypt encrypted key > README.yay` 34 | 35 | 36 | Why don't we just use the existing encryption mechanisms? 37 | 38 | Number one: **trust** 39 | 40 | Do you really feel secure if you use an encryption scheme which you don't understand? 41 | Even if you have access to the source code: Do you really understand everything that happens in all steps? 42 | Are you sure that there is no backdoor somewhere? 43 | 44 | With this project you can be perfectly sure: 45 | Look at the simple source code, understand it and compile yourself. 46 | All you need is a truly a random key. 47 | What if you don't trust the random key generator on your computer? 48 | Just xor the generated key with some other files. 49 | If you combine randomness with noise and chaos, you get almost/practically perfect randomness. 50 | 51 | Number two: **fun and insight** 52 | 53 | Sometimes the topic of encryption can get overwhelming even for people with mathematical background. 54 | Understanding the XOR operation couldn't be any simpler and writing your own encryption feels very empowering. 55 | 56 | Number three: **simplicity** 57 | This scheme is arguably even simpler then the usual ssh-keygen/openssl approach. 58 | You and your friends can start right away with any arbitrary file as key to get reasonable encryption: 59 | `./bin/xor README.md any_random_file > reasonably_encrypted` 60 | `./bin/xor key any_random_file > practically_perfect_key` 61 | 62 | Extra: speed 63 | Once the key is generated, the encryption and decryption runs in [linear time](https://en.wikipedia.org/wiki/Time_complexity#Linear_time). 64 | 65 | 66 | CAUTION: In its current implementation once the master key is stolen* somehow, then all files encrypted with it can be deciphered. 67 | This can easily be mitigated by using several keys, but a better approach is desirable. 68 | If your key is too small or if you are using it too often, you may be reducing security. 69 | However for a key of significant size (i.e. 4GB) it is almost infinitely more likely that the key will get stolen then being reverse-engineered. 70 | 71 | NOTE: You can increase security significantly if you xor/encrypt zipped files, as they already contain very little structure! 72 | 73 | OUTLOOK: This encryption can also be used locally for your own files, if you put the key on a USB stick. And it can be used in the future for peer-to-peer communication applications. 74 | 75 | 76 | PS: * Ideally you would have a little offline device which encrypts every file/keystroke that you make before it reaches your computer. 77 | 78 | PS3: ** You can even securly share the key over the internet using [perfect forward encryption](https://en.wikipedia.org/wiki/Forward_secrecy#Perfect_forward_secrecy), for example through extensions of [Diffie Hellman](https://github.com/pannous/Diffie-Hellman). If you trust ssh/scp/sftp these might be appropriate as well. 79 | 80 | 💡 -------------------------------------------------------------------------------- /bin/decrypt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pannous/xipher/9ad1be4127abb71e896f493a4e96db55a9a694d5/bin/decrypt -------------------------------------------------------------------------------- /bin/encrypt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pannous/xipher/9ad1be4127abb71e896f493a4e96db55a9a694d5/bin/encrypt -------------------------------------------------------------------------------- /bin/random: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pannous/xipher/9ad1be4127abb71e896f493a4e96db55a9a694d5/bin/random -------------------------------------------------------------------------------- /bin/xor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pannous/xipher/9ad1be4127abb71e896f493a4e96db55a9a694d5/bin/xor -------------------------------------------------------------------------------- /decrypt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | long offset_token=1337;// optional offset ('password token') shareable through 3rd channel 7 | 8 | short nr_offsets=10; 9 | 10 | long size(FILE* fp){ 11 | fseek(fp, 0L, SEEK_END); 12 | long sz = ftell(fp); 13 | fseek(fp, 0L, SEEK_SET); 14 | return sz; 15 | } 16 | 17 | int* load_offsets(FILE* file){ 18 | int* offsets=(int*)malloc(nr_offsets*sizeof(int)); 19 | for (int i=0; i < nr_offsets; i++) 20 | { 21 | unsigned int n=fgetc(file); 22 | n=(n<<8)+fgetc(file); 23 | n=(n<<8)+fgetc(file); 24 | n=(n<<8)+fgetc(file); 25 | offsets[i]=n; 26 | // fprintf(stderr,"%d\n",n); 27 | } 28 | return offsets; 29 | } 30 | 31 | int decrypt(char* filename,char* cyphername){ 32 | FILE* file=fopen(filename,"rb"); 33 | FILE* cypher=fopen(cyphername,"rb"); 34 | if(!file) 35 | return fprintf(stderr,"CANT OPEN %s\n",filename); 36 | if(!cypher) 37 | return fprintf(stderr,"CANT OPEN %s\n",cyphername); 38 | 39 | size_t num_bytes=size(file); 40 | size_t num_cypher_bytes=size(cypher); 41 | 42 | int* offsets=load_offsets(file);// skips header!! 43 | if(num_cypher_bytes<100000)// or num_bytes 44 | return fprintf(stderr,"This encryption scheme needs big cypher key files!\n create one with the ./random command."); 45 | 46 | char* cypher_bytes=malloc(num_cypher_bytes*sizeof(char)); 47 | for (long i=0; i < num_cypher_bytes; i++) 48 | { 49 | char c= (char)fgetc(cypher); 50 | cypher_bytes[i]=c; 51 | // printf("%c",c); 52 | } 53 | for (long i=0; i < num_bytes - nr_offsets*sizeof(int); i++) 54 | { 55 | char f=(char)fgetc(file); 56 | for(int o=0;o []\n"); 68 | } 69 | 70 | //srand ((unsigned int) time (NULL)); 71 | int main(int cc,char** cv){ 72 | cc--; 73 | if(cc==2||cc==3){ 74 | char* file=cv[1]; 75 | char* key=cv[2]; 76 | if(cc==3)offset_token=atoll(cv[3]); 77 | decrypt(file,key); 78 | }else{ 79 | usage(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /encrypt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | long offset_token=1337;// optional offset ('password token') shareable through 3rd channel 7 | 8 | short nr_offsets=10; 9 | 10 | long size(FILE* fp){ 11 | fseek(fp, 0L, SEEK_END); 12 | long sz = ftell(fp); 13 | fseek(fp, 0L, SEEK_SET); 14 | return sz; 15 | } 16 | 17 | int* generate_random_offsets(){ 18 | srand(time(NULL));// seed 19 | rand();rand();rand();rand();// skip first 20 | int* offsets=(int*)malloc(nr_offsets*sizeof(int)); 21 | for (int i=0; i < nr_offsets; i++) 22 | { 23 | unsigned int n=rand();// todo: get real good random number here! 24 | offsets[i]=n; 25 | printf("%c", (n >> 24) & 0xFF); 26 | printf("%c", (n >> 16) & 0xFF); 27 | printf("%c", (n >> 8) & 0xFF); 28 | printf("%c", n & 0xFF); 29 | // fprintf(stderr,"%d\n",n); 30 | } 31 | return offsets; 32 | } 33 | 34 | int encrypt(char* filename,char* cyphername){ 35 | FILE* file=fopen(filename,"rb"); 36 | FILE* cypher=fopen(cyphername,"rb"); 37 | if(!file) 38 | return fprintf(stderr,"CANT OPEN %s\n",filename); 39 | if(!cypher) 40 | return fprintf(stderr,"CANT OPEN %s\n",cyphername); 41 | size_t num_bytes=size(file); 42 | size_t num_cypher_bytes=size(cypher); 43 | if(num_cypher_bytes<100000)// or num_bytes 44 | return fprintf(stderr,"This encryption scheme needs big cypher key files!\n create one with the ./random command."); 45 | 46 | int* offsets=generate_random_offsets();// and print as header! 47 | char* cypher_bytes=malloc(num_cypher_bytes*sizeof(char)); 48 | for (long i=0; i < num_cypher_bytes; i++) 49 | { 50 | char c= (char)fgetc(cypher); 51 | cypher_bytes[i]=c; 52 | // printf("%c",c); 53 | } 54 | for (long i=0; i < num_bytes; i++) 55 | { 56 | char f=(char)fgetc(file); 57 | for(int o=0;o []\n"); 69 | } 70 | 71 | //srand ((unsigned int) time (NULL)); 72 | int main(int cc,char** cv){ 73 | cc--; 74 | if(cc==2||cc==3){ 75 | char* file=cv[1]; 76 | char* key=cv[2]; 77 | if(cc==3)offset_token=atoll(cv[3]); 78 | encrypt(file,key); 79 | }else{ 80 | usage(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /huffman.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | typedef struct node_t { 5 | struct node_t *left, *right; 6 | int freq; 7 | char c; 8 | } *node; 9 | 10 | struct node_t pool[256] = {{0}}; 11 | node qqq[255], *q = qqq - 1; 12 | int n_nodes = 0, qend = 1; 13 | char *code[128] = {0}, buf[1024]; 14 | 15 | node new_node(int freq, char c, node a, node b) 16 | { 17 | node n = pool + n_nodes++; 18 | if (freq) n->c = c, n->freq = freq; 19 | else { 20 | n->left = a, n->right = b; 21 | n->freq = a->freq + b->freq; 22 | } 23 | return n; 24 | } 25 | 26 | /* priority queue */ 27 | void qinsert(node n) 28 | { 29 | int j, i = qend++; 30 | while ((j = i / 2)) { 31 | if (q[j]->freq <= n->freq) break; 32 | q[i] = q[j], i = j; 33 | } 34 | q[i] = n; 35 | } 36 | 37 | node qremove() 38 | { 39 | int i, l; 40 | node n = q[i = 1]; 41 | 42 | if (qend < 2) return 0; 43 | qend--; 44 | while ((l = i * 2) < qend) { 45 | if (l + 1 < qend && q[l + 1]->freq < q[l]->freq) l++; 46 | q[i] = q[l], i = l; 47 | } 48 | q[i] = q[qend]; 49 | return n; 50 | } 51 | 52 | /* walk the tree and put 0s and 1s */ 53 | void build_code(node n, char *s, int len) 54 | { 55 | static char *out = buf; 56 | if (n->c) { 57 | s[len] = 0; 58 | strcpy(out, s); 59 | code[n->c] = out; 60 | out += len + 1; 61 | return; 62 | } 63 | 64 | s[len] = '0'; build_code(n->left, s, len + 1); 65 | s[len] = '1'; build_code(n->right, s, len + 1); 66 | } 67 | 68 | void init(const char *s) 69 | { 70 | int i, freq[128] = {0}; 71 | char c[16]; 72 | 73 | while (*s) freq[(int)*s++]++; 74 | 75 | for (i = 0; i < 128; i++) 76 | if (freq[i]) qinsert(new_node(freq[i], i, 0, 0)); 77 | 78 | while (qend > 2) 79 | qinsert(new_node(0, 0, qremove(), qremove())); 80 | 81 | build_code(q[1], c, 0); 82 | } 83 | 84 | void encode(const char *s, char *out) 85 | { 86 | while (*s) { 87 | strcpy(out, code[*s]); 88 | out += strlen(code[*s++]); 89 | } 90 | } 91 | 92 | void decode(const char *s, node t) 93 | { 94 | node n = t; 95 | while (*s) { 96 | if (*s++ == '0') n = n->left; 97 | else n = n->right; 98 | 99 | if (n->c) putchar(n->c), n = t; 100 | } 101 | 102 | putchar('\n'); 103 | if (t != n) printf("garbage input\n"); 104 | } 105 | 106 | int main(void) 107 | { 108 | int i; 109 | const char *str = "this is an example for huffman encoding", buf[1024]; 110 | 111 | init(str); 112 | for (i = 0; i < 128; i++) 113 | if (code[i]) printf("'%c': %s\n", i, code[i]); 114 | 115 | encode(str, buf); 116 | printf("encoded: %s\n", buf); 117 | 118 | printf("decoded: "); 119 | decode(buf, q[1]); 120 | 121 | return 0; 122 | } -------------------------------------------------------------------------------- /random.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define bool short 7 | 8 | char* seedname="/dev/random"; // SECURE, but SLOOOW 9 | // char* seedname="/dev/urandom"; // FAST, but INSECURE 10 | char* cyphername="seed.key"; // To compensate for insecurity 11 | 12 | void create(char* filename,size_t num_bytes) 13 | { 14 | FILE* file=fopen(filename,"wb"); 15 | FILE* seed=fopen(seedname,"rb"); 16 | FILE* cypher=fopen(cyphername,"rb"); 17 | // if(!cypher)printf("CANT OPEN %s\n",cyphername); 18 | long i; 19 | int c=fgetc(seed); 20 | int s=fgetc(seed); 21 | srand(time(NULL));// seed 22 | for (i = 0; i < num_bytes; i++){ 23 | s=fgetc(seed); 24 | if(cypher){ 25 | c= fgetc(cypher); 26 | if(i%1337==0) 27 | srand(c^s);// re-seed 28 | } 29 | if(file) 30 | fprintf(file,"%c",rand()^c^s); 31 | else 32 | printf("%c",rand()^c^s); 33 | } 34 | } 35 | 36 | long size(FILE* fp){ 37 | fseek(fp, 0L, SEEK_END); 38 | long sz = ftell(fp); 39 | fseek(fp, 0L, SEEK_SET); 40 | return sz; 41 | } 42 | 43 | long min(long a,long b){ 44 | return a1)cyphername=cv[2]; 58 | if(cc>=1){ 59 | char* f=cv[1]; 60 | create(f,0x1000000); 61 | }else{ 62 | create(0,0x1000000); 63 | // usage(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /xor.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | long size(FILE* fp){ 7 | fseek(fp, 0L, SEEK_END); 8 | long sz = ftell(fp); 9 | fseek(fp, 0L, SEEK_SET); 10 | return sz; 11 | } 12 | 13 | long min(long a,long b){ 14 | return a