Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for hash chaining to detect modifications in postings #2300

Open
wants to merge 30 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f4c56d1
Add support for hash chaining to detect modifications in postings
jwiegley Nov 23, 2023
0ddea87
--hashes option requires an argument to specify the algorithm
jwiegley Nov 23, 2023
e2e4716
Add positive and negative tests for the --hashes option
jwiegley Nov 23, 2023
853374b
Add documentation for the --hashes option
jwiegley Nov 23, 2023
55287a0
Make xact hashes independent of posting order
jwiegley Nov 25, 2023
8f2d712
Improvements to the hashing tests
jwiegley Nov 27, 2023
0c808e0
Add further documentation on the --hashes option
jwiegley Nov 28, 2023
28c10eb
Add support for --hashes=sha512_256 as another algorithm
jwiegley Nov 30, 2023
d8c341d
Update doc/ledger.1
jwiegley Dec 6, 2023
d4eff3d
Update doc/ledger.1
jwiegley Dec 6, 2023
845ccb5
Update src/sha512.cc
jwiegley Dec 6, 2023
ce5664e
Update src/sha512.cc
jwiegley Dec 11, 2023
b5161c9
Minor doc update
jwiegley Dec 11, 2023
c729035
Type signature fix
jwiegley Dec 12, 2023
0f723d2
Revert a type change
jwiegley Dec 12, 2023
43a173d
Try something else
jwiegley Dec 12, 2023
8b346cf
Fix return type of SHA512
jwiegley Dec 12, 2023
82f98d7
Include stdint.h in sha512.cc
jwiegley Dec 12, 2023
c5fa5fa
Include sys/types.h
jwiegley Dec 12, 2023
2499963
Remove most changes to sha512.cc
jwiegley Dec 12, 2023
bea3498
Change one prototype
jwiegley Dec 12, 2023
8d43d4c
Revert all changes to sha512.c
jwiegley Dec 12, 2023
4514248
Add two missing system headers to sha512.cc
jwiegley Dec 12, 2023
f24640b
Rename SHA-512/256 to the more appropriate SHA-512Half
jwiegley Dec 13, 2023
cf0fadf
Add whitespace to xact_t::hash
jwiegley Dec 13, 2023
60010af
Another whitespace change
jwiegley Dec 13, 2023
2f732b9
Merge remote-tracking branch 'origin/master' into johnw/hashes
jwiegley Jan 4, 2024
a098d7f
Expand the size of an arbitrary safety limit
jwiegley Jan 18, 2024
baddd0e
Change an assertion into an if test
jwiegley Jan 18, 2024
33a70ca
Merge remote-tracking branch 'origin/master' into johnw/hashes
jwiegley Jan 31, 2024
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
12 changes: 9 additions & 3 deletions doc/ledger3.texi
Original file line number Diff line number Diff line change
Expand Up @@ -6702,9 +6702,15 @@ Record the hash of each transaction in a @var{Hash} metadata value,
according to the hashing algorithm given by the @var{ALGO}
argument. Note that if a @var{Hash} metadata value is explicitly
provided and does not match what would have been generated, an error is
reported. Hashes depend on previous entries, such that setting a single
hash value is sufficient to guarantee the shape of the entire history
leading up to that entry.
reported. This provided value can also just be a prefix, at whatever
length the user wishes, in which case only this amount of text is
compared to the generated hash. Printing the entry with hashes enabled
jwiegley marked this conversation as resolved.
Show resolved Hide resolved
will display both this explicit metadata setting and the internally
generated hash value.

Hashes depend on previous entries, such that setting a single hash value
is sufficient to guarantee the shape of the entire history leading up to
that entry.

jwiegley marked this conversation as resolved.
Show resolved Hide resolved
@item --head @var{INT}
@itemx --first @var{INT}
Expand Down
6 changes: 5 additions & 1 deletion src/item.cc
Original file line number Diff line number Diff line change
Expand Up @@ -133,13 +133,17 @@ item_t::set_tag(const string& tag,

string_map::iterator i = metadata->find(tag);
if (i == metadata->end()) {
DEBUG("item.meta", "Setting new metadata value");
std::pair<string_map::iterator, bool> result
= metadata->insert(string_map::value_type(tag, tag_data_t(data, false)));
assert(result.second);
return result.first;
} else {
if (overwrite_existing)
DEBUG("item.meta", "Found old metadata value");
if (overwrite_existing) {
DEBUG("item.meta", "Overwriting old metadata value");
(*i).second = tag_data_t(data, false);
}
return i;
}
}
Expand Down
4 changes: 0 additions & 4 deletions src/item.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,10 +199,6 @@ class item_t : public flags::supports_flags<uint_least16_t>, public scope_t
return _state;
}

