Authentication Flow Diagrams
Authentication Flow Diagrams
HeliosDB Multi-Protocol Authentication Analysis
Researcher Agent Documentation Date: 2025-10-10
1. PostgreSQL SCRAM-SHA-256 Authentication
Flow Diagram
Client Server | | |-------- Startup Message --------------->| | (protocol=3.0, user=demo, db=demo) | | | |<------- AuthenticationSASL -------------| | [SCRAM-SHA-256-PLUS, SCRAM-SHA-256] | | | |-------- SASLInitialResponse ----------->| | mechanism: SCRAM-SHA-256 | | client-first-message: | | "n,,n=demo,r=<client-nonce>" | | | |<------- AuthenticationSASLContinue -----| | server-first-message: | | "r=<client+server-nonce>, | | s=<salt>,i=<iteration-count>" | | | |-------- SASLResponse ------------------>| | client-final-message: | | "c=<channel-binding>, | | r=<nonce>,p=<client-proof>" | | | |<------- AuthenticationSASLFinal --------| | server-final-message: | | "v=<server-signature>" | | | |<------- AuthenticationOk ---------------| | | |<------- ReadyForQuery ------------------| | (transaction status: Idle) | | |Detailed Steps
Step 1: Startup Message (Client → Server)
Message Type: None (first message has no type byte)Length: 4 bytes (includes length field)Protocol: 0x00030000 (version 3.0)Parameters: - user: "demo" - database: "demo" - application_name: "python_app" - client_encoding: "UTF8"Step 2: AuthenticationSASL (Server → Client)
Message Type: 'R' (Authentication request)Length: VariableAuth Type: 10 (SASL)Mechanisms: - "SCRAM-SHA-256-PLUS" (length-prefixed string) - "SCRAM-SHA-256" (length-prefixed string)Step 3: SASLInitialResponse (Client → Server)
Message Type: 'p' (password message repurposed)Length: VariableSelected Mechanism: "SCRAM-SHA-256" (length-prefixed)Initial Response Length: 4 bytesInitial Response: "n,,n=demo,r=fyko+d2lbbFgONRv9qkxdawL" - n,, = no channel binding - n=demo = username (ignored by server, uses startup username) - r=<nonce> = client-generated random nonce (base64)Step 4: AuthenticationSASLContinue (Server → Client)
Message Type: 'R'Length: VariableAuth Type: 11 (SASL Continue)Server Message: "r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096" - r = client_nonce + server_nonce - s = base64(salt) - i = iteration count (4096 minimum)Step 5: SASLResponse (Client → Server)
Message Type: 'p'Length: VariableResponse: "c=biws,r=<nonce>,p=<client-proof>" - c = base64(channel-binding) = "biws" for "n,," - r = combined nonce from step 4 - p = base64(ClientProof)
ClientProof Calculation: SaltedPassword = PBKDF2(password, salt, iterations) ClientKey = HMAC(SaltedPassword, "Client Key") StoredKey = SHA256(ClientKey) AuthMessage = client-first-bare + "," + server-first + "," + client-final-without-proof ClientSignature = HMAC(StoredKey, AuthMessage) ClientProof = ClientKey XOR ClientSignatureStep 6: AuthenticationSASLFinal (Server → Client)
Message Type: 'R'Length: VariableAuth Type: 12 (SASL Final)Final Message: "v=<server-signature>" - v = base64(ServerSignature)
Server Verification: ServerKey = HMAC(SaltedPassword, "Server Key") ServerSignature = HMAC(ServerKey, AuthMessage) (Client verifies this signature)Step 7: AuthenticationOk (Server → Client)
Message Type: 'R'Length: 8Auth Type: 0 (OK)Step 8: ReadyForQuery (Server → Client)
Message Type: 'Z'Length: 5Transaction Status: 'I' (Idle), 'T' (In Transaction), 'E' (Failed Transaction)TLS Integration with ALPN
Pre-Authentication TLS Negotiation:
Client Server | | |-------- SSLRequest ------------------->| | (Length=8, SSL code=80877103) | | | |<------- 'S' (SSL OK) -------------------| | | |====== TLS ClientHello ================>| | ALPN Extension: ["postgresql"] | | | |<===== TLS ServerHello ==================| | ALPN Selected: "postgresql" | | | |<==== TLS Certificate, Finished ========>| | | |-- Startup Message (over TLS) --------->| | (SCRAM flow continues over TLS) | | |Implementation Checklist
- PBKDF2 implementation (iteration count ≥ 4096)
- HMAC-SHA-256 support
- Base64 encoding/decoding
- Nonce generation (cryptographically secure random)
- Channel binding support (SCRAM-SHA-256-PLUS with TLS)
- Username validation (ignore SCRAM username, use startup username)
- Error handling (invalid proof, wrong password)
- TLS ALPN extension (“postgresql” identifier)
2. MySQL caching_sha2_password Authentication
Flow Diagram
Client Server | | |<------- HandshakeV10 -------------------| | protocol=10, auth_plugin_data, | | plugin=caching_sha2_password | | | |-------- HandshakeResponse41 ----------->| | capability_flags, username, | | auth_response (SHA256 hash), | | database, plugin=caching_sha2_password | | | |<------- AuthSwitchRequest (optional) ---| | (if plugin negotiation needed) | | | |-------- AuthSwitchResponse ------------>| | (new auth data) | | | |<------- AuthMoreData -------------------| | status=0x01 (fast auth success) OR | | status=0x03 (perform full auth) OR | | status=0x04 (request public key) | | | [If fast auth (cached)] |<------- OK_Packet ----------------------| | | [If full auth required + TLS] |-------- Password (plaintext) ---------->| | (sent over TLS channel) | | | |<------- OK_Packet ----------------------| | | [If full auth required + no TLS] |<------- PublicKey (0x01) ---------------| | (RSA public key in PEM format) | | | |-------- Encrypted Password ------------>| | (RSA encrypted with public key) | | | |<------- OK_Packet ----------------------| | |Detailed Steps
Step 1: HandshakeV10 (Server → Client)
Payload Length: 3 bytesSequence ID: 0x00Protocol Version: 0x0a (10)Server Version: "8.0.35\0"Connection ID: 4 bytesAuth Plugin Data Part 1: 8 bytes (random)Filler: 0x00Capability Flags Lower: 2 bytesCharacter Set: 0x21 (utf8mb4_general_ci)Status Flags: 2 bytes (SERVER_STATUS_AUTOCOMMIT)Capability Flags Upper: 2 bytesAuth Plugin Data Length: 21 (if CLIENT_PLUGIN_AUTH)Reserved: 10 bytes (0x00)Auth Plugin Data Part 2: 12 bytes (random)Auth Plugin Name: "caching_sha2_password\0"Step 2: HandshakeResponse41 (Client → Server)
Payload Length: VariableSequence ID: 0x01Capability Flags: 4 bytes (CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION | ...)Max Packet Size: 4 bytes (0x01000000 = 16MB)Character Set: 1 byte (0x21 = utf8mb4_general_ci)Reserved: 23 bytes (0x00)Username: "demo\0" (NULL-terminated)Auth Response Length: 1 byte (if CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA)Auth Response: 32 bytes (SHA256 hash) Hash = XOR(SHA256(password), SHA256(SHA256(SHA256(password)) + auth_plugin_data))Database: "demo\0" (if CLIENT_CONNECT_WITH_DB)Auth Plugin Name: "caching_sha2_password\0" (if CLIENT_PLUGIN_AUTH)Step 3a: Fast Path - AuthMoreData (Server → Client)
Payload Length: 2Sequence ID: 0x02Packet Type: 0x01 (AuthMoreData)Status: 0x03 (fast auth successful, password cached)
Followed by:Payload Length: VariableSequence ID: 0x03Packet Type: 0x00 (OK_Packet)Step 3b: Full Auth - AuthMoreData (Server → Client)
Payload Length: 2Sequence ID: 0x02Packet Type: 0x01 (AuthMoreData)Status: 0x04 (perform full authentication)Step 4a: Full Auth with TLS (Client → Server)
Payload Length: VariableSequence ID: 0x03Password: "demo_password\0" (plaintext over TLS)Step 4b: Full Auth without TLS - Request Public Key (Client → Server)
Payload Length: 2Sequence ID: 0x03Request: 0x02 (request public key)
Server Response:Payload Length: VariableSequence ID: 0x04Packet Type: 0x01 (AuthMoreData)Public Key: "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----\n"
Client Response:Payload Length: VariableSequence ID: 0x05Encrypted Password: RSA(password XOR auth_plugin_data, public_key)Step 5: OK_Packet (Server → Client)
Payload Length: VariableSequence ID: VariablePacket Type: 0x00 (OK)Affected Rows: 0 (encoded)Last Insert ID: 0 (encoded)Status Flags: 2 bytes (SERVER_STATUS_AUTOCOMMIT)Warnings: 2 bytesInfo: "Welcome message" (optional)Session State Changes: Variable (if CLIENT_SESSION_TRACK)Auth Response Calculation
Initial Hash (Step 2):
stage1 = SHA256(password)stage2 = SHA256(stage1)stage3 = SHA256(stage2 + auth_plugin_data)auth_response = XOR(stage1, stage3)RSA Encryption (Step 4b):
# XOR password with auth_plugin_datapassword_bytes = password.encode('utf8') + b'\0'auth_data = auth_plugin_dataxor_result = bytes([a ^ b for a, b in zip(password_bytes, cycle(auth_data))])
# Encrypt with RSA public key (OAEP padding)public_key = load_pem_public_key(server_public_key)encrypted = public_key.encrypt(xor_result, OAEP(MGF1(SHA1()), SHA1(), None))Capability Flags (Critical)
CLIENT_LONG_PASSWORD = 0x00000001CLIENT_FOUND_ROWS = 0x00000002CLIENT_LONG_FLAG = 0x00000004CLIENT_CONNECT_WITH_DB = 0x00000008CLIENT_NO_SCHEMA = 0x00000010CLIENT_COMPRESS = 0x00000020CLIENT_ODBC = 0x00000040CLIENT_LOCAL_FILES = 0x00000080CLIENT_IGNORE_SPACE = 0x00000100CLIENT_PROTOCOL_41 = 0x00000200 # RequiredCLIENT_INTERACTIVE = 0x00000400CLIENT_SSL = 0x00000800 # TLS supportCLIENT_IGNORE_SIGPIPE = 0x00001000CLIENT_TRANSACTIONS = 0x00002000 # Transaction statusCLIENT_RESERVED = 0x00004000CLIENT_SECURE_CONNECTION = 0x00008000 # RequiredCLIENT_MULTI_STATEMENTS = 0x00010000CLIENT_MULTI_RESULTS = 0x00020000CLIENT_PS_MULTI_RESULTS = 0x00040000CLIENT_PLUGIN_AUTH = 0x00080000 # Required for caching_sha2CLIENT_CONNECT_ATTRS = 0x00100000CLIENT_PLUGIN_AUTH_LENENC = 0x00200000CLIENT_CAN_HANDLE_EXPIRED_PW = 0x00400000CLIENT_SESSION_TRACK = 0x00800000CLIENT_DEPRECATE_EOF = 0x01000000CLIENT_OPTIONAL_RESULTSET_MD = 0x02000000CLIENT_ZSTD_COMPRESSION = 0x04000000CLIENT_QUERY_ATTRIBUTES = 0x08000000Implementation Checklist
- Server-first handshake (send HandshakeV10)
- SHA-256 hashing (auth response calculation)
- Capability flag negotiation
- Auth plugin framework (caching_sha2_password, mysql_native_password)
- Fast auth path (cached password verification)
- Full auth path (TLS plaintext or RSA encryption)
- RSA public key generation and transmission
- TLS support (SERVER_SSL capability)
- Character set handling (utf8mb4)
- Error packet handling (0xFF packet type)
3. TDS 7.4 (SQL Server) Authentication
Flow Diagram
Client Server | | |-------- PRELOGIN ---------------------->| | VERSION, ENCRYPTION, INSTANCE, MARS | | | |<------- PRELOGIN Response --------------| | VERSION, ENCRYPTION (negotiated), | | INSTANCE (if applicable) | | | [If ENCRYPTION=ON or ENCRYPT_REQ] |====== TLS ClientHello (in PRELOGIN) ===>| | | |<===== TLS ServerHello (in PRELOGIN) ====| | | |<==== TLS Handshake Complete ==========>| | | |-------- LOGIN7 (encrypted) ------------>| | Hostname, Username, Password, | | App name, Server name, Database | | | |<------- LOGINACK ----------------------| | Interface, TDS version, Server name | | | |<------- ENVCHANGE ----------------------| | Database, Language, Collation changes | | | |<------- INFO tokens ---------------------| | (informational messages) | | | |<------- DONE token ----------------------| | (login process complete) | | |Detailed Steps
Step 1: PRELOGIN (Client → Server)
Packet Header: Type: 0x12 (PRELOGIN) Status: 0x01 (EOM - End of Message) Length: Variable (e.g., 0x0034 = 52 bytes) SPID: 0x0000 Packet ID: 0x01 Window: 0x00
Token List (offset/length pairs): VERSION: offset=0x0000, length=0x0006 ENCRYPTION: offset=0x0006, length=0x0001 INSTOPT: offset=0x0007, length=0x0001 THREADID: offset=0x0008, length=0x0004 MARS: offset=0x000C, length=0x0001 TRACEID: offset=0x000D, length=0x0024 TERMINATOR: 0xFF
Token Data: VERSION: UL_VERSION=0x0F000000 (15.0), US_SUBBUILD=0x0000 ENCRYPTION: 0x01 (ENCRYPT_ON) or 0x00 (ENCRYPT_OFF) INSTOPT: 0x00 (no instance) THREADID: 0x00000001 (client thread ID) MARS: 0x00 (MARS disabled) TRACEID: CONNID (16 bytes), ActivityID (16 bytes), Sequence (4 bytes)Step 2: PRELOGIN Response (Server → Client)
Packet Header: Type: 0x04 (TABULAR RESULT) Status: 0x01 (EOM) Length: Variable SPID: 0x0000 Packet ID: 0x01 Window: 0x00
Token List: VERSION: offset=0x0000, length=0x0006 ENCRYPTION: offset=0x0006, length=0x0001 INSTOPT: offset=0x0007, length=0x0001 THREADID: offset=0x0008, length=0x0000 (not returned) MARS: offset=0x0008, length=0x0001 TERMINATOR: 0xFF
Token Data: VERSION: Server version (e.g., 0x10000FA0 = SQL Server 2019) ENCRYPTION: 0x01 (server requires encryption) or 0x00 (not required) INSTOPT: 0x00 MARS: 0x00 (MARS not supported) or 0x01 (supported)Step 3: TLS Handshake (if encryption negotiated)
Client → Server (PRELOGIN packet with TLS payload): Packet Header: Type=0x12, Status=0x00 (more packets) TLS ClientHello: (raw bytes)
Server → Client (PRELOGIN packet with TLS payload): Packet Header: Type=0x04, Status=0x00 TLS ServerHello: (raw bytes)
... (TLS handshake continues in PRELOGIN packets)
Final handshake packet: Packet Header: Type=0x12/0x04, Status=0x01 (EOM, handshake complete)Step 4: LOGIN7 (Client → Server)
Packet Header: Type: 0x10 (LOGIN7) Status: 0x01 (EOM) Length: Variable SPID: 0x0000 Packet ID: 0x01 (or next sequence if TLS used) Window: 0x00
LOGIN7 Structure: Length: 4 bytes (total length) TDS Version: 0x74000004 (TDS 7.4) Packet Size: 4 bytes (0x00001000 = 4096) Client Version: 4 bytes (e.g., 0x0F000000) Client PID: 4 bytes Connection ID: 4 bytes (0x00000000) OptionFlags1: 1 byte - BYTE_ORDER (0x00=little endian, 0x01=big endian) - CHAR (0x00=ASCII, 0x02=EBCDIC) - FLOAT (0x00=IEEE 754, 0x0C=VAX, 0x10=ND5000) - DUMPLOAD (0x20=dump/load on, 0x00=off) - USE_DB_WARN (0x40=warn on use database, 0x00=don't warn) - INIT_DB_FATAL (0x80=fatal on init db, 0x00=not fatal) OptionFlags2: 1 byte - INIT_LANG_WARN (0x01=warn on init language) - ODBC_ON (0x02=ODBC driver) - TRAN_BOUNDARY (0x04=transaction boundary) - CACHE_CONNECT (0x08=cache connection) - USER_NORMAL (0x00), USER_SERVER (0x10), USER_REMUSER (0x20), USER_SQLREPL (0x30) - INTEGRATED_SECURITY (0x80=Windows auth) TypeFlags: 1 byte - SQL_TYPE (0x00=SQL, 0x01=DFLT, 0x02=TSQL) - OLEDB (0x10=OLEDB) - READ_ONLY_INTENT (0x20=read-only) OptionFlags3: 1 byte - CHANGE_PASSWORD (0x01) - SEND_YUKON_BINARY_XML (0x02) - USER_INSTANCE (0x04) - UNKNOWN_COLLATION_HANDLING (0x08) - EXTENSION (0x10=feature extension present) ClientTimeZone: 4 bytes (minutes offset from UTC) ClientLCID: 4 bytes (locale ID)
Offset/Length Pairs (2 bytes offset, 2 bytes length for each): HostName: offset, length (in characters, UTF-16LE) UserName: offset, length Password: offset, length (obfuscated) AppName: offset, length ServerName: offset, length Extension: offset, length (if OptionFlags3 & EXTENSION) CltIntName: offset, length (client interface library) Language: offset, length Database: offset, length ClientID: 6 bytes (MAC address or unique ID) SSPI: offset, length (Windows auth blob) AtchDBFile: offset, length (attach DB filename) ChangePassword: offset, length (new password) SSPILong: 4 bytes length (long SSPI)
Variable Data: (all strings in UTF-16LE, password obfuscated)Password Obfuscation Algorithm:
def obfuscate_password(password_utf16): obfuscated = bytearray() for byte in password_utf16: # XOR with 0xA5 byte ^= 0xA5 # Swap nibbles byte = ((byte & 0x0F) << 4) | ((byte & 0xF0) >> 4) obfuscated.append(byte) return bytes(obfuscated)
def deobfuscate_password(obfuscated_utf16): password = bytearray() for byte in obfuscated_utf16: # Swap nibbles back byte = ((byte & 0x0F) << 4) | ((byte & 0xF0) >> 4) # XOR with 0xA5 byte ^= 0xA5 password.append(byte) return bytes(password).decode('utf-16-le')Step 5: LOGINACK (Server → Client)
Packet Header: Type: 0x04 (TABULAR RESULT) Status: 0x01 (EOM)
Token Stream: Token Type: 0xAD (LOGINACK) Token Length: 2 bytes Interface: 1 byte (0x00=SQL_DFLT, 0x01=SQL_TSQL) TDS Version: 4 bytes (0x74000004 = TDS 7.4) ProgName Length: 1 byte ProgName: Variable (UTF-16LE, server product name) MajorVer: 1 byte MinorVer: 1 byte BuildNumHi: 1 byte BuildNumLow: 1 byteStep 6: ENVCHANGE (Server → Client)
Token Type: 0xE3 (ENVCHANGE)Token Length: 2 bytesType: 1 byte - 0x01: Database changed - 0x02: Language changed - 0x03: Character set changed - 0x04: Packet size changed - 0x07: Collation changed - 0x0D: Begin transaction - 0x0E: Commit transaction - 0x0F: Rollback transactionNewValue Length: 1 byte (B_VARCHAR)NewValue: Variable (UTF-16LE)OldValue Length: 1 byteOldValue: Variable (UTF-16LE)Step 7: DONE (Server → Client)
Token Type: 0xFD (DONE)Status: 2 bytes - 0x0000: DONE_FINAL - 0x0001: DONE_MORE - 0x0002: DONE_ERROR - 0x0010: DONE_COUNTCurCmd: 2 bytes (0x0000 for login)DoneRowCount: 8 bytes (0 for login)Implementation Checklist
- PRELOGIN packet construction and parsing
- Encryption negotiation (ENCRYPT_OFF, ENCRYPT_ON, ENCRYPT_REQ)
- TLS handshake wrapping in PRELOGIN packets
- LOGIN7 packet construction
- Password obfuscation (XOR 0xA5 + nibble swap)
- UTF-16LE encoding for strings
- Token stream parsing (LOGINACK, ENVCHANGE, DONE, ERROR)
- MARS support negotiation (optional)
- Connection ID tracking (TRACEID)
- Error handling (ERROR token 0xAA)
4. DRDA (DB2) Authentication
Flow Diagram
Client Server | | |-------- EXCSAT ------------------------>| | (Exchange Server Attributes) | | EXTNAM, SRVCLSNM, SRVNAM, SRVRLSLV | | | |<------- EXCSATRD -----------------------| | (EXCSAT Reply Data) | | Server attributes | | | |-------- ACCSEC ------------------------>| | (Access Security) | | SECMEC (security mechanism) | | RDBNAM (database name, optional) | | | |<------- ACCSECRD -----------------------| | (Access Security Reply Data) | | Selected SECMEC, security token | | | [If SECMEC=USRIDPWD or USRENCPWD] |-------- SECCHK ------------------------>| | (Security Check) | | USRID, PASSWORD (or encrypted) | | | |<------- SECCHKRD -----------------------| | (Security Check Reply Data) | | (or SECCHKRM if error) | | | |-------- ACCRDB ------------------------>| | (Access RDB) | | RDBNAM, RDBACCCL, PRDID, TYPDEFNAM | | | |<------- ACCRDBRM -----------------------| | (Access RDB Reply Message) | | PRDID, SRVCLSNM, connection status | | | |<------- SQLCARD (optional) -------------| | (SQL Communications Area Reply Data) | | |Detailed Steps
Step 1: EXCSAT (Client → Server)
DDM Packet: Length: 0x002A (42 bytes) Magic: 0xD0 Format: 0x01 Code Point: 0x1041 (EXCSAT)
Objects (nested DDM structures): EXTNAM (0x115E): Length: 0x001C (28 bytes) Magic: 0xD0 Format: 0x01 Code Point: 0x115E Data: 20-byte app ID + 8-byte process/thread ID App ID: "PYTHON_APP_V1.0 " (padded to 20) Process ID: 0x00001234 (4 bytes) + Thread ID: 0x00005678 (4 bytes)
SRVCLSNM (0x1147) [optional]: Length: 0x000C Magic: 0xD0 Format: 0x01 Code Point: 0x1147 Data: "SQLDB2 " (server class, EBCDIC)
SRVNAM (0x116D) [optional]: Length: 0x000C Magic: 0xD0 Format: 0x01 Code Point: 0x116D Data: "CLIENT01" (server name, EBCDIC)
SRVRLSLV (0x115A) [optional]: Length: 0x000A Magic: 0xD0 Format: 0x01 Code Point: 0x115A Data: "QDB2/LNX 11.5.8.0" (release level)Step 2: EXCSATRD (Server → Client)
DDM Packet: Length: Variable Magic: 0xD0 Format: 0x01 Code Point: 0x1443 (EXCSATRD)
Objects: SRVCLSNM (0x1147): Server class: "SQLDB2 "
SRVNAM (0x116D): Server name: "DB2SERVER"
SRVRLSLV (0x115A): Server release: "QDB2/LNX 11.5.8.0"
EXTNAM (0x115E) [optional]: Server's external nameStep 3: ACCSEC (Client → Server)
DDM Packet: Length: Variable Magic: 0xD0 Format: 0x01 Code Point: 0x106D (ACCSEC)
Objects: SECMEC (0x11A4): Length: 0x0008 Magic: 0xD0 Format: 0x01 Code Point: 0x11A4 Data: 2-byte security mechanism code - 0x0003: USRIDPWD (user ID + password) - 0x0004: USRIDONL (user ID only) - 0x0007: USRENCPWD (encrypted password) - 0x0009: EUSRIDPWD (external user + password) - 0x000B: DCE (Kerberos)
RDBNAM (0x2110) [optional]: Length: 0x0018 Magic: 0xD0 Format: 0x01 Code Point: 0x2110 Data: "SAMPLE " (18 bytes, padded with spaces)
SECTKN (0x11DC) [optional, for DCE/Kerberos]: Security token dataStep 4: ACCSECRD (Server → Client)
DDM Packet: Length: Variable Magic: 0xD0 Format: 0x01 Code Point: 0x14AC (ACCSECRD)
Objects: SECMEC (0x11A4): Selected security mechanism (echoed from client)
SECTKN (0x11DC) [if applicable]: Server security token (challenge for auth)Step 5: SECCHK (Client → Server) [if USRIDPWD/USRENCPWD]
DDM Packet: Length: Variable Magic: 0xD0 Format: 0x01 Code Point: 0x106E (SECCHK)
Objects: USRID (0x11A0): Length: Variable Magic: 0xD0 Format: 0x01 Code Point: 0x11A0 Data: User ID (EBCDIC or ASCII based on negotiation) "DEMO " (padded to 8 bytes)
PASSWORD (0x11A1) [if USRIDPWD]: Length: Variable Magic: 0xD0 Format: 0x01 Code Point: 0x11A1 Data: Password (EBCDIC or ASCII) "DEMOPASS" (max 255 bytes)
ENCUSRID (0x11A2) [if USRENCPWD]: Encrypted user ID
ENCPASSWORD (0x11A3) [if USRENCPWD]: Encrypted passwordStep 6: SECCHKRD (Server → Client)
DDM Packet: Length: 0x0006 Magic: 0xD0 Format: 0x01 Code Point: 0x1219 (SECCHKRD)
(No additional data for successful auth)
OR (if error):DDM Packet: Code Point: 0x1121 (SECCHKRM - Security Check Reply Message) SECCHKCD (0x11A5): Security check code (error code)Step 7: ACCRDB (Client → Server)
DDM Packet: Length: Variable Magic: 0xD0 Format: 0x01 Code Point: 0x2001 (ACCRDB)
Objects: RDBNAM (0x2110): Database name: "SAMPLE " (18 bytes)
RDBACCCL (0x210F): Length: 0x0008 Magic: 0xD0 Format: 0x01 Code Point: 0x210F Data: 2-byte access class - 0x2407: SQLAM (SQL Application Manager) - 0x2408: RDA (Remote Data Access)
PRDID (0x112E): Length: Variable Magic: 0xD0 Format: 0x01 Code Point: 0x112E Data: Product ID (e.g., "JCC04210" for IBM Java driver)
TYPDEFNAM (0x002F): Length: Variable Magic: 0xD0 Format: 0x01 Code Point: 0x002F Data: Type definition name - "QTDSQLASC" (ASCII) - "QTDSQLX" (mixed EBCDIC/ASCII) - "QTDSQLJVM" (Java/UTF-8)
CRRTKN (0x2135) [optional]: Correlation token (connection tracking)
TRGDFTRT (0x213B) [optional]: Target default transaction (auto-commit, etc.)Step 8: ACCRDBRM (Server → Client)
DDM Packet: Length: Variable Magic: 0xD0 Format: 0x01 Code Point: 0x2201 (ACCRDBRM)
Objects: PRDID (0x112E): Server product ID: "SQL11058" (DB2 11.5.8)
SRVCLSNM (0x1147): Server class: "SQLDB2 "
TYPDEFNAM (0x002F): Agreed type definition: "QTDSQLASC"
CRRTKN (0x2135) [optional]: Correlation token (echoed or generated)
SRVDGN (0x1153) [optional]: Server diagnostic informationStep 9: SQLCARD (Server → Client) [optional]
DDM Packet: Code Point: 0x2408 (SQLCARD)
Objects: SQLSTATE: 5-byte SQL state code (e.g., "00000" for success) SQLCODE: 4-byte SQL code (0 for success, negative for error) SQLERRMC: Error message text ... (other SQL diagnostic fields)Character Encoding Handling
EBCDIC to ASCII Conversion:
def drda_encode_string(string, use_ebcdic=True): if use_ebcdic: # Convert to EBCDIC (CP037 or CP500) return string.encode('cp037') else: # Convert to ASCII (UTF-8) return string.encode('utf-8')
def drda_decode_string(bytes_data, use_ebcdic=True): if use_ebcdic: return bytes_data.decode('cp037') else: return bytes_data.decode('utf-8')Type Definition Negotiation:
- QTDSQLASC: ASCII encoding, negotiate early to avoid EBCDIC
- QTDSQLX: Mixed (some EBCDIC, some ASCII)
- QTDSQLJVM: UTF-8 encoding (modern clients)
Implementation Checklist
- DDM packet framing (length + 0xD0 + code point)
- EXCSAT/EXCSATRD exchange
- ACCSEC/ACCSECRD security negotiation
- SECCHK/SECCHKRD authentication (USRIDPWD minimum)
- ACCRDB/ACCRDBRM database access
- EBCDIC ↔ UTF-8 conversion (or negotiate ASCII early)
- Security mechanism support (USRIDPWD=0x0003 minimum)
- Database name padding (18 bytes, space-padded)
- Code point mapping (request → reply pairs)
- Error handling (SECCHKRM, ACCRDBRM errors)
5. Oracle Net/TTC Authentication (Simplified)
Flow Diagram
Client Server | | |-------- CONNECT ----------------------->| | (DESCRIPTION=(...), VERSION, SDU/TDU) | | | |<------- ACCEPT or REFUSE ---------------| | (VERSION, SDU/TDU negotiation) | | | |-------- DATA (Protocol Negotiation) --->| | (Supported versions: [6,5,4,3,2,1,0]) | | | |<------- DATA (Protocol Ack) ------------| | (Selected version, character set) | | | |-------- DATA (Data Type Negotiation) -->| | (Endianness, number format, etc.) | | | |<------- DATA (Data Type Ack) -----------| | | |-------- DATA (TTI O3LOGON Phase 1) ---->| | (User, Auth params, service name) | | | |<------- DATA (Auth Challenge) ----------| | (Server AUTH_SESSKEY, salt) | | | |-------- DATA (TTI O3LOGON Phase 2) ---->| | (Password hash, AUTH_SESSKEY) | | | |<------- DATA (Auth Result) -------------| | (Success or error) | | |Detailed Steps
Step 1: CONNECT (Client → Server)
TNS Header: Length: 0x00F4 (244 bytes, big-endian) Checksum: 0x0000 Type: 0x01 (CONNECT) Reserved: 0x00 Header Checksum: 0x0000
CONNECT Data: Version: 0x0138 (312 = Oracle 11g/12c) Version Compatible: 0x012C (300) Service Options: 0x0C41 - Broken Connect Notify (0x0001) - Packet Checksum (0x0040) - Header Checksum (0x0200) - Full Duplex (0x0400) - Half Duplex (0x0800) Session Data Unit: 0x2000 (8192 bytes max) Max Transmission Unit: 0x7FFF (32767 bytes) NT Protocol Characteristics: 0x4E54 ("NT") Line Turnaround: 0x0000 Value of 1: 0x0001 (byte order test) Data Length: Variable (e.g., 0x00A0 = 160 bytes) Data Offset: 0x003A (58 bytes from start) Max Connect Data: 0x00000000 Connect Flags 0: 0x41 Connect Flags 1: 0x41 Trace Facility: Variable Connect Data: "(DESCRIPTION=(CONNECT_DATA=(SERVICE_NAME=DEMO) (CID=(PROGRAM=python)(HOST=client)(USER=demo))) (ADDRESS=(PROTOCOL=TCP)(HOST=server)(PORT=1521)))"Step 2: ACCEPT (Server → Client)
TNS Header: Length: Variable Checksum: 0x0000 Type: 0x02 (ACCEPT) Reserved: 0x00 Header Checksum: 0x0000
ACCEPT Data: Version: 0x0138 (312) Service Options: 0x0C41 (negotiated) Session Data Unit: 0x2000 (agreed SDU) Max Transmission Unit: 0x7FFF Value of 1: 0x0001 Data Length: Variable Data Offset: Variable Connect Flags: Variable Connect Data: (optional, typically empty)Step 3: DATA - Protocol Negotiation (Client → Server)
TNS Header: Length: Variable Checksum: 0x0000 Type: 0x06 (DATA) Reserved: 0x00 Header Checksum: 0x0000
Data Flags: 0x0000Data Type: 0x01 (Protocol Negotiation)Payload: Supported Versions: [0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00]Step 4: DATA - Protocol Ack (Server → Client)
TNS Header: Type: 0x06 (DATA)
Data Flags: 0x0000Data Type: 0x01Payload: Selected Version: 0x06 Character Set: "AL32UTF8\0" or "US7ASCII\0" National Character Set: "AL16UTF16\0"Step 5: DATA - Data Type Negotiation (Client → Server)
TNS Header: Type: 0x06 (DATA)
Data Flags: 0x0000Data Type: 0x02 (Data Type Negotiation)Payload: Endianness: 0x01 (little-endian) or 0x00 (big-endian) Character Set Form: 0x01 (explicit) Conversion: 0x00 ... (internal data representations)Step 6: DATA - TTI O3LOGON Phase 1 (Client → Server)
TNS Header: Type: 0x06 (DATA)
Data Flags: 0x0000Data Type: 0x03 (TTI Function Call)Payload: TTI Opcode: 0x76 (O3LOGON) Function Call Data: - AUTH_TERMINAL: Client hostname - AUTH_PROGRAM_NM: "python\0" - AUTH_MACHINE: Client machine name - AUTH_PID: Process ID - AUTH_SID: User name - AUTH_LOGON_USER: OS user - AUTH_ALTER_SESSION: "ALTER SESSION SET ..." - AUTH_DRIVER_NAME: "python-oracledb : 2.1.0" - SERVICE_NAME: "DEMO" - AUTH_VERSION: Client versionStep 7: DATA - Auth Challenge (Server → Client)
TNS Header: Type: 0x06 (DATA)
Data Type: 0x03 (TTI)Payload: TTI Opcode: 0x76 (O3LOGON response) AUTH_SESSKEY: 64-byte session key (hex) AUTH_VFR_DATA: Server version, features AUTH_SVR_DN: Server distinguished name (optional) AUTH_SALT: Salt for password hashing (optional)Step 8: DATA - TTI O3LOGON Phase 2 (Client → Server)
TNS Header: Type: 0x06 (DATA)
Data Type: 0x03 (TTI)Payload: TTI Opcode: 0x76 (O3LOGON continuation) AUTH_PASSWORD: Hashed password (algorithm varies) - Legacy: DES-based hash - Modern: SHA-1 or SHA-2 based AUTH_SESSKEY: Session key from server (echoed) AUTH_RTT: Round-trip timePassword Hashing (Simplified):
Legacy (pre-11g): - DES encryption of username with password as key - Highly insecure, not recommended
11g+ (SHA-1 based): - PBKDF2-like derivation - Hash = SHA1(password + AUTH_SALT) - Multiple rounds for security
12c+ (Enhanced): - SHA-256 or SHA-512 based - PBKDF2 with salt and iterationsStep 9: DATA - Auth Result (Server → Client)
TNS Header: Type: 0x06 (DATA)
Data Type: 0x03 (TTI)Payload: TTI Opcode: 0x76 (O3LOGON result) AUTH_STATUS: 0x00 (success) or error code AUTH_SESSION_ID: Server session ID AUTH_SERIAL_NUM: Session serial number
OR (if error): ORA-01017: invalid username/password; logon deniedConnect Data Format
Structure:
(DESCRIPTION= (CONNECT_DATA= (SERVICE_NAME=<service>) (SID=<sid>) # Alternative to SERVICE_NAME (SERVER=DEDICATED) # or SHARED (INSTANCE_NAME=<instance>) (CID= (PROGRAM=<program>) (HOST=<hostname>) (USER=<os_user>) ) ) (ADDRESS_LIST= (ADDRESS= (PROTOCOL=TCP) (HOST=<server>) (PORT=<port>) ) ))Parsing:
import re
def parse_connect_data(connect_string): # Simplified parser service_match = re.search(r'SERVICE_NAME=([^)]+)', connect_string) host_match = re.search(r'HOST=([^)]+)', connect_string) port_match = re.search(r'PORT=(\d+)', connect_string)
return { 'service_name': service_match.group(1) if service_match else None, 'host': host_match.group(1) if host_match else None, 'port': int(port_match.group(1)) if port_match else 1521 }Implementation Checklist (Bronze Level)
- TNS packet framing (big-endian length + type)
- CONNECT/ACCEPT handshake
- Connect data parsing (SERVICE_NAME extraction)
- Protocol version negotiation (version 6 recommended)
- Data type negotiation (endianness, character set)
- TTC/TTI basic support (O3LOGON opcode)
- Password hashing (legacy or modern, based on server)
- Character set handling (AL32UTF8)
- Error response parsing (ORA-xxxxx codes)
- “SELECT 1 FROM DUAL” support (OSQL opcode)
Note: Full Oracle protocol implementation is complex. Bronze level focuses on basic connectivity and simple queries. Silver/Gold levels would require reverse engineering or using oracledb Thin mode as reference.
Summary & Recommendations
Implementation Priority
-
PostgreSQL SCRAM-SHA-256 (Highest complexity, most value)
- RFC-compliant implementation
- TLS ALPN integration
- Excellent for testing auth framework
-
MySQL caching_sha2_password (Medium complexity, high value)
- Server-first handshake (unique pattern)
- RSA encryption for non-TLS connections
- Tests capability flag negotiation
-
TDS 7.4 LOGIN7 (Medium-high complexity)
- TLS wrapping in PRELOGIN
- Password obfuscation
- Bronze level: basic auth only
-
DRDA USRIDPWD (Medium complexity)
- DDM packet framing
- EBCDIC handling (or negotiate ASCII)
- Bronze level: simple auth sufficient
-
Oracle O3LOGON (Highest complexity, lowest priority)
- Reverse-engineered protocol
- Start with Bronze level only
- Consider using oracledb Thin as reference
Testing Strategy
Unit Tests:
- Password hashing algorithms (SCRAM, SHA256, obfuscation)
- Packet framing and parsing
- Character encoding conversion
Integration Tests:
- Each Python client library (psycopg2, asyncpg, PyMySQL, etc.)
- Authentication success/failure scenarios
- TLS negotiation and ALPN selection
Security Tests:
- Invalid credentials handling
- TLS enforcement
- Replay attack prevention (nonce validation)
Common Implementation Patterns
Packet Framing:
class ProtocolHandler: def read_packet(self, socket): # Read length prefix (protocol-specific) # Read packet type (if applicable) # Read payload # Return structured packet pass
def write_packet(self, socket, packet): # Encode packet type # Encode length prefix # Encode payload # Send to socket passAuthentication State Machine:
class AuthStateMachine: STATES = ['INIT', 'CHALLENGE', 'RESPONSE', 'SUCCESS', 'FAILURE']
def __init__(self): self.state = 'INIT'
def process(self, message): if self.state == 'INIT': # Send challenge or request credentials self.state = 'CHALLENGE' elif self.state == 'CHALLENGE': # Verify credentials if valid: self.state = 'SUCCESS' else: self.state = 'FAILURE' # ...Error Handling:
- Map protocol-specific errors to generic HeliosDB errors
- Preserve original error codes for debugging
- Log authentication attempts and failures
Document Status: Complete Next Steps: Coordinate with architect on auth framework design, coordinate with coder on implementation modules