python3 thisScript.py arg1 arg2
arg1: Server password
arg2: Client password
Last active
January 28, 2024 09:50
-
-
Save teramako/9d2d62aeb60b720e7e40acdbaac366ae to your computer and use it in GitHub Desktop.
MySQL caching_sha2_password fast-path Test
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
import hashlib | |
import secrets | |
import sys | |
from termcolor import colored | |
USAGE = ''' | |
UASAGE: | |
python3 thisScript.py arg1 arg2 | |
arg1: Server password | |
arg2: Client password | |
See: | |
- Cache Operation for SHA-2 Pluggable Authentication | |
https://dev.mysql.com/doc/refman/8.0/en/caching-sha2-pluggable-authentication.html#caching-sha2-pluggable-authentication-cache-operation | |
- Generate_scramble::scramble() | |
https://github.com/mysql/mysql-server/blob/trunk/sql/auth/sha2_password_common.cc#L205-L263 | |
- Validate_scramble::validate() | |
https://github.com/mysql/mysql-server/blob/trunk/sql/auth/sha2_password_common.cc#L316-L361 | |
''' | |
def hex_dump(data: bytes) -> str: | |
return ''.join(format(c, "02x") for c in data) | |
def sha2(msg: bytes) -> bytes: | |
return hashlib.sha256(msg).digest() | |
def xor(x:bytes, y:bytes) -> bytes: | |
return bytes(a ^ b for a, b in zip(x, y)) | |
class MyClient: | |
prefix = colored("Client", "magenta") | |
def __init__(self, password:str): | |
print("%s: Input Password : %s" % (self.prefix, password)) | |
self.password = password.encode() | |
self._nonce = b'' | |
def set_nonce(self, nonce:bytes): | |
print("%s: set nonce : %s" % (self.prefix, hex_dump(nonce))) | |
self._nonce = nonce | |
def get_scramble(self) -> bytes: | |
''' | |
Generate scramble data from password and nonce. | |
scramble = xor(sha2(client_password), sha2(sha2(client_password) + nonce)) | |
''' | |
print("%s: Generate scramble data from inputed PASSWORD and NONCE" % self.prefix) | |
p1 = sha2(self.password) | |
result = xor(p1, sha2(sha2(p1) + self._nonce)) | |
print("%s: Send Scrambled data: %s" % (self.prefix, colored(hex_dump(result), "yellow"))) | |
return result | |
class MyServer: | |
prefix = colored("Server", "cyan") | |
def __init__(self, password:str): | |
self.cache = sha2(sha2(password.encode())) | |
self.nonce = b'' | |
def generate_nonce(self) -> bytes: | |
self.nonce = secrets.token_bytes(8) | |
print("%s: Generate nonce : %s" % (self.prefix, hex_dump(self.nonce))) | |
return self.nonce | |
def validate(self, scramble:bytes) -> bool: | |
''' | |
scramble = xor(sha2(client_password), sha2(sha2(client_password) + nonce)) | |
cashe = sha2(sha2(server_password)) | |
result = sha2(xor(scramble, sha2(cache + nonce))) | |
= sha2(xor(xor(sha2(client_password), sha2(sha2(sha2(client_password)) + nonce)), sha2(cache + nonce))) | |
= sha2(xor(xor(sha2(client_password), sha2(sha2(sha2(client_password)) + nonce)), sha2(sha2(sha2(server_password)) + nonce))) | |
If client_password == server_password: | |
= sha2(xor(xor(sha2(server_password), sha2(sha2(sha2(server_password)) + nonce)), sha2(sha2(sha2(server_password)) + nonce))) | |
// ~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
// xor(xor(A , B ), B ) = A | |
= sha2(sha2(server_password)) | |
= cache | |
=> Validate OK | |
''' | |
if not self.cache: | |
print("%s: has not cache" % self.prefx) | |
return False | |
print("%s: Compare client's scrambled data and server's cache" % self.prefix) | |
result = sha2(xor(scramble, sha2(self.cache + self.nonce))) | |
print("%s: cache : %s" % (self.prefix, colored(hex_dump(self.cache), "green"))) | |
res = (result == self.cache) | |
if res: | |
print("%s: calced result : %s" % (self.prefix, colored(hex_dump(result), "green"))) | |
print("%s: %s" % (self.prefix, colored("Validate Success", "green"))) | |
else: | |
print("%s: calced result : %s" % (self.prefix, colored(hex_dump(result), "red"))) | |
print("%s: %s" % (self.prefix, colored("Validate Failed", "red"))) | |
return res | |
def main(): | |
header = ''' | |
----------------------------------------------------------------- | |
MySQL caching_sha2_password fast-path Test | |
----------------------------------------------------------------- | |
'''.strip() | |
print(colored(header, "yellow")) | |
if len(sys.argv) < 3: | |
print(USAGE) | |
sys.exit(1) | |
server_password = sys.argv[1] | |
client_password = sys.argv[2] | |
my_server = MyServer(server_password) | |
my_client = MyClient(client_password) | |
# Server generate a random data(Nonce) and set to a client | |
my_client.set_nonce(my_server.generate_nonce()) | |
if my_server.validate(my_client.get_scramble()): | |
sys.exit(0) | |
else: | |
sys.exit(1) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment