I'm looking for a very simple hash function to implement on a microcontroller. The microcontroller displays a 4 digit alphanumeric session id on a display. I want to give the hash function a count and get back a number, which would later be turned into a 4-digit alphanumeric number. Any suggestions for a lightweight hash function?
-
\$\begingroup\$ This might be better suited at Security SE. In the meantime, does it need to be cryptographically secure? \$\endgroup\$– PolynomialCommented Sep 4, 2013 at 15:46
-
\$\begingroup\$ Doesn't have to be cryptographically secure, just lightweight. \$\endgroup\$– alexbbCommented Sep 4, 2013 at 15:47
-
\$\begingroup\$ What is the range of input "count" values? Why not just use the input value itself...why bother hashing it? \$\endgroup\$– Joe HassCommented Sep 4, 2013 at 15:49
-
2\$\begingroup\$ How about CRC16/32? \$\endgroup\$– pjc50Commented Sep 4, 2013 at 15:49
-
\$\begingroup\$ The count value is a number that I'd like to keep private. Nothing bad will happen if it gets discovered, but its just from a usability perspective I don't want it to be shown. About CRC, I never though about it, it seems like a good option. \$\endgroup\$– alexbbCommented Sep 4, 2013 at 15:54
5 Answers
How secure do you want this thing to be, and what are your code size versus space trade-offs? Are you going to be needing sequential numbers only, or values in arbitrary sequence?
Two simple hash-generation approaches are: // Get a random bit via linear congruential generator: int random_bit1(void) { static unsigned long seed; seed = seed*magicConstant + 12345; return (seed >> 31); // Use upper bit--not lower bit! }
int random_bit2(void)
{
// Get a 'random' bit via linear feedback shift register
unsigned long shifter;
if (!shifter)
shifter = 1;
else if (shifter & 1)
shifter = (shifter>>1);
else
shifter = (shifter>>1);
return (shifter & 1);
}
To generate a four digit number, use something like the following:
int result = 0;
int i;
for (i=0; i<30; i++) // The higher the count, the less biased the results
{
result *= 2;
if (result >= 10000)
result -= 10000;
result += randomBit();
}
Note that both of the above bit-generation algorithms can be adapted to return the nth value for any arbitrary n in a reasonable amount of time, though the code to do that will be a fair bit more complicated. On the other hand, neither algorithm will be hard to reverse-engineer.
If you're only going to need values in sequence (meaning that after you've outputted the nth thing, you'll never need to output any lower-numbered thing, and you won't mind if computing a higher-numbered thing requires computing all intervening values) it is possible to build up some security by using six or so independent pseudo-random-bit generators (perhaps shift registers with different periods), and then use something like:
int reallyrandombit(void)
{
if (randombit1())
{
if (randombit2())
return randombit3();
else
return randombit4();
}
else
{
if (randombit3())
return randombit5();
else
return randombit6();
}
}
Note that for good results there should be some mixing of how random generators get used (note that random3 is used in two different places) but one should avoid feedback paths (none of the random generators affects the shifting of anything which would affect it) unless one has the tools to fully analyze them.
CRC32 would do the job if you don't need cryptographic security. It gives you a 32-bit output value, and only requires simple operations. Plenty of implementations out there for most major micro families, too. Split down to 4 bytes and do modulus on a 32-character alphabet (1-9 A-Z, skipping a few chars like I and J) for each, and there's your code.
-
\$\begingroup\$ I'll second this - CRC32 is a good choice. It has excellent non-collision properties and it you can spare the memory for a 256 bytes LUT, if can be implemented in couple of shifts, and XOR and a table lookup for each input byte. \$\endgroup\$– MarkoCommented Sep 5, 2013 at 9:02
-
\$\begingroup\$ CRC's error-detection properties only kick in when the hamming distance is low enough. So it's not optimal for general hashing (there are faster alternatives with better pseudorandomness). But if OP's messages are small enough, CRC-16 might be ideal for his dinky 4-digit display. \$\endgroup\$– brycCommented Mar 18, 2019 at 16:11
You might be interested in this Crypto library. Its released under the GPL v3.
Crypto-avr-lib is a set of implementations of different cryptographic primitives. Due to the special limitations of microcontrollers (very little space, RAM and flash are ranging from a few bytes to a few KiB) reference or "normal" optimised implementations are not usable. Therefore we try to provide special implementations which respect the extreme limited resources of microcontroller applications.
There are several hash algorithms available:
- Blake
- BlueMidnightWish
- Grøstl
- MD5
- SHA-256
- SHA-1
- SHA-3 (Keccak)
- SHABAL
- Skein
- Twister
- Whirlpool
You can access the full code through their Subversion repository at http://das-labor.org/svn/microcontroller-2/crypto-lib.
-
\$\begingroup\$ Way overkill. OP has a 4-digit display. And those crypto hashes are significantly slower than even a MurmurHash3 (which arguably, is slow for a microcontroller due to using multiplication) \$\endgroup\$– brycCommented Mar 18, 2019 at 16:13
-
1\$\begingroup\$ @bryc: True, but might be interesting for others anyway. \$\endgroup\$– RevCommented Mar 19, 2019 at 7:38
- If you're really not bothered about security
- If all you want to do is 'mess up' a number
Then there are a bunch of really simple ways to do this, here are two I can think of:
void really_simple_hash(uint16_t x)
{
SWAP_NIBBLES(FIRST_BYTE(x));
SWAP_NIBBLES(SECOND_BYTE(x));
x ^= 0xAAAA;
return x;
}
On a PIC18, this translates into very simple code:
SWAPF x, f
SWAPF x+1, f
movlw 0xAA
xorwf x, f
xorwf x+1, f
It's pretty lightweight at only 5uS execution time on a PIC18 running at 10MIPS. If that's not enough of a mess, you can shuffle the bits around in the number. Again, on a PIC18:
rrcf x, f ; Then we take each bit of the source word
rlcf y+1, f ; and shift it into one of the destination bytes
rrcf x+1, f
rlcf y, f ; There are:
rrcf x, f ; - eight right shifts of x,
rlcf y+1, f ; - eight right shifts of x+1,
rrcf x+1, f ; - eight left shifts of y,
rlcf y+1, f ; - eight left shifts of y+1,
rrcf x, f
rlcf y, f ; You can do them in whatever sequence you want
rrcf x+1, f
rlcf y, f
rrcf x, f
rlcf y+1, f
rrcf x, f
rlcf y, f
rrcf x, f
rlcf y, f
rrcf x, f
rlcf y+1, f
rrcf x+1, f
rlcf y, f
rrcf x, f
rlcf y+1, f
rrcf x+1, f
rlcf y+1, f
rrcf x, f
rlcf y, f
rrcf x+1, f
rlcf y, f
rrcf x, f
rlcf y+1, f
rrcf x, f
rlcf y, f
rrcf x, f
rlcf y, f ; Finally, the result is stored in y
It's still pretty lightweight, with an execution time of only 37uS for that same PIC18.
If it doesn't need to be strong cryptographic:
- pick a random key of same length in bits;
- share the key between sender and receiver;
- sender-side: crypto-text = clear-text EOR key (you can do this byte by byte).
Optional:
- receiver-side clear-text = crypto-text EOR key.
-
1\$\begingroup\$ The OP doesn't want to encrypt a message, he just needs a simple hash function. There is no "sender" and "receiver". I don't think this answer helps at all. \$\endgroup\$– Joe HassCommented Sep 4, 2013 at 18:51
-
\$\begingroup\$ So skip the receiver side and use the crypt-text as hash. A hash usually has the property that it can not be reversed to the original clear-text, but I don't think the OP will mind. \$\endgroup\$– jippieCommented Sep 4, 2013 at 19:05
-
\$\begingroup\$ Also, hashes turn a variable length source into a fixed-size hash. \$\endgroup\$– fadedbeeCommented Aug 9, 2017 at 11:58