1use std::convert::TryInto;
2use std::io::Write;
3
4use data_encoding::{BASE64_MIME, HEXLOWER_PERMISSIVE};
5use pkcs1::DecodeRsaPrivateKey;
6use ssh_key::private::RsaKeypair;
7use ssh_key::PrivateKey;
8
9use super::is_base64_char;
10use crate::Error;
11
12pub mod openssh;
13
14#[cfg(feature = "legacy-ed25519-pkcs8-parser")]
15mod pkcs8_legacy;
16
17pub use self::openssh::*;
18
19pub mod pkcs5;
20pub use self::pkcs5::*;
21
22pub mod pkcs8;
23
24const AES_128_CBC: &str = "DEK-Info: AES-128-CBC,";
25
26#[derive(Clone, Copy, Debug)]
27pub enum Encryption {
29 Aes128Cbc([u8; 16]),
31 Aes256Cbc([u8; 16]),
33}
34
35#[derive(Clone, Debug)]
36enum Format {
37 Rsa,
38 Openssh,
39 Pkcs5Encrypted(Encryption),
40 Pkcs8Encrypted,
41 Pkcs8,
42}
43
44pub fn decode_secret_key(secret: &str, password: Option<&str>) -> Result<PrivateKey, Error> {
47 let mut format = None;
48 let secret = {
49 let mut started = false;
50 let mut sec = String::new();
51 for l in secret.lines() {
52 if started {
53 if l.starts_with("-----END ") {
54 break;
55 }
56 if l.chars().all(is_base64_char) {
57 sec.push_str(l)
58 } else if l.starts_with(AES_128_CBC) {
59 let iv_: Vec<u8> =
60 HEXLOWER_PERMISSIVE.decode(l.split_at(AES_128_CBC.len()).1.as_bytes())?;
61 if iv_.len() != 16 {
62 return Err(Error::CouldNotReadKey);
63 }
64 let mut iv = [0; 16];
65 iv.clone_from_slice(&iv_);
66 format = Some(Format::Pkcs5Encrypted(Encryption::Aes128Cbc(iv)))
67 }
68 }
69 if l == "-----BEGIN OPENSSH PRIVATE KEY-----" {
70 started = true;
71 format = Some(Format::Openssh);
72 } else if l == "-----BEGIN RSA PRIVATE KEY-----" {
73 started = true;
74 format = Some(Format::Rsa);
75 } else if l == "-----BEGIN ENCRYPTED PRIVATE KEY-----" {
76 started = true;
77 format = Some(Format::Pkcs8Encrypted);
78 } else if l == "-----BEGIN PRIVATE KEY-----" {
79 started = true;
80 format = Some(Format::Pkcs8);
81 }
82 }
83 sec
84 };
85
86 let secret = BASE64_MIME.decode(secret.as_bytes())?;
87 match format {
88 Some(Format::Openssh) => decode_openssh(&secret, password),
89 Some(Format::Rsa) => Ok(decode_rsa_pkcs1_der(&secret)?.into()),
90 Some(Format::Pkcs5Encrypted(enc)) => decode_pkcs5(&secret, password, enc),
91 Some(Format::Pkcs8Encrypted) | Some(Format::Pkcs8) => {
92 let result = self::pkcs8::decode_pkcs8(&secret, password.map(|x| x.as_bytes()));
93 #[cfg(feature = "legacy-ed25519-pkcs8-parser")]
94 {
95 if result.is_err() {
96 let legacy_result =
97 pkcs8_legacy::decode_pkcs8(&secret, password.map(|x| x.as_bytes()));
98 if let Ok(key) = legacy_result {
99 return Ok(key);
100 }
101 }
102 }
103 result
104 }
105 None => Err(Error::CouldNotReadKey),
106 }
107}
108
109pub fn encode_pkcs8_pem<W: Write>(key: &PrivateKey, mut w: W) -> Result<(), Error> {
110 let x = self::pkcs8::encode_pkcs8(key)?;
111 w.write_all(b"-----BEGIN PRIVATE KEY-----\n")?;
112 w.write_all(BASE64_MIME.encode(&x).as_bytes())?;
113 w.write_all(b"\n-----END PRIVATE KEY-----\n")?;
114 Ok(())
115}
116
117pub fn encode_pkcs8_pem_encrypted<W: Write>(
118 key: &PrivateKey,
119 pass: &[u8],
120 rounds: u32,
121 mut w: W,
122) -> Result<(), Error> {
123 let x = self::pkcs8::encode_pkcs8_encrypted(pass, rounds, key)?;
124 w.write_all(b"-----BEGIN ENCRYPTED PRIVATE KEY-----\n")?;
125 w.write_all(BASE64_MIME.encode(&x).as_bytes())?;
126 w.write_all(b"\n-----END ENCRYPTED PRIVATE KEY-----\n")?;
127 Ok(())
128}
129
130fn decode_rsa_pkcs1_der(secret: &[u8]) -> Result<RsaKeypair, Error> {
131 Ok(rsa::RsaPrivateKey::from_pkcs1_der(secret)?.try_into()?)
132}