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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
use aes::cipher::block_padding::NoPadding;
use aes::cipher::{BlockDecryptMut, KeyIvInit, StreamCipher};
use bcrypt_pbkdf;
use ctr::Ctr64LE;
#[cfg(feature = "openssl")]
use openssl::bn::BigNum;
use crate::encoding::Reader;
use crate::{key, Error, KEYTYPE_ED25519, KEYTYPE_RSA};
pub fn decode_openssh(secret: &[u8], password: Option<&str>) -> Result<key::KeyPair, Error> {
    if matches!(secret.get(0..15), Some(b"openssh-key-v1\0")) {
        let mut position = secret.reader(15);
        let ciphername = position.read_string()?;
        let kdfname = position.read_string()?;
        let kdfoptions = position.read_string()?;
        let nkeys = position.read_u32()?;
        for _ in 0..nkeys {
            position.read_string()?;
        }
        let secret_ = position.read_string()?;
        let secret = decrypt_secret_key(ciphername, kdfname, kdfoptions, password, secret_)?;
        let mut position = secret.reader(0);
        let _check0 = position.read_u32()?;
        let _check1 = position.read_u32()?;
        #[allow(clippy::never_loop)]
        for _ in 0..nkeys {
            let key_type = position.read_string()?;
            if key_type == KEYTYPE_ED25519 {
                let pubkey = position.read_string()?;
                let seckey = position.read_string()?;
                let _comment = position.read_string()?;
                if Some(pubkey) != seckey.get(32..) {
                    return Err(Error::KeyIsCorrupt);
                }
                let secret = ed25519_dalek::SecretKey::from_bytes(
                    seckey.get(..32).ok_or(Error::KeyIsCorrupt)?,
                )?;
                let public = (&secret).into();
                return Ok(key::KeyPair::Ed25519(ed25519_dalek::Keypair {
                    secret,
                    public,
                }));
            } else if key_type == KEYTYPE_RSA && cfg!(feature = "openssl") {
                #[cfg(feature = "openssl")]
                {
                    let n = BigNum::from_slice(position.read_string()?)?;
                    let e = BigNum::from_slice(position.read_string()?)?;
                    let d = BigNum::from_slice(position.read_string()?)?;
                    let iqmp = BigNum::from_slice(position.read_string()?)?;
                    let p = BigNum::from_slice(position.read_string()?)?;
                    let q = BigNum::from_slice(position.read_string()?)?;
                    let mut ctx = openssl::bn::BigNumContext::new()?;
                    let un = openssl::bn::BigNum::from_u32(1)?;
                    let mut p1 = openssl::bn::BigNum::new()?;
                    let mut q1 = openssl::bn::BigNum::new()?;
                    p1.checked_sub(&p, &un)?;
                    q1.checked_sub(&q, &un)?;
                    let mut dmp1 = openssl::bn::BigNum::new()?; dmp1.checked_rem(&d, &p1, &mut ctx)?;
                    let mut dmq1 = openssl::bn::BigNum::new()?; dmq1.checked_rem(&d, &q1, &mut ctx)?;
                    let key = openssl::rsa::RsaPrivateKeyBuilder::new(n, e, d)?
                        .set_factors(p, q)?
                        .set_crt_params(dmp1, dmq1, iqmp)?
                        .build();
                    key.check_key()?;
                    return Ok(key::KeyPair::RSA {
                        key,
                        hash: key::SignatureHash::SHA2_512,
                    });
                }
            } else {
                return Err(Error::UnsupportedKeyType(key_type.to_vec()));
            }
        }
        Err(Error::CouldNotReadKey)
    } else {
        Err(Error::CouldNotReadKey)
    }
}
use aes::*;
fn decrypt_secret_key(
    ciphername: &[u8],
    kdfname: &[u8],
    kdfoptions: &[u8],
    password: Option<&str>,
    secret_key: &[u8],
) -> Result<Vec<u8>, Error> {
    if kdfname == b"none" {
        if password.is_none() {
            Ok(secret_key.to_vec())
        } else {
            Err(Error::CouldNotReadKey)
        }
    } else if let Some(password) = password {
        let mut key = [0; 48];
        let n = match ciphername {
            b"aes128-cbc" | b"aes128-ctr" => 32,
            b"aes256-cbc" | b"aes256-ctr" => 48,
            _ => return Err(Error::CouldNotReadKey),
        };
        match kdfname {
            b"bcrypt" => {
                let mut kdfopts = kdfoptions.reader(0);
                let salt = kdfopts.read_string()?;
                let rounds = kdfopts.read_u32()?;
                #[allow(clippy::unwrap_used)] #[allow(clippy::indexing_slicing)] bcrypt_pbkdf::bcrypt_pbkdf(password, salt, rounds, &mut key[..n]).unwrap();
            }
            _kdfname => {
                return Err(Error::CouldNotReadKey);
            }
        };
        let (key, iv) = key.split_at(n - 16);
        let mut dec = secret_key.to_vec();
        dec.resize(dec.len() + 32, 0u8);
        match ciphername {
            b"aes128-cbc" => {
                #[allow(clippy::unwrap_used)] let cipher = cbc::Decryptor::<Aes128>::new_from_slices(key, iv).unwrap();
                let n = cipher.decrypt_padded_mut::<NoPadding>(&mut dec)?.len();
                dec.truncate(n)
            }
            b"aes256-cbc" => {
                #[allow(clippy::unwrap_used)] let cipher = cbc::Decryptor::<Aes256>::new_from_slices(key, iv).unwrap();
                let n = cipher.decrypt_padded_mut::<NoPadding>(&mut dec)?.len();
                dec.truncate(n)
            }
            b"aes128-ctr" => {
                #[allow(clippy::unwrap_used)] let mut cipher = Ctr64LE::<Aes128>::new_from_slices(key, iv).unwrap();
                cipher.apply_keystream(&mut dec);
                dec.truncate(secret_key.len())
            }
            b"aes256-ctr" => {
                #[allow(clippy::unwrap_used)] let mut cipher = Ctr64LE::<Aes256>::new_from_slices(key, iv).unwrap();
                cipher.apply_keystream(&mut dec);
                dec.truncate(secret_key.len())
            }
            _ => {}
        }
        Ok(dec)
    } else {
        Err(Error::KeyIsEncrypted)
    }
}