Skip to main content

opcua_crypto/aes/
rsa_private_key.rs

1// OPCUA for Rust
2// SPDX-License-Identifier: MPL-2.0
3// Copyright (C) 2017-2024 Adam Lock
4
5//! Asymmetric encryption / decryption, signing / verification wrapper.
6use std::{
7    self,
8    fmt::{self, Debug, Formatter},
9    result::Result,
10};
11
12use rsa::pkcs1;
13use rsa::pkcs1v15;
14use rsa::pkcs8;
15use rsa::pss;
16use rsa::signature::{RandomizedSigner, SignatureEncoding, Verifier};
17use rsa::{RsaPrivateKey, RsaPublicKey};
18
19use x509_cert::spki::SubjectPublicKeyInfoOwned;
20
21use opcua_types::{status_code::StatusCode, Error};
22
23use crate::policy::aes::AesAsymmetricEncryptionAlgorithm;
24
25#[derive(Debug)]
26/// Error from working with a private key.
27pub struct PKeyError;
28
29impl fmt::Display for PKeyError {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        write!(f, "PKeyError")
32    }
33}
34
35impl std::error::Error for PKeyError {}
36
37impl From<pkcs8::Error> for PKeyError {
38    fn from(_err: pkcs8::Error) -> Self {
39        PKeyError
40    }
41}
42
43impl From<pkcs1::Error> for PKeyError {
44    fn from(_err: pkcs1::Error) -> Self {
45        PKeyError
46    }
47}
48
49impl From<rsa::Error> for PKeyError {
50    fn from(_err: rsa::Error) -> Self {
51        PKeyError
52    }
53}
54
55/// This is a wrapper around an asymmetric key pair. Since the PKey is either
56/// a public or private key so we have to differentiate that as well.
57#[derive(Clone)]
58pub struct PKey<T> {
59    pub(crate) value: T,
60}
61
62/// A public key
63pub type PublicKey = PKey<RsaPublicKey>;
64/// A private key
65pub type PrivateKey = PKey<RsaPrivateKey>;
66
67impl<T> Debug for PKey<T> {
68    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
69        // This impl will not write out the key, but it exists to keep structs happy
70        // that contain a key as a field
71        write!(f, "[pkey]")
72    }
73}
74
75/// Trait for computing the key size of a private key.
76pub trait KeySize {
77    /// Length in bits.
78    fn bit_length(&self) -> usize {
79        self.size() * 8
80    }
81
82    /// Length in bytes.
83    fn size(&self) -> usize;
84
85    /// Get the cipher text block size.
86    fn cipher_text_block_size(&self) -> usize {
87        self.size()
88    }
89}
90
91/// Get the cipher block size with given data size and padding.
92pub(crate) fn calculate_cipher_text_size<T: AesAsymmetricEncryptionAlgorithm>(
93    key_size: usize,
94    data_size: usize,
95) -> usize {
96    let plain_text_block_size = T::get_plaintext_block_size(key_size);
97    let block_count = if data_size.is_multiple_of(plain_text_block_size) {
98        data_size / plain_text_block_size
99    } else {
100        (data_size / plain_text_block_size) + 1
101    };
102
103    block_count * key_size
104}
105
106impl KeySize for PrivateKey {
107    /// Length in bits
108    fn size(&self) -> usize {
109        use rsa::traits::PublicKeyParts;
110        self.value.size()
111    }
112}
113
114impl PrivateKey {
115    /// Generate a new private key with the given length in bits.
116    pub fn new(bit_length: u32) -> Result<PrivateKey, rsa::Error> {
117        let mut rng = rand::thread_rng();
118
119        let key = RsaPrivateKey::new(&mut rng, bit_length as usize)?;
120        Ok(PKey { value: key })
121    }
122
123    /// Read a private key from the given path.
124    pub fn read_pem_file(path: &std::path::Path) -> Result<PrivateKey, PKeyError> {
125        use pkcs8::DecodePrivateKey;
126        use rsa::pkcs1::DecodeRsaPrivateKey;
127
128        let r = RsaPrivateKey::read_pkcs8_pem_file(path);
129        match r {
130            Err(_) => {
131                let val = RsaPrivateKey::read_pkcs1_pem_file(path)?;
132                Ok(PKey { value: val })
133            }
134            Ok(val) => Ok(PKey { value: val }),
135        }
136    }
137
138    /// Create a private key from a pem file loaded into a byte array.
139    pub fn from_pem(bytes: &[u8]) -> Result<PrivateKey, PKeyError> {
140        use pkcs8::DecodePrivateKey;
141        use rsa::pkcs1::DecodeRsaPrivateKey;
142
143        let converted = std::str::from_utf8(bytes);
144        match converted {
145            Err(_) => Err(PKeyError),
146            Ok(pem) => {
147                let r = RsaPrivateKey::from_pkcs8_pem(pem);
148                match r {
149                    Err(_) => {
150                        let val = RsaPrivateKey::from_pkcs1_pem(pem)?;
151                        Ok(PKey { value: val })
152                    }
153                    Ok(val) => Ok(PKey { value: val }),
154                }
155            }
156        }
157    }
158
159    /// Serialize the private key to a der file.
160    pub fn to_der(&self) -> pkcs8::Result<pkcs8::SecretDocument> {
161        use pkcs8::EncodePrivateKey;
162
163        self.value.to_pkcs8_der()
164    }
165
166    /// Get the public key info for this private key.
167    pub fn public_key_to_info(&self) -> x509_cert::spki::Result<SubjectPublicKeyInfoOwned> {
168        use rsa::pkcs8::EncodePublicKey;
169        SubjectPublicKeyInfoOwned::try_from(
170            self.value
171                .to_public_key()
172                .to_public_key_der()
173                .unwrap()
174                .as_bytes(),
175        )
176    }
177
178    /// Create a public key based on this private key.
179    pub fn to_public_key(&self) -> PublicKey {
180        PublicKey {
181            value: self.value.to_public_key(),
182        }
183    }
184
185    /// Signs the data using RSA-SHA1
186    pub fn sign_sha1(&self, data: &[u8], signature: &mut [u8]) -> Result<usize, Error> {
187        let mut rng = rand::thread_rng();
188        let signing_key = pkcs1v15::SigningKey::<sha1::Sha1>::new(self.value.clone());
189        match signing_key.try_sign_with_rng(&mut rng, data) {
190            Err(e) => Err(Error::new(StatusCode::BadUnexpectedError, e)),
191            Ok(signed) => {
192                let val = signed.to_vec();
193                signature.copy_from_slice(&val);
194                Ok(val.len())
195            }
196        }
197    }
198
199    /// Signs the data using RSA-SHA256
200    pub fn sign_sha256(&self, data: &[u8], signature: &mut [u8]) -> Result<usize, Error> {
201        let mut rng = rand::thread_rng();
202        let signing_key = pkcs1v15::SigningKey::<sha2::Sha256>::new(self.value.clone());
203        match signing_key.try_sign_with_rng(&mut rng, data) {
204            Err(e) => Err(Error::new(StatusCode::BadUnexpectedError, e)),
205            Ok(signed) => {
206                let val = signed.to_vec();
207                signature.copy_from_slice(&val);
208                Ok(val.len())
209            }
210        }
211    }
212
213    /// Signs the data using RSA-SHA256-PSS
214    pub fn sign_sha256_pss(&self, data: &[u8], signature: &mut [u8]) -> Result<usize, Error> {
215        let mut rng = rand::thread_rng();
216        let signing_key = pss::BlindedSigningKey::<sha2::Sha256>::new(self.value.clone());
217        match signing_key.try_sign_with_rng(&mut rng, data) {
218            Err(e) => Err(Error::new(StatusCode::BadUnexpectedError, e)),
219            Ok(signed) => {
220                let val = signed.to_vec();
221                signature.copy_from_slice(&val);
222                Ok(val.len())
223            }
224        }
225    }
226
227    pub(crate) fn private_decrypt<T: AesAsymmetricEncryptionAlgorithm>(
228        &self,
229        src: &[u8],
230        dst: &mut [u8],
231    ) -> Result<usize, PKeyError> {
232        let cipher_text_block_size = self.cipher_text_block_size();
233        // Decrypt the data
234        let mut src_idx = 0;
235        let mut dst_idx = 0;
236
237        let src_len = src.len();
238        while src_idx < src_len {
239            let src_end_index = src_idx + cipher_text_block_size;
240
241            // Decrypt and advance
242            dst_idx += {
243                let src = &src[src_idx..src_end_index];
244                let dst = &mut dst[dst_idx..(dst_idx + cipher_text_block_size)];
245
246                let padding = T::get_padding();
247                let decrypted = self.value.decrypt(padding, src)?;
248
249                let size = decrypted.len();
250                if size == dst.len() {
251                    dst.copy_from_slice(&decrypted);
252                } else {
253                    dst[0..size].copy_from_slice(&decrypted);
254                }
255                size
256            };
257            src_idx = src_end_index;
258        }
259        Ok(dst_idx)
260    }
261}
262
263impl KeySize for PublicKey {
264    /// Length in bits
265    fn size(&self) -> usize {
266        use rsa::traits::PublicKeyParts;
267        self.value.size()
268    }
269}
270
271impl PublicKey {
272    /// Verifies the data using RSA-SHA1
273    pub fn verify_sha1(&self, data: &[u8], signature: &[u8]) -> Result<bool, Error> {
274        let verifying_key = pkcs1v15::VerifyingKey::<sha1::Sha1>::new(self.value.clone());
275        let r = pkcs1v15::Signature::try_from(signature);
276        match r {
277            Err(e) => Err(Error::new(StatusCode::BadUnexpectedError, e)),
278            Ok(val) => match verifying_key.verify(data, &val) {
279                Err(_) => Ok(false),
280                _ => Ok(true),
281            },
282        }
283    }
284
285    /// Verifies the data using RSA-SHA256
286    pub fn verify_sha256(&self, data: &[u8], signature: &[u8]) -> Result<bool, Error> {
287        let verifying_key = pkcs1v15::VerifyingKey::<sha2::Sha256>::new(self.value.clone());
288        let r = pkcs1v15::Signature::try_from(signature);
289        match r {
290            Err(e) => Err(Error::new(StatusCode::BadUnexpectedError, e)),
291            Ok(val) => match verifying_key.verify(data, &val) {
292                Err(_) => Ok(false),
293                _ => Ok(true),
294            },
295        }
296    }
297
298    /// Verifies the data using RSA-SHA256-PSS
299    pub fn verify_sha256_pss(&self, data: &[u8], signature: &[u8]) -> Result<bool, Error> {
300        let verifying_key = pss::VerifyingKey::<sha2::Sha256>::new(self.value.clone());
301        let r = pss::Signature::try_from(signature);
302        match r {
303            Err(e) => Err(Error::new(StatusCode::BadUnexpectedError, e)),
304            Ok(val) => match verifying_key.verify(data, &val) {
305                Err(_) => Ok(false),
306                _ => Ok(true),
307            },
308        }
309    }
310
311    /// Encrypts data from src to dst using the specified padding and returns the size of encrypted
312    /// data in bytes or an error.
313    pub(crate) fn public_encrypt<T: AesAsymmetricEncryptionAlgorithm>(
314        &self,
315        src: &[u8],
316        dst: &mut [u8],
317    ) -> Result<usize, PKeyError> {
318        let cipher_text_block_size = self.cipher_text_block_size();
319        let plain_text_block_size = T::get_plaintext_block_size(self.size());
320
321        let mut rng = rand::thread_rng();
322
323        let mut src_idx = 0;
324        let mut dst_idx = 0;
325
326        let src_len = src.len();
327        while src_idx < src_len {
328            let bytes_to_encrypt = if src_len < plain_text_block_size {
329                src_len
330            } else if (src_len - src_idx) < plain_text_block_size {
331                src_len - src_idx
332            } else {
333                plain_text_block_size
334            };
335
336            let src_end_index = src_idx + bytes_to_encrypt;
337
338            // Encrypt data, advance dst index by number of bytes after encrypted
339            dst_idx += {
340                let src = &src[src_idx..src_end_index];
341
342                let padding = T::get_padding();
343                let encrypted = self.value.encrypt(&mut rng, padding, src)?;
344                dst[dst_idx..(dst_idx + cipher_text_block_size)].copy_from_slice(&encrypted);
345                encrypted.len()
346            };
347
348            // Src advances by bytes to encrypt
349            src_idx = src_end_index;
350        }
351
352        Ok(dst_idx)
353    }
354}