opcua_crypto/
lib.rs

1// OPCUA for Rust
2// SPDX-License-Identifier: MPL-2.0
3// Copyright (C) 2017-2022 Adam Lock
4
5//! Crypto related functionality. It is used for establishing
6//! trust between a client and server via certificate exchange and validation. It also used for
7//! encrypting / decrypting messages and signing messages.
8
9#[macro_use]
10extern crate log;
11#[macro_use]
12extern crate serde_derive;
13
14use std::fmt;
15
16use opcua_types::{service_types::SignatureData, status_code::StatusCode, ByteString, UAString};
17pub use {
18    aeskey::*, certificate_store::*, hash::*, pkey::*, security_policy::*, thumbprint::*,
19    user_identity::*, x509::*,
20};
21
22#[cfg(test)]
23mod tests;
24
25pub mod aeskey;
26pub mod certificate_store;
27pub mod hash;
28pub mod pkey;
29pub mod random;
30pub mod security_policy;
31pub mod thumbprint;
32pub mod user_identity;
33pub mod x509;
34
35// Size of a SHA1 hash value in bytes
36pub const SHA1_SIZE: usize = 20;
37// Size of a SHA256 hash value bytes
38pub const SHA256_SIZE: usize = 32;
39
40/// These are algorithms that are used by various policies or external to this file
41pub(crate) mod algorithms {
42    // Symmetric encryption algorithm AES128-CBC
43    //pub const ENC_AES128_CBC: &str = "http://www.w3.org/2001/04/xmlenc#aes128-cbc";
44
45    // Symmetric encryption algorithm AES256-CBC
46    //pub const ENC_AES256_CBC: &str = "http://www.w3.org/2001/04/xmlenc#aes256-cbc";
47
48    /// Asymmetric encryption algorithm RSA15
49    pub const ENC_RSA_15: &str = "http://www.w3.org/2001/04/xmlenc#rsa-1_5";
50
51    /// Asymmetric encryption algorithm RSA-OAEP
52    pub const ENC_RSA_OAEP: &str = "http://www.w3.org/2001/04/xmlenc#rsa-oaep";
53
54    /// Asymmetrric encrypttion
55    pub const ENC_RSA_OAEP_SHA256: &str = "http://opcfoundation.org/UA/security/rsa-oaep-sha2-256";
56
57    // Asymmetric encryption algorithm RSA-OAEP-MGF1P
58    //pub const ENC_RSA_OAEP_MGF1P: &str = "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p";
59
60    /// SymmetricSignatureAlgorithm – HmacSha1 – (http://www.w3.org/2000/09/xmldsig#hmac-sha1).
61    pub const DSIG_HMAC_SHA1: &str = "http://www.w3.org/2000/09/xmldsig#hmac-sha1";
62
63    /// SymmetricSignatureAlgorithm – HmacSha256 – (http://www.w3.org/2000/09/xmldsig#hmac-sha256).
64    pub const DSIG_HMAC_SHA256: &str = "http://www.w3.org/2000/09/xmldsig#hmac-sha256";
65
66    /// Asymmetric digital signature algorithm using RSA-SHA1
67    pub const DSIG_RSA_SHA1: &str = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
68
69    /// Asymmetric digital signature algorithm using RSA-SHA256
70    pub const DSIG_RSA_SHA256: &str = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
71
72    /// Asymmetric digital signature algorithm using RSA-PSS_SHA2-256
73    pub const DSIG_RSA_PSS_SHA2_256: &str = "http://opcfoundation.org/UA/security/rsa-pss-sha2-256";
74
75    // Key derivation algorithm P_SHA1
76    //pub const KEY_P_SHA1: &str = "http://docs.oasis-open.org/ws-sx/ws-secureconversation/200512/dk/p_sha1";
77
78    // Key derivation algorithm P_SHA256
79    //pub const KEY_P_SHA256: &str = "http://docs.oasis-open.org/ws-sx/ws-secureconversation/200512/dk/p_sha256";
80}
81
82fn concat_data_and_nonce(data: &[u8], nonce: &[u8]) -> Vec<u8> {
83    let mut buffer: Vec<u8> = Vec::with_capacity(data.len() + nonce.len());
84    buffer.extend_from_slice(data);
85    buffer.extend_from_slice(nonce);
86    buffer
87}
88
89/// Creates a `SignatureData` object by signing the supplied certificate and nonce with a pkey
90pub fn create_signature_data(
91    signing_key: &PrivateKey,
92    security_policy: SecurityPolicy,
93    contained_cert: &ByteString,
94    nonce: &ByteString,
95) -> Result<SignatureData, StatusCode> {
96    // TODO this function should be refactored to return an error if the contained cert or nonce is incorrect, not a blank signature. That
97    //  very much depends on reading the spec to see what should happen if its not possible to create a signature, e.g. because
98    //  policy is None.
99
100    let (algorithm, signature) = if contained_cert.is_null() || nonce.is_null() {
101        (UAString::null(), ByteString::null())
102    } else {
103        let data = concat_data_and_nonce(contained_cert.as_ref(), nonce.as_ref());
104        // Sign the bytes and return the algorithm, signature
105        match security_policy {
106            SecurityPolicy::None => (UAString::null(), ByteString::null()),
107            SecurityPolicy::Unknown => {
108                error!(
109                    "An unknown security policy was passed to create_signature_data and rejected"
110                );
111                (UAString::null(), ByteString::null())
112            }
113            security_policy => {
114                let signing_key_size = signing_key.size();
115                let mut signature = vec![0u8; signing_key_size];
116                let _ = security_policy.asymmetric_sign(signing_key, &data, &mut signature)?;
117                (
118                    UAString::from(security_policy.asymmetric_signature_algorithm()),
119                    ByteString::from(&signature),
120                )
121            }
122        }
123    };
124    let signature_data = SignatureData {
125        algorithm,
126        signature,
127    };
128    trace!("Creating signature contained_cert = {:?}", signature_data);
129    Ok(signature_data)
130}
131
132/// Verifies that the supplied signature data was produced by the signing cert. The contained cert and nonce are supplied so
133/// the signature can be verified against the expected data.
134pub fn verify_signature_data(
135    signature: &SignatureData,
136    security_policy: SecurityPolicy,
137    signing_cert: &X509,
138    contained_cert: &X509,
139    contained_nonce: &[u8],
140) -> StatusCode {
141    if let Ok(verification_key) = signing_cert.public_key() {
142        // This is the data that the should have been signed
143        let contained_cert = contained_cert.as_byte_string();
144        let data = concat_data_and_nonce(contained_cert.as_ref(), contained_nonce);
145
146        // Verify the signature
147        let result = security_policy.asymmetric_verify_signature(
148            &verification_key,
149            &data,
150            signature.signature.as_ref(),
151            None,
152        );
153        match result {
154            Ok(_) => StatusCode::Good,
155            Err(result) => {
156                error!(
157                    "Client signature verification failed, status code = {}",
158                    result
159                );
160                result
161            }
162        }
163    } else {
164        error!(
165            "Signature verification failed, signing certificate has no public key to verify with"
166        );
167        StatusCode::BadUnexpectedError
168    }
169}
170
171#[derive(Debug)]
172pub 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
182/// Returns this computer's hostname
183pub fn hostname() -> Result<String, HostnameError> {
184    use gethostname::gethostname;
185    gethostname().into_string().map_err(|_| HostnameError {})
186}