1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//! Support for the **PKCS#8** private key format described in [RFC 5208]
//! and [RFC 5915].
//!
//! [RFC 5208]: https://tools.ietf.org/html/rfc5208
//! [RFC 5915]: https://tools.ietf.org/html/rfc5915

#[cfg(all(unix, feature = "std"))]
use super::FILE_MODE;
use crate::encoding::error::Error;
#[cfg(feature = "std")]
use crate::encoding::error::ErrorKind;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "std")]
use std::{
    fs::File,
    io::{Read, Write},
    path::Path,
};
#[cfg(all(unix, feature = "std"))]
use std::{fs::OpenOptions, os::unix::fs::OpenOptionsExt};
#[cfg(feature = "std")]
use zeroize::Zeroize;

/// Load this type from a **PKCS#8** private key
pub trait FromPkcs8: Sized {
    /// Load from the given **PKCS#8**-encoded private key, returning `Self`
    /// or an error if the given data couldn't be loaded.
    fn from_pkcs8<K: AsRef<[u8]>>(secret_key: K) -> Result<Self, Error>;

    /// Read **PKCS#8** data from the given `std::io::Read`.
    #[cfg(feature = "std")]
    fn read_pkcs8<R: Read>(mut reader: R) -> Result<Self, Error> {
        let mut bytes = vec![];
        reader.read_to_end(&mut bytes)?;

        let result = Self::from_pkcs8(&bytes);
        bytes.zeroize();
        result
    }

    /// Read **PKCS#8** data from the file at the given path.
    #[cfg(feature = "std")]
    fn from_pkcs8_file<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
        let path = path.as_ref();
        let file = File::open(path).map_err(|e| {
            Error::new(
                ErrorKind::Io,
                Some(&format!("couldn't open {}: {}", path.display(), e)),
            )
        })?;
        Self::read_pkcs8(file)
    }
}

/// Generate a random **PKCS#8** private key of this type
#[cfg(feature = "std")]
pub trait GeneratePkcs8: Sized + FromPkcs8 {
    /// Randomly generate a **PKCS#8** private key for this type loadable
    /// via `from_pkcs8()`.
    fn generate_pkcs8() -> Result<SecretKey, Error>;

    /// Write randomly generated **PKCS#8** private key to the file at the
    /// given path.
    ///
    /// If the file does not exist, it will be created with a mode of
    /// `FILE_MODE` (i.e. `600`). If the file does exist, it will be erased
    /// and replaced.
    #[cfg(unix)]
    fn generate_pkcs8_file<P: AsRef<Path>>(path: P) -> Result<File, Error> {
        let path = path.as_ref();
        let secret_key = Self::generate_pkcs8()?;

        let mut file = OpenOptions::new()
            .create(true)
            .write(true)
            .truncate(true)
            .mode(FILE_MODE)
            .open(path)
            .map_err(|e| {
                Error::new(
                    ErrorKind::Io,
                    Some(&format!("couldn't create {}: {}", path.display(), e)),
                )
            })?;

        file.write_all(secret_key.as_ref())?;
        Ok(file)
    }

    /// Encode `self` and write it to a file at the given path, returning the
    /// resulting `File` or a `Error`.
    ///
    /// If the file does not exist, it will be created.
    #[cfg(not(unix))]
    fn generate_pkcs8_file<P: AsRef<Path>>(path: P) -> Result<File, Error> {
        let path = path.as_ref();
        let secret_key = Self::generate_pkcs8()?;
        let mut file = File::create(path).map_err(|e| {
            Error::new(
                ErrorKind::Io,
                Some(&format!("couldn't create {}: {}", path.display(), e)),
            )
        })?;

        file.write_all(secret_key.as_ref())?;
        Ok(file)
    }
}

/// **PKCS#8** keypairs containing public keys and secret keys
#[cfg(feature = "alloc")]
pub struct SecretKey(Vec<u8>);

#[cfg(feature = "alloc")]
impl SecretKey {
    /// Create a new **PKCS#8** `SecretKey` from the given bytes.
    // TODO: parse the document and verify it's well-formed
    pub fn from_bytes(secret_key_bytes: &[u8]) -> Result<Self, Error> {
        Ok(SecretKey(secret_key_bytes.to_vec()))
    }
}

#[cfg(feature = "alloc")]
impl AsRef<[u8]> for SecretKey {
    fn as_ref(&self) -> &[u8] {
        self.0.as_ref()
    }
}

#[cfg(feature = "alloc")]
impl Drop for SecretKey {
    fn drop(&mut self) {
        self.0.clear()
    }
}