SPECK

From Crypto++ Wiki
Jump to: navigation, search

SPECK64 and SPECK128 are lightweight block ciphers designed by Ray Beaulieu, Douglas Shors, Jason Smith, Stefan Treatman-Clark, Bryan Weeks and Louis Wingers. The SIMON and SPECK homepage is located at http://iadgov.github.io/simon-speck. The ciphers were designed for resource constrained devices and gadgets that participate in the Internet of Things. NIST is considering applications of lightweight cryptography for sensor networks, healthcare and the smart grid. NASA has expanding programs for small satellites such as CubeSats which may need lightweight algorithms.

One of the security goals of SIMON was maintain an acceptable level of security in an environment where power, memory and processors were [sometimes severely] limited. The ciphers are associated with the NSA so there is some controversy about them. The ciphers also have a number of good attributes so speculation should not be hard to overcome. Also see Notes on the design and analysis of Simon and Speck.

SPECK-128 offers a specialized SSSE3 implementation for AMD and Intel that runs around 2.3 cycles per byte (cpb) on a modern Intel core; see Commit e7fee716d68a. SPECK-128 also provides a NEON based implementation for ARM A-32 that runs around 10 cpb; see Commit 304809a65dc3. The Aarch64 implementation also runs around 8 cpb, but we had to work around a GCC or GAS bug (the workaround cost us about 1 cpb).

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 understand why you should prefer to use CCM, GCM, or EAX over other modes, such as CBC or CTR.

Algorithm Name

If you call StaticAlgorithmName then the function will return the partial name "SPECK-<block size>". Once the cipher is keyed you can call AlgorithmName which returns the full name presented as "SPECK-<block size>(<key length>)". In the case of SPECK the algorithms names are SPECK-64(96), SPECK-64(128), SPECK-128(128), SPECK-128(192) and SPECK-128(256).

The naming follows DSTU 7624:2014, where block size is provided first and then key length. The library uses a dash to identify block size and parenthesis to identify key length. For example, Kalyna-128(256) is Kalyna with a 128-bit block size and a 256-bit key length. If a mode is associated with the object, then it follows as expected. For example, Kalyna-256(512)/ECB denotes Kalyna with a 256-bit block and 512-bit key operated in ECB mode.

int main(int argc, char* argv[])
{
    SPECK128::Encryption speck;

    std::cout << "StaticAlgorithmName: " << speck.StaticAlgorithmName() << std::endl;
    std::cout << "AlgorithmName (unkeyed): " << speck.AlgorithmName() << std::endl;

    byte key[SPECK128::DEFAULT_KEYLENGTH] = {};
    speck.SetKey(key, sizeof(key));

    std::cout << "AlgorithmName (keyed): " << speck.AlgorithmName() << std::endl;

    return 0;
}

The program results in the following output.

$ ./test.exe
StaticAlgorithmName: SPECK-128
AlgorithmName (unkeyed): SPECK-128
AlgorithmName (keyed): SPECK-128(128)

Sample Programs

The first snippet dumps the minimum, maximum, and default key lengths used by SPECK128.

std::cout << "key length: " << SPECK128::DEFAULT_KEYLENGTH << std::endl;
std::cout << "key length (min): " << SPECK128::MIN_KEYLENGTH << std::endl;
std::cout << "key length (max): " << SPECK128::MAX_KEYLENGTH << std::endl;
std::cout << "block size: " << SPECK128::BLOCKSIZE << std::endl;

Output from the above snippet produces the following. Notice the default key size is 128 bits or 16 bytes.

$ ./test.exe
key length: 16
key length (min): 16
key length (max): 32
block size: 16

The following program shows how t operate SPECK128 in CBC mode using a pipeline. The key is declared on the stack using a SecByteBlock to ensure the sensitive material is zeroized. Similar could be used for both plain text and recovered text.

AutoSeededRandomPool prng;

SecByteBlock key(SPECK128::DEFAULT_KEYLENGTH);
prng.GenerateBlock(key, key.size());

byte iv[SPECK128::BLOCKSIZE];
prng.GenerateBlock(iv, sizeof(iv));

std::cout << "Key: ";
StringSource(key, key.size(), true, new HexEncoder(new FileSink(std::cout)));
std::cout << std::endl;

std::cout << "IV: ";
StringSource(iv, sizeof(iv), true, new HexEncoder(new FileSink(std::cout)));
std::cout << std::endl;

string plain = "CBC Mode Test";
string cipher, encoded, recovered;

/*********************************\
\*********************************/

try
{
    std::cout << "plain text: " << plain << std::endl;

    CBC_Mode< SPECK128 >::Encryption e;
    e.SetKeyWithIV(key, key.size(), iv);

    // The StreamTransformationFilter adds padding
    //  as required. ECB and CBC Mode must be padded
    //  to the block size of the cipher.
    StringSource(plain, true, 
        new StreamTransformationFilter(e,
            new StringSink(cipher)
        ) // StreamTransformationFilter
    ); // StringSource
}
catch(const CryptoPP::Exception& e)
{
    cerr << e.what() << std::endl;
    exit(1);
}

/*********************************\
\*********************************/

// Pretty print
std::cout << "Cipher text: ";
StringSource(cipher, true, new HexEncoder(new FileSink(std::cout)));
std::cout << std::endl;

/*********************************\
\*********************************/

try
{
    CBC_Mode< SPECK128 >::Decryption d;
    d.SetKeyWithIV(key, key.size(), iv);

    // The StreamTransformationFilter removes
    //  padding as required.
    StringSource s(cipher, true, 
        new StreamTransformationFilter(d,
            new StringSink(recovered)
        ) // StreamTransformationFilter
    ); // StringSource

    std::cout << "recovered text: " << recovered << std::endl;
}
catch(const CryptoPP::Exception& e)
{
    cerr << e.what() << std::endl;
    exit(1);
}

A typical output is shown below. Note that each run will produce different results because the key and initialization vector are randomly generated.

$ ./test.exe
Key: F36D4289293A07A0C1E3D8EAFBF83C6F
IV: 50650B834D62457D3D5CBFE9708EC927
plain text: CBC Mode Test
Cipher text: 952FB25AF03C17F972BEB9357AA48719
recovered text: CBC Mode Test

By switching to EAX mode, authenticity assurances can placed on the cipher text for nearly no programming costs. Below the StreamTransformationFilter was replaced by AuthenticatedEncryptionFilter and AuthenticatedDecryptionFilter.

EAX< SPECK128 >::Encryption e;
e.SetKeyWithIV(key, key.size(), iv);

StringSource(plain, true, 
    new AuthenticatedEncryptionFilter(e,
        new StringSink(cipher)
    ) // StreamTransformationFilter
); // StringSource

...

EAX< SPECK128 >::Decryption d;
d.SetKeyWithIV(key, key.size(), iv);

StringSource s(cipher, true, 
    new AuthenticatedDecryptionFilter(d,
        new StringSink(recovered)
    ) // StreamTransformationFilter
); // StringSource

Typical output is as follows. Notice the additional cipher text bytes due to the MAC bytes. See EAX Mode for details.

$ ./test.exe
Key: 802F1C05845AAE68BA546A13442FA097
IV: 552EB9F4B252F2ABA24577124C18D26A
plain text: EAX Mode Test
Cipher text: E03C5BD43C94BB59772B4FF812556970EE926DAB965DF092750B8AA96A
recovered text: EAX Mode Test

To manually insert bytes into the filter, perform multiple Puts. Though Get is used below, a StringSink could easily be attached and save the administrivia.

const size_t SIZE = 16 * 4;
string plain(SIZE, 0x00);

for(size_t i = 0; i < plain.size(); i++)
    plain[i] = 'A' + (i%26);
...

CBC_Mode < SPECK128 >::Encryption encryption(key, sizeof(key), iv);
StreamTransformationFilter encryptor(encryption, NULL);

for(size_t j = 0; j < plain.size(); j++)
    encryptor.Put((byte)plain[j]);

encryptor.MessageEnd();
size_t ready = encryptor.MaxRetrievable();

string cipher(ready, 0x00);
encryptor.Get((byte*) &cipher[0], cipher.size());

Downloads

No downloads.