forked from Xeeynamo/sonic-hybrid-rsdk
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
361 additions
and
0 deletions.
There are no files selected for viewing
340 changes: 340 additions & 0 deletions
340
...c3air-main/Oxygen/oxygenengine/source/oxygen_netcore/network/internal/CryptoFunctions.cpp
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,340 @@ | ||
/* | ||
* Part of the Oxygen Engine / Sonic 3 A.I.R. software distribution. | ||
* Copyright (C) 2017-2024 by Eukaryot | ||
* | ||
* Published under the GNU GPLv3 open source software license, see license.txt | ||
* or https://www.gnu.org/licenses/gpl-3.0.en.html | ||
*/ | ||
|
||
#include "oxygen_netcore/pch.h" | ||
#include "oxygen_netcore/network/internal/CryptoFunctions.h" | ||
|
||
|
||
namespace sha1 | ||
{ | ||
// Based on https://github.com/vog/sha1/blob/master/sha1.hpp (public domain) | ||
class SHA1 | ||
{ | ||
public: | ||
SHA1(); | ||
void update(const std::string& s); | ||
void update(std::istream &is); | ||
void finalize(uint32* output); | ||
|
||
private: | ||
static const unsigned int DIGEST_INTS = 5; /* number of 32bit integers per SHA1 digest */ | ||
static const unsigned int BLOCK_INTS = 16; /* number of 32bit integers per SHA1 block */ | ||
static const unsigned int BLOCK_BYTES = BLOCK_INTS * 4; | ||
|
||
uint32 digest[DIGEST_INTS]; | ||
std::string buffer; | ||
uint64 transforms = 0; | ||
|
||
void reset(); | ||
void transform(uint32 block[BLOCK_BYTES]); | ||
|
||
static void buffer_to_block(const std::string& buffer, uint32 block[BLOCK_BYTES]); | ||
static void read(std::istream &is, std::string& s, int max); | ||
}; | ||
|
||
|
||
/* Help macros */ | ||
#define SHA1_ROL(value, bits) (((value) << (bits)) | (((value) & 0xffffffff) >> (32 - (bits)))) | ||
#define SHA1_BLK(i) (block[i&15] = SHA1_ROL(block[(i+13)&15] ^ block[(i+8)&15] ^ block[(i+2)&15] ^ block[i&15],1)) | ||
|
||
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ | ||
#define SHA1_R0(v,w,x,y,z,i) z += ((w&(x^y))^y) + block[i] + 0x5a827999 + SHA1_ROL(v,5); w=SHA1_ROL(w,30); | ||
#define SHA1_R1(v,w,x,y,z,i) z += ((w&(x^y))^y) + SHA1_BLK(i) + 0x5a827999 + SHA1_ROL(v,5); w=SHA1_ROL(w,30); | ||
#define SHA1_R2(v,w,x,y,z,i) z += (w^x^y) + SHA1_BLK(i) + 0x6ed9eba1 + SHA1_ROL(v,5); w=SHA1_ROL(w,30); | ||
#define SHA1_R3(v,w,x,y,z,i) z += (((w|x)&y)|(w&x)) + SHA1_BLK(i) + 0x8f1bbcdc + SHA1_ROL(v,5); w=SHA1_ROL(w,30); | ||
#define SHA1_R4(v,w,x,y,z,i) z += (w^x^y) + SHA1_BLK(i) + 0xca62c1d6 + SHA1_ROL(v,5); w=SHA1_ROL(w,30); | ||
|
||
SHA1::SHA1() | ||
{ | ||
reset(); | ||
} | ||
|
||
void SHA1::update(const std::string& s) | ||
{ | ||
std::istringstream is(s); | ||
update(is); | ||
} | ||
|
||
void SHA1::update(std::istream &is) | ||
{ | ||
std::string rest_of_buffer; | ||
read(is, rest_of_buffer, (int)BLOCK_BYTES - (int)buffer.size()); | ||
buffer += rest_of_buffer; | ||
|
||
while (is) | ||
{ | ||
uint32 block[BLOCK_INTS]; | ||
buffer_to_block(buffer, block); | ||
transform(block); | ||
read(is, buffer, BLOCK_BYTES); | ||
} | ||
} | ||
|
||
void SHA1::finalize(uint32* output) | ||
{ | ||
/* Total number of hashed bits */ | ||
uint64 total_bits = (transforms*BLOCK_BYTES + buffer.size()) * 8; | ||
|
||
/* Padding */ | ||
buffer += (char)0x80; | ||
unsigned int orig_size = (unsigned int)buffer.size(); | ||
while (buffer.size() < BLOCK_BYTES) | ||
{ | ||
buffer += (char)0x00; | ||
} | ||
|
||
uint32 block[BLOCK_INTS]; | ||
buffer_to_block(buffer, block); | ||
|
||
if (orig_size > BLOCK_BYTES - 8) | ||
{ | ||
transform(block); | ||
for (unsigned int i = 0; i < BLOCK_INTS - 2; i++) | ||
{ | ||
block[i] = 0; | ||
} | ||
} | ||
|
||
/* Append total_bits, split this uint64 into two uint32 */ | ||
block[BLOCK_INTS - 1] = (uint32)total_bits; | ||
block[BLOCK_INTS - 2] = (uint32)(total_bits >> 32); | ||
transform(block); | ||
|
||
for (uint32 k = 0; k < DIGEST_INTS; ++k) | ||
output[k] = digest[k]; | ||
} | ||
|
||
void SHA1::reset() | ||
{ | ||
/* SHA1 initialization constants */ | ||
digest[0] = 0x67452301; | ||
digest[1] = 0xefcdab89; | ||
digest[2] = 0x98badcfe; | ||
digest[3] = 0x10325476; | ||
digest[4] = 0xc3d2e1f0; | ||
|
||
/* Reset counters */ | ||
transforms = 0; | ||
buffer = ""; | ||
} | ||
|
||
void SHA1::transform(uint32 block[BLOCK_BYTES]) | ||
{ | ||
/* Copy digest[] to working vars */ | ||
uint32 a = digest[0]; | ||
uint32 b = digest[1]; | ||
uint32 c = digest[2]; | ||
uint32 d = digest[3]; | ||
uint32 e = digest[4]; | ||
|
||
/* 4 rounds of 20 operations each. Loop unrolled. */ | ||
SHA1_R0(a,b,c,d,e, 0); | ||
SHA1_R0(e,a,b,c,d, 1); | ||
SHA1_R0(d,e,a,b,c, 2); | ||
SHA1_R0(c,d,e,a,b, 3); | ||
SHA1_R0(b,c,d,e,a, 4); | ||
SHA1_R0(a,b,c,d,e, 5); | ||
SHA1_R0(e,a,b,c,d, 6); | ||
SHA1_R0(d,e,a,b,c, 7); | ||
SHA1_R0(c,d,e,a,b, 8); | ||
SHA1_R0(b,c,d,e,a, 9); | ||
SHA1_R0(a,b,c,d,e,10); | ||
SHA1_R0(e,a,b,c,d,11); | ||
SHA1_R0(d,e,a,b,c,12); | ||
SHA1_R0(c,d,e,a,b,13); | ||
SHA1_R0(b,c,d,e,a,14); | ||
SHA1_R0(a,b,c,d,e,15); | ||
SHA1_R1(e,a,b,c,d,16); | ||
SHA1_R1(d,e,a,b,c,17); | ||
SHA1_R1(c,d,e,a,b,18); | ||
SHA1_R1(b,c,d,e,a,19); | ||
SHA1_R2(a,b,c,d,e,20); | ||
SHA1_R2(e,a,b,c,d,21); | ||
SHA1_R2(d,e,a,b,c,22); | ||
SHA1_R2(c,d,e,a,b,23); | ||
SHA1_R2(b,c,d,e,a,24); | ||
SHA1_R2(a,b,c,d,e,25); | ||
SHA1_R2(e,a,b,c,d,26); | ||
SHA1_R2(d,e,a,b,c,27); | ||
SHA1_R2(c,d,e,a,b,28); | ||
SHA1_R2(b,c,d,e,a,29); | ||
SHA1_R2(a,b,c,d,e,30); | ||
SHA1_R2(e,a,b,c,d,31); | ||
SHA1_R2(d,e,a,b,c,32); | ||
SHA1_R2(c,d,e,a,b,33); | ||
SHA1_R2(b,c,d,e,a,34); | ||
SHA1_R2(a,b,c,d,e,35); | ||
SHA1_R2(e,a,b,c,d,36); | ||
SHA1_R2(d,e,a,b,c,37); | ||
SHA1_R2(c,d,e,a,b,38); | ||
SHA1_R2(b,c,d,e,a,39); | ||
SHA1_R3(a,b,c,d,e,40); | ||
SHA1_R3(e,a,b,c,d,41); | ||
SHA1_R3(d,e,a,b,c,42); | ||
SHA1_R3(c,d,e,a,b,43); | ||
SHA1_R3(b,c,d,e,a,44); | ||
SHA1_R3(a,b,c,d,e,45); | ||
SHA1_R3(e,a,b,c,d,46); | ||
SHA1_R3(d,e,a,b,c,47); | ||
SHA1_R3(c,d,e,a,b,48); | ||
SHA1_R3(b,c,d,e,a,49); | ||
SHA1_R3(a,b,c,d,e,50); | ||
SHA1_R3(e,a,b,c,d,51); | ||
SHA1_R3(d,e,a,b,c,52); | ||
SHA1_R3(c,d,e,a,b,53); | ||
SHA1_R3(b,c,d,e,a,54); | ||
SHA1_R3(a,b,c,d,e,55); | ||
SHA1_R3(e,a,b,c,d,56); | ||
SHA1_R3(d,e,a,b,c,57); | ||
SHA1_R3(c,d,e,a,b,58); | ||
SHA1_R3(b,c,d,e,a,59); | ||
SHA1_R4(a,b,c,d,e,60); | ||
SHA1_R4(e,a,b,c,d,61); | ||
SHA1_R4(d,e,a,b,c,62); | ||
SHA1_R4(c,d,e,a,b,63); | ||
SHA1_R4(b,c,d,e,a,64); | ||
SHA1_R4(a,b,c,d,e,65); | ||
SHA1_R4(e,a,b,c,d,66); | ||
SHA1_R4(d,e,a,b,c,67); | ||
SHA1_R4(c,d,e,a,b,68); | ||
SHA1_R4(b,c,d,e,a,69); | ||
SHA1_R4(a,b,c,d,e,70); | ||
SHA1_R4(e,a,b,c,d,71); | ||
SHA1_R4(d,e,a,b,c,72); | ||
SHA1_R4(c,d,e,a,b,73); | ||
SHA1_R4(b,c,d,e,a,74); | ||
SHA1_R4(a,b,c,d,e,75); | ||
SHA1_R4(e,a,b,c,d,76); | ||
SHA1_R4(d,e,a,b,c,77); | ||
SHA1_R4(c,d,e,a,b,78); | ||
SHA1_R4(b,c,d,e,a,79); | ||
|
||
/* Add the working vars back into digest[] */ | ||
digest[0] += a; | ||
digest[1] += b; | ||
digest[2] += c; | ||
digest[3] += d; | ||
digest[4] += e; | ||
|
||
/* Count the number of transformations */ | ||
++transforms; | ||
} | ||
|
||
void SHA1::buffer_to_block(const std::string& buffer, uint32 block[BLOCK_BYTES]) | ||
{ | ||
/* Convert the std::string (byte buffer) to a uint32 array (MSB) */ | ||
for (unsigned int i = 0; i < BLOCK_INTS; i++) | ||
{ | ||
block[i] = (buffer[4*i+3] & 0xff) | ||
| (buffer[4*i+2] & 0xff) << 8 | ||
| (buffer[4*i+1] & 0xff) << 16 | ||
| (buffer[4*i+0] & 0xff) << 24; | ||
} | ||
} | ||
|
||
void SHA1::read(std::istream &is, std::string& s, int max) | ||
{ | ||
char sbuf[BLOCK_BYTES]; | ||
is.read(sbuf, max); | ||
s.assign(sbuf, (size_t)is.gcount()); | ||
} | ||
} | ||
|
||
|
||
namespace Base64 | ||
{ | ||
static const constexpr char ENCODER_LOOKUP[64] = | ||
{ | ||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', | ||
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', | ||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', | ||
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', | ||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' | ||
}; | ||
|
||
static uint8 DECODER_LOOKUP[0x100] = { 0 }; | ||
} | ||
|
||
|
||
bool Crypto::decodeBase64(std::string_view encodedString, std::vector<uint8>& outData) | ||
{ | ||
if (Base64::DECODER_LOOKUP[0] == 0) | ||
{ | ||
// Build reverse lookup | ||
for (int k = 0; k < 256; ++k) | ||
Base64::DECODER_LOOKUP[k] = 0xff; | ||
for (int k = 0; k < 64; ++k) | ||
Base64::DECODER_LOOKUP[Base64::ENCODER_LOOKUP[k]] = k; | ||
Base64::DECODER_LOOKUP[(size_t)'='] = 0; | ||
} | ||
|
||
outData.clear(); | ||
outData.reserve(encodedString.length() / 4 * 3); | ||
for (size_t k = 0; k < encodedString.length() - 3; k += 4) | ||
{ | ||
const uint8 bits0 = Base64::DECODER_LOOKUP[encodedString[k]]; | ||
const uint8 bits1 = Base64::DECODER_LOOKUP[encodedString[k+1]]; | ||
const uint8 bits2 = Base64::DECODER_LOOKUP[encodedString[k+2]]; | ||
const uint8 bits3 = Base64::DECODER_LOOKUP[encodedString[k+3]]; | ||
if (bits0 == 0xff || bits1 == 0xff || bits2 == 0xff || bits3 == 0xff) | ||
return false; | ||
|
||
// TODO: Leave out bytes added just for padding? | ||
outData.push_back((bits0 << 2) | (bits1 >> 4)); | ||
outData.push_back((bits1 << 4) | (bits2 >> 2)); | ||
outData.push_back((bits2 << 6) | bits3); | ||
} | ||
return true; | ||
} | ||
|
||
std::string Crypto::encodeBase64(const uint8* data, size_t length) | ||
{ | ||
const size_t numOutputBytesMinimal = (length * 4 + 2) / 3; | ||
const size_t numOutputBytesTotal = (length + 2) / 3 * 4; | ||
std::string output; | ||
output.reserve(numOutputBytesTotal); | ||
|
||
size_t offset = 0; | ||
while (offset < length) | ||
{ | ||
// Build 4 values of 6-bits from the next 3 bytes | ||
uint8 buffer[3]; | ||
const uint8* bytes; | ||
if (offset + 3 < length) | ||
{ | ||
bytes = &data[offset]; | ||
offset += 3; | ||
} | ||
else | ||
{ | ||
for (int k = 0; k < 3; ++k) | ||
buffer[k] = (offset + k < length) ? data[offset + k] : 0; | ||
bytes = buffer; | ||
offset = length; | ||
} | ||
|
||
uint8 values[4]; | ||
values[0] = ((bytes[0] >> 2) & 0x3f); | ||
values[1] = ((bytes[0] << 4) & 0x30) | ((bytes[1] >> 4) & 0x0f); | ||
values[2] = ((bytes[1] << 2) & 0x3c) | ((bytes[2] >> 6) & 0x03); | ||
values[3] = (bytes[2] & 0x3f); | ||
|
||
for (int k = 0; k < 4; ++k) | ||
output.push_back(Base64::ENCODER_LOOKUP[values[k]]); | ||
} | ||
for (size_t k = numOutputBytesMinimal; k < numOutputBytesTotal; ++k) | ||
output[k] = '='; | ||
return output; | ||
} | ||
|
||
void Crypto::buildSHA1(const std::string& string, uint32* output) | ||
{ | ||
sha1::SHA1 checksum; | ||
checksum.update(string); | ||
checksum.finalize(output); | ||
} |
21 changes: 21 additions & 0 deletions
21
sonic3air-main/Oxygen/oxygenengine/source/oxygen_netcore/network/internal/CryptoFunctions.h
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/* | ||
* Part of the Oxygen Engine / Sonic 3 A.I.R. software distribution. | ||
* Copyright (C) 2017-2024 by Eukaryot | ||
* | ||
* Published under the GNU GPLv3 open source software license, see license.txt | ||
* or https://www.gnu.org/licenses/gpl-3.0.en.html | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <rmxbase.h> | ||
|
||
|
||
class Crypto | ||
{ | ||
public: | ||
static bool decodeBase64(std::string_view encodedString, std::vector<uint8>& outData); | ||
static std::string encodeBase64(const uint8* data, size_t length); | ||
|
||
static void buildSHA1(const std::string& string, uint32* output); // Output is supposed to be 5 uint32 values | ||
}; |