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.
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.