Created
October 31, 2025 20:14
-
-
Save ArrayIterator/2454b5e7054349ac59e57e8966136910 to your computer and use it in GitHub Desktop.
HMAC Sha256 Based Nonce https://codepen.io/arrayiterator/pen/ogbaLrd
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * 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