Skip to content

Instantly share code, notes, and snippets.

@ArrayIterator
Created October 31, 2025 20:14
Show Gist options
  • Select an option

  • Save ArrayIterator/2454b5e7054349ac59e57e8966136910 to your computer and use it in GitHub Desktop.

Select an option

Save ArrayIterator/2454b5e7054349ac59e57e8966136910 to your computer and use it in GitHub Desktop.
/**
* REAL HMAC HASHING FUNCTION:
* Relies on the globally loaded CryptoJS library being available.
* Or use another hmacsha256
*/
const hmacSha256 = (data, key) => {
if (typeof CryptoJS === "undefined") {
// If CryptoJS is missing in CodePen setup
return "ERROR_CRYPTOJS_NOT_READY_".repeat(2).substring(0, HMAC_LENGTH);
}
const hash = CryptoJS.HmacSHA256(data, key);
return hash.toString(CryptoJS.enc.Hex);
};
// --- CORE NONCE LOGIC FUNCTIONS (Split-and-Sandwich) ---
// --- Nonce Logic Constants ---
const TIMESTAMP_LENGTH = 10;
const TS_PAD_LENGTH = TIMESTAMP_LENGTH;
const HMAC_LENGTH = 64;
const HMAC_SPLIT_POINT = 30; // The 64-char HMAC is split into a 30-char part and a 34-char part.
const MIN_NONCE_LENGTH = HMAC_LENGTH + TS_PAD_LENGTH; // Total length: 74 chars
const DEFAULT_EXPIRATION_SECONDS = 60; // 60 seconds for a quick demo
/**
* Generates a non-sequential nonce using the "Split and Sandwich" method.
* Structure: [Hash Part 1 (30)] + [Timestamp (10)] + [Hash Part 2 (34)]
*/
const create_nonce = (name, token, salt, secret) => {
const timestamp = Math.floor(Date.now() / 1000);
const dataToAuth = `${timestamp}:${name}:${salt}:${token}`;
const timestamp_string = String(timestamp).padStart(TS_PAD_LENGTH, "0");
const hash = hmacSha256(dataToAuth, secret); // 64 chars hash
if (hash.startsWith("ERROR_")) {
return { nonce: hash, timestamp: 0 };
}
// 1. Split the 64-char hash into two parts based on HMAC_SPLIT_POINT (30/34)
const hash_part_1 = hash.substring(0, HMAC_SPLIT_POINT);
const hash_part_2 = hash.substring(HMAC_SPLIT_POINT);
// 2. Sandwich the 10-char timestamp between the hash parts
const final_nonce = hash_part_1 + timestamp_string + hash_part_2;
return { nonce: final_nonce, timestamp };
};
/**
* Verifies a dynamic nonce using the "Split and Sandwich" method.
*/
const verify_nonce = (
name,
nonce,
token,
salt,
secret,
maxExpiration = DEFAULT_EXPIRATION_SECONDS
) => {
const result = { success: false, reason: "", timestamp: null };
if (!nonce) {
result.reason = "Nonce input is empty. Please generate a nonce first.";
return result;
}
if (nonce.length !== MIN_NONCE_LENGTH) {
result.reason = `Invalid length or format (Expected ${MIN_NONCE_LENGTH} chars). Current length: ${nonce.length}.`;
return result;
}
const now = Math.floor(Date.now() / 1000);
try {
// 1. Deconstruct the nonce based on the known split point and timestamp length
const hmac_provided_part_1 = nonce.substring(0, HMAC_SPLIT_POINT);
const full_timestamp_string = nonce.substring(
HMAC_SPLIT_POINT,
HMAC_SPLIT_POINT + TIMESTAMP_LENGTH
);
const hmac_provided_part_2 = nonce.substring(
HMAC_SPLIT_POINT + TIMESTAMP_LENGTH
);
// 2. Recombine the provided hash parts
const hmac_provided = hmac_provided_part_1 + hmac_provided_part_2;
// 3. Extract and validate timestamp
const epoch_timestamp_string = full_timestamp_string.slice(
-TIMESTAMP_LENGTH
);
const timestamp = parseInt(epoch_timestamp_string, 10);
result.timestamp = timestamp;
if (isNaN(timestamp)) {
result.reason = "Invalid timestamp format within nonce.";
return result;
}
// Time Checks
if (timestamp > now) {
result.reason = "Nonce timestamp is in the future.";
return result;
}
if (timestamp + maxExpiration < now) {
result.reason = `Expired. Valid for ${maxExpiration}s. Current time difference: ${
now - timestamp
}s.`;
return result;
}
// 4. HMAC Re-calculation
const dataToAuth = `${timestamp}:${name}:${salt}:${token}`;
const hmac_expected = hmacSha256(dataToAuth, secret);
if (hmac_expected.startsWith("ERROR_")) {
result.reason = "Cryptographic library not ready for verification.";
return result;
}
if (hmac_provided !== hmac_expected) {
result.reason =
"HMAC mismatch. Parameters (Action, Token, Salt, or Secret) altered.";
return result;
}
result.success = true;
result.reason = "Verification SUCCESSFUL!";
return result;
} catch (e) {
result.reason = `Verification error: ${e.message}`;
return result;
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment