13

I find myself needing to generate a checksum for a string of data, for consistency purposes. The broad idea is that the client can regenerate the checksum based on the payload it recieves and thus detect any corruption that took place in transit. I am vaguely aware that there are all kinds of mathematical principles behind this kind of thing, and that it's very easy for subtle errors to make the whole algorithm ineffective if you try to roll it yourself.

So I'm looking for advice on a hashing/checksum algorithm with the following criteria:

  • It will be generated by Javascript, so needs to be relatively light computationally.
  • The validation will be done by Java (though I cannot see this actually being an issue).
  • It will take textual input (URL-encoded Unicode, which I believe is ASCII) of a moderate length; typically around 200-300 characters and in all cases below 2000.
  • The output should be ASCII text as well, and the shorter it can be the better.

I'm primarily interested in something lightweight rather than getting the absolute smallest potential for collisions possible. Would I be naive to imagine that an eight-character hash would be suitable for this? I should also clarify that it's not the end of the world if corruption isn't picked up at the validation stage (and I do realise that this will not be 100% reliable), though the rest of my code is markedly less efficient for every corrupt entry that slips through.

Edit - thanks to all that contributed. I went with the Adler32 option and given that it was natively supported in Java, extremely easy to implement in Javascript, fast to calculate at both ends and have an 8-byte output it was exactly right for my requirements.

