Skip to main content

rustica_keys/ssh/
privkey.rs

1use super::keytype::{Curve, KeyType, KeyTypeKind};
2use super::error::{Error, ErrorKind};
3use super::PublicKey;
4use super::reader::Reader;
5
6use std::fs::File;
7use std::io::{Read};
8use std::path::Path;
9
10/// RSA private key.
11#[derive(Debug, PartialEq, Clone)]
12pub struct RsaPrivateKey {
13    /// Private key exponent.
14    pub d: Vec<u8>,
15
16    /// Modulus of key.
17    pub n: Vec<u8>,
18}
19
20/// ECDSA private key.
21#[derive(Debug, PartialEq, Clone)]
22pub struct EcdsaPrivateKey {
23    /// The curve being used.
24    pub curve: Curve,
25
26    /// The private key.
27    pub key: Vec<u8>,
28}
29
30/// ED25519 private key.
31#[derive(Debug, PartialEq, Clone)]
32pub struct Ed25519PrivateKey {
33    /// The private key.
34    pub key: Vec<u8>,
35}
36
37/// A type which represents the different kinds a public key can be.
38#[derive(Debug, PartialEq, Clone)]
39pub enum PrivateKeyKind {
40    /// Represents an RSA public key.
41    Rsa(RsaPrivateKey),
42
43    /// Represents an ECDSA public key.
44    Ecdsa(EcdsaPrivateKey),
45
46    /// Represents an ED25519 public key.
47    Ed25519(Ed25519PrivateKey),
48}
49
50/// A type which represents an OpenSSH public key.
51#[derive(Debug, PartialEq, Clone)]
52pub struct PrivateKey {
53    /// Key type.
54    pub key_type: KeyType,
55
56    /// The kind of public key.
57    pub kind: PrivateKeyKind,
58
59    /// The corresponding public key
60    pub pubkey: PublicKey,
61
62    /// Associated comment, if any.
63    pub comment: Option<String>,
64}
65
66impl PrivateKey {
67    /// Reads an OpenSSH private key from a given path.
68    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<PrivateKey, Error> {
69        let mut contents = String::new();
70        File::open(path)?.read_to_string(&mut contents)?;
71
72        PrivateKey::from_string(&contents)
73    }
74
75    /// Reads an OpenSSH private key from a given string.
76    pub fn from_string(contents: &str) -> Result<PrivateKey, Error> {
77        let mut iter = contents.lines();
78        let header = iter.next().unwrap_or("");
79        if header != "-----BEGIN OPENSSH PRIVATE KEY-----" {
80            return Err(Error::with_kind(ErrorKind::InvalidFormat));
81        }
82
83        let mut encoded_key = String::new();
84        loop {
85            let part = match iter.next() {
86                Some(p) => p,
87                None => return Err(Error::with_kind(ErrorKind::InvalidFormat)),
88            };
89
90            if part == "-----END OPENSSH PRIVATE KEY-----" {
91                break;
92            }
93            encoded_key.push_str(part);
94        }
95
96        let decoded = base64::decode(encoded_key)?;
97        let mut reader = Reader::new(&decoded);
98
99        // Construct a new `PrivateKey`
100        let k = PrivateKey::from_reader(&mut reader)?;
101
102        Ok(k)
103    }
104
105    /// This function is used for extracting a private key from an existing reader.
106    pub(crate) fn from_reader(reader: &mut Reader) -> Result<PrivateKey, Error> {
107        let preamble = reader.read_cstring()?;
108
109        if preamble != "openssh-key-v1" {
110            return Err(Error::with_kind(ErrorKind::InvalidFormat));
111        }
112
113        // These values are for encrypted keys which are not supported
114        let cipher_name = reader.read_string()?;
115        let kdf = reader.read_string()?;
116        if cipher_name != "none" || kdf != "none" {
117            return Err(Error::with_kind(ErrorKind::EncryptedPrivateKeyNotSupported));
118        }
119
120        // This appears to be en empty value
121        reader.read_string()?;
122
123        // This seems to be hardcoded into the standard
124        let number_of_keys = reader.read_u32()?;
125        if number_of_keys != 1 {
126            return Err(Error::with_kind(ErrorKind::InvalidFormat));
127        }
128
129        // A full pubkey with the same format as seen in certificates
130        let pubkey = reader
131        .read_bytes()
132        .and_then(|v| PublicKey::from_bytes(&v))?;
133
134        // This contains the length of the rest of the bytes in the key
135        // We could use this to do a read bytes into a new reader but I don't
136        // think there is an advantage to that right now (other than verifying)
137        // that this value is correct.
138        let _remaining_length = reader.read_u32()?;
139
140        // These four bytes are repeated and I'm not sure what they do
141        let c1 = reader.read_u32()?;
142        let c2 = reader.read_u32()?;
143
144        if c1 != c2 {
145            return Err(Error::with_kind(ErrorKind::InvalidFormat));
146        }
147
148        // The key type is repeated here.
149        let key_type = reader.read_string()?;
150        let kt = KeyType::from_name(&key_type)?;
151        
152        let kind = match kt.kind {
153            /*KeyTypeKind::Rsa => {
154                let k = RsaPublicKey {
155                    e: reader.read_mpint()?,
156                    n: reader.read_mpint()?,
157                };
158
159                PublicKeyKind::Rsa(k)
160            }*/
161            KeyTypeKind::Ecdsa => {
162                let identifier = reader.read_string()?;
163                let curve = Curve::from_identifier(&identifier)?;
164                // The pub key is also repeated here
165                let _pubkey = reader.read_bytes()?;
166                let key = reader.read_bytes()?;
167                let k = EcdsaPrivateKey {
168                    curve,
169                    key,
170                };
171
172                PrivateKeyKind::Ecdsa(k)
173            }
174            /*KeyTypeKind::Ed25519 => {
175                let k = Ed25519PrivateKey {
176                    key: reader.read_bytes()?,
177                };
178
179                PrivateKeyKind::Ed25519(k)
180            }*/
181            _ => return Err(Error::with_kind(ErrorKind::UnknownKeyType(kt.name.to_string()))),
182        };
183
184        Ok(PrivateKey {
185            key_type: kt,
186            kind,
187            pubkey,
188            comment: None,
189        })
190    }
191}
192