From 5463c34177066b9860c9d77c6d7d59c265052399 Mon Sep 17 00:00:00 2001 From: fusion32 Date: Tue, 15 Jul 2025 23:37:52 -0300 Subject: finish game queries --- src/sha256.cc | 247 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 src/sha256.cc (limited to 'src/sha256.cc') diff --git a/src/sha256.cc b/src/sha256.cc new file mode 100644 index 0000000..1070c6b --- /dev/null +++ b/src/sha256.cc @@ -0,0 +1,247 @@ +#include "querymanager.hh" + +static const uint32 SHA256IV[8] = { + 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, + 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19, +}; + +static const uint32 SHA256K[64] = { + 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, + 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, + 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, + 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, + 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, + 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, + 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, + 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, + 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, + 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, + 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, + 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, + 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, + 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, + 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, + 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2, +}; + +static uint32 RotR32(uint32 Value, int N){ + ASSERT(N >= 0 && N < 32); + return (Value >> N) | (Value << (32 - N)); +} + +static void SHA256Compress(uint32 *H, const uint8 *Block){ + uint32 W[64]; + + for(int i = 0; i < 16; i += 1){ + W[i] = BufferRead32BE(&Block[i * 4]); + } + + for(int i = 16; i < 64; i += 1){ + uint32 S0 = RotR32(W[i - 15], 7) ^ RotR32(W[i - 15], 18) ^ (W[i - 15] >> 3); + uint32 S1 = RotR32(W[i - 2], 17) ^ RotR32(W[i - 2], 19) ^ (W[i - 2] >> 10); + W[i] = W[i - 16] + S0 + W[i - 7] + S1; + } + + uint32 Aux[8]; + memcpy(Aux, H, sizeof(uint32) * 8); + for(int i = 0; i < 64; i += 1){ + uint32 S1 = RotR32(Aux[4], 6) ^ RotR32(Aux[4], 11) ^ RotR32(Aux[4], 25); + uint32 Ch = (Aux[4] & Aux[5]) ^ (~Aux[4] & Aux[6]); + uint32 T1 = Aux[7] + S1 + Ch + SHA256K[i] + W[i]; + + uint32 S0 = RotR32(Aux[0], 2) ^ RotR32(Aux[0], 13) ^ RotR32(Aux[0], 22); + uint32 Maj = (Aux[0] & Aux[1]) ^ (Aux[0] & Aux[2]) ^ (Aux[1] & Aux[2]); + uint32 T2 = S0 + Maj; + + Aux[7] = Aux[6]; + Aux[6] = Aux[5]; + Aux[5] = Aux[4]; + Aux[4] = Aux[3] + T1; + Aux[3] = Aux[2]; + Aux[2] = Aux[1]; + Aux[1] = Aux[0]; + Aux[0] = T1 + T2; + } + + H[0] += Aux[0]; + H[1] += Aux[1]; + H[2] += Aux[2]; + H[3] += Aux[3]; + H[4] += Aux[4]; + H[5] += Aux[5]; + H[6] += Aux[6]; + H[7] += Aux[7]; +} + +void SHA256(const uint8 *Input, int InputBytes, uint8 *Digest){ + ASSERT(Input != NULL && InputBytes >= 0 && Digest != NULL); + uint32 H[8]; + memcpy(H, SHA256IV, sizeof(uint32) * 8); + + const uint8 *InputPtr = Input; + int InputRem = InputBytes; + while(InputRem >= 64){ + SHA256Compress(H, InputPtr); + InputPtr += 64; + InputRem -= 64; + } + + ASSERT(InputRem < 64); + uint8 Block[64] = {}; + memcpy(Block, InputPtr, InputRem); + BufferWrite8(&Block[InputRem], 0x80); + if(InputRem > 55){ + SHA256Compress(H, Block); + memset(Block, 0, sizeof(Block)); + } + BufferWrite64BE(&Block[56], ((uint64)InputBytes * 8)); + SHA256Compress(H, Block); + + BufferWrite32BE(&Digest[ 0], H[0]); + BufferWrite32BE(&Digest[ 4], H[1]); + BufferWrite32BE(&Digest[ 8], H[2]); + BufferWrite32BE(&Digest[12], H[3]); + BufferWrite32BE(&Digest[16], H[4]); + BufferWrite32BE(&Digest[20], H[5]); + BufferWrite32BE(&Digest[24], H[6]); + BufferWrite32BE(&Digest[28], H[7]); +} + +bool TestPassword(const uint8 *Auth, int AuthSize, const char *Password){ + if(AuthSize != 64){ + LOG_ERR("Expected 64 bytes of authentication data (got %d)", AuthSize); + return false; + } + + // NOTE(fusion): Constant time comparison to check whether the authentication + // data is set. I'm considering all zeros to be NOT set. + bool IsSet = false; + for(int i = 0; i < AuthSize; i += 1){ + if(Auth[i] != 0){ + IsSet = true; + } + } + + if(!IsSet){ + LOG_ERR("Authentication data not set"); + return false; + } + + const uint8 *Hash = &Auth[ 0]; + const uint8 *Salt = &Auth[32]; + + // TODO(fusion): It's probably not the best way to mix the salt but should + // be better than using plaintext or non-salted hashing schemes. + uint8 Digest[32]; + SHA256((const uint8*)Password, (int)strlen(Password), Digest); + for(int i = 0; i < 32; i += 1){ + Digest[i] ^= Salt[i]; + } + SHA256(Digest, 32, Digest); + + // NOTE(fusion): Constant time comparison. + uint8 Result = 0; + for(int i = 0; i < 32; i += 1){ + Result |= Digest[i] ^ Hash[i]; + } + return Result == 0;; +} + +// CheckSHA256 +//============================================================================== +static int HexDigit(int Ch){ + if(Ch >= '0' && Ch <= '9'){ + return (Ch - '0'); + }else if(Ch >= 'A' && Ch <= 'F'){ + return (Ch - 'A') + 10; + }else if(Ch >= 'a' && Ch <= 'f'){ + return (Ch - 'a') + 10; + }else{ + return -1; + } +} + +static int ParseHexString(uint8 *Buffer, int BufferSize, const char *String){ + int StringLen = (int)strlen(String); + if(StringLen % 2 != 0){ + LOG_ERR("Expected even number of characters"); + return -1; + } + + int NumBytes = (StringLen / 2); + if(NumBytes > BufferSize){ + LOG_ERR("Supplied buffer is too small (Size: %d, Required: %d)", + BufferSize, NumBytes); + return -1; + } + + for(int i = 0; i < NumBytes; i += 1){ + int Digit0 = HexDigit(String[i * 2 + 0]); + int Digit1 = HexDigit(String[i * 2 + 1]); + if(Digit0 == -1 || Digit1 == -1){ + LOG_ERR("Invalid hex digit at offset %d", i * 2); + return -1; + } + + Buffer[i] = ((uint8)Digit0 << 4) | (uint8)Digit1; + } + + return NumBytes; +} + +bool CheckSHA256(void){ + // NOTE(fusion): We're using only a few NIST test vectors. This is to make + // sure there are no blatant implementation errors but we'd ideally run it + // against all of them to be sure. + struct{ + const char *Input; + const char *Expected; + } Tests[] = { + { + "", + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + }, + { + "5738c929c4f4ccb6", + "963bb88f27f512777aab6c8b1a02c70ec0ad651d428f870036e1917120fb48bf", + }, + { + "1b503fb9a73b16ada3fcf1042623ae7610", + "d5c30315f72ed05fe519a1bf75ab5fd0ffec5ac1acb0daf66b6b769598594509", + }, + { + "09fc1accc230a205e4a208e64a8f204291f581a12756392da4b8c0cf5ef02b95", + "4f44c1c7fbebb6f9601829f3897bfd650c56fa07844be76489076356ac1886a4", + }, + { + "03b264be51e4b941864f9b70b4c958f5355aac294b4b87cb037f11f85f07eb57" + "b3f0b89550", + "d1f8bd684001ac5a4b67bbf79f87de524d2da99ac014dec3e4187728f4557471", + }, + { + "d1be3f13febafefc14414d9fb7f693db16dc1ae270c5b647d80da8583587c1ad" + "8cb8cb01824324411ca5ace3ca22e179a4ff4986f3f21190f3d7f3", + "02804978eba6e1de65afdbc6a6091ed6b1ecee51e8bff40646a251de6678b7ef", + } + }; + + uint8 Input[64]; + uint8 Expected[32]; + uint8 Digest[32]; + for(int i = 0; i < NARRAY(Tests); i += 1){ + int InputBytes = ParseHexString(Input, sizeof(Input), Tests[i].Input); + int ExpectedBytes = ParseHexString(Expected, sizeof(Expected), Tests[i].Expected); + if(InputBytes == -1 || ExpectedBytes != sizeof(Expected)){ + LOG_ERR("Invalid test vector %d", i); + return false; + } + + SHA256(Input, InputBytes, Digest); + if(memcmp(Expected, Digest, 32) != 0){ + LOG_ERR("Test vector %d failed", i); + return false; + } + } + + return true; +} -- cgit v1.2.3