virtual string hash(string _nonce) const {
return "";
}

virtual void define(const symbol_t::kind_t, const string&,
expr_t::ptr_op_t);
virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind,
Expand Down
24 changes: 0 additions & 24 deletions src/post.cc
Original file line number Diff line number Diff line change
Expand Up @@ -670,30 +670,6 @@ void post_t::set_reported_account(account_t * acct)
acct->xdata().reported_posts.push_back(this);
}

extern "C" unsigned char *SHA512(
void *data, unsigned int data_len, unsigned char *digest);

namespace {
std::string bufferToHex(const unsigned char* buffer, std::size_t size) {
std::ostringstream oss;
oss << std::hex << std::setfill('0');
for(std::size_t i = 0; i < size; ++i)
oss << std::setw(2) << static_cast<int>(buffer[i]);
return oss.str();
}
}

string post_t::hash(string nonce) const {
unsigned char data[128];
std::ostringstream repr;
repr << nonce;
repr << account->fullname();
repr << amount.to_string();
string repr_str(repr.str());
SHA512((void *)repr_str.c_str(), repr_str.length(), data);
return bufferToHex(data, 64 /*SHA512_DIGEST_LENGTH*/);
}

void extend_post(post_t& post, journal_t& journal)
{
commodity_t& comm(post.amount.commodity());
Expand Down
2 changes: 0 additions & 2 deletions src/post.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,6 @@ class post_t : public item_t
return const_cast<post_t *>(this)->reported_account();
}

virtual string hash(string nonce) const;

friend class xact_t;

struct compare_by_date_and_sequence
Expand Down
6 changes: 4 additions & 2 deletions src/textual.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2027,10 +2027,12 @@ xact_t * instance_t::parse_xact(char * line,
previous_xact->get_tag("Hash")->to_string() : "");
if (xact->has_tag("Hash")) {
string current_hash = xact->get_tag("Hash")->to_string();
if (expected_hash != current_hash) {
if (! std::equal(expected_hash.begin(),
expected_hash.begin() +
std::min(expected_hash.size(), current_hash.size()),
current_hash.begin()))
throw_(parse_error, _f("Expected hash %1% != %2%") %
expected_hash % current_hash);
}
} else {
xact->set_tag("Hash", string_value(expected_hash));
}
Expand Down
34 changes: 31 additions & 3 deletions src/xact.cc
Original file line number Diff line number Diff line change
Expand Up @@ -581,19 +581,47 @@ bool xact_t::valid() const
return true;
}

extern "C" unsigned char *SHA512(
void *data, unsigned int data_len, unsigned char *digest);

namespace {
std::string bufferToHex(const unsigned char* buffer, std::size_t size) {
std::ostringstream oss;
oss << std::hex << std::setfill('0');
for(std::size_t i = 0; i < size; ++i)
oss << std::setw(2) << static_cast<int>(buffer[i]);
return oss.str();
}
}

string xact_t::hash(string nonce) const {
std::ostringstream repr;
repr << nonce;
repr << date();
repr << aux_date();
repr << code;
repr << payee;
string hash(repr.str());
posts_list all_posts(posts.begin(), posts.end());
std::vector<std::string> strings;
foreach (post_t * post, all_posts) {
hash = post->hash(hash);
std::ostringstream posting;
posting << post->account->fullname();
if (! post->amount.is_null())
posting << post->amount.to_fullstring();
if (post->cost)
posting << post->cost->to_fullstring();
posting << post->checkin;
posting << post->checkout;
strings.push_back(posting.str());
}
std::sort(strings.begin(), strings.end());
foreach (string& str, strings) {
repr << str;
}
return hash;
unsigned char data[128];
string repr_str(repr.str());
SHA512((void *)repr_str.c_str(), repr_str.length(), data);
return bufferToHex(data, 64 /*SHA512_DIGEST_LENGTH*/);
}

namespace {
Expand Down
2 changes: 1 addition & 1 deletion src/xact.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ class xact_t : public xact_base_t

virtual bool valid() const;

virtual string hash(string nonce) const;
string hash(string nonce) const;
};

class auto_xact_t : public xact_base_t
Expand Down
Loading