SipHash

From Crypto++ Wiki
Jump to navigation Jump to search
SipHash
Documentation
#include <cryptopp/siphash.h>

SipHash is a Message Authentication Code (MAC) created by Jean-Philippe Aumasson and Daniel J. Bernstein. SipHash uses a 128-bit key, and a variable number of compression rounds and finalization rounds. Also see SipHash: a fast short-input PRF

SipHash was designed to quickly produce keys for hash tables or hash_maps. The algorithm is unique in that it is not intended as a non-keyed hash. Rather, its primary application is MACs over short messages. Also see Hash-flooding DoS reloaded: attacks and defenses.

The message authentication code is a lightweight add–rotate–xor (ARX) design intended to quickly produce a keyed hash over the message. ARX designs are becoming more popular because they are fast and easy to analyze. The design is being used in block ciphers like SIMON and SPECK.

Template Parameters

SipHash needs three template parameters. The first parameter is C, which is the number of compression rounds. The second parameter is D, which is the number of finalization rounds. The third parameter selects between 64-bit and 128-bit MACs. The same round function is used during compression and finalization, but the compression function and finalization function are different.

Typically you use the following combinations. However, you are free to use any combination of template parameters.

SipHash Parameters
C D 128-bit
2 4 false
4 8 true

Construction

SipHash needs three template parameters. Typically you use either {C=2, D=4, 128-bit=false} or {C=4, D=8, 128-bit=true}, which results in the following constructors. However, you are free to use any combination of template parameters.

SipHash<2, 4, false>(const byte* key, size_t length);

SipHash<4, 8, true>(const byte* key, size_t length);

SipHash<C, D, T_128bit>(const byte* key, size_t length);

C is the number of compression rounds.

D is the number of finalization rounds.

T_128bit selects between 64-bit and 128-bit tags.

key is a byte array used to key the cipher.

length the size of the byte array, in bytes

Sample Programs

The sample programs below demonstrate using filters in a pipeline and C-style input/output using Update, Final and Verify on using HashTransformation base class.

Key and Digest Sizes

You can determine key sizes and digest size using the following program.

using namespace CryptoPP;
typedef SipHash<4, 8, true> SipHashMac;
    
std::cout << "key length: " << SipHashMac::DEFAULT_KEYLENGTH << std::endl;
std::cout << "key length (min): " << SipHashMac::MIN_KEYLENGTH << std::endl;
std::cout << "key length (max): " << SipHashMac::MAX_KEYLENGTH << std::endl;
std::cout << "digest size: " << SipHashMac::DIGESTSIZE << std::endl;

The 128-bit SipHash produces the following output.

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

The 64-bit SipHash would produce digest size: 8.

Pipeline and Filters

The sample program below demonstrates a SipHash using filters. The key is declared on the stack and a SecByteBlock is used to ensure the sensitive material is zeroized. Similar could be used for the message and MAC if desired.

#include "cryptlib.h"
#include "secblock.h"
#include "osrng.h"
#include "files.h"
#include "hex.h"

#include "siphash.h"

#include <iostream>
#include <string>

int main(int argc, char* argv[])
{
    using namespace CryptoPP;

    AutoSeededRandomPool prng;
    HexEncoder encoder(new FileSink(std::cout));

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

    std::string plain = "SipHash Test";
    std::string digest;

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

    try
    {
        SipHash<4, 8, true> mac(key.data(), key.size());

        StringSource ss(plain, true, 
            new HashFilter(mac,
                new StringSink(digest)
            ) // HashFilter      
        ); // StringSource
    }
    catch(const Exception& e)
    {
        std::cerr << e.what() << std::endl;
        exit(1);
    }

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

    // Pretty print
    std::cout << "key: ";
    encoder.Put(key, key.size());
    encoder.MessageEnd();
    std::cout << std::endl;

    std::cout << "message: ";
    std::cout << plain;
    std::cout << std::endl;

    std::cout << "mac: ";
    encoder.Put((const byte*)&digest[0], digest.size());
    encoder.MessageEnd();
    std::cout << std::endl;

    return 0;
}

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

$ ./test.exe 
key: A70EE7965EB1FFDE5195C5C8F44873A2
message: SipHash Test
mac: CA3D1138958F9D4FD122A4BE1CBA2419

To verify a SipHash on a message, use a HashVerificationFilter.

try
{
    SipHash<4, 8, true> mac(key.data(), key.size());
    const int flags = HashVerificationFilter::THROW_EXCEPTION | HashVerificationFilter::HASH_AT_END;
    
    StringSource ss(plain + mac, true, 
        new HashVerificationFilter(mac, NULL, flags)
    ); // StringSource

    cout << "Verified message" << endl;
}
catch(const CryptoPP::Exception& e)
{
    cerr << e.what() << endl;
    ...
}

We can tamper with a message as follows, which will cause the HashVerificationFilter to throw the exception, HashVerificationFilter: message hash or MAC not valid:

SipHash<4, 8, true> mac(key.data(), key.size());

// Tamper with message
plain[0] ^= 0x01;

StringSource ss(plain + mac, true, 
    new HashVerificationFilter(mac, NULL, THROW_EXCEPTION | HASH_AT_END)
); // StringSource

HashTransformation

The sample program below demonstrates a SipHash using C-style input/output with Update, Final and Verify from the HashTransformation base class.

Under the hood, the Pipeline and Filter example does this for you. The HashFilter knows to call Update and Final, while the HashVerificationFilter knows to call Update and Verify.

#include "cryptlib.h"
#include "secblock.h"
#include "osrng.h"
#include "files.h"
#include "hex.h"

#include "siphash.h"

#include <iostream>
#include <string>

int main(int argc, char* argv[])
{
    using namespace CryptoPP;
    AutoSeededRandomPool prng;

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

    std::string digest, plain = "SipHash Test";
    HexEncoder encoder(new FileSink(std::cout));

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

    try
    {
        SipHash<4, 8, true> mac(key.data(), key.size());
        mac.Update((const byte*)&plain[0], plain.size());

        digest.resize(mac.DigestSize());
        mac.Final((byte*)&digest[0]);
    }
    catch(const CryptoPP::Exception& e)
    {
        std::cerr << e.what() << std::endl;
        exit(1);
    }

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

    // Pretty print
    std::cout << "key: ";
    encoder.Put(key, key.size());
    encoder.MessageEnd();
    std::cout << std::endl;

    std::cout << "message: ";
    std::cout << plain;
    std::cout << std::endl;

    std::cout << "mac: ";
    encoder.Put((const byte*)&digest[0], digest.size());
    encoder.MessageEnd();
    std::cout << std::endl;

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

    // Verify
    try
    {
        SipHash<4, 8, true> mac(key.data(), key.size());
        mac.Update((const byte*)&plain[0], plain.size());

        // Call Verify() instead of Final()
        bool verified = mac.Verify((const byte*)&digest[0]);
        if (!verified)
            throw Exception(Exception::DATA_INTEGRITY_CHECK_FAILED, "SipHash: message MAC not valid");

        std::cout << "Verified message MAC" << std::endl;
    }
    catch(const CryptoPP::Exception& e)
    {
        exit(1);
    }

    return 0;
}

Typical output of the program is:

$ ./test.exe 
key: 9195E1828E2B7667B565FFEE1879AAAD
message: SipHash Test
mac: D26633DD108A0D85E1E3CF12253480B0
Verified message MAC

Downloads

No downloads available.