ssh_keys/
openssh.rs

1//! Parser of OpenSSH keys
2//!
3//! Key formats supported:
4//!
5//! * `ssh-rsa`
6//! * `ssh-ed25519`
7//!
8//! Both ASN1 and openssh-key-v1 format supported.
9//!
10//! Password-protected private keys are not supported yet
11
12use std::str::from_utf8;
13
14use base64;
15use byteorder::{BigEndian, ByteOrder};
16
17
18use {PublicKey, PrivateKey, Error};
19
20struct Cursor<'a> {
21    data: &'a [u8],
22    offset: usize,
23}
24
25struct Asn1<'a> {
26    data: &'a [u8],
27    offset: usize,
28}
29
30
31/// Parse a single SSH public key in openssh format
32pub fn parse_public_key(line: &str) -> Result<PublicKey, Error> {
33    let mut iter = line.split_whitespace();
34    let kind = iter.next().ok_or(Error::InvalidFormat)?;
35    let data = iter.next().ok_or(Error::InvalidFormat)?;
36    let buf = b64decode(data.as_bytes())?;
37
38    let mut cur = Cursor::new(&buf);
39    let int_kind = cur.read_string()?;
40    if int_kind != int_kind {
41        return Err(Error::InvalidFormat);
42    }
43
44    match kind {
45        "ssh-rsa" => {
46            let e = cur.read_bytes()?;
47            let n = cur.read_bytes()?;
48            Ok(PublicKey::Rsa { exponent: e.to_vec(), modulus: n.to_vec() })
49        }
50        "ssh-ed25519" => {
51            let key = cur.read_bytes()?;
52            if key.len() != 32 {
53                return Err(Error::InvalidFormat);
54            }
55            let mut array_key = [0u8; 32];
56            array_key.copy_from_slice(key);
57            Ok(PublicKey::Ed25519(array_key))
58        }
59        _ => Err(Error::UnsupportedType(kind.to_string()))
60    }
61}
62
63fn b64decode(data: &[u8]) -> Result<Vec<u8>, Error> {
64    base64::decode_config(data, base64::Config::new(
65        base64::CharacterSet::Standard,
66        /*pad*/ true,
67        /*strip_whitepace*/ true,
68        base64::LineWrap::NoWrap, // irrelevant
69    ))
70    .map_err(|_| Error::InvalidFormat)
71}
72
73/// Parse a SSH private key in openssh format
74///
75/// Note new format of SSH key can potentially contain more than one key
76pub fn parse_private_key(data: &str) -> Result<Vec<PrivateKey>, Error> {
77    if data.starts_with("-----BEGIN RSA PRIVATE KEY-----") {
78        let end = data.find("-----END RSA PRIVATE KEY-----")
79            .ok_or(Error::InvalidFormat)?;
80        let start = match (data[..end].find("\n\n"),
81                           data[..end].find("\r\n\r\n"))
82        {
83            (Some(x), Some(y)) if x < y => x,
84            (Some(_), Some(y)) => y,
85            (Some(x), None) => x,
86            (None, Some(y)) => y,
87            (None, None) => "-----BEGIN RSA PRIVATE KEY-----".len(),
88        };
89        let data = b64decode(data[start..end].trim().as_bytes())?;
90        let mut cur = Asn1::new(&data);
91        let mut items = cur.sequence()?;
92        let ver = items.read_short_int()?;
93        if ver != 0 {
94            return Err(Error::UnsupportedType(format!("version {}", ver)));
95        }
96        let n = items.read_big_int()?;
97        let e = items.read_big_int()?;
98        let d = items.read_big_int()?;
99        let p = items.read_big_int()?;
100        let q = items.read_big_int()?;
101        let _x = items.read_big_int()?;
102        let _y = items.read_big_int()?;
103        let iqmp = items.read_big_int()?;
104        return Ok(vec![PrivateKey::Rsa {
105            n: n.to_vec(), e: e.to_vec(), d: d.to_vec(),
106            iqmp: iqmp.to_vec(),
107            p: p.to_vec(), q: q.to_vec(),
108        }]);
109    } else if data.starts_with("-----BEGIN OPENSSH PRIVATE KEY-----") {
110        let start = "-----BEGIN OPENSSH PRIVATE KEY-----".len();
111        let end = data.find("-----END OPENSSH PRIVATE KEY-----")
112            .ok_or(Error::InvalidFormat)?;
113        if start >= end {
114            return Err(Error::InvalidFormat);
115        }
116        let data = b64decode(data[start..end].trim().as_bytes())?;
117        let end = data.iter().position(|&x| x == 0)
118            .ok_or(Error::InvalidFormat)?;
119        let kind = from_utf8(&data[..end]).map_err(|_| Error::InvalidFormat)?;
120        match kind {
121            "openssh-key-v1" => {
122                let mut cur = Cursor::new(&data[end+1..]);
123                let cipher_name = cur.read_string()?;
124                let kdf_name = cur.read_string()?;
125                let opt = cur.read_string()?;
126                if cipher_name != "none" || kdf_name != "none" || opt != "" {
127                    return Err(Error::Encrypted);
128                }
129                let num_keys = cur.read_int()?;
130                let mut result = Vec::new();
131                for _ in 0..num_keys {
132                    let _pub_key = cur.read_bytes()?;
133                    let priv_key = cur.read_bytes()?;
134                    let mut pcur = Cursor::new(priv_key);
135                    let c1 = pcur.read_int()?;
136                    let c2 = pcur.read_int()?;
137                    if c1 != c2 {
138                        return Err(Error::InvalidFormat);
139                    }
140                    let key_type = pcur.read_string()?;
141                    match key_type {
142                        "ssh-ed25519" => {
143                            let _pub_key = pcur.read_bytes()?;
144                            let priv_key = pcur.read_bytes()?;
145                            let _comment = pcur.read_string()?;
146
147                            let mut array_key = [0u8; 64];
148                            array_key.copy_from_slice(priv_key);
149                            result.push(PrivateKey::Ed25519(array_key));
150                        }
151                        "ssh-rsa" => {
152                            let n = pcur.read_bytes()?;
153                            let e = pcur.read_bytes()?;
154                            let d = pcur.read_bytes()?;
155                            let iqmp = pcur.read_bytes()?;
156                            let p = pcur.read_bytes()?;
157                            let q = pcur.read_bytes()?;
158                            let _comment = pcur.read_string()?;
159                            result.push(PrivateKey::Rsa {
160                                n: n.to_vec(), e: e.to_vec(), d: d.to_vec(),
161                                iqmp: iqmp.to_vec(),
162                                p: p.to_vec(), q: q.to_vec(),
163                            })
164                        }
165                        _ => {
166                            return Err(Error::UnsupportedType(
167                                key_type.to_string()));
168                        }
169                    }
170                }
171                return Ok(result);
172            }
173            _ => return Err(Error::UnsupportedType(kind.to_string())),
174        }
175    } else {
176        Err(Error::UnsupportedType("unknown".to_string()))
177    }
178}
179
180impl<'a> Cursor<'a> {
181    pub fn new(data: &[u8]) -> Cursor {
182        Cursor {
183            data: data,
184            offset: 0,
185        }
186    }
187    fn read_int(&mut self) -> Result<u32, Error> {
188        let cur = &self.data[self.offset..];
189        if cur.len() < 4 {
190            return Err(Error::InvalidFormat);
191        }
192        self.offset += 4;
193        return Ok(BigEndian::read_u32(&cur[..4]));
194    }
195    fn read_bytes(&mut self) -> Result<&'a [u8], Error> {
196        let cur = &self.data[self.offset..];
197        if cur.len() < 4 {
198            return Err(Error::InvalidFormat);
199        }
200        let len = BigEndian::read_u32(&cur[..4]) as usize;
201        if cur.len() < len + 4 {
202            return Err(Error::InvalidFormat);
203        }
204        self.offset += len + 4;
205        return Ok(&cur[4..len+4]);
206    }
207    fn read_string(&mut self) -> Result<&'a str, Error> {
208        from_utf8(self.read_bytes()?)
209        .map_err(|_| Error::InvalidFormat)
210    }
211}
212
213
214/// A limited ASN1 (DER) parser suitable to decode RSA key
215impl<'a> Asn1<'a>  {
216    pub fn new(data: &[u8]) -> Asn1 {
217        Asn1 {
218            data: data,
219            offset: 0,
220        }
221    }
222    fn read_len(&mut self) -> Result<usize, Error> {
223        if self.offset >= self.data.len() {
224            return Err(Error::InvalidFormat);
225        }
226        let lbyte = self.data[self.offset];
227        self.offset += 1;
228        if lbyte == 128 || lbyte == 255 {
229            return Err(Error::InvalidFormat);
230        }
231        if lbyte & 128 == 0 {
232            return Ok(lbyte as usize);
233        }
234        let nbytes = (lbyte & 127) as usize;
235        if self.data.len() < self.offset + nbytes {
236            return Err(Error::InvalidFormat);
237        }
238        let mut result: usize = 0;
239        for i in 0..nbytes {
240            result = result.checked_mul(256).ok_or(Error::InvalidFormat)?
241                     + self.data[self.offset+i] as usize;
242        }
243        self.offset += nbytes;
244        return Ok(result);
245    }
246    pub fn sequence(&mut self) -> Result<Asn1<'a>, Error> {
247        if self.offset >= self.data.len() {
248            return Err(Error::InvalidFormat);
249        }
250        let byte = self.data[self.offset];
251        // Universal, construct, sequence
252        if byte != (0 << 6) | (1 << 5) | 16 {
253            return Err(Error::InvalidFormat);
254        }
255        self.offset += 1;
256        let bytes = self.read_len()?;
257        if self.offset+bytes > self.data.len() {
258            return Err(Error::InvalidFormat);
259        }
260        let res = Asn1::new(&self.data[self.offset..self.offset+bytes]);
261        self.offset += bytes;
262        return Ok(res);
263    }
264    pub fn read_big_int(&mut self) -> Result<&'a [u8], Error> {
265        if self.offset >= self.data.len() {
266            return Err(Error::InvalidFormat);
267        }
268        let byte = self.data[self.offset];
269        // Universal, primitive, integer
270        if byte != (0 << 6) | (0 << 5) | 2 {
271            return Err(Error::InvalidFormat);
272        }
273        self.offset += 1;
274        let len = self.read_len()?;
275        if self.data.len() < self.offset + len {
276            return Err(Error::InvalidFormat);
277        }
278        let result = &self.data[self.offset..self.offset + len];
279        self.offset += len;
280        return Ok(result);
281    }
282    pub fn read_short_int(&mut self) -> Result<u8, Error> {
283        let data = self.read_big_int()?;
284        if data.len() != 1 {
285            return Err(Error::InvalidFormat);
286        }
287        return Ok(data[0]);
288    }
289}