opcua_crypto/security_policy.rs
1// OPCUA for Rust
2// SPDX-License-Identifier: MPL-2.0
3// Copyright (C) 2017-2024 Adam Lock
4
5//! Security policy is the symmetric, asymmetric encryption / decryption + signing / verification
6//! algorithms to use and enforce for the current session.
7use std::fmt;
8use std::str::FromStr;
9
10use tracing::error;
11
12use opcua_types::{constants, ByteString, Error};
13
14use crate::{
15 policy::{PaddingInfo, SecurityPolicyImpl},
16 PrivateKey, PublicKey,
17};
18
19use super::random;
20
21use crate::policy::{aes::*, AesDerivedKeys, AesPolicy, NonePolicy};
22
23macro_rules! call_with_policy {
24 (_inner $r:expr, $($p:ident: $ty:ty,)+ |$x:ident| $t:tt) => {
25 match $r {
26 $(
27 Self::$p => {
28 type $x = $ty;
29 #[allow(unused_braces)]
30 $t
31 }
32 )*
33 Self::Unknown => panic!("Unknown security policy"),
34 }
35 };
36
37 ($r:expr, |$x:ident| $t:tt) => {
38 call_with_policy!(_inner $r,
39 None: NonePolicy,
40 Aes128Sha256RsaOaep: AesPolicy<Aes128Sha256RsaOaep>,
41 Basic256Sha256: AesPolicy<Basic256Sha256>,
42 Aes256Sha256RsaPss: AesPolicy<Aes256Sha256RsaPss>,
43 Basic128Rsa15: AesPolicy<Basic128Rsa15>,
44 Basic256: AesPolicy<Basic256>,
45 |$x| $t
46 )
47 };
48}
49
50/// SecurityPolicy implies what encryption and signing algorithms and their relevant key strengths
51/// are used during an encrypted session.
52#[derive(Debug, Clone, PartialEq, Copy)]
53pub enum SecurityPolicy {
54 /// Security policy is unknown, this is generally an error.
55 Unknown,
56 /// No security.
57 None,
58 /// AES128/SHA256 RSA-OAEP.
59 Aes128Sha256RsaOaep,
60 /// Basic256/SHA256
61 Basic256Sha256,
62 /// AES256/SHA256 RSA-PSS
63 Aes256Sha256RsaPss,
64 /// Basic128. Note that this security policy is deprecated.
65 Basic128Rsa15,
66 /// Basic256.
67 Basic256,
68}
69
70impl fmt::Display for SecurityPolicy {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 write!(f, "{}", self.to_str())
73 }
74}
75
76impl FromStr for SecurityPolicy {
77 type Err = ();
78
79 fn from_str(s: &str) -> Result<Self, Self::Err> {
80 Ok(match s {
81 constants::SECURITY_POLICY_NONE | constants::SECURITY_POLICY_NONE_URI => {
82 SecurityPolicy::None
83 }
84 Basic128Rsa15::SECURITY_POLICY | Basic128Rsa15::SECURITY_POLICY_URI => {
85 SecurityPolicy::Basic128Rsa15
86 }
87 Basic256::SECURITY_POLICY | Basic256::SECURITY_POLICY_URI => SecurityPolicy::Basic256,
88 Basic256Sha256::SECURITY_POLICY | Basic256Sha256::SECURITY_POLICY_URI => {
89 SecurityPolicy::Basic256Sha256
90 }
91 Aes128Sha256RsaOaep::SECURITY_POLICY | Aes128Sha256RsaOaep::SECURITY_POLICY_URI => {
92 SecurityPolicy::Aes128Sha256RsaOaep
93 }
94 Aes256Sha256RsaPss::SECURITY_POLICY | Aes256Sha256RsaPss::SECURITY_POLICY_URI => {
95 SecurityPolicy::Aes256Sha256RsaPss
96 }
97 _ => {
98 error!("Specified security policy \"{}\" is not recognized", s);
99 SecurityPolicy::Unknown
100 }
101 })
102 }
103}
104
105impl From<SecurityPolicy> for String {
106 fn from(v: SecurityPolicy) -> String {
107 v.to_str().to_string()
108 }
109}
110
111impl SecurityPolicy {
112 /// Get the security policy URI from this policy.
113 ///
114 /// This will panic if the security policy is `Unknown`.
115 pub fn to_uri(&self) -> &'static str {
116 call_with_policy!(self, |T| { T::uri() })
117 }
118
119 /// Returns true if the security policy is supported. It might be recognized but be unsupported by the implementation
120 pub fn is_supported(&self) -> bool {
121 matches!(
122 self,
123 SecurityPolicy::None
124 | SecurityPolicy::Basic128Rsa15
125 | SecurityPolicy::Basic256
126 | SecurityPolicy::Basic256Sha256
127 | SecurityPolicy::Aes128Sha256RsaOaep
128 | SecurityPolicy::Aes256Sha256RsaPss
129 )
130 }
131
132 /// Returns true if the security policy has been deprecated by the OPC UA specification
133 pub fn is_deprecated(&self) -> bool {
134 // Since 1.04 because SHA-1 is no longer considered safe
135 call_with_policy!(self, |T| { T::is_deprecated() })
136 }
137
138 /// Get a string representation of this policy.
139 ///
140 /// This will panic if the security policy is `Unknown`.
141 pub fn to_str(&self) -> &'static str {
142 call_with_policy!(self, |T| { T::as_str() })
143 }
144
145 /// Get the asymmetric encryption algorithm for this security policy.
146 ///
147 /// This will panic if the security policy is `Unknown` or `None`.
148 pub fn asymmetric_encryption_algorithm(&self) -> Option<&'static str> {
149 call_with_policy!(self, |T| { T::asymmetric_encryption_algorithm() })
150 }
151
152 /// Get the asymmetric signature algorithm for this security policy.
153 ///
154 /// This will panic if the security policy is `Unknown` or `None`.
155 pub fn asymmetric_signature_algorithm(&self) -> &'static str {
156 call_with_policy!(self, |T| { T::asymmetric_signature_algorithm() })
157 }
158
159 /// Plaintext block size in bytes.
160 ///
161 /// This will panic if the security policy is `Unknown` or `None`.
162 pub fn plain_block_size(&self) -> usize {
163 call_with_policy!(self, |T| { T::plain_text_block_size() })
164 }
165
166 /// Signature size in bytes.
167 ///
168 /// This will panic if the security policy is `Unknown`.
169 pub fn symmetric_signature_size(&self) -> usize {
170 call_with_policy!(self, |T| { T::symmetric_signature_size() })
171 }
172
173 /// Tests if the supplied key length is valid for this policy
174 pub fn is_valid_keylength(&self, keylength: usize) -> bool {
175 call_with_policy!(self, |T| { T::is_valid_key_length(keylength) })
176 }
177
178 /// Creates a random nonce in a bytestring with a length appropriate for the policy
179 pub fn random_nonce(&self) -> ByteString {
180 match self {
181 SecurityPolicy::None => ByteString::null(),
182 _ => random::byte_string(self.secure_channel_nonce_length()),
183 }
184 }
185
186 /// Length of the secure channel nonce for this security policy.
187 pub fn secure_channel_nonce_length(&self) -> usize {
188 if matches!(self, SecurityPolicy::Unknown) {
189 // Fallback, but this probably isn't valid and will fail shortly.
190 return 32;
191 }
192
193 call_with_policy!(self, |T| { T::nonce_length() })
194 }
195
196 /// Get the security policy from the given URI. Returns `Unknown`
197 /// if the URI does not match any known policy.
198 pub fn from_uri(uri: &str) -> SecurityPolicy {
199 match uri {
200 constants::SECURITY_POLICY_NONE_URI => SecurityPolicy::None,
201 Basic128Rsa15::SECURITY_POLICY_URI => SecurityPolicy::Basic128Rsa15,
202 Basic256::SECURITY_POLICY_URI => SecurityPolicy::Basic256,
203 Basic256Sha256::SECURITY_POLICY_URI => SecurityPolicy::Basic256Sha256,
204 Aes128Sha256RsaOaep::SECURITY_POLICY_URI => SecurityPolicy::Aes128Sha256RsaOaep,
205 Aes256Sha256RsaPss::SECURITY_POLICY_URI => SecurityPolicy::Aes256Sha256RsaPss,
206 _ => {
207 error!(
208 "Specified security policy uri \"{}\" is not recognized",
209 uri
210 );
211 SecurityPolicy::Unknown
212 }
213 }
214 }
215
216 /// Returns whether the security policy uses legacy sequence numbers.
217 pub fn legacy_sequence_numbers(&self) -> bool {
218 call_with_policy!(self, |T| { T::uses_legacy_sequence_numbers() })
219 }
220
221 /// Part 6
222 /// 6.7.5
223 /// Deriving keys Once the SecureChannel is established the Messages are signed and encrypted with
224 /// keys derived from the Nonces exchanged in the OpenSecureChannel call. These keys are derived
225 /// by passing the Nonces to a pseudo-random function which produces a sequence of bytes from a
226 /// set of inputs. A pseudo-random function is represented by the following function declaration:
227 ///
228 /// ```c++
229 /// Byte[] PRF( Byte[] secret, Byte[] seed, i32 length, i32 offset)
230 /// ```
231 ///
232 /// Where length is the number of bytes to return and offset is a number of bytes from the beginning of the sequence.
233 ///
234 /// The lengths of the keys that need to be generated depend on the SecurityPolicy used for the channel.
235 /// The following information is specified by the SecurityPolicy:
236 ///
237 /// a) SigningKeyLength (from the DerivedSignatureKeyLength);
238 /// b) EncryptingKeyLength (implied by the SymmetricEncryptionAlgorithm);
239 /// c) EncryptingBlockSize (implied by the SymmetricEncryptionAlgorithm).
240 ///
241 /// The parameters passed to the pseudo random function are specified in Table 33.
242 ///
243 /// Table 33 – Cryptography key generation parameters
244 ///
245 /// Key | Secret | Seed | Length | Offset
246 /// ClientSigningKey | ServerNonce | ClientNonce | SigningKeyLength | 0
247 /// ClientEncryptingKey | ServerNonce | ClientNonce | EncryptingKeyLength | SigningKeyLength
248 /// ClientInitializationVector | ServerNonce | ClientNonce | EncryptingBlockSize | SigningKeyLength + EncryptingKeyLength
249 /// ServerSigningKey | ClientNonce | ServerNonce | SigningKeyLength | 0
250 /// ServerEncryptingKey | ClientNonce | ServerNonce | EncryptingKeyLength | SigningKeyLength
251 /// ServerInitializationVector | ClientNonce | ServerNonce | EncryptingBlockSize | SigningKeyLength + EncryptingKeyLength
252 ///
253 /// The Client keys are used to secure Messages sent by the Client. The Server keys
254 /// are used to secure Messages sent by the Server.
255 ///
256 pub fn make_secure_channel_keys(&self, secret: &[u8], seed: &[u8]) -> AesDerivedKeys {
257 call_with_policy!(self, |T| { T::derive_secure_channel_keys(secret, seed) })
258 }
259
260 /// Produce a signature of the data using an asymmetric key. Stores the signature in the supplied
261 /// `signature` buffer. Returns the size of the signature within that buffer.
262 pub fn asymmetric_sign(
263 &self,
264 signing_key: &PrivateKey,
265 data: &[u8],
266 signature: &mut [u8],
267 ) -> Result<usize, Error> {
268 call_with_policy!(self, |T| {
269 T::asymmetric_sign(signing_key, data, signature)
270 })
271 }
272
273 /// Verifies a signature of the data using an asymmetric key. In a debugging scenario, the
274 /// signing key can also be supplied so that the supplied signature can be compared to a freshly
275 /// generated signature.
276 pub fn asymmetric_verify_signature(
277 &self,
278 verification_key: &PublicKey,
279 data: &[u8],
280 signature: &[u8],
281 ) -> Result<(), Error> {
282 call_with_policy!(self, |T| {
283 T::asymmetric_verify_signature(verification_key, data, signature)
284 })
285 }
286
287 /// Get information about message padding for symmetric encryption using this policy.
288 pub fn symmetric_padding_info(&self) -> PaddingInfo {
289 call_with_policy!(self, |T| { T::symmetric_padding_info() })
290 }
291
292 /// Get information about message padding for asymmetric encryption using this policy,
293 /// requires the public key of the receiver.
294 pub fn asymmetric_padding_info(&self, remote_key: &PublicKey) -> PaddingInfo {
295 call_with_policy!(self, |T| { T::asymmetric_padding_info(remote_key) })
296 }
297
298 /// Calculate the size of the cipher text for asymmetric encryption.
299 pub fn calculate_cipher_text_size(&self, plain_text_size: usize, key: &PublicKey) -> usize {
300 call_with_policy!(self, |T| {
301 T::calculate_cipher_text_size(plain_text_size, key)
302 })
303 }
304
305 /// Encrypts a message using the supplied encryption key, returns the encrypted size. Destination
306 /// buffer must be large enough to hold encrypted bytes including any padding.
307 pub fn asymmetric_encrypt(
308 &self,
309 encryption_key: &PublicKey,
310 src: &[u8],
311 dst: &mut [u8],
312 ) -> Result<usize, Error> {
313 call_with_policy!(self, |T| {
314 T::asymmetric_encrypt(encryption_key, src, dst)
315 })
316 }
317
318 /// Decrypts a message whose thumbprint matches the x509 cert and private key pair.
319 ///
320 /// Returns the number of decrypted bytes
321 pub fn asymmetric_decrypt(
322 &self,
323 decryption_key: &PrivateKey,
324 src: &[u8],
325 dst: &mut [u8],
326 ) -> Result<usize, Error> {
327 call_with_policy!(self, |T| {
328 T::asymmetric_decrypt(decryption_key, src, dst)
329 })
330 }
331
332 /// Produce a signature of some data using the supplied symmetric key. Signing algorithm is determined
333 /// by the security policy. Signature is stored in the supplied `signature` argument.
334 pub fn symmetric_sign(
335 &self,
336 keys: &AesDerivedKeys,
337 data: &[u8],
338 signature: &mut [u8],
339 ) -> Result<(), Error> {
340 call_with_policy!(self, |T| { T::symmetric_sign(keys, data, signature) })
341 }
342
343 /// Verify the signature of a data block using the supplied symmetric key.
344 pub fn symmetric_verify_signature(
345 &self,
346 keys: &AesDerivedKeys,
347 data: &[u8],
348 signature: &[u8],
349 ) -> Result<(), Error> {
350 call_with_policy!(self, |T| {
351 T::symmetric_verify_signature(keys, data, signature)
352 })
353 }
354
355 /// Encrypt the supplied data using the supplied key storing the result in the destination.
356 pub fn symmetric_encrypt(
357 &self,
358 keys: &AesDerivedKeys,
359 src: &[u8],
360 dst: &mut [u8],
361 ) -> Result<usize, Error> {
362 call_with_policy!(self, |T| { T::symmetric_encrypt(keys, src, dst) })
363 }
364
365 /// Decrypts the supplied data using the supplied key storing the result in the destination.
366 pub fn symmetric_decrypt(
367 &self,
368 keys: &AesDerivedKeys,
369 src: &[u8],
370 dst: &mut [u8],
371 ) -> Result<usize, Error> {
372 call_with_policy!(self, |T| { T::symmetric_decrypt(keys, src, dst) })
373 }
374
375 /// Get the key length used for symmetric encryption.
376 pub fn encrypting_key_length(&self) -> usize {
377 call_with_policy!(self, |T| { T::encrypting_key_length() })
378 }
379}