ARC4
Documentation |
#include <cryptopp/arc4.h>
|
ARC4, ot RC4 is a stream cipher developed by Ron Rivest in 1987 for RSA Data Security Inc. The cipher uses a 40-bit to 2048-bit key, and no initialization vector. RC4 was initially a trade secret, but the source code was subsequently reverse engineered and leaked on the Cypherpunks mailing list. Also see Thank you Bob Anderson.
ARC4 is declared in the Weak
namespace. You must define CRYPTOPP_ENABLE_NAMESPACE_WEAK
before you include ARC4 header file or use ARC4. You must also declare variables in the weak namespace using Weak::ARC4
.
If you are used to working in languages like Java or libraries like OpenSSL, then you might want to visit the Init-Update-Final wiki page. Crypto++ provides the transformation model, but its not obvious because its often shrouded behind Pipelines.
Note: if your project is using encryption alone to secure your data, encryption alone is usually not enough. Please take a moment to read Authenticated Encryption and consider using an algorithm or mode like CCM, GCM, EAX or ChaCha20Poly1305.
Key and IV
The first sample program prints ARC4's key and iv sizes.
int main() { using namespace CryptoPP; Weak::ARC4::Encryption enc; std::cout << "key length: " << enc.DefaultKeyLength() << std::endl; std::cout << "key length (min): " << enc.MinKeyLength () << std::endl; std::cout << "key length (max): " << enc.MaxKeyLength () << std::endl; std::cout << "iv size: " << enc.IVSize() << std::endl; return 0; }
A typical output is shown below. Notice ARC4 does not use an iv.
$ ./test.exe key length: 16 key length (min): 1 key length (max): 256 iv size: 0
Encryption and Decryption
The following example shows you how to use Weak::ARC4::Encryption
and Weak::ARC4::Decryption
. &cipher[0]
may look odd, but its how to get the non-const pointer from a std::string
.
#include "cryptlib.h" #include "secblock.h" #include "osrng.h" #include "files.h" #include "hex.h" #define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1 #include "arc4.h" #include <iostream> #include <string> int main() { using namespace CryptoPP; AutoSeededRandomPool prng; HexEncoder encoder(new FileSink(std::cout)); std::string plain("ARC4 stream cipher test"), cipher, recover; SecByteBlock key(16); prng.GenerateBlock(key, key.size()); std::cout << "Key: "; encoder.Put((const byte*)key.data(), key.size()); encoder.MessageEnd(); std::cout << std::endl; // Encryption object Weak::ARC4::Encryption enc; enc.SetKey(key, key.size()); // Perform the encryption cipher.resize(plain.size()); enc.ProcessData((byte*)&cipher[0], (const byte*)&plain[0], plain.size()); std::cout << "Plain: " << plain << std::endl; std::cout << "Cipher: "; encoder.Put((const byte*)&cipher[0], cipher.size()); encoder.MessageEnd(); std::cout << std::endl; std::cout << "Self inverting: " << enc.IsSelfInverting() << std::endl; std::cout << "Resynchronizable: " << enc.IsResynchronizable() << std::endl; Weak::ARC4::Decryption dec; dec.SetKey(key, key.size()); // Perform the decryption recover.resize(cipher.size()); dec.ProcessData((byte*)&recover[0], (const byte*)&cipher[0], cipher.size()); std::cout << "Recovered: " << recover << std::endl; return 0; }
A typical output is shown below.
$ ./test.exe Key: 49FB42D76C311EA37FBB4A6F819496F4 Plain: ARC4 stream cipher test Cipher: 24B99F0ABABAF284F61EB43479E5A8CF108D77354552E6 Self inverting: 1 Resynchronizable: 0 Recovered: ARC4 stream cipher test
Pipelines
You can also use stream ciphers in a Pipeline. Below is an example of ARC4 participating in a pipeline. Internally, StreamTransformationFilter
calls ProcessData
on the incoming data stream. The filter also buffers output if there is no attached transformation or sink.
#include "cryptlib.h" #include "secblock.h" #include "filters.h" #include "osrng.h" #include "files.h" #include "hex.h" #define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1 #include "arc4.h" #include <iostream> #include <string> int main() { using namespace CryptoPP; AutoSeededRandomPool prng; HexEncoder encoder(new FileSink(std::cout)); std::string plain("ARC4 stream cipher test"), cipher, recover; SecByteBlock key(32); prng.GenerateBlock(key, key.size()); std::cout << "Key: "; encoder.Put(key.data(), key.size()); encoder.MessageEnd(); std::cout << std::endl; // Encryption object Weak::ARC4::Encryption enc; enc.SetKey(key, key.size()); // Decryption object Weak::ARC4::Decryption dec; dec.SetKey(key, key.size()); StringSource ss1(plain, true, new StreamTransformationFilter(enc, new StringSink(cipher))); StringSource ss2(cipher, true, new StreamTransformationFilter(dec, new StringSink(recover))); std::cout << "Plain: " << plain << std::endl; std::cout << "Cipher: "; encoder.Put((const byte*)&cipher[0], cipher.size()); encoder.MessageEnd(); std::cout << std::endl; std::cout << "Recovered: " << recover << std::endl; return 0; }
The program produces the expected output:
$ ./test.exe Key: EBC47CE30CDAED5612DBD9F55A4048BE2B5B071D1790E39C01D9FCAD62B03B16 Plain: ARC4 stream cipher test Cipher: 48C6E438C80FEAA18561237012081598FC7779EF6CACF6 Recovered: ARC4 stream cipher test
Discard Bytes
A modified ARC4 algorithm that discard bytes is called "RC4-drop(nbytes)", where nbytes is the number of initial keystream bytes that are dropped. The Standard Cryptographic Algorithm Name (SCAN) default is nbytes = 768 bytes.
You can discard bytes from the keystream using SetKey
with NameValuePairs and DiscardBytes
parameter. The code below uses the Crypto++ default value for DiscardBytes
, which is 0 bytes.
HexEncoder encoder(new FileSink(std::cout)); std::string plain("ARC4 stream cipher test"), cipher, recover; SecByteBlock key(32); memset(key, 0x00, key.size()); std::cout << "Key: "; encoder.Put(key.data(), key.size()); encoder.MessageEnd(); std::cout << std::endl; // Encryption object Weak::ARC4::Encryption enc; enc.SetKey(key, key.size()); // Decryption object Weak::ARC4::Decryption dec; dec.SetKey(key, key.size()); StringSource ss1(plain, true, new StreamTransformationFilter(enc, new StringSink(cipher))); StringSource ss2(cipher, true, new StreamTransformationFilter(dec, new StringSink(recover))); std::cout << "Plain: " << plain << std::endl; std::cout << "Cipher: "; encoder.Put((const byte*)&cipher[0], cipher.size()); encoder.MessageEnd(); std::cout << std::endl; std::cout << "Recovered: " << recover << std::endl;
The result is:
$ ./test.exe Key: 0000000000000000000000000000000000000000000000000000000000000000 Plain: ARC4 stream cipher test Cipher: 9F4ACA7583442948EF6773473407E205A2685FD795BF9F Recovered: ARC4 stream cipher test
And the code below discards 2048 bytes. Notice the addition of AlgorithmParameters params
.
using namespace CryptoPP; HexEncoder encoder(new FileSink(std::cout)); std::string plain("ARC4 stream cipher test"), cipher, recover; AlgorithmParameters params = MakeParameters("DiscardBytes", 2048); SecByteBlock key(32); memset(key, 0x00, key.size()); std::cout << "Key: "; encoder.Put(key.data(), key.size()); encoder.MessageEnd(); std::cout << std::endl; // Encryption object Weak::ARC4::Encryption enc; enc.SetKey(key, key.size(), params); // Decryption object Weak::ARC4::Decryption dec; dec.SetKey(key, key.size(), params); StringSource ss1(plain, true, new StreamTransformationFilter(enc, new StringSink(cipher))); StringSource ss2(cipher, true, new StreamTransformationFilter(dec, new StringSink(recover))); std::cout << "Plain: " << plain << std::endl; std::cout << "Cipher: "; encoder.Put((const byte*)&cipher[0], cipher.size()); encoder.MessageEnd(); std::cout << std::endl; std::cout << "Recovered: " << recover << std::endl;
And the result is:
$ ./test.exe Key: 0000000000000000000000000000000000000000000000000000000000000000 Plain: ARC4 stream cipher test Cipher: 82195259806BAE0C2B66FB7321D7BD0C0004E2090F0739 Recovered: ARC4 stream cipher test