1use std::fmt;
10
11use opcua_types::{
12 status_code::StatusCode, ByteString, EncodingResult, Error, SignatureData, UAString,
13};
14use tracing::{error, trace};
15
16pub use crate::aes::{AesKey, KeySize, PKey, PrivateKey, PublicKey};
17pub use crate::x509::{X509Data, X509Error, X509};
18pub use certificate_store::CertificateStore;
19pub use policy::{AesDerivedKeys, PaddingInfo};
20pub use security_policy::SecurityPolicy;
21pub use thumbprint::Thumbprint;
22pub use user_identity::{
23 legacy_decrypt_secret, legacy_encrypt_secret, verify_x509_identity_token,
24 LegacyEncryptedSecret, LegacySecret,
25};
26
27#[cfg(test)]
28mod tests;
29
30mod certificate_store;
31mod hash;
32pub mod random;
33mod security_policy;
34mod thumbprint;
35mod user_identity;
36mod x509;
37
38mod policy;
39
40mod aes;
41
42pub const SHA1_SIZE: usize = 20;
44pub const SHA256_SIZE: usize = 32;
46
47pub(crate) mod algorithms {
49 #[allow(unused)]
51 pub(crate) const ENC_AES128_CBC: &str = "http://www.w3.org/2001/04/xmlenc#aes128-cbc";
52
53 #[allow(unused)]
55 pub(crate) const ENC_AES256_CBC: &str = "http://www.w3.org/2001/04/xmlenc#aes256-cbc";
56
57 pub(crate) const ENC_RSA_15: &str = "http://www.w3.org/2001/04/xmlenc#rsa-1_5";
59
60 pub(crate) const ENC_RSA_OAEP: &str = "http://www.w3.org/2001/04/xmlenc#rsa-oaep";
62
63 pub(crate) const ENC_RSA_OAEP_SHA256: &str =
65 "http://opcfoundation.org/UA/security/rsa-oaep-sha2-256";
66
67 #[allow(unused)]
72 pub(crate) const DSIG_HMAC_SHA1: &str = "http://www.w3.org/2000/09/xmldsig#hmac-sha1";
73
74 #[allow(unused)]
76 pub(crate) const DSIG_HMAC_SHA256: &str = "http://www.w3.org/2000/09/xmldsig#hmac-sha256";
77
78 pub(crate) const DSIG_RSA_SHA1: &str = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
80
81 pub(crate) const DSIG_RSA_SHA256: &str = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
83
84 pub(crate) const DSIG_RSA_PSS_SHA2_256: &str =
86 "http://opcfoundation.org/UA/security/rsa-pss-sha2-256";
87
88 }
94
95fn concat_data_and_nonce(data: &[u8], nonce: &[u8]) -> Vec<u8> {
96 let mut buffer: Vec<u8> = Vec::with_capacity(data.len() + nonce.len());
97 buffer.extend_from_slice(data);
98 buffer.extend_from_slice(nonce);
99 buffer
100}
101
102pub fn create_signature_data(
104 signing_key: &PrivateKey,
105 security_policy: SecurityPolicy,
106 contained_cert: &ByteString,
107 nonce: &ByteString,
108) -> Result<SignatureData, StatusCode> {
109 let (algorithm, signature) = match security_policy {
110 SecurityPolicy::Unknown => return Err(StatusCode::BadSecurityPolicyRejected),
111 SecurityPolicy::None => (UAString::null(), ByteString::null()),
112 security_policy => {
113 if contained_cert.is_null_or_empty() {
114 error!("Null certificate passed to create_signature_data");
115 return Err(StatusCode::BadCertificateInvalid);
116 }
117 if nonce.is_null_or_empty() {
118 error!("Null nonce passed to create_signature_data");
119 return Err(StatusCode::BadNonceInvalid);
120 }
121
122 let data = concat_data_and_nonce(contained_cert.as_ref(), nonce.as_ref());
123 let signing_key_size = signing_key.size();
124 let mut signature = vec![0u8; signing_key_size];
125 let _ = security_policy.asymmetric_sign(signing_key, &data, &mut signature)?;
126 (
127 UAString::from(security_policy.asymmetric_signature_algorithm()),
128 ByteString::from(&signature),
129 )
130 }
131 };
132
133 let signature_data = SignatureData {
134 algorithm,
135 signature,
136 };
137 trace!("Creating signature contained_cert = {:?}", signature_data);
138 Ok(signature_data)
139}
140
141pub fn verify_signature_data(
144 signature: &SignatureData,
145 security_policy: SecurityPolicy,
146 signing_cert: &X509,
147 contained_cert: &X509,
148 contained_nonce: &[u8],
149) -> EncodingResult<()> {
150 if let Ok(verification_key) = signing_cert.public_key() {
151 let contained_cert = contained_cert.as_byte_string();
153 let data = concat_data_and_nonce(contained_cert.as_ref(), contained_nonce);
154
155 security_policy.asymmetric_verify_signature(
157 &verification_key,
158 &data,
159 signature.signature.as_ref(),
160 )?;
161 Ok(())
162 } else {
163 Err(Error::new(
164 StatusCode::BadUnexpectedError,
165 "Signature verification failed, signing certificate has no public key to verify with",
166 ))
167 }
168}
169
170#[derive(Debug)]
171pub struct HostnameError;
173
174impl fmt::Display for HostnameError {
175 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176 write!(f, "HostnameError")
177 }
178}
179
180impl std::error::Error for HostnameError {}
181
182pub fn hostname() -> Result<String, HostnameError> {
184 use gethostname::gethostname;
185 gethostname().into_string().map_err(|_| HostnameError {})
186}
187
188#[cfg(test)]
193fn from_hex(v: &str) -> Vec<u8> {
194 let mut b = Vec::with_capacity(v.len() / 2);
196 let mut modulus = 0;
197 let mut buf = 0;
198
199 for (idx, byte) in v.bytes().enumerate() {
200 buf <<= 4;
201
202 match byte {
203 b'A'..=b'F' => buf |= byte - b'A' + 10,
204 b'a'..=b'f' => buf |= byte - b'a' + 10,
205 b'0'..=b'9' => buf |= byte - b'0',
206 b' ' | b'\r' | b'\n' | b'\t' => {
207 buf >>= 4;
208 continue;
209 }
210 _ => {
211 let ch = v[idx..].chars().next().unwrap();
212 panic!("Invalid hex character {ch} at {idx}");
213 }
214 }
215
216 modulus += 1;
217 if modulus == 2 {
218 modulus = 0;
219 b.push(buf);
220 }
221 }
222
223 match modulus {
224 0 => b.into_iter().collect(),
225 _ => panic!("Invalid hex length"),
226 }
227}