Advanced EOS Series — Part 1 — Cryptographic Hashes
Welcome to the Advanced EOS development series where I’ll be touching on techniques and functionality which is rarely covered by tutorials and courses. The purpose of this series is to bring together the missing pieces you’ll need to complete your skills as an application developer on the EOS network. Each post is ordered by difficulty, so if you’d like a general overview I’d recommend starting right here and working your way up.
As these are advanced or extended topics, I’m dangerously assuming you already know the basics and are looking to further your knowledge. For that reason, the code shared in these articles will be concise to the topic being discussed.
The Cryptographic Hash Function
Let’s begin by looking at hashes, all resources for this example can be found here on GitHub. In cryptography, a hashing algorithm takes an input and generates a fixed sized jumble known as the output digest. The digest is deterministic and irreversible, meaning the same input will always generate the same output and the output can’t be reversed to obtain the original input. A good hashing algorithm minimizes output collision by using a ‘high degree of difficulty’ in the shortest amount of computation time. Collision or clashing being the likelihood of two different inputs producing the same output.
Application of Hashes
Typically we will use a hash function for authentication and indexing data, I like to think of it as a method of compressing uniqueness. For example, comparing two arbitrarily sized files or users emails would be time intensive and expose vulnerable user data. We could instead generate a fingerprint by hashing the files contents or users email and more efficiently index and compare the data using the hash result.
This is as about as much as we need to know at this point, but if you’d like to itch your curiosity then I’d encourage some proactive Googling or take a look at Blockgeeks article on The In’s and Outs of Cryptographic Hash Functions.
Coding Our Hash Function
Now that we have a very basic understanding of hashes, we can look into how to generate our very own within our EOS smart contract. We’re going to create an action which takes an input string
and outputs a checksum256
hash digest, then for the purpose of this example, print the result.
Let’s start by including the crypto.h library and the EOS print.h wrapper from the EOS framework.
include <eosiolib/crypto.h>
Now we can define our action and allocate a reference for our checksum256
output like so.
checksum256 sum{};
The EOS framework provides a set of methods for various hashing algorithms. We’ll use the sha256 for this example because it’s fast, secure and typical used in most cases.
sha256(const_cast<char*>(str.c_str()), str.size(), &sum);
Our sha256 method expects C type inputs, so we first need to convert our string to a char set, then pass the size of the string and a checksum256
reference which will be updated with the output digest. Now all that remains is printing the hashed output result.
printhex(&sum, sizeof(sum));
We could also return the checksum256
here and use it as a private internal action.
Taking it Further
Checksum to String
Credit for this implementation goes to Miguel Mota for developing a solution to convert checksum256 too a hexadecimal string.
So what happens if we want to convert our digest back into a string
? For this we will need a new method. To begin with, let’s define a template for our input argument and define the method itself. This template is defined outside of our method definition, typically at the top of our file. Templates simply allow us to define the variable type at the time we call the function, then the compiler will take care of substitution and making it all work.
template <typename CharT>
string to_hex(const CharT* data, uint32_t length) {
// Method Body
}
Now we can start working on the body of our method. To start with we will create a string for our result, a char
set to draw characters from later, and force our data to a uint8_t
.
string result;
const char* hex_chars = "0123456789abcdef";
uint8_t* c = (uint8_t*)data;
Finally we will iterate over each char in our input data, match the corresponding hexadecimal character, and append it to our resulting string.
for (uint32_t i = 0; i < length; ++i) {
(result += hex_chars[(c[i] >> 4)]) += hex_chars[(c[i] & 0x0f)];
}
return result;
And that’s it! We now have a method to create a checksum256
from a char
set, and a method to convert that checksum256
into a string
. In the next article we will be looking at singletons and how we can utilize them to store application state and configuration.