(Note that I realise that the network transport is unlikely to be responsible for any corruption errors and won't be folding my arms on this issue just yet; however adding the checksum validation removes one point of failure and means we can focus on other areas should this reoccur.)

Andrzej Doyle
  • 102,507
  • 33
  • 189
  • 228
  • So the whole TCP/IP checksum thing isn't working... I'm thinking that anything corrupted in transit is going to get rejected at a much lower layer than the application level. – tvanfosson Jan 07 '09 at 18:50
  • Yeah, this sounds like something usually left to the transport layer. Can you explain your scenario a little more? Where is your data being sent and what specific causes of data corruption are you trying to guard against? – C. Dragon 76 Jan 07 '09 at 19:19
  • @dtsazza - you mentioned in a comment below that this is for security (malicious users). Can you elaborate? Especially since this will probably run in a browser. – orip Jan 07 '09 at 22:01

9 Answers9

14

CRC32 is not too hard to implement in any language, it is good enough to detect simple data corruption and when implemted in a good fashion, it is very fast. However you can also try Adler32, which is almost equally good as CRC32, but it's even easier to implement (and about equally fast).

Adler32 in the Wikipedia

CRC32 JavaScript implementation sample

Either of these two (or maybe even both) are available in Java right out of the box.

Mecki
  • 125,244
  • 33
  • 244
  • 253
  • CRC32, definitely was designed to be exactly what you describe. – Die in Sente Jan 07 '09 at 19:00
  • 2
    A word of caution: the JavaScript in the link implements the algorithm with a table[256] of literal values. If you should modify even a single digit of that table, you will have a nasty bug that is very, very, hard to find! I prefer implementations that generate the table on the 1st call. – Die in Sente Jan 07 '09 at 19:02
  • I'll second @D.i.S's comment. Testability is a minus. – Jason S Jan 07 '09 at 20:11
  • One might also consider [Fletcher's checksum](https://en.wikipedia.org/wiki/Fletcher's_checksum). It's faster, detects more errors and a little bit easier to implement than Adler32 ([source](https://www.zlib.net/maxino06_fletcher-adler.pdf)). – Frank Kusters Jan 14 '20 at 12:53
6

Are aware that both TCP and UDP (and IP, and Ethernet, and...) already provide checksum protection to data in transit?

Unless you're doing something really weird, if you're seeing corruption, something is very wrong. I suggest starting with a memory tester.

Also, you receive strong data integrity protection if you use SSL/TLS.

derobert
  • 49,731
  • 15
  • 94
  • 124
  • Yes, I am was aware of that, though you were right to point it out. Unfortunately it's in input coming from the world at large, so we need to be able to cope with this anyway (malicious/mischevious users could mangle this for example). – Andrzej Doyle Jan 07 '09 at 20:36
  • It might be worth pointing out that for any change-detection algorithm, there is always a chance that it won't detect an error. They all can have collisions or false-negatives, though usually the more expensive algorithms reduce this chance to near-astronomically small probabilities. – Alan Krueger Jan 07 '09 at 21:11
  • 3
    @dtsazza: I wonder about the malicious/mischievous users who can mangle packets going across the network, but can't defeat Javascript. Or Adler32. – derobert Jan 09 '09 at 03:25
2

In my search for a JavaScript implementation of a good checksum algorithm I came across this question. Andrzej Doyle rightfully chose Adler32 as the checksum, as it is indeed easy to implement and has some excellent properties. DroidOS then provided an actual implementation in JavaScript, which demonstrated the simplicity.

However, the algorithm can be further improved upon as detailed in the Wikipedia page and as implemented below. The trick is that you need not determine the modulo in each step. Rather, you can defer this to the end. This considerably increases the speed of the implementation, up to 6x faster on Chrome and Safari. In addition, this optimalisation does not affect the readability of the code making it a win-win. As such, it definitely fits in well with the original question as to having an algorithm / implementation that is computationally light.

function adler32(data) {
  var MOD_ADLER = 65521;
  var a = 1, b = 0;

  var len = data.length;

  for (var i = 0; i < len; i++) {
    a += data.charCodeAt(i);
    b += a;
  }

  a %= MOD_ADLER;
  b %= MOD_ADLER;

  return (b << 16) | a;
}

edit: imaya created a jsperf comparison a while back showing the difference in speed when running the simple version, as detailed by DroidOS, compared to an optimised version that defers the modulo operation. I have added the above implementation under the name full-length to the jsperf page showing that the above implementation is about 25% faster than the one from imaya and about 570% faster than the simple implementation (tests run on Chrome 30): http://jsperf.com/adler-32-simple-vs-optimized/6

edit2: please don't forget that, when working on large files, you will eventually hit the limit of your JavaScript implementation in terms of the a and b variables. As such, when working with a large data source, you should perform intermediate modulo operations as to ensure that you do not exceed the maximum value of the integer that you can reliably store.

kvaruni
  • 832
  • 1
  • 10
  • 20
2

[UPDATE 30/5/2013: The link to the old JS CRC32 implementation died, so I've now linked to a different one.]

Google CRC32: fast, and much lighter weight than MD5 et al. There is a Javascript implementation here.

j_random_hacker
  • 50,331
  • 10
  • 105
  • 169
  • Link now broken unfortunately. – James Westgate May 30 '13 at 10:50
  • @JamesWestgate: Thanks James, I've found a new one and linked to that. Incidentally the first JS version I found (at http://noteslog.com/post/crc32-for-javascript/) actually reparses part of the string containing the table for each character processed, which will make it *much* slower than necessary. – j_random_hacker May 30 '13 at 15:21
  • Awesome! Thanks :) Looking for something to checksum javascript functions for a browser based animation code generation tool. – James Westgate May 30 '13 at 19:08
2

Javascript implementation of MD4, MD5 and SHA1. BSD license.

sastanin
  • 40,473
  • 13
  • 103
  • 130
2

Other people have mentioned CRC32 already, but here's a link to the W3C implementation of CRC-32 for PNG, as one of the few well-known, reputable sites with a reference CRC implementation.

(A few years back I tried to find a well-known site with a CRC algorithm or at least one that cited the source for its algorithm, & was almost tearing my hair out until I found the PNG page.)

Jason S
  • 184,598
  • 164
  • 608
  • 970
1

Here's a relatively simple one I've 'invented' - there's no mathematical research behind it but it's extremely fast and works in practice. I've also included the Java equivalent that tests the algorithm and shows that there's less than 1 in 10,000,000 chance of failure (it takes a minute or two to run).

JavaScript

function getCrc(s) {
    var result = 0;
    for(var i = 0; i < s.length; i++) {
        var c = s.charCodeAt(i);
        result = (result << 1) ^ c;
    }
    return result;
}

Java

package test;

import java.util.*;

public class SimpleCrc {

    public static void main(String[] args) {
        final Random randomGenerator = new Random();
        int lastCrc = -1;
        int dupes = 0;
        for(int i = 0; i < 10000000; i++) {
            final StringBuilder sb = new StringBuilder();
            for(int j = 0; j < 1000; j++) {
                final char c = (char)(randomGenerator.nextInt(128 - 32) + 32);
                sb.append(c);
            }
            final int crc = crc(sb.toString());
            if(lastCrc == crc) {
                dupes++;
            }
            lastCrc = crc;
        }
        System.out.println("Dupes: " + dupes);
    }

    public static int crc(String string) {
        int result = 0;
        for(final char c : string.toCharArray()) {
            result = (result << 1) ^ c;
        }
        return result;
    }
}
  • Why? Would you like to share an example? – Keith Whittingham Jun 16 '13 at 13:34
  • Won't any data contributed by bits other than the last 32 or so be shifted off the left of the result register and be lost? – David Given Aug 15 '21 at 21:21
  • The code above is far from perfect but that's not a problem. The each character in the string interacts with the result. In other words, by the time we get to the last 32 characters result contains a value that incorporates the previous 32 characters. – Keith Whittingham Aug 17 '21 at 06:38
  • Yeah, but _only_ the last 32 characters, so if the string is longer than that, any other characters won't be taken into account in the checksum... it would work if you used a rotate instead of a shift, though. – David Given Aug 17 '21 at 20:16
  • Yes, sorry, you're right. I wrote it a long time ago and someone else pointed out but that thread seems to have been removed. I originally used this in assembler which had a roll left so problem solved. However I originally limited it to 16 bits and the last 16 characters of the string - so it was super fast. It was just for hashing rather than error detection so worked pretty well. – Keith Whittingham Aug 18 '21 at 08:34
1

Use SHA-1 JS implementation. It's not as slow as you think (Firefox 3.0 on Core 2 Duo 2.4Ghz hashes over 100KB per second).

Kornel
  • 97,764
  • 37
  • 219
  • 309
0

This is a rather old thread but I suspect it is still viewed quite often so - if all you need is a short but reliable piece of code to generate a checksum the Adler32 bit algorithm has to be your choice. Here is the JavaScript code

function adler32(data)
{
 var MOD_ADLER = 65521;
 var a = 1, b = 0;

 for (var i = 0;i < data.length;i++) 
 {
  a = (a + data.charCodeAt(i)) % MOD_ADLER;
  b = (b + a) % MOD_ADLER;
 }

 var adler = a | (b << 16);
 return adler;
}

The corresponding fiddle demonsrating the algorithm in action is here.

DroidOS
  • 8,530
  • 16
  • 99
  • 171