Encryption Machine

Pages: 12
Hi, fellow coders of cplusplus.com! I have created a simple program that takes in input from a user, and then sort or 'encrypts' it using a completely random sequence of characters, different each time. What I want is that the program I create to actually encrypt the data given to it. Here is my program so far.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <iostream>
#include <cstdlib>
#include <string>
#include <unistd.h>
#include "main.h"

using std::cout;
using std::cin;
using std::endl;
using std::string;


int command;
char aknowledge;
string input;

int main()
{
    cout << "Welcome to the Encryption machine! Here are your tasks! To start, please type the number of the command!\n\n1 : Encrypt text\n2 : Exit\n\n>>> ";
    cin >> command;
    cin.ignore();
    if (command == 1) 
    {
        startProgram();
    }
    else if (command == 2)
    {
        return EXIT_SUCCESS;
    }
    else 
    {
        cout << "Invalid input. Restarting...";
        main();
    }
    return 0;
}

void startProgram()
{
    cout << "Warning! This encryption is very strong and COMPLETELY random!" << endl;
    cout << "Please aknowledge: [y,n] ";
    cin >> aknowledge;
    cin.ignore();
    if (aknowledge == 'y')
    {
        system("clear");
        cout << "Provide text to encrypt with: \n\n";
        getline(cin,input);
        for (int c = 0; c < input.length();c++)
        {
            cout << encryptText(input);   
        }
        system("clear");
        return;
    }
    else if (aknowledge == 'n')
    {
        return;
    }
    else
    {
        return;
    }
}

char encryptText(string input)
{
    static const char symbols[] ="0123456789""!@#$^&*()_+-=[]{}|\\:\"';<>.,?/~`%""ABCDEFGHIJKLMNOPQRSTUVWXYZ""abcdefghijklmnopqrstuvwxyz";
    int len = sizeof(symbols) -1;
    return symbols[rand() % len];
}


Here is the corresponding header file:

1
2
3
4
5
6
7
8
9
10
#ifndef MAIN_H
#define MAIN_H

#include <iostream>
using std::string;

void startProgram();
char encryptText(std::string input);

#endif 
If I were to mess around with crypto (which I actually am), I'd start with base64 encoding. Base 64 encoding is not a cryptographic algorithm, but rather provides convenient notation for handling arbitrary bit streams in human-readable format.

In other words, base64 is the input and the output format your crypto system can use.

https://en.wikipedia.org/wiki/Base64

Here's my implementation (note: not tested completely, but I am using it bravely nevertheless hehe).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
#include <iostream>
#include <sstream>
#include<assert.h>
#include<stdint.h>

unsigned char GetCharFrom6bit(uint32_t input)
{ switch(input)
  { case 0: return  'A';
    case 1: return  'B';
    case 2: return  'C';
    case 3: return  'D';
    case 4: return  'E';
    case 5: return  'F';
    case 6: return  'G';
    case 7: return  'H';
    case 8: return  'I';
    case 9: return  'J';
    case 10: return 'K';
    case 11: return 'L';
    case 12: return 'M';
    case 13: return 'N';
    case 14: return 'O';
    case 15: return 'P';
    case 16: return 'Q';
    case 17: return 'R';
    case 18: return 'S';
    case 19: return 'T';
    case 20: return 'U';
    case 21: return 'V';
    case 22: return 'W';
    case 23: return 'X';
    case 24: return 'Y';
    case 25: return 'Z';
    case 26: return 'a';
    case 27: return 'b';
    case 28: return 'c';
    case 29: return 'd';
    case 30: return 'e';
    case 31: return 'f';
    case 32: return 'g';
    case 33: return 'h';
    case 34: return 'i';
    case 35: return 'j';
    case 36: return 'k';
    case 37: return 'l';
    case 38: return 'm';
    case 39: return 'n';
    case 40: return 'o';
    case 41: return 'p';
    case 42: return 'q';
    case 43: return 'r';
    case 44: return 's';
    case 45: return 't';
    case 46: return 'u';
    case 47: return 'v';
    case 48: return 'w';
    case 49: return 'x';
    case 50: return 'y';
    case 51: return 'z';
    case 52: return '0';
    case 53: return '1';
    case 54: return '2';
    case 55: return '3';
    case 56: return '4';
    case 57: return '5';
    case 58: return '6';
    case 59: return '7';
    case 60: return '8';
    case 61: return '9';
    case 62: return '+';
    case 63: return '/';
    default:
      std::cout << "Requesting character from a purported 6 bit integer, which in fact has more significant bits. ";
      assert(false);
      break;
  }
  return -1;
}

bool Get6bitFromChar(char input, uint32_t& output)
{ switch (input)
  { case 'A': output= 0;  return true;
    case 'B': output= 1;  return true;
    case 'C': output= 2;  return true;
    case 'D': output= 3;  return true;
    case 'E': output= 4;  return true;
    case 'F': output= 5;  return true;
    case 'G': output= 6;  return true;
    case 'H': output= 7;  return true;
    case 'I': output= 8;  return true;
    case 'J': output= 9;  return true;
    case 'K': output= 10; return true;
    case 'L': output= 11; return true;
    case 'M': output= 12; return true;
    case 'N': output= 13; return true;
    case 'O': output= 14; return true;
    case 'P': output= 15; return true;
    case 'Q': output= 16; return true;
    case 'R': output= 17; return true;
    case 'S': output= 18; return true;
    case 'T': output= 19; return true;
    case 'U': output= 20; return true;
    case 'V': output= 21; return true;
    case 'W': output= 22; return true;
    case 'X': output= 23; return true;
    case 'Y': output= 24; return true;
    case 'Z': output= 25; return true;
    case 'a': output= 26; return true;
    case 'b': output= 27; return true;
    case 'c': output= 28; return true;
    case 'd': output= 29; return true;
    case 'e': output= 30; return true;
    case 'f': output= 31; return true;
    case 'g': output= 32; return true;
    case 'h': output= 33; return true;
    case 'i': output= 34; return true;
    case 'j': output= 35; return true;
    case 'k': output= 36; return true;
    case 'l': output= 37; return true;
    case 'm': output= 38; return true;
    case 'n': output= 39; return true;
    case 'o': output= 40; return true;
    case 'p': output= 41; return true;
    case 'q': output= 42; return true;
    case 'r': output= 43; return true;
    case 's': output= 44; return true;
    case 't': output= 45; return true;
    case 'u': output= 46; return true;
    case 'v': output= 47; return true;
    case 'w': output= 48; return true;
    case 'x': output= 49; return true;
    case 'y': output= 50; return true;
    case 'z': output= 51; return true;
    case '0': output= 52; return true;
    case '1': output= 53; return true;
    case '2': output= 54; return true;
    case '3': output= 55; return true;
    case '4': output= 56; return true;
    case '5': output= 57; return true;
    case '6': output= 58; return true;
    case '7': output= 59; return true;
    case '8': output= 60; return true;
    case '9': output= 61; return true;
    case '+': output= 62; return true;
    case '/': output= 63; return true;
    case '=': return false;
    default: return false;
  }
  return false;
}

bool StringBase64ToNormalString(const std::string& input, std::string& output, std::stringstream* comments)
{ output.reserve((3*input.size())/4+1);
  output="";
  uint32_t theStack=0, sixBitDigit=0;
  int numBitsInStack=0;
  for (unsigned i=0; i<input.size(); i++)
  { if (!Get6bitFromChar(input[i], sixBitDigit))
    { if (input[i]!='=')
      { if (comments!=0)
          *comments << "<hr>Error: the input string: <br>\n" << input << "\n<br>had characters outside of base64";
        return false;
      }
      theStack=0;
      numBitsInStack+=6;
      numBitsInStack%=8;
      continue;
    }
    theStack*=64;
    theStack+=sixBitDigit;
    numBitsInStack+=6;
    if (numBitsInStack==12)
    { output.push_back(theStack/16);
      numBitsInStack=4;
      theStack=theStack%16;
    }
    if (numBitsInStack==8)
    { output.push_back(theStack);
      numBitsInStack=0;
      theStack=0;
    }
    if (numBitsInStack==10)
    { output.push_back(theStack/4);
      numBitsInStack=2;
      theStack=theStack%4;
    }
  }
//  stOutput << "<br>output is: " << output << ", Converted back: " << Crypto::CharsToBase64String(output);
  if (comments!=0 && numBitsInStack!=0)
  { *comments << "<br>Input " << input << " corresponds modulo 8 to " << numBitsInStack
    << " bits. Perhaps the input was not padded correctly with = signs.";
  }
  return true;
}

