Following comments from @koraa :
- fix endianess issue, by using canonical representation
- all segments are derived from first one, in order to reduce dependency chain
- all derived segments use a seed, which is a combination raw custom seed content and segNb
- updated documentation

This variant breaks the possibility for an actor
to derive the entire secret from the knowledge of one of its segments
since it requires the seed, which is derived from original custom seed,
which is not present anywhere, condensed in the scrambling operation.
This commit is contained in:
Yann Collet 2020-06-05 16:46:55 -07:00
parent 58922a773b
commit c546d08ff3
2 changed files with 44 additions and 9 deletions

23
xxh3.h
View File

@ -2036,14 +2036,31 @@ XXH3_generateSecret(void* secretBuffer, const void* customSeed, size_t customSee
{ size_t const segmentSize = sizeof(XXH128_hash_t);
size_t const nbSegments = XXH_SECRET_DEFAULT_SIZE / segmentSize;
XXH128_hash_t scrambler = XXH128(customSeed, customSeedSize, 0);
XXH128_canonical_t scrambler;
XXH64_hash_t seeds[12];
size_t segnb;
XXH_ASSERT(nbSegments == 12);
XXH_ASSERT(segmentSize * nbSegments == XXH_SECRET_DEFAULT_SIZE); /* exact multiple */
XXH128_canonicalFromHash(&scrambler, XXH128(customSeed, customSeedSize, 0));
/* prepare seeds */
{ size_t toFill = (customSeedSize > sizeof(seeds)) ? sizeof(seeds) : customSeedSize;
size_t filled = toFill;
memcpy(secretBuffer, customSeed, toFill);
while (filled < sizeof(seeds)) {
toFill = (filled > sizeof(seeds) - filled) ? sizeof(seeds) - filled : filled;
memcpy((char*)secretBuffer + filled, secretBuffer, toFill);
filled += toFill;
} }
/* generate secret */
memcpy(secretBuffer, &scrambler, sizeof(scrambler));
for (segnb=1; segnb < nbSegments; segnb++) {
size_t const segmentStart = segnb * segmentSize;
scrambler = XXH128(&scrambler, sizeof(scrambler), 0);
memcpy((char*)secretBuffer + segmentStart, &scrambler, sizeof(scrambler));
XXH128_canonical_t segment;
XXH128_canonicalFromHash(&segment,
XXH128(&scrambler, sizeof(scrambler), XXH_readLE64(seeds + segnb) + segnb) );
memcpy((char*)secretBuffer + segmentStart, &segment, sizeof(segment));
} }
}

View File

@ -367,7 +367,7 @@ XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void*
XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* statePtr);
/******* Canonical representation *******/
typedef struct { unsigned char digest[8]; } XXH64_canonical_t;
typedef struct { unsigned char digest[sizeof(XXH64_hash_t)]; } XXH64_canonical_t;
XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash);
XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src);
@ -534,12 +534,30 @@ XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSecret(const void* data, size_t len,
/*
* XXH3_generateSecret():
*
* Derive a secret for use with `*_withSecret()` prototypes of XXH3.
* Use this if you need a higher level of security than the one provided by 64bit seed.
*
* Take as input a custom seed of any length and any content,
* generate from it a high-quality secret of length XXH3_SECRET_DEFAULT_SIZE
* generate from it a high-entropy secret of length XXH3_SECRET_DEFAULT_SIZE
* into already allocated buffer secretBuffer.
* The generated secret ALWAYS is XXH_SECRET_DEFAULT_SIZE bytes long.
*
* The generated secret can then be used with any `*_withSecret()` variant.
* customSeed can be anything, even a "low entropy" source, such as a bunch of zeroes.
* It can also have any size, even < XXH3_SECRET_SIZE_MIN.
* The functions `XXH3_128bits_withSecret()`, `XXH3_64bits_withSecret()`,
* `XXH3_128bits_reset_withSecret()` and `XXH3_64bits_reset_withSecret()`
* are part of this list. They all accept a `secret` parameter
* which must be very long for implementation reasons (>= XXH3_SECRET_SIZE_MIN)
* _and_ feature very high entropy (consist of random-looking bytes).
* These conditions can be a high bar to meet, so
* this function can be used to generate a secret of proper quality.
*
* customSeed can be anything. It can have any size, even small ones,
* and its content can be anything, even some "low entropy" source such as a bunch of zeroes.
* The resulting `secret` will nonetheless respect all expected qualities.
*
* Supplying NULL as the customSeed copies the default secret into `secretBuffer`.
* When customSeedSize > 0, supplying NULL as customSeed is undefined behavior.
*/
#define XXH3_SECRET_DEFAULT_SIZE 192
XXH_PUBLIC_API void XXH3_generateSecret(void* secretBuffer, const void* customSeed, size_t customSeedSize);
@ -676,14 +694,14 @@ XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2);
* This comparator is compatible with stdlib's `qsort()`/`bsearch()`.
*
* return: >0 if *h128_1 > *h128_2
* <0 if *h128_1 < *h128_2
* =0 if *h128_1 == *h128_2
* <0 if *h128_1 < *h128_2
*/
XXH_PUBLIC_API int XXH128_cmp(const void* h128_1, const void* h128_2);
/******* Canonical representation *******/
typedef struct { unsigned char digest[16]; } XXH128_canonical_t;
typedef struct { unsigned char digest[sizeof(XXH128_hash_t)]; } XXH128_canonical_t;
XXH_PUBLIC_API void XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash);
XXH_PUBLIC_API XXH128_hash_t XXH128_hashFromCanonical(const XXH128_canonical_t* src);