ssh_key/
cipher.rs
1use crate::{algorithm::AlgString, Error, Result};
6use core::{fmt, str};
7
8#[cfg(feature = "encryption")]
9use aes::{
10 cipher::{InnerIvInit, KeyInit, StreamCipherCore},
11 Aes256,
12};
13
14const AES256_CTR: &str = "aes256-ctr";
16
17#[cfg(feature = "encryption")]
19type Ctr128BE<Cipher> = ctr::CtrCore<Cipher, ctr::flavors::Ctr128BE>;
20
21#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
23#[non_exhaustive]
24pub enum Cipher {
25 None,
27
28 Aes256Ctr,
30}
31
32impl Cipher {
33 pub fn new(ciphername: &str) -> Result<Self> {
38 match ciphername {
39 "none" => Ok(Self::None),
40 AES256_CTR => Ok(Self::Aes256Ctr),
41 _ => Err(Error::Algorithm),
42 }
43 }
44
45 pub fn as_str(self) -> &'static str {
47 match self {
48 Self::None => "none",
49 Self::Aes256Ctr => AES256_CTR,
50 }
51 }
52
53 pub fn key_and_iv_size(self) -> Option<(usize, usize)> {
55 match self {
56 Self::None => None,
57 Self::Aes256Ctr => Some((32, 16)),
58 }
59 }
60
61 pub fn block_size(self) -> usize {
63 match self {
64 Self::None => 8,
65 Self::Aes256Ctr => 16,
66 }
67 }
68
69 #[allow(clippy::integer_arithmetic)]
72 pub fn padding_len(self, input_size: usize) -> usize {
73 match input_size % self.block_size() {
74 0 => 0,
75 input_rem => self.block_size() - input_rem,
76 }
77 }
78
79 pub fn is_none(self) -> bool {
81 self == Self::None
82 }
83
84 pub fn is_some(self) -> bool {
86 !self.is_none()
87 }
88
89 #[cfg(feature = "encryption")]
91 #[cfg_attr(docsrs, doc(cfg(feature = "encryption")))]
92 pub fn decrypt(self, key: &[u8], iv: &[u8], buffer: &mut [u8]) -> Result<()> {
93 match self {
94 Self::None => return Err(Error::Crypto),
95 Self::Aes256Ctr => self.encrypt(key, iv, buffer)?,
97 }
98
99 Ok(())
100 }
101
102 #[cfg(feature = "encryption")]
104 #[cfg_attr(docsrs, doc(cfg(feature = "encryption")))]
105 pub fn encrypt(self, key: &[u8], iv: &[u8], buffer: &mut [u8]) -> Result<()> {
106 match self {
107 Self::None => return Err(Error::Crypto),
108 Self::Aes256Ctr => {
109 let cipher = Aes256::new_from_slice(key)
110 .and_then(|aes| Ctr128BE::inner_iv_slice_init(aes, iv))
111 .map_err(|_| Error::Crypto)?;
112
113 cipher
114 .try_apply_keystream_partial(buffer.into())
115 .map_err(|_| Error::Crypto)?;
116 }
117 }
118
119 Ok(())
120 }
121}
122
123impl AsRef<str> for Cipher {
124 fn as_ref(&self) -> &str {
125 self.as_str()
126 }
127}
128
129impl AlgString for Cipher {}
130
131impl Default for Cipher {
132 fn default() -> Cipher {
133 Cipher::Aes256Ctr
134 }
135}
136
137impl fmt::Display for Cipher {
138 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139 f.write_str(self.as_str())
140 }
141}
142
143impl str::FromStr for Cipher {
144 type Err = Error;
145
146 fn from_str(id: &str) -> Result<Self> {
147 Self::new(id)
148 }
149}