Advertisement
mamanegoryu1

exonum/src/helpers/fabric/password.rs

May 21st, 2020
684
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Rust 5.22 KB | None | 0 0
  1. // Copyright 2019 The Exonum Team
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. //   http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14.  
  15. //! This module contains utilities for passphrase entry.
  16.  
  17. use failure::bail;
  18. use rpassword::read_password_from_tty;
  19.  
  20. use std::{env, io, str::FromStr};
  21.  
  22. use crate::helpers::ZeroizeOnDrop;
  23.  
  24. /// Passphrase input methods
  25. #[derive(Debug, Serialize, Deserialize, PartialEq)]
  26. pub enum PassInputMethod {
  27.     /// Prompt passphrase from terminal.
  28.     Terminal,
  29.     /// Get passphrase from environment variable (default if `None`).
  30.     EnvVariable(Option<String>),
  31.     /// Passphrase is passed as a command line parameter.
  32.     CmdLineParameter(ZeroizeOnDrop<String>),
  33. }
  34.  
  35. /// Secret key types.
  36. #[derive(Copy, Clone)]
  37. pub enum SecretKeyType {
  38.     Consensus,
  39.     Service,
  40. }
  41.  
  42. impl PassInputMethod {
  43.     /// Get passphrase using selected method.
  44.     /// Details of this process differs for different secret key types and whether we run node
  45.     /// or generate config files.
  46.     pub fn get_passphrase(self, key_type: SecretKeyType, node_run: bool) -> ZeroizeOnDrop<String> {
  47.         match self {
  48.             PassInputMethod::Terminal => {
  49.                 let prompt = match key_type {
  50.                     SecretKeyType::Consensus => "Enter consensus key passphrase",
  51.                     SecretKeyType::Service => "Enter service key passphrase",
  52.                 };
  53.                 prompt_passphrase(prompt, node_run).expect("Failed to read password from stdin")
  54.             }
  55.             PassInputMethod::EnvVariable(name) => {
  56.                 let var = if let Some(ref name) = name {
  57.                     name
  58.                 } else {
  59.                     match key_type {
  60.                         SecretKeyType::Consensus => "EXONUM_CONSENSUS_PASS",
  61.                         SecretKeyType::Service => "EXONUM_SERVICE_PASS",
  62.                     }
  63.                 };
  64.                 ZeroizeOnDrop(env::var(var).unwrap_or_else(|e| {
  65.                     panic!("Failed to get password from env variable: {}, {}", var, e)
  66.                 }))
  67.             }
  68.             PassInputMethod::CmdLineParameter(pass) => pass,
  69.         }
  70.     }
  71. }
  72.  
  73. impl Default for PassInputMethod {
  74.     fn default() -> Self {
  75.         PassInputMethod::Terminal
  76.     }
  77. }
  78.  
  79. impl FromStr for PassInputMethod {
  80.     type Err = failure::Error;
  81.  
  82.     fn from_str(s: &str) -> Result<Self, Self::Err> {
  83.         if s.is_empty() {
  84.             return Ok(Default::default());
  85.         }
  86.  
  87.         if s == "stdin" {
  88.             return Ok(PassInputMethod::Terminal);
  89.         }
  90.  
  91.         if s.starts_with("env") {
  92.             let env_var = s.split(':').nth(1).map(String::from);
  93.             return Ok(PassInputMethod::EnvVariable(env_var));
  94.         }
  95.  
  96.         if s.starts_with("pass") {
  97.             let pass = s.split(':').nth(1).unwrap_or_default();
  98.             return Ok(PassInputMethod::CmdLineParameter(ZeroizeOnDrop(
  99.                 pass.to_owned(),
  100.             )));
  101.         }
  102.  
  103.         bail!("Failed to parse passphrase input method")
  104.     }
  105. }
  106.  
  107. fn prompt_passphrase(prompt: &str, node_run: bool) -> io::Result<ZeroizeOnDrop<String>> {
  108.     if node_run {
  109.         return Ok(ZeroizeOnDrop(read_password_from_tty(Some(prompt))?));
  110.     }
  111.  
  112.     loop {
  113.         let password = ZeroizeOnDrop(read_password_from_tty(Some(prompt))?);
  114.         if password.is_empty() {
  115.             eprintln!("Passphrase must not be empty. Try again.");
  116.             continue;
  117.         }
  118.  
  119.         let confirmation = ZeroizeOnDrop(read_password_from_tty(Some(
  120.             "Enter same passphrase again: ",
  121.         ))?);
  122.  
  123.         if password == confirmation {
  124.             return Ok(password);
  125.         } else {
  126.             eprintln!("Passphrases do not match. Try again.");
  127.         }
  128.     }
  129. }
  130.  
  131. #[cfg(test)]
  132. mod tests {
  133.     use std::str::FromStr;
  134.  
  135.     use super::PassInputMethod;
  136.     use crate::helpers::ZeroizeOnDrop;
  137.  
  138.     #[test]
  139.     fn test_pass_input_method_parse() {
  140.         let correct_cases = vec![
  141.             ("", <PassInputMethod as Default>::default()),
  142.             ("stdin", PassInputMethod::Terminal),
  143.             ("env", PassInputMethod::EnvVariable(None)),
  144.             (
  145.                 "env:VAR",
  146.                 PassInputMethod::EnvVariable(Some("VAR".to_owned())),
  147.             ),
  148.             (
  149.                 "pass",
  150.                 PassInputMethod::CmdLineParameter(ZeroizeOnDrop("".to_owned())),
  151.             ),
  152.             (
  153.                 "pass:PASS",
  154.                 PassInputMethod::CmdLineParameter(ZeroizeOnDrop("PASS".to_owned())),
  155.             ),
  156.         ];
  157.  
  158.         for (inp, out) in correct_cases {
  159.             let method = <PassInputMethod as FromStr>::from_str(inp);
  160.             assert!(method.is_ok());
  161.             assert_eq!(method.unwrap(), out)
  162.         }
  163.     }
  164. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement