I have been studying Oracle’s mechanism for authenticating against a 10g database. Although it is less documented than its 9i counterpart, I have still managed to find many of its details on various web sites and blogs. One piece remains a mystery, however. Before I mention what is missing, let me explain what is known about the protocol in pseudo code:
// CLIENT SIDE PSEUDO CODE user = 'SCOTT' password = 'TIGER' password_hash = oracle_password_hash(user, password) // 1. Client provides user name to server send(user) // 2. Server responds with its encrypted AUTH_SESSKEY, // a randomly generated number associated with the current session encrypted_server_AUTH_SESSKEY = receive_AUTH_SESSKEY() // 32 bytes decrypted_server_AUTH_SESSKEY = aes_decrypt( encrypted_input => encrypted_server_AUTH_SESSKEY, decryption_key => password_hash ) // 3. Client generates its own AUTH_SESSKEY for this session unencrypted_client_AUTH_SESSKEY = generate_random_AUTH_SESSKEY() // 32 bytes encrypted_client_AUTH_SESSKEY = aes_encrypt( unencrypted_input => unencrypted_client_AUTH_SESSKEY, encryption_key => password_hash ) // 4. Client combines the two AUTH_SESSKEYs using a known Oracle-specific algorithm combined_AUTH_SESSKEYs = oracle_combine(decrypted_server_AUTH_SESSKEY, unencrypted_client_AUTH_SESSKEY) // 5. Client builds AUTH_PASSWORD unencrypted_AUTH_PASSWORD = byte[32] unencrypted_AUTH_PASSWORD[0 .. 16] = ??? // THIS IS THE PROBLEM unencrypted_AUTH_PASSWORD[16 .. 16 + len(password)] = password unencrypted_AUTH_PASSWORD[16 + len(password) .. ] = PKCS#7 padding // 6. Client encrypts the AUTH_PASSWORD data using the combined AUTH_SESSKEYs as the encryption key encrypted_AUTH_PASSWORD = aes_encrypt( unencrypted_input => unencrypted_AUTH_PASSWORD, encryption_key => combined_AUTH_SESSKEYs ) // 7. Client transmits its encrypted AUTH_SESSKEY and AUTH_PASSWORD to server for verification send(encrypted_client_AUTH_SESSKEY, encrypted_AUTH_PASSWORD)
What does the Oracle client put in the lower 16 bytes of the AUTH_PASSWORD value in step 5?
Almost all documentation I have found cares only about obtaining the plain text password contained within, paying little attention to these first bytes. I have tried looking at the JDBC driver, but it appears that even the 10g version avoids this authentication scheme by requesting that the server revert back to an older scheme (which happens to be much better understood). An excellent C program demonstrates the decryption of AUTH_PASSWORD.
Can anyone point me in the right direction?
I have determined that the 16 bytes immediately before the plain text password are randomly generated (for the curious, take a look at the ztvo5pe function exported by the oran10.dll library – you will see two successive calls to ztcen, the first call fills it in).
I originally posted the question because I was writing a small program to connect to an Oracle database without the use of Oracle’s JDBC driver. I was finding that the database was rejecting my 32-byte AUTH_PASSWORD. I assumed that it was rejected because I had put an incorrect value in these first 16 bytes. I was wrong. It appears that these do not have any effect on whether or not a user authenticates.
Rather, it turns out that the database was rejecting my AUTH_PASSWORD because of the trailing bytes that come immediately after the plain text password. I naively padded the buffer with zeroes. It should have been padded according to the PKCS #7 specification.