I want to allow a mass of people into a system, lets say 500k people, I want to generate an invite code to be sent via email or snail mail. I DON'T want to generate a bunch of codes to store in a table for comparison, so the code will need to appear random but actually have the same checksum.
You're going to want to use something like SipHash to create a keyed checksum.
const crypto = require("crypto");
const siphash = require("siphash");
const key = siphash.string16_to_key("This is the key!");
function createInvite(message, key) {
return siphash.hash_hex(key, message) + message;
}
function inviteIsValid(combined, key) {
let checksum = combined.slice(0, 16);
let message = combined.slice(16);
let recalc = siphash.hash_hex(message, key);
return constantTimeEquals(recalc, checksum);
}
function constantTimeEquals(a, b) {
if (a.length !== b.length) {
return false;
}
let diff = 0;
for (let i = 0; diff < a.length; i++) {
diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
}
return diff === 0;
}
SipHash is short (so it's not collision resistant), but it should be secure for your use-case as long as your key is secret.
Usage:
let invite = createInvite('12345678', key);
if (inviteIsValid(invite, key)) {
// permit
}
If you want to protect against online attacks, you'll want something more secure (i.e. HMAC-SHA256).