oracledb_protocol/
crypto.rs1#![forbid(unsafe_code)]
2
3use aes::cipher::{BlockDecrypt, BlockEncrypt, KeyInit};
4use aes::Aes256;
5use hex::FromHex;
6use md5::Md5;
7use pbkdf2::pbkdf2_hmac;
8use rand::rngs::OsRng;
9use rand::RngCore;
10use sha1::Sha1;
11use sha2::{Digest, Sha512};
12
13use crate::thin::{TNS_VERIFIER_TYPE_11G_1, TNS_VERIFIER_TYPE_11G_2, TNS_VERIFIER_TYPE_12C};
14use crate::{ProtocolError, Result};
15
16#[derive(Clone, Debug, Eq, PartialEq)]
17pub struct EncryptedPassword {
18 pub session_key: String,
19 pub speedy_key: Option<String>,
20 pub password: String,
21 pub combo_key: Vec<u8>,
22}
23
24pub fn generate_verifier(
25 password: &[u8],
26 session_data: &std::collections::BTreeMap<String, String>,
27 verifier_type: u32,
28) -> Result<EncryptedPassword> {
29 let verifier_data = decode_session_hex(session_data, "AUTH_VFR_DATA")?;
30 let (key_len, password_hash, password_key) = match verifier_type {
31 TNS_VERIFIER_TYPE_12C => {
32 let iterations = parse_session_u32(session_data, "AUTH_PBKDF2_VGEN_COUNT")?;
33 let mut salt = verifier_data.clone();
34 salt.extend_from_slice(b"AUTH_PBKDF2_SPEEDY_KEY");
35 let mut password_key = [0u8; 64];
36 pbkdf2_hmac::<Sha512>(password, &salt, iterations, &mut password_key);
37 let mut hasher = Sha512::new();
38 hasher.update(password_key);
39 hasher.update(&verifier_data);
40 (
41 32usize,
42 hasher.finalize()[..32].to_vec(),
43 Some(password_key.to_vec()),
44 )
45 }
46 TNS_VERIFIER_TYPE_11G_1 | TNS_VERIFIER_TYPE_11G_2 => {
47 let mut hasher = Sha1::new();
48 hasher.update(password);
49 hasher.update(&verifier_data);
50 let mut hash = hasher.finalize().to_vec();
51 hash.extend_from_slice(&[0, 0, 0, 0]);
52 (24usize, hash, None)
53 }
54 other => {
55 return Err(ProtocolError::UnsupportedVerifier {
56 verifier_type: other,
57 })
58 }
59 };
60
61 let encoded_server_key = decode_session_hex(session_data, "AUTH_SESSKEY")?;
62 let session_key_part_a = decrypt_cbc_raw(&password_hash, &encoded_server_key)?;
63 let mut session_key_part_b = vec![0u8; session_key_part_a.len()];
64 OsRng.fill_bytes(&mut session_key_part_b);
65 let encoded_client_key = encrypt_cbc(&password_hash, &session_key_part_b, false)?;
66
67 let (session_key, combo_key) = if session_key_part_a.len() == 48 {
68 let session_key = hex_upper_truncated(&encoded_client_key, 96);
69 let mut mixed = [0u8; 24];
70 for ix in 16..40 {
71 mixed[ix - 16] = session_key_part_a[ix] ^ session_key_part_b[ix];
72 }
73 let mut first = Md5::new();
74 first.update(&mixed[..16]);
75 let mut second = Md5::new();
76 second.update(&mixed[16..]);
77 let mut combo = first.finalize().to_vec();
78 combo.extend_from_slice(&second.finalize());
79 combo.truncate(key_len);
80 (session_key, combo)
81 } else {
82 let session_key = hex_upper_truncated(&encoded_client_key, 64);
83 let salt = decode_session_hex(session_data, "AUTH_PBKDF2_CSK_SALT")?;
84 let iterations = parse_session_u32(session_data, "AUTH_PBKDF2_SDER_COUNT")?;
85 if session_key_part_a.len() < key_len || session_key_part_b.len() < key_len {
86 return Err(ProtocolError::TtcDecode("session key too short"));
87 }
88 let mut temp_key = session_key_part_b[..key_len].to_vec();
89 temp_key.extend_from_slice(&session_key_part_a[..key_len]);
90 let temp_key_hex = hex_upper(&temp_key);
91 let mut combo_key = vec![0u8; key_len];
92 pbkdf2_hmac::<Sha512>(temp_key_hex.as_bytes(), &salt, iterations, &mut combo_key);
93 (session_key, combo_key)
94 };
95
96 let speedy_key = if matches!(verifier_type, TNS_VERIFIER_TYPE_12C) {
97 let password_key =
98 password_key.ok_or(ProtocolError::TtcDecode("missing 12c password key"))?;
99 let mut salt = [0u8; 16];
100 OsRng.fill_bytes(&mut salt);
101 let mut plain = salt.to_vec();
102 plain.extend_from_slice(&password_key);
103 let encrypted = encrypt_cbc(&combo_key, &plain, false)?;
104 Some(hex_upper_truncated(&encrypted, 160))
105 } else {
106 None
107 };
108
109 let mut salt = [0u8; 16];
110 OsRng.fill_bytes(&mut salt);
111 let mut password_with_salt = salt.to_vec();
112 password_with_salt.extend_from_slice(password);
113 let encrypted_password = encrypt_cbc(&combo_key, &password_with_salt, false)?;
114
115 Ok(EncryptedPassword {
116 session_key,
117 speedy_key,
118 password: hex_upper(&encrypted_password),
119 combo_key,
120 })
121}
122
123pub fn encrypt_change_password_pair(
127 combo_key: &[u8],
128 old_password: &[u8],
129 new_password: &[u8],
130) -> Result<(String, String)> {
131 let mut salt = [0u8; 16];
132 OsRng.fill_bytes(&mut salt);
133 let mut old_with_salt = salt.to_vec();
134 old_with_salt.extend_from_slice(old_password);
135 let encrypted_old = encrypt_cbc(combo_key, &old_with_salt, false)?;
136 let mut new_with_salt = salt.to_vec();
137 new_with_salt.extend_from_slice(new_password);
138 let encrypted_new = encrypt_cbc(combo_key, &new_with_salt, false)?;
139 Ok((hex_upper(&encrypted_old), hex_upper(&encrypted_new)))
140}
141
142pub fn verify_server_response(
143 combo_key: &[u8],
144 session_data: &std::collections::BTreeMap<String, String>,
145) -> Result<()> {
146 let Some(response_hex) = session_data.get("AUTH_SVR_RESPONSE") else {
147 return Err(ProtocolError::InvalidServerResponse);
148 };
149 let encoded_response =
150 Vec::from_hex(response_hex).map_err(|_| ProtocolError::InvalidServerResponse)?;
151 let response = decrypt_cbc_raw(combo_key, &encoded_response)?;
152 if response.get(16..32) == Some(b"SERVER_TO_CLIENT") {
153 Ok(())
154 } else {
155 Err(ProtocolError::InvalidServerResponse)
156 }
157}
158
159fn decode_session_hex(
160 session_data: &std::collections::BTreeMap<String, String>,
161 key: &'static str,
162) -> Result<Vec<u8>> {
163 let value = session_data
164 .get(key)
165 .ok_or(ProtocolError::MissingAuthParameter { key })?;
166 Vec::from_hex(value).map_err(|_| ProtocolError::MissingAuthParameter { key })
167}
168
169fn parse_session_u32(
170 session_data: &std::collections::BTreeMap<String, String>,
171 key: &'static str,
172) -> Result<u32> {
173 session_data
174 .get(key)
175 .ok_or(ProtocolError::MissingAuthParameter { key })?
176 .parse()
177 .map_err(|_| ProtocolError::MissingAuthParameter { key })
178}
179
180fn encrypt_cbc(key: &[u8], plain_text: &[u8], zeros: bool) -> Result<Vec<u8>> {
181 let mut data = plain_text.to_vec();
182 let pad_len = 16 - (data.len() % 16);
183 if zeros {
184 data.extend(std::iter::repeat_n(0, pad_len));
185 } else {
186 data.extend(std::iter::repeat_n(pad_len as u8, pad_len));
187 }
188
189 let cipher = Aes256::new_from_slice(key).map_err(|_| ProtocolError::InvalidAesKey)?;
190 let mut previous = [0u8; 16];
191 for block in data.chunks_mut(16) {
192 for (byte, prev) in block.iter_mut().zip(previous) {
193 *byte ^= prev;
194 }
195 let block_ref = aes::cipher::generic_array::GenericArray::from_mut_slice(block);
196 cipher.encrypt_block(block_ref);
197 previous.copy_from_slice(block_ref);
198 }
199 Ok(data)
200}
201
202fn decrypt_cbc_raw(key: &[u8], encrypted_text: &[u8]) -> Result<Vec<u8>> {
203 if !encrypted_text.len().is_multiple_of(16) {
204 return Err(ProtocolError::TtcDecode(
205 "AES-CBC ciphertext length is not block aligned",
206 ));
207 }
208 let cipher = Aes256::new_from_slice(key).map_err(|_| ProtocolError::InvalidAesKey)?;
209 let mut out = encrypted_text.to_vec();
210 let mut previous = [0u8; 16];
211 for block in out.chunks_mut(16) {
212 let current: [u8; 16] = block
213 .try_into()
214 .map_err(|_| ProtocolError::TtcDecode("invalid AES block"))?;
215 let block_ref = aes::cipher::generic_array::GenericArray::from_mut_slice(block);
216 cipher.decrypt_block(block_ref);
217 for (byte, prev) in block.iter_mut().zip(previous) {
218 *byte ^= prev;
219 }
220 previous = current;
221 }
222 Ok(out)
223}
224
225fn hex_upper(bytes: &[u8]) -> String {
226 hex::encode_upper(bytes)
227}
228
229fn hex_upper_truncated(bytes: &[u8], chars: usize) -> String {
230 let mut text = hex_upper(bytes);
231 text.truncate(chars);
232 text
233}