std::string StringToStringBase64(const std::string& input)
{ uint32_t theStack=0;
  int numBitsInTheStack=0;
  std::string result;
  result.reserve((input.size()*4)/3+1);
  for (unsigned i =0; i<input.size(); i++)
  { theStack*=256;
    theStack+=input[i];
    numBitsInTheStack+=8;
    if (numBitsInTheStack==8)
    { result.push_back(GetCharFrom6bit(theStack/4));
      numBitsInTheStack=2;
      theStack%=4;
    }
    if (numBitsInTheStack==10)
    { result.push_back(GetCharFrom6bit(theStack/16));
      numBitsInTheStack=4;
      theStack%=16;
    }
    if (numBitsInTheStack==12)
    { result.push_back(GetCharFrom6bit(theStack/64));
      result.push_back(GetCharFrom6bit(theStack%64));
      numBitsInTheStack=0;
      theStack=0;
    }
  }
  if (numBitsInTheStack==2)
  { result.push_back(GetCharFrom6bit(theStack*16));
    result.push_back('=');
    result.push_back('=');
  }
  if (numBitsInTheStack==4)
  { result.push_back(GetCharFrom6bit(theStack*4));
    result.push_back('=');
  }
  return result;
}

int main()
{
  std::string theString="Hello world!\r\n\t And hello again...";
  std::string theStringbase64=  StringToStringBase64(theString);
  std::string theStringConvertedBack;
  std::stringstream comments;
  std::cout << theString;
  std::cout << "\nIn base 64: \n" << theStringbase64;
  if (!StringBase64ToNormalString(theStringbase64, theStringConvertedBack, &comments))
    std::cout << "Failed to convert your purported base 64 string to a normal string. Comments: " << comments.str();
  else
    std::cout << "Converted back to string: \n" << theStringConvertedBack;
  int x;
  std::cin >> x;
  return 0;
}
Goodness Grace, this is a lot more code than I am willing to endure. It will take a VERY long time to write all that, and providing a statement of code for EVERY statement seems very inconvenient. I like your code, tition, but I want one that is much less to write. In fact, the whole reason I did random characters, was because I did not want to have to define each and every character for my program.
The problem with your code, though, is that it isn't reversible. It just spews out random characters that happen to share a size with the original string.

Also, if that code is too much for you to endure, realize that most C++ programs go a lot more than just 250 lines. Most of that is just switch statements anyway.
No, the whole idea of this topic was to do what tition proposed, just with a less REPETITIVE solution, otherwise I'm fine with what he said. I'm more or less looking for a better algorithm as a solution.

@isplis
Didn't you read my question! That was what I wanted to do, as you said!
You know what, based on titios answer, THANK YOU, I just thought of something. Wouldn't it be possible to create a multidimensional array, bidimensional to be exact, and using the same loop as before, print the character at the same index as the one found? How would I implement it, though?
closed account (18hRX9L8)
@tition: GetCharFrom6bit and Get6bitFromChar can be reduced to around 5-10 lines of formatted code...
That's what I'm looking for, can you provide an example?
@tition: GetCharFrom6bit and Get6bitFromChar can be reduced to around 5-10 lines of formatted code...


I am sure your statement is correct: how would you do it? In my defense though it took me about 5 minutes to type that code in (I have been a gaming style copy+ paste ninja* all my computer life, also am good at using multi-line inputs). Looking up the standard libraries usually takes longer than 5 minutes. Also in my defense these two functions are very fast and I think as portable as they get.


*I am talking about the alt+shift (+arrows, can use mouse too) shortcuts available in code blocks, visual studio, and many other good editors.
Last edited on
I am sure your statement is correct: how would you do it?

Off the cuff, and untested..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <cstddef>
#include <cstdint>
#include <cassert>

const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const std::size_t alphabet_size = sizeof(alphabet) - 1;

unsigned char GetCharFrom6bit(uint32_t input)
{
    assert(input < alphabet_size);
    return alphabet[input];
}

