Skip to main content

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}