Skip to content
This repository was archived by the owner on Feb 16, 2025. It is now read-only.

highlevel: detect if spec properly mounted and unmodified #4047

Merged
merged 63 commits into from
Sep 28, 2021
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
a4be8d4
highlevel: Improve documentation
Sep 8, 2021
7d804b6
highlevel: Implement check for proper mount of specification file
Sep 8, 2021
4c8b61d
highlevel: Refactor specProperlyMounted(), implement check for proper…
Sep 8, 2021
4813a72
highlevel: Add documentation for specProperlyMounted()
Sep 8, 2021
44a2476
highlevel: Fix wrong resource management
Sep 8, 2021
96360f4
highlevel: Change name of specProperlyMounted() to checkSpecProperlyM…
Sep 8, 2021
b2140ef
highlevel: Improve documentation
Sep 8, 2021
e461630
highlevel: Add sanity check to elektraClose()
Sep 8, 2021
e602f93
highlevel: Reformat C code using `scripts/dev/reformat-c`
Sep 8, 2021
9263f3a
Add license for asmonier's sha-2
Sep 15, 2021
41296f9
Add source of amosnier's sha-2
Sep 15, 2021
8f22f07
Add commit hash to sha-256.c source files
Sep 15, 2021
b39ce1a
Add function to compute sha256 hash of a key set
Sep 15, 2021
4d3003d
Include meta keys in sha 256 calculation
Sep 15, 2021
d64dfd9
Configure builds to include computeSha256OfKeySetWithoutValues
Sep 15, 2021
38b87a2
Change name of function to computeSha256OfKeySetWithoutValues to be m…
Sep 15, 2021
5220d9d
Fix wrong call order of hash_to_string() and sha_256_close()
Sep 15, 2021
b27740b
Rename variable to easier understand code
Sep 15, 2021
86508d5
Improve token calculation so it works with cascading lookups.
Sep 15, 2021
2343a09
Add inline documentation to gen.cpp
Sep 16, 2021
bffe90d
Fix wrong variable name, addendum to 24fd8e24d248cde8b104f629fbca27c9…
Sep 16, 2021
ba142c2
Fix calculation of specification key.
Sep 16, 2021
a3e33d5
Fix order of declarations in header
Sep 17, 2021
a7d72b3
Add tests for sha-256
Sep 17, 2021
5649bce
Duplicate param ks in calculateSpecificationToken() before ksCut, so …
Sep 17, 2021
e5ae659
Change calculateSpecificationToken() to not take NULL terminator into…
Sep 17, 2021
caf94f2
Add tests for hash.c
Sep 17, 2021
4bcb5aa
Add error handling to hash.c
Sep 17, 2021
d45de58
Implement checking spec token in HL API and modularize elektraOpen().
Sep 17, 2021
0e3775e
Revert type of param that was accidentally changed in a previous commit.
Sep 17, 2021
9185107
Remove "minimal validation" in favor of the new checkSpecProperlyMoun…
Sep 17, 2021
222b62f
Add missing checks for NULL to prevent segfault
Sep 17, 2021
495fcff
Implement token calculation in kdb gen highlevel
Sep 17, 2021
282691a
Update contract in the generated C sources for kdb gen tests
Sep 17, 2021
acdbc54
Replace ksRewind()/ksNext() by ksAtCursor()
Sep 17, 2021
80c8287
Improve checkSpecProperlyMounted and checkSpecToken:
Sep 18, 2021
1e78456
Remove obsolete function specMountExecuted()
Sep 18, 2021
9d824a6
Change specification checks to not use contract passed from applicati…
Sep 18, 2021
04103b8
Improve usability of checkSpecificationMountPoint()'s error messages
Sep 18, 2021
9150eee
Fix token calculation: Set parentKey namespace already for kdbGet() i…
Sep 21, 2021
fb74162
Ignore array parents in token calculation
Sep 21, 2021
82a21ad
Remove debug print statements
Sep 21, 2021
436c7f7
Update token in *.expected.c files of highlevel code generator tests.
Sep 22, 2021
018162c
Execute scripts/dev/reformat-all
Sep 22, 2021
93e93d7
Add details about how to fix errors relating to specification mountin…
Sep 22, 2021
e3217ea
Add note to function documentation about ignored array parent keys.
Sep 22, 2021
c9e0717
Improve usability of error messages for problems with specification.
Sep 22, 2021
9a2e47d
Add release notes
Sep 22, 2021
b8af270
Merge branch 'master' into 3998-detect-spec-mounted
Sep 22, 2021
dbd1f3d
Reformat .c and .md files
Sep 22, 2021
55b9044
Update kdb gen and elektra highlevel documentation to reflect changes…
Sep 22, 2021
e0fa19b
Apply patch to man pages.
Sep 22, 2021
5492b76
Improve key hierarchy of spec/token
Sep 22, 2021
3487313
Improve key hierarchy of spec/mounted
Sep 22, 2021
a0f0bba
Change error message to only advise users to kdb rm -r the spec names…
Sep 22, 2021
85a977f
Remove unused data argument
Sep 22, 2021
8257864
Merge branch 'master' into 3998-detect-spec-mounted
Sep 23, 2021
103474a
Update contract key names ".../spec/token" and ".../spec/mounted" in …
Sep 23, 2021
39f9853
Execute reformat-all script
Sep 24, 2021
bcfe59b
Fix hash.c and highlevel.cpp so that calculateSpecificationToken() do…
Sep 27, 2021
af52c71
Update token value in *.expected.c files to match that now the namesp…
Sep 27, 2021
4150c73
Merge branch 'qwepoizt-master' into 3998-detect-spec-mounted
Sep 27, 2021
39230d5
Simplify detection of array parents
Sep 27, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions doc/THIRD-PARTY-LICENSES
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,25 @@ Files: src/tools/kdb/gen/mustache.hpp
Copyright: 2015-2018 Kevin Wojniak
License: BSL-1.0

Files: src/libs/ease/sha-256.c
src/libs/ease/sha-256.h
Copyright: 2021 Alain Mosnier
License: BSD-0-clause

Files: src/bindings/jna/gradlew
src/bindings/jna/gradlew.bat
src/bindings/jna/gradle/wrapper*
Copyright: 2015 the original author or authors
License: Apache-2.0

License: BSD-0-clause
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

License: BSD-3-clause
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Expand Down
2 changes: 2 additions & 0 deletions src/include/kdbease.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ char * elektraLongDoubleToString (kdb_long_double_t value);

#endif // ELEKTRA_HAVE_KDB_LONG_DOUBLE

kdb_boolean_t calculateSpecificationToken (char hash_string[65], KeySet * ks, Key * parentKey);

#ifdef __cplusplus
}
}
Expand Down
1 change: 0 additions & 1 deletion src/include/kdbprivate.h
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,6 @@ ElektraError * elektraErrorKeyNotFound (const char * keyname);
ElektraError * elektraErrorWrongType (const char * keyname, KDBType expectedType, KDBType actualType);
ElektraError * elektraErrorNullError (const char * function);
ElektraError * elektraErrorEnsureFailed (const char * reason);
ElektraError * elektraErrorMinimalValidationFailed (const char * function);

#ifdef __cplusplus
}
Expand Down
3 changes: 2 additions & 1 deletion src/libs/ease/CMakeLists.txt
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})
118 changes: 118 additions & 0 deletions src/libs/ease/hash.c
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) {
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);
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) {
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]);
}
}
231 changes: 231 additions & 0 deletions src/libs/ease/sha-256.c
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);
}

Loading