Created
March 16, 2026 14:50
-
-
Save BrianLeishman/bd6dc69b9581d440f4d73df103f72079 to your computer and use it in GitHub Desktop.
SHA3(str, bits) — Pure MySQL SHA-3 hash, mirrors SHA2(str, bits)
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
| -- ============================================================================= | |
| -- SHA3(str, bits) — Pure MySQL SHA-3 hash, mirrors SHA2(str, bits) | |
| -- Supports SHA-3-224, SHA-3-256, SHA-3-384, SHA-3-512 | |
| -- Usage: SELECT SHA3('hello', 256); | |
| -- SELECT SHA3('hello', 0); -- 0 is alias for 256, same as SHA2() | |
| -- ============================================================================= | |
| SET GLOBAL log_bin_trust_function_creators = 1; | |
| DROP FUNCTION IF EXISTS _keccak_f1600; | |
| DROP FUNCTION IF EXISTS sha3; | |
| DELIMITER // | |
| -- ============================================================================= | |
| -- _keccak_f1600: Keccak-f[1600] permutation (identical for all SHA-3 variants) | |
| -- Input: 200-byte binary state (25 lanes × 8 bytes, little-endian) | |
| -- Output: 200-byte binary state after 24 rounds of θ, ρ, π, χ, ι | |
| -- ============================================================================= | |
| CREATE FUNCTION _keccak_f1600(state BINARY(200)) | |
| RETURNS BINARY(200) | |
| DETERMINISTIC | |
| NO SQL | |
| BEGIN | |
| DECLARE s0, s1, s2, s3, s4 BIGINT UNSIGNED; | |
| DECLARE s5, s6, s7, s8, s9 BIGINT UNSIGNED; | |
| DECLARE s10, s11, s12, s13, s14 BIGINT UNSIGNED; | |
| DECLARE s15, s16, s17, s18, s19 BIGINT UNSIGNED; | |
| DECLARE s20, s21, s22, s23, s24 BIGINT UNSIGNED; | |
| DECLARE c0, c1, c2, c3, c4 BIGINT UNSIGNED; | |
| DECLARE d0, d1, d2, d3, d4 BIGINT UNSIGNED; | |
| DECLARE b0, b1, b2, b3, b4 BIGINT UNSIGNED; | |
| DECLARE b5, b6, b7, b8, b9 BIGINT UNSIGNED; | |
| DECLARE b10, b11, b12, b13, b14 BIGINT UNSIGNED; | |
| DECLARE b15, b16, b17, b18, b19 BIGINT UNSIGNED; | |
| DECLARE b20, b21, b22, b23, b24 BIGINT UNSIGNED; | |
| DECLARE rnd INT DEFAULT 0; | |
| DECLARE rc BIGINT UNSIGNED; | |
| -- Unpack 25 little-endian 64-bit lanes | |
| SET s0 = CAST(CONV(HEX(REVERSE(SUBSTRING(state, 1, 8))), 16, 10) AS UNSIGNED); | |
| SET s1 = CAST(CONV(HEX(REVERSE(SUBSTRING(state, 9, 8))), 16, 10) AS UNSIGNED); | |
| SET s2 = CAST(CONV(HEX(REVERSE(SUBSTRING(state, 17, 8))), 16, 10) AS UNSIGNED); | |
| SET s3 = CAST(CONV(HEX(REVERSE(SUBSTRING(state, 25, 8))), 16, 10) AS UNSIGNED); | |
| SET s4 = CAST(CONV(HEX(REVERSE(SUBSTRING(state, 33, 8))), 16, 10) AS UNSIGNED); | |
| SET s5 = CAST(CONV(HEX(REVERSE(SUBSTRING(state, 41, 8))), 16, 10) AS UNSIGNED); | |
| SET s6 = CAST(CONV(HEX(REVERSE(SUBSTRING(state, 49, 8))), 16, 10) AS UNSIGNED); | |
| SET s7 = CAST(CONV(HEX(REVERSE(SUBSTRING(state, 57, 8))), 16, 10) AS UNSIGNED); | |
| SET s8 = CAST(CONV(HEX(REVERSE(SUBSTRING(state, 65, 8))), 16, 10) AS UNSIGNED); | |
| SET s9 = CAST(CONV(HEX(REVERSE(SUBSTRING(state, 73, 8))), 16, 10) AS UNSIGNED); | |
| SET s10 = CAST(CONV(HEX(REVERSE(SUBSTRING(state, 81, 8))), 16, 10) AS UNSIGNED); | |
| SET s11 = CAST(CONV(HEX(REVERSE(SUBSTRING(state, 89, 8))), 16, 10) AS UNSIGNED); | |
| SET s12 = CAST(CONV(HEX(REVERSE(SUBSTRING(state, 97, 8))), 16, 10) AS UNSIGNED); | |
| SET s13 = CAST(CONV(HEX(REVERSE(SUBSTRING(state, 105, 8))), 16, 10) AS UNSIGNED); | |
| SET s14 = CAST(CONV(HEX(REVERSE(SUBSTRING(state, 113, 8))), 16, 10) AS UNSIGNED); | |
| SET s15 = CAST(CONV(HEX(REVERSE(SUBSTRING(state, 121, 8))), 16, 10) AS UNSIGNED); | |
| SET s16 = CAST(CONV(HEX(REVERSE(SUBSTRING(state, 129, 8))), 16, 10) AS UNSIGNED); | |
| SET s17 = CAST(CONV(HEX(REVERSE(SUBSTRING(state, 137, 8))), 16, 10) AS UNSIGNED); | |
| SET s18 = CAST(CONV(HEX(REVERSE(SUBSTRING(state, 145, 8))), 16, 10) AS UNSIGNED); | |
| SET s19 = CAST(CONV(HEX(REVERSE(SUBSTRING(state, 153, 8))), 16, 10) AS UNSIGNED); | |
| SET s20 = CAST(CONV(HEX(REVERSE(SUBSTRING(state, 161, 8))), 16, 10) AS UNSIGNED); | |
| SET s21 = CAST(CONV(HEX(REVERSE(SUBSTRING(state, 169, 8))), 16, 10) AS UNSIGNED); | |
| SET s22 = CAST(CONV(HEX(REVERSE(SUBSTRING(state, 177, 8))), 16, 10) AS UNSIGNED); | |
| SET s23 = CAST(CONV(HEX(REVERSE(SUBSTRING(state, 185, 8))), 16, 10) AS UNSIGNED); | |
| SET s24 = CAST(CONV(HEX(REVERSE(SUBSTRING(state, 193, 8))), 16, 10) AS UNSIGNED); | |
| -- 24 rounds | |
| WHILE rnd < 24 DO | |
| -- θ (Theta) | |
| SET c0 = s0 ^ s5 ^ s10 ^ s15 ^ s20; | |
| SET c1 = s1 ^ s6 ^ s11 ^ s16 ^ s21; | |
| SET c2 = s2 ^ s7 ^ s12 ^ s17 ^ s22; | |
| SET c3 = s3 ^ s8 ^ s13 ^ s18 ^ s23; | |
| SET c4 = s4 ^ s9 ^ s14 ^ s19 ^ s24; | |
| SET d0 = c4 ^ ((c1 << 1) | (c1 >> 63)); | |
| SET d1 = c0 ^ ((c2 << 1) | (c2 >> 63)); | |
| SET d2 = c1 ^ ((c3 << 1) | (c3 >> 63)); | |
| SET d3 = c2 ^ ((c4 << 1) | (c4 >> 63)); | |
| SET d4 = c3 ^ ((c0 << 1) | (c0 >> 63)); | |
| SET s0=s0^d0; SET s1=s1^d1; SET s2=s2^d2; SET s3=s3^d3; SET s4=s4^d4; | |
| SET s5=s5^d0; SET s6=s6^d1; SET s7=s7^d2; SET s8=s8^d3; SET s9=s9^d4; | |
| SET s10=s10^d0; SET s11=s11^d1; SET s12=s12^d2; SET s13=s13^d3; SET s14=s14^d4; | |
| SET s15=s15^d0; SET s16=s16^d1; SET s17=s17^d2; SET s18=s18^d3; SET s19=s19^d4; | |
| SET s20=s20^d0; SET s21=s21^d1; SET s22=s22^d2; SET s23=s23^d3; SET s24=s24^d4; | |
| -- ρ (Rho) + π (Pi) | |
| SET b0 = s0; | |
| SET b1 = (s6 << 44) | (s6 >> 20); | |
| SET b2 = (s12 << 43) | (s12 >> 21); | |
| SET b3 = (s18 << 21) | (s18 >> 43); | |
| SET b4 = (s24 << 14) | (s24 >> 50); | |
| SET b5 = (s3 << 28) | (s3 >> 36); | |
| SET b6 = (s9 << 20) | (s9 >> 44); | |
| SET b7 = (s10 << 3) | (s10 >> 61); | |
| SET b8 = (s16 << 45) | (s16 >> 19); | |
| SET b9 = (s22 << 61) | (s22 >> 3); | |
| SET b10 = (s1 << 1) | (s1 >> 63); | |
| SET b11 = (s7 << 6) | (s7 >> 58); | |
| SET b12 = (s13 << 25) | (s13 >> 39); | |
| SET b13 = (s19 << 8) | (s19 >> 56); | |
| SET b14 = (s20 << 18) | (s20 >> 46); | |
| SET b15 = (s4 << 27) | (s4 >> 37); | |
| SET b16 = (s5 << 36) | (s5 >> 28); | |
| SET b17 = (s11 << 10) | (s11 >> 54); | |
| SET b18 = (s17 << 15) | (s17 >> 49); | |
| SET b19 = (s23 << 56) | (s23 >> 8); | |
| SET b20 = (s2 << 62) | (s2 >> 2); | |
| SET b21 = (s8 << 55) | (s8 >> 9); | |
| SET b22 = (s14 << 39) | (s14 >> 25); | |
| SET b23 = (s15 << 41) | (s15 >> 23); | |
| SET b24 = (s21 << 2) | (s21 >> 62); | |
| -- χ (Chi) | |
| SET s0 =b0 ^((~b1 )&b2 ); SET s1 =b1 ^((~b2 )&b3 ); SET s2 =b2 ^((~b3 )&b4 ); | |
| SET s3 =b3 ^((~b4 )&b0 ); SET s4 =b4 ^((~b0 )&b1 ); | |
| SET s5 =b5 ^((~b6 )&b7 ); SET s6 =b6 ^((~b7 )&b8 ); SET s7 =b7 ^((~b8 )&b9 ); | |
| SET s8 =b8 ^((~b9 )&b5 ); SET s9 =b9 ^((~b5 )&b6 ); | |
| SET s10=b10^((~b11)&b12); SET s11=b11^((~b12)&b13); SET s12=b12^((~b13)&b14); | |
| SET s13=b13^((~b14)&b10); SET s14=b14^((~b10)&b11); | |
| SET s15=b15^((~b16)&b17); SET s16=b16^((~b17)&b18); SET s17=b17^((~b18)&b19); | |
| SET s18=b18^((~b19)&b15); SET s19=b19^((~b15)&b16); | |
| SET s20=b20^((~b21)&b22); SET s21=b21^((~b22)&b23); SET s22=b22^((~b23)&b24); | |
| SET s23=b23^((~b24)&b20); SET s24=b24^((~b20)&b21); | |
| -- ι (Iota) | |
| SET rc = CASE rnd | |
| WHEN 0 THEN CAST(0x0000000000000001 AS UNSIGNED) | |
| WHEN 1 THEN CAST(0x0000000000008082 AS UNSIGNED) | |
| WHEN 2 THEN CAST(0x800000000000808A AS UNSIGNED) | |
| WHEN 3 THEN CAST(0x8000000080008000 AS UNSIGNED) | |
| WHEN 4 THEN CAST(0x000000000000808B AS UNSIGNED) | |
| WHEN 5 THEN CAST(0x0000000080000001 AS UNSIGNED) | |
| WHEN 6 THEN CAST(0x8000000080008081 AS UNSIGNED) | |
| WHEN 7 THEN CAST(0x8000000000008009 AS UNSIGNED) | |
| WHEN 8 THEN CAST(0x000000000000008A AS UNSIGNED) | |
| WHEN 9 THEN CAST(0x0000000000000088 AS UNSIGNED) | |
| WHEN 10 THEN CAST(0x0000000080008009 AS UNSIGNED) | |
| WHEN 11 THEN CAST(0x000000008000000A AS UNSIGNED) | |
| WHEN 12 THEN CAST(0x000000008000808B AS UNSIGNED) | |
| WHEN 13 THEN CAST(0x800000000000008B AS UNSIGNED) | |
| WHEN 14 THEN CAST(0x8000000000008089 AS UNSIGNED) | |
| WHEN 15 THEN CAST(0x8000000000008003 AS UNSIGNED) | |
| WHEN 16 THEN CAST(0x8000000000008002 AS UNSIGNED) | |
| WHEN 17 THEN CAST(0x8000000000000080 AS UNSIGNED) | |
| WHEN 18 THEN CAST(0x000000000000800A AS UNSIGNED) | |
| WHEN 19 THEN CAST(0x800000008000000A AS UNSIGNED) | |
| WHEN 20 THEN CAST(0x8000000080008081 AS UNSIGNED) | |
| WHEN 21 THEN CAST(0x8000000000008080 AS UNSIGNED) | |
| WHEN 22 THEN CAST(0x0000000080000001 AS UNSIGNED) | |
| WHEN 23 THEN CAST(0x8000000080008008 AS UNSIGNED) | |
| END; | |
| SET s0 = s0 ^ rc; | |
| SET rnd = rnd + 1; | |
| END WHILE; | |
| -- Repack to 200-byte little-endian binary | |
| RETURN CONCAT( | |
| REVERSE(UNHEX(LPAD(HEX(s0),16,'0'))), REVERSE(UNHEX(LPAD(HEX(s1),16,'0'))), | |
| REVERSE(UNHEX(LPAD(HEX(s2),16,'0'))), REVERSE(UNHEX(LPAD(HEX(s3),16,'0'))), | |
| REVERSE(UNHEX(LPAD(HEX(s4),16,'0'))), REVERSE(UNHEX(LPAD(HEX(s5),16,'0'))), | |
| REVERSE(UNHEX(LPAD(HEX(s6),16,'0'))), REVERSE(UNHEX(LPAD(HEX(s7),16,'0'))), | |
| REVERSE(UNHEX(LPAD(HEX(s8),16,'0'))), REVERSE(UNHEX(LPAD(HEX(s9),16,'0'))), | |
| REVERSE(UNHEX(LPAD(HEX(s10),16,'0'))), REVERSE(UNHEX(LPAD(HEX(s11),16,'0'))), | |
| REVERSE(UNHEX(LPAD(HEX(s12),16,'0'))), REVERSE(UNHEX(LPAD(HEX(s13),16,'0'))), | |
| REVERSE(UNHEX(LPAD(HEX(s14),16,'0'))), REVERSE(UNHEX(LPAD(HEX(s15),16,'0'))), | |
| REVERSE(UNHEX(LPAD(HEX(s16),16,'0'))), REVERSE(UNHEX(LPAD(HEX(s17),16,'0'))), | |
| REVERSE(UNHEX(LPAD(HEX(s18),16,'0'))), REVERSE(UNHEX(LPAD(HEX(s19),16,'0'))), | |
| REVERSE(UNHEX(LPAD(HEX(s20),16,'0'))), REVERSE(UNHEX(LPAD(HEX(s21),16,'0'))), | |
| REVERSE(UNHEX(LPAD(HEX(s22),16,'0'))), REVERSE(UNHEX(LPAD(HEX(s23),16,'0'))), | |
| REVERSE(UNHEX(LPAD(HEX(s24),16,'0'))) | |
| ); | |
| END// | |
| -- ============================================================================= | |
| -- SHA3(msg, bits): Full SHA-3 hash — mirrors SHA2(msg, bits) | |
| -- | |
| -- bits = 224 → SHA-3-224 (rate=144, output=28 bytes, 56 hex chars) | |
| -- bits = 256 → SHA-3-256 (rate=136, output=32 bytes, 64 hex chars) | |
| -- bits = 384 → SHA-3-384 (rate=104, output=48 bytes, 96 hex chars) | |
| -- bits = 512 → SHA-3-512 (rate=72, output=64 bytes, 128 hex chars) | |
| -- bits = 0 → SHA-3-256 (alias, same as SHA2 behavior) | |
| -- ============================================================================= | |
| CREATE FUNCTION sha3(msg BLOB, bits INT) | |
| RETURNS VARCHAR(128) CHARSET ascii | |
| DETERMINISTIC | |
| NO SQL | |
| BEGIN | |
| DECLARE rate INT; | |
| DECLARE output_bytes INT; | |
| DECLARE state BINARY(200); | |
| DECLARE padded BLOB; | |
| DECLARE msg_len INT; | |
| DECLARE pad_len INT; | |
| DECLARE block_count INT; | |
| DECLARE i INT; | |
| DECLARE blk VARBINARY(144); | |
| DECLARE j INT; | |
| DECLARE lane_count INT; | |
| DECLARE lane_val BIGINT UNSIGNED; | |
| DECLARE blk_lane BIGINT UNSIGNED; | |
| -- ========================================================================= | |
| -- Resolve parameters by variant | |
| -- SHA-3-N: capacity = 2N bits, rate = 1600 - 2N bits | |
| -- ========================================================================= | |
| SET bits = COALESCE(bits, 256); | |
| IF bits = 0 THEN SET bits = 256; END IF; | |
| SET rate = CASE bits | |
| WHEN 224 THEN 144 -- (1600 - 448) / 8 | |
| WHEN 256 THEN 136 -- (1600 - 512) / 8 | |
| WHEN 384 THEN 104 -- (1600 - 768) / 8 | |
| WHEN 512 THEN 72 -- (1600 - 1024) / 8 | |
| ELSE NULL | |
| END; | |
| SET output_bytes = CASE bits | |
| WHEN 224 THEN 28 | |
| WHEN 256 THEN 32 | |
| WHEN 384 THEN 48 | |
| WHEN 512 THEN 64 | |
| ELSE NULL | |
| END; | |
| IF rate IS NULL THEN | |
| RETURN NULL; -- Invalid bit length, mirrors SHA2() returning NULL | |
| END IF; | |
| SET lane_count = rate DIV 8; -- lanes to XOR per block: 18/17/13/9 | |
| -- Initialize 1600-bit state to zeros | |
| SET state = REPEAT(0x00, 200); | |
| -- ========================================================================= | |
| -- Padding: msg || 0x06 || 0x00...0x00 || 0x80 | |
| -- ========================================================================= | |
| SET msg_len = COALESCE(LENGTH(msg), 0); | |
| SET pad_len = rate - (msg_len % rate); -- always 1..rate | |
| IF pad_len = 1 THEN | |
| SET padded = CONCAT(msg, UNHEX('86')); | |
| ELSE | |
| SET padded = CONCAT( | |
| msg, | |
| UNHEX('06'), | |
| REPEAT(0x00, pad_len - 2), | |
| UNHEX('80') | |
| ); | |
| END IF; | |
| -- ========================================================================= | |
| -- Absorb: XOR each rate-sized block into state, then permute | |
| -- ========================================================================= | |
| SET block_count = LENGTH(padded) DIV rate; | |
| SET i = 0; | |
| WHILE i < block_count DO | |
| SET blk = SUBSTRING(padded, i * rate + 1, rate); | |
| SET j = 0; | |
| WHILE j < lane_count DO | |
| SET lane_val = CAST(CONV(HEX(REVERSE(SUBSTRING(state, j*8+1, 8))), 16, 10) AS UNSIGNED); | |
| SET blk_lane = CAST(CONV(HEX(REVERSE(SUBSTRING(blk, j*8+1, 8))), 16, 10) AS UNSIGNED); | |
| SET lane_val = lane_val ^ blk_lane; | |
| SET state = CONCAT( | |
| LEFT(state, j*8), | |
| REVERSE(UNHEX(LPAD(HEX(lane_val), 16, '0'))), | |
| SUBSTRING(state, j*8+9) | |
| ); | |
| SET j = j + 1; | |
| END WHILE; | |
| SET state = _keccak_f1600(state); | |
| SET i = i + 1; | |
| END WHILE; | |
| -- ========================================================================= | |
| -- Squeeze: extract output_bytes from state | |
| -- All SHA-3 variants: output_bytes ≤ rate, so single squeeze suffices | |
| -- ========================================================================= | |
| RETURN LOWER(HEX(LEFT(state, output_bytes))); | |
| END// | |
| DELIMITER ; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment