Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ Timesaving Techniques (2005) [eng].pdf
Скачиваний:
65
Добавлен:
16.08.2013
Размер:
8.35 Mб
Скачать

Testing the Rot13 Algorithm 345

if ( strIn )

{

_encrypt = rot13( strIn );

}

return _encrypt.c_str();

}

string String(void) const

{

return _encrypt;

}

};

ostream& operator<<( ostream& out, const Rot13Encryption& r13 )

{

out << r13.String().c_str(); return out;

}

}

The code in the above listing implements a simple Rot13 algorithm. The bulk of the work is done in the Rot13 function, which simply rotates characters 13 positions in the alphabet. If you look at the code at 1, you can see how this works. As the comment in this function specifies, it assumes that the alphabet is contiguous for the character set you are working with. This means that this code will not work on older EBCDIC systems, nor will it work with non-English character sets. Unfortunately, this is true of most textbased encryption algorithms. The other methods of the class, such as the operator << method, are utility functions that can be used to convert the encrypted string for output, or to stream it to an output file.

3. Save the source file in your text editor.

This class will handle the Rot13 algorithm. This algorithm works by simply rotating data about in the alphabet. The string is then unreadable by humans, which is the entire point of encryption.

Testing the Rot13 Algorithm

After you create a class, you should create a test driver that not only ensures that your code is correct, but also shows people how to use your code.

The following list shows you how to create a test driver that illustrates various kinds of input from the user, and shows how the class is intended to be used.

1. In the code editor of your choice, reopen the source file to hold the code for your test program.

In this example, I named the test program ch57.cpp.

2. Type the code from Listing 57-2 into your file.

Better yet, copy the code from the source file on this book’s companion Web site.

LISTING 57-2: TESTING THE ROT13 ENCRYPTION CLASS

int main( int argc, char **argv )

{

Rot13Encryption r13(“This is a test”); cout << r13.String().c_str() << endl; cout <<

Rot13Encryption(r13.String().c_str()) << endl;

return 0;

}

3. Compile the source file, using your favorite compiler on your favorite operating system.

4. Run the application in the console.

If you have done everything properly, you should see the following output on the console window:

Guvf

vf

n

grfg

 

2

This

is

a

test

 

346 Technique 57: Encrypting and Decrypting Strings

The first output (shown at

 

2) is the rotated ver-

sion of the string. It

remains human-readable, at

 

 

 

least up to a point, because the substituted characters are all in the alphabet), but it certainly provides no clue to the semantic content of the text it is encrypting. Thus the purpose of encryption is preserved.

Encryption is intended to hide the purpose of the text from the user, not to make the text vanish or compress. Note that the string used is embedded in the application; this particular program does not accept any input from the user. It is a very simple test driver.

Unfortunately, Rot13 is one of the most common algorithms in use; hackers know it like the backs of their hands. We need a slightly better approach.

Implementing the

XOR Algorithm

The next encryption algorithm we examine is the XOR algorithm. XOR stands for Exclusive OR, which means that it uses the mathematical “exclusive or” operator in order to convert text. One property of the exclusive or operation is that a character that is exclusively or’d with another character can be returned to its original state by or’ing it again with the same character. This means that an encryption password can be used to both encode and decode a string using XOR. In this technique, we build a simple class that implements the XOR algorithm and provides methods for encoding and decoding strings.

1. In the code editor of your choice, reopen the source file to hold the code for the source file of the technique.

In this example, the file is named ch57.cpp, although you can use whatever you choose.

2. Append the code from Listing 57-3 into your file.

Better yet, copy the code from the source file on this book’s companion Web site.

LISTING 57-3: THE XORENCRYPTION CLASS

class XOREncryption

 

{

 

 

private:

 

 

char

*_encrypt;

 

int

_length;

 

string

_key;

 

protected:

 

 

char *do_xor( const char *sIn, int

3

{ length, const string& key)

int idx = 0;

 

char *strOut = new char[ length ];

if ( !key.length() ) return strOut;

for ( int i=0; i<length; ++i )

{

char c = (sIn[i] ^ key[idx]); strOut[i] = c;

idx ++;

if ( idx >= key.length() ) idx = 0;

}

return strOut;

}

public:

XOREncryption(void)

{

_encrypt = NULL;

}

XOREncryption( const char *strIn, int length, const char *keyIn )

{

if ( keyIn )

_key = keyIn; if ( strIn )

{

_length = length; _encrypt = do_xor( strIn,

Testing the XOR Algorithm 347

length, _key );

}

}

XOREncryption( const XOREncryption& aCopy )

{

_encrypt = new char [ aCopy._length

];

memcpy ( _encrypt, aCopy._encrypt, aCopy._length );

_key = aCopy._key; _length = aCopy._length;

}

XOREncryption operator=( const XOREncryption& aCopy )

{

_encrypt = new char [ aCopy._length ];

memcpy ( _encrypt, aCopy._encrypt, aCopy._length );

_key = aCopy._key; _length = aCopy._length; return *this;

}

~XOREncryption(void)

{

delete _encrypt;

}

const char *operator=( const char *strIn

)

{

if ( _encrypt ) delete _encrypt;

if ( strIn )

{

_encrypt = do_xor( strIn, strlen(strIn), _key );

}

return _encrypt;

}

const char *operator<<( const char *strIn )

{

if ( strIn )

{

_encrypt = do_xor( strIn, strlen(strIn), _key );

}

return _encrypt;

}

const char *String(void) const

{

return _encrypt;

}

int Length(void) const

{

return _length;

}

};

The code in this class implements a simple XOR algorithm. The main functionality of the class is shown in the do_xor method, shown at 3. As you can see, the method takes the input encryption key and XORs it with the string that is provided by the user. The class requires two strings, one that is a “key” used for encrypting or decrypting strings. The second string is the input string to be encrypted or decrypted. Running the algorithm with the same inputs twice results in the original string.

3. Save the source file in your text editor.

Testing the XOR Algorithm

The following steps show you how to create a test driver that illustrates various kinds of input from the user, and show how the class is intended to be used:

1. In the code editor of your choice, reopen the source file to hold the code for your test program.

In this example, I named the test program ch57.cpp.

2. Type the code from Listing 57-4 into your file.

Better yet, copy the code from the source file on this book’s companion Web site.

348 Technique 57: Encrypting and Decrypting Strings

LISTING 57-4: TESTING THE XORENCRYPTION CLASS

int main( int argc, char **argv )

{

Rot13Encryption r13(“This is a test”); cout << r13.String().c_str() << endl; cout <<

Rot13Encryption(r13.String().c_str()) << endl;

XOREncryption x1(“This is a test”,

 

 

strlen(“This is a

test”), “C++Test”);

 

cout << x1.String()

<< endl;

 

 

XOREncryption x2(x1.String(),

 

4

x1.Length(), “C++Test”);

 

cout << x2.String()

<< endl;

 

return 0;

}

3. Compile the source file, using your favorite compiler on your favorite operating system.

4. Run the application in the console.

If you have done everything properly, you should see the following output on the console window:

$ ./a.exe

 

 

Guvf vf n grfg

 

 

This is a test

 

5

_CB’E_cJ_

 

This is a test

6

 

 

 

Note that the above output includes the Rot13 encryption that we developed earlier in this technique for comparison. The strings are all hard-coded into the application, and your output might vary depending on the font and terminal type you are using. The output for the XOREncryption class is shown at 5 and 6. Our original string is This is a test and the two lines following it show how that line is first encrypted and then decrypted using the same key. In this case, our “key” is the string

C++Test.

The XOREncryption class does not use a string to hold the encrypted version of the input (see 4 in Listing 57-4), nor does it return the value as a string object. This is because the string class holds only alphanumeric data. The xor operation can result in non-alphanumeric values, and at times can cause the string class to return only a portion of the original string.

Never use a string object to store character buffers that might contain nulls or control characters. String classes assume that the null character terminates the string and will not store any characters past the null.

58 Converting the Case

of a String

Technique

Save Time By

Using modern techniques to convert the case of input strings

Using the Standard Template Library’s transform function

Interpreting output

In the good old days of C programming, converting the case of a string was a simple matter. You just called the strupr function and the string was instantly converted to uppercase. Similarly, if you called the

strlwr function, the string was converted to lowercase. Have things really changed all that much since then? Well, in some ways, things have changed a lot. For example, the following code is not permissible:

string myString = “Hello world” strupr( myString );

This code will not compile, since the strupr function does not accept a string argument. Nor can you write the following code and expect it to work:

strupr(myString.c_str());

This code will not compile either; the strupr function cannot accept a const character pointer — which is what the c_str method returns. So how do you write code to convert modern string objects to upperand lowercase? You could use the brute-force technique, like this:

for ( int i=0; i<myString.length(); ++i ) myString[i] = toupper(myString[i]);

This code certainly does work, but it is not very elegant and it does not anticipate all circumstances. It assumes, for example, that the string characters are in contiguous order in memory — which is a bad assumption to make about any Standard Template Library (STL) collection. The entire purpose of the STL is to provide the developer with a way in which to access containers (strings are just containers of characters) without any assumptions about how they are organized in memory. The better choice, of course, is to use an iterator (see Technique 49 for more on iterators). However, the STL provides an even better approach to the whole thing, which is the transform function found in the algorithms of the STL. The transform function allows you to operate in a container-independent