1use std::io::{Cursor, Write};
11use std::str::FromStr;
12
13use opcua_types::{
14 encoding::{read_u32, write_u32},
15 status_code::StatusCode,
16 ByteString, UAString,
17 {SignatureData, UserNameIdentityToken, UserTokenPolicy, X509IdentityToken},
18};
19use opcua_types::{Error, IssuedIdentityToken, MessageSecurityMode};
20use tracing::{error, warn};
21
22use crate::policy::aes::{AesAsymmetricEncryptionAlgorithm, OaepSha1, OaepSha256, Pkcs1v15};
23
24use super::{PrivateKey, SecurityPolicy, X509};
25
26pub trait LegacySecret {
28 fn raw_secret(&self) -> &ByteString;
30 fn encryption_algorithm(&self) -> &UAString;
32}
33
34impl LegacySecret for UserNameIdentityToken {
35 fn raw_secret(&self) -> &ByteString {
36 &self.password
37 }
38
39 fn encryption_algorithm(&self) -> &UAString {
40 &self.encryption_algorithm
41 }
42}
43
44impl LegacySecret for IssuedIdentityToken {
45 fn raw_secret(&self) -> &ByteString {
46 &self.token_data
47 }
48
49 fn encryption_algorithm(&self) -> &UAString {
50 &self.encryption_algorithm
51 }
52}
53
54impl LegacySecret for LegacyEncryptedSecret {
55 fn raw_secret(&self) -> &ByteString {
56 &self.secret
57 }
58
59 fn encryption_algorithm(&self) -> &UAString {
60 &self.encryption_algorithm
61 }
62}
63
64pub fn legacy_decrypt_secret(
66 secret: &impl LegacySecret,
67 server_nonce: &[u8],
68 server_key: &PrivateKey,
69) -> Result<ByteString, Error> {
70 if secret.encryption_algorithm().is_empty() {
71 Ok(secret.raw_secret().clone())
72 } else {
73 let encryption_algorithm = secret.encryption_algorithm().as_ref();
75 match encryption_algorithm {
76 super::algorithms::ENC_RSA_15 => {
77 legacy_secret_decrypt::<Pkcs1v15>(secret.raw_secret(), server_nonce, server_key)
78 }
79 super::algorithms::ENC_RSA_OAEP => {
80 legacy_secret_decrypt::<OaepSha1>(secret.raw_secret(), server_nonce, server_key)
81 }
82 super::algorithms::ENC_RSA_OAEP_SHA256 => {
83 legacy_secret_decrypt::<OaepSha256>(secret.raw_secret(), server_nonce, server_key)
84 }
85 r => {
86 error!("decrypt_user_identity_token_password has rejected unsupported user identity encryption algorithm \"{}\"", encryption_algorithm);
87 Err(Error::new(
88 StatusCode::BadIdentityTokenInvalid,
89 format!("Identity token rejected, unsupported encryption algorithm {r}"),
90 ))
91 }
92 }
93 }
94}
95
96pub struct LegacyEncryptedSecret {
98 pub policy: UAString,
100 pub secret: ByteString,
102 pub encryption_algorithm: UAString,
104}
105
106enum EncryptionMode {
107 None,
108 AsymmetricFor(SecurityPolicy),
109}
110
111pub fn legacy_encrypt_secret(
114 channel_security_policy: SecurityPolicy,
115 channel_security_mode: MessageSecurityMode,
116 user_token_policy: &UserTokenPolicy,
117 nonce: &[u8],
118 cert: &Option<X509>,
119 secret_to_encrypt: &[u8],
120) -> Result<LegacyEncryptedSecret, Error> {
121 let token_security_policy = if user_token_policy.security_policy_uri.is_empty() {
122 None
123 } else {
124 Some(SecurityPolicy::from_str(user_token_policy.security_policy_uri.as_ref()).unwrap())
125 };
126
127 let encryption_mode = match (
129 channel_security_policy,
130 channel_security_mode,
131 token_security_policy,
132 ) {
133 (_, _, Some(SecurityPolicy::Unknown)) | (SecurityPolicy::Unknown, _, _) => {
135 return Err(Error::new(
137 StatusCode::BadSecurityPolicyRejected,
138 "Unknown user token security policy",
139 ));
140 }
141
142 (SecurityPolicy::None, MessageSecurityMode::None, Some(SecurityPolicy::None) | None) => {
144 EncryptionMode::None
145 }
146 (SecurityPolicy::None, MessageSecurityMode::None, Some(p)) => {
147 EncryptionMode::AsymmetricFor(p)
148 }
149 (p, MessageSecurityMode::Sign | MessageSecurityMode::SignAndEncrypt, None) => {
150 EncryptionMode::AsymmetricFor(p)
151 }
152 (_, MessageSecurityMode::SignAndEncrypt, Some(SecurityPolicy::None)) => {
153 EncryptionMode::None
154 }
155 (_, MessageSecurityMode::Sign, Some(SecurityPolicy::None)) => {
156 return Err(Error::new(
157 StatusCode::BadSecurityPolicyRejected,
158 "User token policy security policy is None but message security mode is Sign",
159 ))
160 }
161 (_, MessageSecurityMode::Sign | MessageSecurityMode::SignAndEncrypt, Some(p)) => {
162 EncryptionMode::AsymmetricFor(p)
163 }
164 (_, MessageSecurityMode::None | MessageSecurityMode::Invalid, _) => {
166 return Err(Error::new(
167 StatusCode::BadSecurityChecksFailed,
168 "Invalid message security mode",
169 ));
170 }
171 };
172
173 match encryption_mode {
174 EncryptionMode::None => {
175 if matches!(channel_security_policy, SecurityPolicy::None)
176 || matches!(
177 channel_security_mode,
178 MessageSecurityMode::None | MessageSecurityMode::Sign
179 )
180 {
181 warn!("A user identity's password is being sent over the network in plain text. This could be a serious security issue");
182 }
183 Ok(LegacyEncryptedSecret {
184 secret: ByteString::from(secret_to_encrypt),
185 encryption_algorithm: UAString::null(),
186 policy: user_token_policy.policy_id.clone(),
187 })
188 }
189 EncryptionMode::AsymmetricFor(security_policy) => {
190 let password = legacy_secret_encrypt(
191 secret_to_encrypt,
192 nonce,
193 cert.as_ref().unwrap(),
194 security_policy,
195 )?;
196
197 Ok(LegacyEncryptedSecret {
198 secret: password,
199 encryption_algorithm: UAString::from(
200 security_policy
201 .asymmetric_encryption_algorithm()
202 .ok_or_else(|| {
203 Error::new(
204 StatusCode::BadSecurityPolicyRejected,
205 "Security policy does not support asymmetric encryption",
206 )
207 })?,
208 ),
209 policy: user_token_policy.policy_id.clone(),
210 })
211 }
212 }
213}
214
215pub(crate) fn legacy_secret_encrypt(
218 password: &[u8],
219 server_nonce: &[u8],
220 server_cert: &X509,
221 policy: SecurityPolicy,
222) -> Result<ByteString, Error> {
223 let plaintext_size = 4 + password.len() + server_nonce.len();
225 let mut src = Cursor::new(vec![0u8; plaintext_size]);
226
227 write_u32(&mut src, (plaintext_size - 4) as u32)?;
229 src.write(password).map_err(Error::decoding)?;
230 src.write(server_nonce).map_err(Error::decoding)?;
231
232 let public_key = server_cert.public_key()?;
234
235 let cipher_size = policy.calculate_cipher_text_size(plaintext_size, &public_key);
236 let mut dst = vec![0u8; cipher_size];
237 let actual_size = policy
238 .asymmetric_encrypt(&public_key, &src.into_inner(), &mut dst)
239 .map_err(Error::encoding)?;
240
241 assert_eq!(actual_size, cipher_size);
242
243 Ok(ByteString::from(dst))
244}
245
246pub(crate) fn legacy_secret_decrypt<T: AesAsymmetricEncryptionAlgorithm>(
249 secret: &ByteString,
250 server_nonce: &[u8],
251 server_key: &PrivateKey,
252) -> Result<ByteString, Error> {
253 if secret.is_null_or_empty() {
254 Err(Error::decoding("Missing server secret"))
255 } else {
256 let src = secret.value.as_ref().unwrap();
258 let mut dst = vec![0u8; src.len()];
259 let mut actual_size = server_key
260 .private_decrypt::<T>(src, &mut dst)
261 .map_err(Error::decoding)?;
262
263 let mut dst = Cursor::new(dst);
264 let plaintext_size = read_u32(&mut dst)? as usize;
265
266 let mut dst = dst.into_inner();
275 if actual_size > plaintext_size + 4 {
276 let padding_bytes = &dst[plaintext_size + 4..];
277 if !padding_bytes.iter().all(|&x| x == 0) {
282 return Err(Error::decoding(
283 "Non-zero padding bytes in decrypted password",
284 ));
285 } else {
286 dst.truncate(plaintext_size + 4);
287 actual_size = dst.len();
288 }
289 }
290
291 if plaintext_size + 4 != actual_size {
292 Err(Error::decoding("Invalid plaintext size"))
293 } else {
294 let nonce_len = server_nonce.len();
295 let nonce_begin = actual_size - nonce_len;
296 let nonce = &dst[nonce_begin..(nonce_begin + nonce_len)];
297 if nonce != server_nonce {
298 Err(Error::decoding("Invalid nonce"))
299 } else {
300 let password = &dst[4..nonce_begin];
301 Ok(ByteString::from(password))
302 }
303 }
304 }
305}
306
307pub fn verify_x509_identity_token(
309 token: &X509IdentityToken,
310 user_token_signature: &SignatureData,
311 security_policy: SecurityPolicy,
312 server_cert: &X509,
313 server_nonce: &[u8],
314) -> Result<(), Error> {
315 let signing_cert = super::x509::X509::from_byte_string(&token.certificate_data)?;
328 super::verify_signature_data(
329 user_token_signature,
330 security_policy,
331 &signing_cert,
332 server_cert,
333 server_nonce,
334 )
335}