This repository was archived by the owner on Feb 16, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 122
highlevel: detect if spec properly mounted and unmodified #4047
Merged
mpranj
merged 63 commits into
ElektraInitiative:master
from
qwepoizt:3998-detect-spec-mounted
Sep 28, 2021
Merged
Changes from 34 commits
Commits
Show all changes
63 commits
Select commit
Hold shift + click to select a range
a4be8d4
highlevel: Improve documentation
7d804b6
highlevel: Implement check for proper mount of specification file
4c8b61d
highlevel: Refactor specProperlyMounted(), implement check for proper…
4813a72
highlevel: Add documentation for specProperlyMounted()
44a2476
highlevel: Fix wrong resource management
96360f4
highlevel: Change name of specProperlyMounted() to checkSpecProperlyM…
b2140ef
highlevel: Improve documentation
e461630
highlevel: Add sanity check to elektraClose()
e602f93
highlevel: Reformat C code using `scripts/dev/reformat-c`
9263f3a
Add license for asmonier's sha-2
41296f9
Add source of amosnier's sha-2
8f22f07
Add commit hash to sha-256.c source files
b39ce1a
Add function to compute sha256 hash of a key set
4d3003d
Include meta keys in sha 256 calculation
d64dfd9
Configure builds to include computeSha256OfKeySetWithoutValues
38b87a2
Change name of function to computeSha256OfKeySetWithoutValues to be m…
5220d9d
Fix wrong call order of hash_to_string() and sha_256_close()
b27740b
Rename variable to easier understand code
86508d5
Improve token calculation so it works with cascading lookups.
2343a09
Add inline documentation to gen.cpp
bffe90d
Fix wrong variable name, addendum to 24fd8e24d248cde8b104f629fbca27c9…
ba142c2
Fix calculation of specification key.
a3e33d5
Fix order of declarations in header
a7d72b3
Add tests for sha-256
5649bce
Duplicate param ks in calculateSpecificationToken() before ksCut, so …
e5ae659
Change calculateSpecificationToken() to not take NULL terminator into…
caf94f2
Add tests for hash.c
4bcb5aa
Add error handling to hash.c
d45de58
Implement checking spec token in HL API and modularize elektraOpen().
0e3775e
Revert type of param that was accidentally changed in a previous commit.
9185107
Remove "minimal validation" in favor of the new checkSpecProperlyMoun…
222b62f
Add missing checks for NULL to prevent segfault
495fcff
Implement token calculation in kdb gen highlevel
282691a
Update contract in the generated C sources for kdb gen tests
acdbc54
Replace ksRewind()/ksNext() by ksAtCursor()
80c8287
Improve checkSpecProperlyMounted and checkSpecToken:
1e78456
Remove obsolete function specMountExecuted()
9d824a6
Change specification checks to not use contract passed from applicati…
04103b8
Improve usability of checkSpecificationMountPoint()'s error messages
9150eee
Fix token calculation: Set parentKey namespace already for kdbGet() i…
fb74162
Ignore array parents in token calculation
82a21ad
Remove debug print statements
436c7f7
Update token in *.expected.c files of highlevel code generator tests.
018162c
Execute scripts/dev/reformat-all
93e93d7
Add details about how to fix errors relating to specification mountin…
e3217ea
Add note to function documentation about ignored array parent keys.
c9e0717
Improve usability of error messages for problems with specification.
9a2e47d
Add release notes
b8af270
Merge branch 'master' into 3998-detect-spec-mounted
dbd1f3d
Reformat .c and .md files
55b9044
Update kdb gen and elektra highlevel documentation to reflect changes…
e0fa19b
Apply patch to man pages.
5492b76
Improve key hierarchy of spec/token
3487313
Improve key hierarchy of spec/mounted
a0f0bba
Change error message to only advise users to kdb rm -r the spec names…
85a977f
Remove unused data argument
8257864
Merge branch 'master' into 3998-detect-spec-mounted
103474a
Update contract key names ".../spec/token" and ".../spec/mounted" in …
39f9853
Execute reformat-all script
bcfe59b
Fix hash.c and highlevel.cpp so that calculateSpecificationToken() do…
af52c71
Update token value in *.expected.c files to match that now the namesp…
4150c73
Merge branch 'qwepoizt-master' into 3998-detect-spec-mounted
39230d5
Simplify detection of array parents
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
file (GLOB SOURCES *.c) | ||
add_lib (ease SOURCES ${SOURCES} COMPONENT libelektra${SO_VERSION}) | ||
file (GLOB HEADERS *.h) | ||
add_lib (ease SOURCES ${SOURCES} ${HEADERS} COMPONENT libelektra${SO_VERSION}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
/** | ||
* @file | ||
* | ||
* @brief Provides functions to hash Elektra data structures. | ||
* | ||
* @copyright BSD License (see LICENSE.md or https://www.libelektra.org) | ||
*/ | ||
|
||
#include <kdb.h> | ||
#include <kdbease.h> | ||
#include <kdbtypes.h> | ||
#include <kdberrors.h> | ||
|
||
#include <string.h> | ||
#include <stdio.h> | ||
|
||
#include "sha-256.h" | ||
|
||
static void hash_to_string(char string[65], const uint8_t hash[32]); | ||
|
||
/** | ||
* Calculate a specification token for the KeySet of an application. | ||
* | ||
* The KeySet of an application is identified as all keys below the applications root key. | ||
* | ||
* @pre The parentKey must have the correct namespace. E.g. If only keys from the spec:/ should be considered for the token calculation, | ||
* pass a key with KEY_NS_SPEC. | ||
* | ||
* @param hash_string A string. After successful execution this will contain the hash as hex-string. | ||
* @param ks The KeySet for the application. | ||
* @param parentKey The Key below which all the relevant keys are. Keys that are not below @p parentKey are ignored. The key's namespace is important (see preconditions) | ||
* @retval false If an error occurred. | ||
* @retval true If the computation was successful. | ||
*/ | ||
kdb_boolean_t calculateSpecificationToken (char hash_string[65], KeySet * ks, Key * parentKey) { | ||
if(parentKey == NULL) { | ||
// Can't set error to parentKey when it is null. | ||
return false; | ||
} | ||
if(hash_string == NULL) { | ||
ELEKTRA_SET_INTERNAL_ERROR(parentKey, "Param hash_string was NULL"); | ||
return false; | ||
} | ||
if(ks == NULL) { | ||
ELEKTRA_SET_INTERNAL_ERROR(parentKey, "Param ks was NULL"); | ||
return false; | ||
} | ||
|
||
// Initialize sha_256 for streaming | ||
uint8_t hash[SIZE_OF_SHA_256_HASH]; | ||
struct Sha_256 sha_256; | ||
sha_256_init(&sha_256, hash); | ||
|
||
// Duplicate ks, then cut out parentKey and all keys below. These are the ones we take into account for token calculation. | ||
KeySet * dupKs = ksDup (ks); | ||
KeySet * cutKs = ksCut (dupKs, parentKey); | ||
|
||
/** | ||
* Loop through all keys relevant for token calculation. | ||
*/ | ||
Key * currentKey; | ||
ksRewind(cutKs); | ||
while ((currentKey = ksNext (cutKs)) != NULL) { | ||
Key * cascadingKey = keyDup (currentKey, KEY_CP_NAME); | ||
if(cascadingKey == NULL) { | ||
ELEKTRA_SET_INTERNAL_ERROR (parentKey, "keyDup() failed!"); | ||
return false; | ||
} | ||
/** | ||
* Key namespace is ignored for token calculation (via setting it to cascading). | ||
* Reason: Different callers of this function pass the keys for token calculation using different namespaces, | ||
* but the specification they pass is actually the same. | ||
* (e.g. tools/kdb/gen.cpp passes keys in cascading namespace while src/libs/highlevel/elektra.c passes keys in spec namespace). | ||
*/ | ||
int result = keySetNamespace (cascadingKey, KEY_NS_CASCADING); | ||
qwepoizt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if(result == -1) { | ||
ELEKTRA_SET_INTERNAL_ERROR (parentKey, "keySetNamespace() failed!"); | ||
return false; | ||
} | ||
// Feed key name into sha_256_write() without NULL terminator (hence -1). | ||
// This makes it easier to compare expected results with other sha256 tools. | ||
sha_256_write (&sha_256, keyName(cascadingKey), keyGetNameSize(cascadingKey) - 1); | ||
// Note: The value of the key itself is not relevant / part of specification. Only the key's name + its metadata! | ||
|
||
KeySet * currentMetaKeys = keyMeta(currentKey); | ||
Key * currentMetaKey; | ||
ksRewind(currentMetaKeys); | ||
// Feed name + values from meta keys into sha_256_write(). | ||
while ((currentMetaKey = ksNext (currentMetaKeys)) != NULL) { | ||
qwepoizt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
sha_256_write (&sha_256, keyName(currentMetaKey), keyGetNameSize (currentMetaKey) - 1 ); | ||
sha_256_write (&sha_256, keyString(currentMetaKey), keyGetValueSize(currentMetaKey) - 1); | ||
} | ||
|
||
keyDel(cascadingKey); | ||
} | ||
|
||
sha_256_close(&sha_256); | ||
hash_to_string(hash_string, hash); | ||
|
||
ksDel(dupKs); | ||
ksDel(cutKs); | ||
|
||
return true; | ||
} | ||
|
||
/** | ||
* Convert hash array to hex string | ||
* Copied from https://github.com/amosnier/sha-2/blob/f0d7baf076207b943649e68946049059f018c10b/test.c | ||
* @param string A string array of length 65 (64 characters + \0) | ||
* @param hash The input hash array | ||
*/ | ||
static void hash_to_string(char string[65], const uint8_t hash[32]) | ||
{ | ||
size_t i; | ||
for (i = 0; i < 32; i++) { | ||
string += sprintf(string, "%02x", hash[i]); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
/** Copied from https://github.com/amosnier/sha-2 @ f0d7baf076207b943649e68946049059f018c10b. | ||
* See https://github.com/ElektraInitiative/libelektra/issues/3998#issuecomment-918111622 */ | ||
|
||
|
||
#include "sha-256.h" | ||
|
||
#define TOTAL_LEN_LEN 8 | ||
|
||
/* | ||
* ABOUT bool: this file does not use bool in order to be as pre-C99 compatible as possible. | ||
*/ | ||
|
||
/* | ||
* Comments from pseudo-code at https://en.wikipedia.org/wiki/SHA-2 are reproduced here. | ||
* When useful for clarification, portions of the pseudo-code are reproduced here too. | ||
*/ | ||
|
||
/* | ||
* @brief Rotate a 32-bit value by a number of bits to the right. | ||
* @param value The value to be rotated. | ||
* @param count The number of bits to rotate by. | ||
* @return The rotated value. | ||
*/ | ||
static inline uint32_t right_rot(uint32_t value, unsigned int count) | ||
{ | ||
/* | ||
* Defined behaviour in standard C for all count where 0 < count < 32, which is what we need here. | ||
*/ | ||
return value >> count | value << (32 - count); | ||
} | ||
|
||
/* | ||
* @brief Update a hash value under calculation with a new chunk of data. | ||
* @param h Pointer to the first hash item, of a total of eight. | ||
* @param p Pointer to the chunk data, which has a standard length. | ||
* | ||
* @note This is the SHA-256 work horse. | ||
*/ | ||
static inline void consume_chunk(uint32_t *h, const uint8_t *p) | ||
{ | ||
unsigned i, j; | ||
uint32_t ah[8]; | ||
|
||
/* Initialize working variables to current hash value: */ | ||
for (i = 0; i < 8; i++) | ||
ah[i] = h[i]; | ||
|
||
/* | ||
* The w-array is really w[64], but since we only need 16 of them at a time, we save stack by | ||
* calculating 16 at a time. | ||
* | ||
* This optimization was not there initially and the rest of the comments about w[64] are kept in their | ||
* initial state. | ||
*/ | ||
|
||
/* | ||
* create a 64-entry message schedule array w[0..63] of 32-bit words (The initial values in w[0..63] | ||
* don't matter, so many implementations zero them here) copy chunk into first 16 words w[0..15] of the | ||
* message schedule array | ||
*/ | ||
uint32_t w[16]; | ||
|
||
/* Compression function main loop: */ | ||
for (i = 0; i < 4; i++) { | ||
for (j = 0; j < 16; j++) { | ||
if (i == 0) { | ||
w[j] = | ||
(uint32_t)p[0] << 24 | (uint32_t)p[1] << 16 | (uint32_t)p[2] << 8 | (uint32_t)p[3]; | ||
p += 4; | ||
} else { | ||
/* Extend the first 16 words into the remaining 48 words w[16..63] of the | ||
* message schedule array: */ | ||
const uint32_t s0 = right_rot(w[(j + 1) & 0xf], 7) ^ right_rot(w[(j + 1) & 0xf], 18) ^ | ||
(w[(j + 1) & 0xf] >> 3); | ||
const uint32_t s1 = right_rot(w[(j + 14) & 0xf], 17) ^ | ||
right_rot(w[(j + 14) & 0xf], 19) ^ (w[(j + 14) & 0xf] >> 10); | ||
w[j] = w[j] + s0 + w[(j + 9) & 0xf] + s1; | ||
} | ||
const uint32_t s1 = right_rot(ah[4], 6) ^ right_rot(ah[4], 11) ^ right_rot(ah[4], 25); | ||
const uint32_t ch = (ah[4] & ah[5]) ^ (~ah[4] & ah[6]); | ||
|
||
/* | ||
* Initialize array of round constants: | ||
* (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311): | ||
*/ | ||
static const uint32_t k[] = { | ||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, | ||
0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, | ||
0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, | ||
0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, | ||
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, | ||
0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, | ||
0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, | ||
0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, | ||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, | ||
0xc67178f2}; | ||
|
||
const uint32_t temp1 = ah[7] + s1 + ch + k[i << 4 | j] + w[j]; | ||
const uint32_t s0 = right_rot(ah[0], 2) ^ right_rot(ah[0], 13) ^ right_rot(ah[0], 22); | ||
const uint32_t maj = (ah[0] & ah[1]) ^ (ah[0] & ah[2]) ^ (ah[1] & ah[2]); | ||
const uint32_t temp2 = s0 + maj; | ||
|
||
ah[7] = ah[6]; | ||
ah[6] = ah[5]; | ||
ah[5] = ah[4]; | ||
ah[4] = ah[3] + temp1; | ||
ah[3] = ah[2]; | ||
ah[2] = ah[1]; | ||
ah[1] = ah[0]; | ||
ah[0] = temp1 + temp2; | ||
} | ||
} | ||
|
||
/* Add the compressed chunk to the current hash value: */ | ||
for (i = 0; i < 8; i++) | ||
h[i] += ah[i]; | ||
} | ||
|
||
/* | ||
* Public functions. See header file for documentation. | ||
*/ | ||
|
||
void sha_256_init(struct Sha_256 *sha_256, uint8_t hash[SIZE_OF_SHA_256_HASH]) | ||
{ | ||
sha_256->hash = hash; | ||
sha_256->chunk_pos = sha_256->chunk; | ||
sha_256->space_left = SIZE_OF_SHA_256_CHUNK; | ||
sha_256->total_len = 0; | ||
/* | ||
* Initialize hash values (first 32 bits of the fractional parts of the square roots of the first 8 primes | ||
* 2..19): | ||
*/ | ||
sha_256->h[0] = 0x6a09e667; | ||
sha_256->h[1] = 0xbb67ae85; | ||
sha_256->h[2] = 0x3c6ef372; | ||
sha_256->h[3] = 0xa54ff53a; | ||
sha_256->h[4] = 0x510e527f; | ||
sha_256->h[5] = 0x9b05688c; | ||
sha_256->h[6] = 0x1f83d9ab; | ||
sha_256->h[7] = 0x5be0cd19; | ||
} | ||
|
||
void sha_256_write(struct Sha_256 *sha_256, const void *data, size_t len) | ||
{ | ||
sha_256->total_len += len; | ||
|
||
const uint8_t *p = data; | ||
|
||
while (len > 0) { | ||
/* | ||
* If the input chunks have sizes that are multiples of the calculation chunk size, no copies are | ||
* necessary. We operate directly on the input data instead. | ||
*/ | ||
if (sha_256->space_left == SIZE_OF_SHA_256_CHUNK && len >= SIZE_OF_SHA_256_CHUNK) { | ||
consume_chunk(sha_256->h, p); | ||
len -= SIZE_OF_SHA_256_CHUNK; | ||
p += SIZE_OF_SHA_256_CHUNK; | ||
continue; | ||
} | ||
/* General case, no particular optimization. */ | ||
const size_t consumed_len = len < sha_256->space_left ? len : sha_256->space_left; | ||
memcpy(sha_256->chunk_pos, p, consumed_len); | ||
sha_256->space_left -= consumed_len; | ||
len -= consumed_len; | ||
p += consumed_len; | ||
if (sha_256->space_left == 0) { | ||
consume_chunk(sha_256->h, sha_256->chunk); | ||
sha_256->chunk_pos = sha_256->chunk; | ||
sha_256->space_left = SIZE_OF_SHA_256_CHUNK; | ||
} else { | ||
sha_256->chunk_pos += consumed_len; | ||
} | ||
} | ||
} | ||
|
||
uint8_t *sha_256_close(struct Sha_256 *sha_256) | ||
{ | ||
uint8_t *pos = sha_256->chunk_pos; | ||
size_t space_left = sha_256->space_left; | ||
uint32_t *const h = sha_256->h; | ||
|
||
/* | ||
* The current chunk cannot be full. Otherwise, it would already have be consumed. I.e. there is space left for | ||
* at least one byte. The next step in the calculation is to add a single one-bit to the data. | ||
*/ | ||
*pos++ = 0x80; | ||
--space_left; | ||
|
||
/* | ||
* Now, the last step is to add the total data length at the end of the last chunk, and zero padding before | ||
* that. But we do not necessarily have enough space left. If not, we pad the current chunk with zeroes, and add | ||
* an extra chunk at the end. | ||
*/ | ||
if (space_left < TOTAL_LEN_LEN) { | ||
memset(pos, 0x00, space_left); | ||
consume_chunk(h, sha_256->chunk); | ||
pos = sha_256->chunk; | ||
space_left = SIZE_OF_SHA_256_CHUNK; | ||
} | ||
const size_t left = space_left - TOTAL_LEN_LEN; | ||
memset(pos, 0x00, left); | ||
pos += left; | ||
size_t len = sha_256->total_len; | ||
pos[7] = (uint8_t)(len << 3); | ||
len >>= 5; | ||
int i; | ||
for (i = 6; i >= 0; --i) { | ||
pos[i] = (uint8_t)len; | ||
len >>= 8; | ||
} | ||
consume_chunk(h, sha_256->chunk); | ||
/* Produce the final hash value (big-endian): */ | ||
int j; | ||
uint8_t *const hash = sha_256->hash; | ||
for (i = 0, j = 0; i < 8; i++) { | ||
hash[j++] = (uint8_t)(h[i] >> 24); | ||
hash[j++] = (uint8_t)(h[i] >> 16); | ||
hash[j++] = (uint8_t)(h[i] >> 8); | ||
hash[j++] = (uint8_t)h[i]; | ||
} | ||
return sha_256->hash; | ||
} | ||
|
||
void calc_sha_256(uint8_t hash[SIZE_OF_SHA_256_HASH], const void *input, size_t len) | ||
{ | ||
struct Sha_256 sha_256; | ||
sha_256_init(&sha_256, hash); | ||
sha_256_write(&sha_256, input, len); | ||
(void)sha_256_close(&sha_256); | ||
} | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This uses the old kind of iteration, we should deprecate ksRewind/ksNext more clearly! @lawli3t can you please do that?
Iteration now works via
ksAtCursor
.We should also have an example how to iterate the whole KeySet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see https://github.com/ElektraInitiative/libelektra/pull/4047/files/91851070110fad6ab8ba633f6e9ca91b20957d12#r711209833