bool Get6bitFromChar(char input, uint32_t& output)
{
    std::size_t i = 0;
    while (i < alphabet_size && input != alphabet[i])
        ++i;

    const bool success = i < alphabet_size;
    if (success)
        output = i;

    return success;
}
Last edited on
h'mmm, that seems correct, although I'm not really sure. Could you explain the whole code to me?
Cire, your code appears slower than mine. Yours will run its main cycle 64 times in the worst case scenario. It will be more than 128 machine instructions in the worst case scenario, on average more than 64 instructions. My code's speed is supposed to not depend on character you are looking up.
Last edited on
So tition and cire, I still don't have an answer, which one of your solutions should I use?
tition wrote:
Cire, your code appears slower than mine.

Probably Get6bitFromChar is a tad slower. It was off-the cuff. On the other hand, I expect the complementary function to be a tad faster than the typical jump table a switch turns into.

RUNNER PROG AGARIO wrote:
So tition and cire, I still don't have an answer, which one of your solutions should I use?

Use your own.
For kicks I profiled the functions. On average, my version of Get6bitFromChar took about 1.5 times as long your version took. On average, your version of GetCharFrom6bit took 3 times as long as my version took. At least, on my machine with VC++.

Modifying my version of Get6bitFromChar to use a static look-up table reduced the execution time considerably.

You can see the results on g++ here:
http://melpon.org/wandbox/permlink/tTXclvfnW9XHT7H4
Last edited on
@Cire: oh yes, indeed! Your cire_Get6bitFromChar is much better than mine, my comments were for your first version (the one posted above). Your new one (in the link you gave) is a lot more elegant! Your GetCharFrom6bit is faster than mine too, but I get less than 10% difference on the website you gave [edit:]but not if I do some dirty optimizations.

I hope you wouldn't mind if I use your solution to refactor my code (for my own use)?


[Edit:] Aha! Got it: if I first convert the switched variable to unsigned char, my version of GetCharFrom6bit beats yours by a hair's width, so that's really down to how the machine does things. And yes, your cire_Get6bitFromChar is about 4 times faster - in fact eliminating your val intermediate variable appears to speed it up a bit. However, I do dare say my version is much simpler to come up with and explain than yours - yours does require some algorithmic insight. You could also avoid the init preparation by hard-coding the entire 256 element array, this will make it even faster and more reliable.

http://melpon.org/wandbox/permlink/6tATv1JBw9mOi0eA
Last edited on
I hope you wouldn't mind if I use your solution to refactor my code (for my own use)?

There's nothing earth-shattering about a look-up table. Knock yourself out.


in fact eliminating your val intermediate variable appears to speed it up a bit.

The tests are set up to use containers to avoid the optimizer optimizing too much. The more you take away from that, the less likely the test results are to resemble real usage patterns.
[Edit: I see the change you made wasn't to where I was thinking when I saw your comment. If you're going to make that change, you may as well get rid of the return value and let the user check the value in output to determine if the call was successful. At the very least, it should be documented when a function makes a (surprising?) change on failure.]


However, I do dare say my version is much simpler to come up with and explain than yours - yours does require some algorithmic insight.

From my point of view, it's not. We use look up-tables all the time in everyday life. Switch analogs, not so much.


You can also avoid the init preparation by hard-coding the entire 256 element array, this will make it even faster and more reliable.

Hard-coding things rarely makes things more reliable, and this case is no exception. In fact, I should've done the initialization of the table a little differently to allow for different letter encodings (although I doubt that code'll ever see a system with EBCDIC,) but this wasn't really intended to go past the toy phase. As for speed, the cost of the initialization is a one-time cost that doesn't affect the look-up speed at all. Hard-coding won't buy you much.
Last edited on
We use look up-tables all the time in everyday life. Switch analogs, not so much.
Now that you are saying it: how are switches actually implemented ... I remember the creator of D making a point about his language having or planning to have switches over arbitrary data types (including user-defined ones).
Last edited on
RUNNER PRO AGARIO, there's a bug in your original program. Lines 49-52 encrypt the text N times. You probably need to get rid of the loop, or change encryptText() to be encryptChar() (i.e., to encrypt a single character).

I suggest that you also add an option to the program that will decrypt the text too. That way you can write and test both encryption and decryption at the same time.

Hmm. You probably want an option to enter the encryption and/or decryption key too, assuming that there is one.

Regarding base-64 encoding: you could use a lookup table for both encoding and decoding, but why not just encrypt the bytes directly? That way you can encrypt non-text data too.

Now the real question: what encryption algorithm do you want to use?
Pages: 12