1use crate::algorithm::{
2 hash::{self, HashCtx, HashType},
3 PubKey,
4};
5use crate::model::Data;
6use crate::{SshError, SshResult};
7use rsa::pkcs1::DecodeRsaPrivateKey;
8use rsa::pkcs1v15::Pkcs1v15Sign;
9use rsa::traits::PublicKeyParts;
10use std::fmt::Debug;
11use std::fs::File;
12use std::io::Read;
13use std::path::Path;
14
15const KEY_FILE_MAGIC_START: &str = "-----BEGIN OPENSSH PRIVATE KEY-----";
16
17#[derive(Clone, Default)]
18pub struct KeyPair {
19 pub(super) private_key: String,
20 pub(super) key_type: KeyType,
21}
22
23impl KeyPair {
24 pub fn from_str(key_str: &str) -> SshResult<Self> {
25 let key_str = key_str.trim().to_owned();
27
28 let (key_type, private_key) = if rsa::RsaPrivateKey::from_pkcs1_pem(&key_str).is_ok() {
29 (KeyType::PemRsa, key_str)
30 } else if key_str.starts_with(KEY_FILE_MAGIC_START) {
31 match ssh_key::PrivateKey::from_openssh(&key_str) {
32 Ok(prk) => match prk.algorithm() {
33 ssh_key::Algorithm::Rsa { hash: _hash } => (KeyType::SshRsa, key_str),
34 ssh_key::Algorithm::Ed25519 => (KeyType::SshEd25519, key_str),
35 x => {
36 return Err(SshError::SshPubKeyError(format!(
37 "Currently don't support the key file type {}",
38 x
39 )))
40 }
41 },
42 Err(e) => return Err(SshError::SshPubKeyError(e.to_string())),
43 }
44 } else {
45 return Err(SshError::SshPubKeyError(
46 "Unable to detect the pulic key type".to_owned(),
47 ));
48 };
49
50 let pair = KeyPair {
52 private_key,
53 key_type,
54 };
55 Ok(pair)
56 }
57
58 pub fn get_blob(&self, alg: &PubKey) -> Vec<u8> {
59 match self.key_type {
60 KeyType::PemRsa => {
61 let rprk = rsa::RsaPrivateKey::from_pkcs1_pem(&self.private_key).unwrap();
63 let rpuk = rprk.to_public_key();
64 let es = rpuk.e().to_bytes_be();
65 let ns = rpuk.n().to_bytes_be();
66 let mut blob = Data::new();
67 blob.put_str(alg.as_ref());
68 blob.put_mpint(&es);
69 blob.put_mpint(&ns);
70 blob.to_vec()
71 }
72 KeyType::SshRsa => {
73 let prk = ssh_key::PrivateKey::from_openssh(&self.private_key).unwrap();
74 let rsa = prk.key_data().rsa().unwrap();
75 let es = rsa.public.e.as_bytes();
76 let ns = rsa.public.n.as_bytes();
77 let mut blob = Data::new();
78 blob.put_str(alg.as_ref());
79 blob.put_mpint(es);
80 blob.put_mpint(ns);
81 blob.to_vec()
82 }
83 KeyType::SshEd25519 => {
84 let prk = ssh_key::PrivateKey::from_openssh(&self.private_key).unwrap();
85 let ed25519 = prk.key_data().ed25519().unwrap();
86 let mut blob = Data::new();
87 blob.put_str(alg.as_ref());
88 blob.put_u8s(ed25519.public.as_ref());
89 blob.to_vec()
90 }
91 }
92 }
93
94 fn sign(&self, sd: &[u8], alg: &PubKey) -> Vec<u8> {
95 match self.key_type {
96 KeyType::PemRsa | KeyType::SshRsa => {
97 let (scheme, digest) = match alg {
98 PubKey::RsaSha2_512 => (
99 Pkcs1v15Sign::new::<sha2::Sha512>(),
100 ring::digest::digest(&ring::digest::SHA512, sd),
101 ),
102 PubKey::RsaSha2_256 => (
103 Pkcs1v15Sign::new::<sha2::Sha256>(),
104 ring::digest::digest(&ring::digest::SHA256, sd),
105 ),
106 #[cfg(feature = "deprecated-rsa-sha1")]
107 PubKey::SshRsa => (
108 Pkcs1v15Sign::new::<sha1::Sha1>(),
109 ring::digest::digest(&ring::digest::SHA1_FOR_LEGACY_USE_ONLY, sd),
110 ),
111 _ => unreachable!(),
112 };
113
114 let msg = digest.as_ref();
115 let rprk = match self.key_type {
116 KeyType::PemRsa => {
117 rsa::RsaPrivateKey::from_pkcs1_pem(self.private_key.as_str()).unwrap()
118 }
119 KeyType::SshRsa => {
120 let prk = ssh_key::PrivateKey::from_openssh(&self.private_key).unwrap();
123 let rsa = prk.key_data().rsa().unwrap();
124 rsa::RsaPrivateKey::try_from(rsa).unwrap()
125 }
126 _ => unreachable!(),
127 };
128
129 rprk.sign(scheme, msg).unwrap()
130 }
131 KeyType::SshEd25519 => {
132 use signature::Signer;
133 let prk = ssh_key::PrivateKey::from_openssh(&self.private_key).unwrap();
134 let sign = prk.try_sign(sd).unwrap();
135 sign.as_bytes().to_vec()
136 }
137 }
138 }
139
140 pub(crate) fn signature(
141 &self,
142 buf: &[u8],
143 hash_ctx: HashCtx,
144 hash_type: HashType,
145 alg: &PubKey,
146 ) -> Vec<u8> {
147 let session_id = hash::digest(hash_ctx.as_bytes().as_slice(), hash_type);
148 let mut sd = Data::new();
149 sd.put_u8s(session_id.as_slice());
150 sd.extend_from_slice(buf);
151 let sign = self.sign(&sd, alg);
152 let mut ss = Data::new();
153 ss.put_str(alg.as_ref());
154 ss.put_u8s(&sign);
155 ss.to_vec()
156 }
157}
158
159#[derive(Clone, Default)]
160pub(super) enum KeyType {
161 #[default]
162 PemRsa,
163 SshRsa,
164 SshEd25519,
165}
166
167#[derive(Clone, Default)]
168pub(crate) struct AuthInfo {
169 pub username: String,
170 pub password: String,
171 pub key_pair: Option<KeyPair>,
172}
173
174impl Debug for AuthInfo {
175 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176 write!(f, "username: {}", self.username)?;
177 Ok(())
178 }
179}
180
181impl AuthInfo {
182 pub fn username<U>(&mut self, u: U) -> SshResult<()>
183 where
184 U: ToString,
185 {
186 self.username = u.to_string();
187 Ok(())
188 }
189
190 pub fn password<P>(&mut self, p: P) -> SshResult<()>
191 where
192 P: ToString,
193 {
194 self.password = p.to_string();
195 Ok(())
196 }
197
198 pub fn private_key<K>(&mut self, k: K) -> SshResult<()>
199 where
200 K: ToString,
201 {
202 self.key_pair = Some((KeyPair::from_str(&k.to_string()))?);
203 Ok(())
204 }
205
206 pub fn private_key_path<P>(&mut self, p: P) -> SshResult<()>
207 where
208 P: AsRef<Path>,
209 {
210 let mut file = File::open(p)?;
211 let mut prks = String::new();
212 file.read_to_string(&mut prks)?;
213
214 self.key_pair = Some((KeyPair::from_str(&prks))?);
215 Ok(())
216 }
217}