keeper_secrets_manager_core/
utils.rs

1// -*- coding: utf-8 -*-
2//  _  __
3// | |/ /___ ___ _ __  ___ _ _ (R)
4// | ' </ -_) -_) '_ \/ -_) '_|
5// |_|\_\___\___| .__/\___|_|
6//              |_|
7//
8// Keeper Secrets Manager
9// Copyright 2024 Keeper Security Inc.
10// Contact: sm@keepersecurity.com
11//
12
13use crate::{
14    crypto::{unpad_data, CryptoUtils},
15    custom_error::KSMRError,
16};
17use base64::{
18    engine::general_purpose::{STANDARD, URL_SAFE_NO_PAD},
19    prelude::BASE64_URL_SAFE,
20    Engine as _,
21};
22use chrono::Utc;
23use core::str;
24use data_encoding::BASE32;
25use hmac::{Hmac, Mac};
26use log::warn;
27use num_bigint::BigUint;
28use rand::{
29    seq::{IteratorRandom, SliceRandom},
30    thread_rng,
31};
32use serde::Serialize;
33use serde_json::Value;
34use sha1::Sha1;
35use sha2::{Sha256, Sha512};
36use std::process::Output;
37use std::{collections::HashMap, option::Option};
38use std::{env, io};
39use url::{form_urlencoded::parse, Url};
40
41#[cfg(unix)]
42use std::os::unix::fs::PermissionsExt;
43
44#[cfg(target_os = "windows")]
45use log::debug;
46use std::fs::File;
47#[cfg(target_os = "windows")]
48use std::process::Command;
49
50#[cfg(unix)]
51use std::fs;
52
53/// Allowed Windows configuration administrators.
54pub const ALLOWED_WINDOWS_CONFIG_ADMINS: [&[u8]; 2] = [b"Administrators", b"SYSTEM"];
55
56/// Encoding format.
57pub const ENCODING: &str = "UTF-8";
58
59/// Special characters used in passwords or other contexts.
60pub const SPECIAL_CHARACTERS: &str = r#"""!@#$%()+;<>=?[]{}^.,"""#;
61
62/// Default password length.
63pub const DEFAULT_PASSWORD_LENGTH: usize = 32;
64
65/// Converts a string representation of truth to a boolean value.
66///
67/// The function accepts string values that represent true or false:
68/// - True values: "y", "yes", "t", "true", "on", "1"
69/// - False values: "n", "no", "f", "false", "off", "0"
70///
71/// # Errors
72///
73/// Returns an error if the input string does not match any of the valid
74/// truth values.
75///
76/// # Examples
77///
78/// ```
79/// use keeper_secrets_manager_core::utils::str_to_bool;
80/// let true_value = str_to_bool("yes").unwrap();
81/// assert_eq!(true_value, true);
82///
83/// let false_value = str_to_bool("no").unwrap();
84/// assert_eq!(false_value, false);
85///
86/// let invalid_value = str_to_bool("maybe");
87/// assert!(invalid_value.is_err());
88/// ```
89pub fn str_to_bool(val: &str) -> Result<bool, String> {
90    let val = val.to_lowercase();
91    match val.as_str() {
92        "y" | "yes" | "t" | "true" | "on" | "1" => Ok(true),
93        "n" | "no" | "f" | "false" | "off" | "0" => Ok(false),
94        _ => Err(format!("invalid truth value {:?}", val)),
95    }
96}
97
98/// Gets the name of the operating system.
99///
100/// This function returns a string slice that indicates the current
101/// operating system. The possible return values are:
102///
103/// - `"linux"` for Linux operating systems.
104/// - `"macOS"` for macOS.
105/// - The value of `std::env::consts::OS` for any other operating systems.
106///
107/// # Examples
108///
109/// ```
110/// use keeper_secrets_manager_core::utils::get_os;
111/// let os_name = get_os();
112/// println!("Operating System: {}", os_name);
113/// ```
114pub fn get_os() -> &'static str {
115    determine_os(env::consts::OS)
116}
117
118// Helper function for testability
119pub(crate) fn determine_os(os: &str) -> &str {
120    match os {
121        "linux" => "linux",
122        "macos" => "macOS",
123        "windows" => {
124            if cfg!(target_os = "windows") {
125                "win32"
126            } else {
127                "win64"
128            }
129        }
130        _ => os,
131    }
132}
133
134/// Converts a byte slice to a String using the specified encoding.
135///
136/// # Arguments
137///
138/// * `b` - A byte slice (`&[u8]`) that needs to be converted to a String.
139///
140/// # Returns
141///
142/// A `Result<String, std::str::Utf8Error>` where:
143/// - `Ok(String)` contains the decoded string if successful.
144/// - `Err(std::str::Utf8Error)` if the byte slice is not valid UTF-8.
145///
146/// # Examples
147///
148/// ```
149/// use keeper_secrets_manager_core::utils::bytes_to_string;
150/// let bytes = b"Hello, world!";
151/// let result = bytes_to_string(bytes);
152/// assert_eq!(result.unwrap(), "Hello, world!");
153/// ```
154pub fn bytes_to_string(b: &[u8]) -> Result<String, KSMRError> {
155    // let bytes_sorted = unpad_data(b)?;
156    let string_of_bytes: String = str::from_utf8(b)
157        .map_err(|e| KSMRError::DecodeError(e.to_string()))?
158        .to_string();
159    Ok(string_of_bytes)
160}
161
162pub fn bytes_to_string_unpad(b: &[u8]) -> Result<String, KSMRError> {
163    let bytes_sorted = unpad_data(b)?;
164    let string_of_bytes: String = str::from_utf8(&bytes_sorted)
165        .map_err(|e| KSMRError::DecodeError(e.to_string()))?
166        .to_string();
167    Ok(string_of_bytes)
168}
169
170/// Converts a byte slice to an integer using big-endian byte order.
171///
172/// # Arguments
173///
174/// * `b` - A byte slice (`&[u8]`) that needs to be converted to an integer.
175///
176/// # Returns
177///
178/// An `Option<u64>` where:
179/// - `Some(u64)` contains the converted integer if successful.
180/// - `None` if the byte slice is empty or too long to fit in a u64.
181///
182/// # Examples
183///
184/// ```
185/// use keeper_secrets_manager_core::utils::bytes_to_int;
186/// use num_bigint::BigUint;
187/// let bytes = [0x00, 0x01, 0x02, 0x03];
188/// let result = bytes_to_int(&bytes);
189/// assert_eq!(result.unwrap(), BigUint::from(66051u64));
190/// ```
191pub fn bytes_to_int(b: &[u8]) -> Result<BigUint, KSMRError> {
192    Ok(BigUint::from_bytes_be(b))
193}
194
195/// Converts a byte slice to a Base64-encoded string.
196///
197/// # Arguments
198///
199/// * `b` - A byte slice (`&[u8]`) that needs to be converted to a Base64 string.
200///
201/// # Returns
202///
203/// A `String` containing the Base64-encoded representation of the input byte slice.
204///
205/// # Examples
206///
207/// ```
208/// use keeper_secrets_manager_core::utils::bytes_to_base64;
209/// let bytes = b"Hello, world!";
210/// let result = bytes_to_base64(bytes);
211/// assert_eq!(result, "SGVsbG8sIHdvcmxkIQ==");
212/// ```
213pub fn bytes_to_base64(b: &[u8]) -> String {
214    STANDARD.encode(b)
215}
216
217/// Converts a Base64-encoded string to a byte vector.
218///
219/// # Arguments
220///
221/// * `s` - A string slice (`&str`) that contains the Base64-encoded data.
222///
223/// # Returns
224///
225/// A `Result<Vec<u8>, base64::DecodeError>` where:
226/// - `Ok(Vec<u8>)` contains the decoded byte vector if successful.
227/// - `Err(base64::DecodeError)` if the input string is not valid Base64.
228///
229/// # Examples
230///
231/// ```
232/// use keeper_secrets_manager_core::utils::base64_to_bytes;
233/// let encoded = "SGVsbG8sIHdvcmxkIQ==";
234/// let result = base64_to_bytes(encoded);
235/// assert_eq!(result.unwrap(), b"Hello, world!");
236/// ```
237pub fn base64_to_bytes(s: &str) -> Result<Vec<u8>, KSMRError> {
238    let decode_confed_str = s.to_string().replace("+", "-").replace("/", "_");
239
240    let decoded_bytes = BASE64_URL_SAFE
241        .decode(decode_confed_str)
242        .map_err(|e| KSMRError::DecodeError(e.to_string()))?;
243
244    Ok(decoded_bytes)
245}
246
247/// Converts a Base64-encoded string to a UTF-8 string.
248///
249/// # Arguments
250///
251/// * `b64s` - A string slice (`&str`) containing the Base64-encoded data.
252///
253/// # Returns
254///
255/// A `Result<String, KSMRError>` where:
256/// - `Ok(String)` contains the decoded UTF-8 string if successful.
257/// - `Err(KSMRError)` if there is an error during decoding.
258///
259/// # Examples
260///
261/// ```
262/// use keeper_secrets_manager_core::utils::base64_to_string;
263///  use base64::{ engine::general_purpose::STANDARD, Engine as _};
264/// let encoded = STANDARD.encode("hello");
265/// let result = base64_to_string(&encoded.to_string());
266/// assert_eq!(result.unwrap(), "hello");
267/// ```
268pub fn base64_to_string(b64s: &str) -> Result<String, KSMRError> {
269    let decoded_bytes = STANDARD
270        .decode(b64s)
271        .map_err(|_| KSMRError::DecodeError("Failed to decode Base64 string".to_string()))?;
272
273    let decoded_string = String::from_utf8(decoded_bytes)
274        .map_err(|_| KSMRError::DecodeError("Failed to convert bytes to UTF-8".to_string()))?;
275
276    Ok(decoded_string)
277}
278
279pub fn base64_to_string_lossy(b64s: &str) -> Result<String, KSMRError> {
280    let decoded_bytes = STANDARD
281        .decode(b64s)
282        .map_err(|_| KSMRError::DecodeError("Failed to decode Base64 string".to_string()))?;
283
284    let decoded_string = String::from_utf8_lossy(&decoded_bytes).to_string();
285
286    Ok(decoded_string)
287}
288
289/// Converts a string to a byte vector using UTF-8 encoding.
290///
291/// # Arguments
292///
293/// * `s` - A string slice (`&str`) to be converted to bytes.
294///
295/// # Returns
296///
297/// A `Vec<u8>` containing the UTF-8 encoded bytes of the input string.
298///
299/// # Examples
300///
301/// ```
302/// use keeper_secrets_manager_core::utils::string_to_bytes;
303/// let input = "Hello, world!";
304/// let bytes = string_to_bytes(input);
305/// assert_eq!(bytes, b"Hello, world!");
306/// ```
307pub fn string_to_bytes(s: &str) -> Vec<u8> {
308    s.as_bytes().to_vec() // Convert the string slice to a byte vector
309}
310
311/// Converts a URL-safe Base64-encoded string to a byte vector.
312///
313/// # Arguments
314///
315/// * `s` - A string slice (`&str`) containing the URL-safe Base64-encoded data.
316///
317/// # Returns
318///
319/// A `Result<Vec<u8>, base64::DecodeError>` where:
320/// - `Ok(Vec<u8>)` contains the decoded byte vector if successful.
321/// - `Err(base64::DecodeError)` if the input string is not valid Base64.
322///
323/// # Examples
324///
325/// ```
326/// use keeper_secrets_manager_core::utils::url_safe_str_to_bytes;
327/// let encoded = "SGVsbG8sIFdvcmxkIQ"; // URL-safe Base64 string
328/// let result = url_safe_str_to_bytes(encoded);
329/// assert_eq!(result.unwrap(), b"Hello, World!");
330/// ```
331pub fn url_safe_str_to_bytes(s: &str) -> Result<Vec<u8>, crate::custom_error::KSMRError> {
332    // // Add padding manually if necessary
333    // let padded_str = if s.len() % 4 != 0 {
334    //     format!("{}{}", s, "=".repeat(4 - s.len() % 4))
335    // } else {
336    //     s.to_string()
337    // };
338    URL_SAFE_NO_PAD
339        .decode(s)
340        .map_err(|e| KSMRError::DecodeError(e.to_string()))
341}
342
343/// Converts a URL-safe Base64-encoded string to a u64 integer.
344///
345/// # Arguments
346///
347/// * `s` - A string slice (`&str`) containing the URL-safe Base64-encoded data.
348///
349/// # Returns
350///
351/// A `Result<u64, String>` where:
352/// - `Ok(u64)` contains the converted integer if successful.
353/// - `Err(String)` if the input string is not valid Base64 or if the conversion fails.
354/// # Examples
355///
356/// ```
357/// use keeper_secrets_manager_core::utils::url_safe_str_to_int;
358/// use num_bigint::BigUint;
359/// let encoded = "4oCU"; // URL-safe Base64 string
360/// let decoded = url_safe_str_to_int(encoded).unwrap();
361/// assert_eq!(decoded, BigUint::from(14844052u64));
362pub fn url_safe_str_to_int(s: &str) -> Result<BigUint, KSMRError> {
363    let bytes_of_str = url_safe_str_to_bytes(s)?;
364    bytes_to_int(bytes_of_str.as_slice()).map_err(|e| KSMRError::DecodeError(e.to_string()))
365}
366
367/// Generates a vector of random bytes.
368///
369/// # Arguments
370///
371/// * `length` - The number of random bytes to generate.
372///
373/// # Returns
374///
375/// A `Vec<u8>` containing the generated random bytes.
376///
377/// # Examples
378///
379/// ```
380/// use keeper_secrets_manager_core::utils::generate_random_bytes;
381/// let random_bytes = generate_random_bytes(16);
382/// assert_eq!(random_bytes.len(), 16); // Should be 16 bytes long
383/// ```
384pub fn generate_random_bytes(length: usize) -> Vec<u8> {
385    CryptoUtils::generate_random_bytes(length)
386}
387
388/// Generates UID bytes with specific bit conditions.
389///
390/// # Returns
391///
392/// A `Vec<u8>` containing the generated UID bytes.
393///
394/// # Examples
395///
396/// ```
397/// use keeper_secrets_manager_core::utils::generate_uid_bytes;
398/// let uid = generate_uid_bytes();
399/// assert_eq!(uid.len(), 16); // Should generate a UID of 16 bytes
400/// // Further checks can be added based on expected UID properties
401/// ```
402pub fn generate_uid_bytes() -> Vec<u8> {
403    let dash = [0xf8, 0x7f]; // Represents [11111000, 01111111]
404    let mut uid_bytes: Vec<u8> = Vec::new();
405
406    for _ in 0..8 {
407        uid_bytes = generate_random_bytes(16);
408        if dash[0] & uid_bytes[0] != dash[0] {
409            break;
410        }
411    }
412
413    if dash[0] & uid_bytes[0] == dash[0] {
414        uid_bytes[0] &= dash[1];
415    }
416
417    uid_bytes
418}
419
420pub fn generate_uid() -> String {
421    let uid_bytes = generate_uid_bytes();
422    CryptoUtils::bytes_to_url_safe_str(&uid_bytes)
423}
424
425/// Converts a dictionary to a JSON string with pretty formatting.
426///
427/// # Arguments
428///
429/// * `dictionary` - A reference to a serializable object that can be converted to JSON.
430///
431/// # Returns
432///
433/// A `Result<String, serde_json::Error>` containing the formatted JSON string if successful,
434/// or an error if serialization fails.
435///
436/// # Examples
437///
438/// ```
439/// use keeper_secrets_manager_core::utils::dict_to_json;
440/// let dictionary = [("key1", "value1"), ("key2", "value2")].iter().cloned().collect::<std::collections::HashMap<_, _>>();
441/// let json = dict_to_json(&dictionary).unwrap();
442/// println!("{}", json); // Outputs the JSON representation of the dictionary
443/// ```
444pub fn dict_to_json<T: Serialize>(dictionary: &T) -> serde_json::Result<String> {
445    serde_json::to_string_pretty(dictionary)
446}
447
448/// Converts a JSON string to a dictionary (HashMap).
449///
450/// # Arguments
451///
452/// * `json_str` - A string slice containing the JSON data.
453///
454/// # Returns
455///
456/// An `Option<Value>` which will be `Some(Value)` containing the parsed JSON
457/// if successful, or `None` if parsing fails.
458///
459/// # Examples
460///
461/// ```
462/// use keeper_secrets_manager_core::utils::json_to_dict;
463/// let json_str = r#"{"key1": "value1", "key2": "value2"}"#;
464/// let dict = json_to_dict(json_str);
465/// assert!(dict.is_some());
466/// ```
467pub fn json_to_dict(json_str: &str) -> Option<HashMap<String, Value>> {
468    let return_value = serde_json::from_str(json_str).map_err(|err| {
469        warn!("JSON decode error: {}", err);
470    });
471    // return Some(return_value);
472    match return_value {
473        Ok(map) => Some(map),
474        Err(err) => {
475            warn!("JSON decode error: {:?}", err);
476            None
477        }
478    }
479}
480
481/// Returns the current time in milliseconds since the Unix epoch.
482///
483/// This function retrieves the current UTC time and converts it into
484/// milliseconds since January 1, 1970 (the Unix epoch).
485///
486/// # Returns
487///
488/// An `i64` representing the current time in milliseconds.
489///
490/// # Examples
491///
492/// ```
493/// use keeper_secrets_manager_core::utils::now_milliseconds;
494/// let millis = now_milliseconds();
495/// println!("Current time in milliseconds: {}", millis);
496/// ```
497///
498/// In the example above, the function returns the current time in milliseconds,
499/// which can be used for timestamping events or measuring time intervals.
500pub fn now_milliseconds() -> i64 {
501    Utc::now().timestamp_millis()
502}
503/// Represents a TOTP code along with its time left and period.
504#[derive(Debug, Clone)]
505pub struct TotpCode {
506    code: String,
507    time_left: u64, // Assuming time_left is in seconds
508    period: u64,    // Assuming period is also in seconds
509}
510
511impl TotpCode {
512    /// Creates a new `TotpCode`.
513    ///
514    /// # Arguments
515    ///
516    /// * `code` - A string representing the TOTP code.
517    /// * `time_left` - The time left until the code expires, in seconds.
518    /// * `period` - The period for which the TOTP code is valid, in seconds.
519    ///
520    /// # Returns
521    ///
522    /// A new instance of `TotpCode`.
523    pub fn new(code: String, time_left: u64, period: u64) -> Self {
524        TotpCode {
525            code,
526            time_left,
527            period,
528        }
529    }
530
531    /// Returns the TOTP code.
532    pub fn get_code(&self) -> &str {
533        &self.code
534    }
535
536    /// Returns the time left.
537    pub fn get_time_left(&self) -> u64 {
538        self.time_left
539    }
540
541    /// Returns the period.\
542    pub fn get_period(&self) -> u64 {
543        self.period
544    }
545}
546
547/// Generates a TOTP code from a given otp auth URL.
548///
549/// # Arguments
550///
551/// * `url` - A string slice containing the otp auth URL.
552///
553/// # Returns
554///
555/// A `Result<TotpCode, String>` which contains the generated TOTP code
556/// if successful, or an error message if parsing or generation fails.
557///
558/// # Examples
559///
560/// ```
561/// use keeper_secrets_manager_core::utils::get_totp_code;
562/// let url = "otpauth://totp/Example?secret=JBSWY3DPEHPK3PXP&issuer=Example";
563/// match get_totp_code(url) {
564///     Ok(totp_code) => println!("Generated TOTP code: {:?}", totp_code),
565///     Err(e) => println!("Error: {}", e),
566/// }
567/// ```
568pub fn get_totp_code(url: &str) -> Result<TotpCode, KSMRError> {
569    let comp = Url::parse(url).map_err(|_| KSMRError::TOTPError("Invalid URL".to_string()))?;
570    if comp.scheme() != "otpauth" {
571        return Err(KSMRError::TOTPError("Not an otpauth URI".to_string()));
572    }
573
574    let mut secret = None;
575    let mut algorithm = "SHA1".to_string();
576    let mut digits = 6;
577    let mut period = 30;
578    let mut counter = 0;
579
580    // Parse URL query string
581    let query_pairs = parse(comp.query().unwrap_or("").as_bytes());
582    for (key, value) in query_pairs {
583        match key.as_ref() {
584            "secret" => secret = Some(value.into_owned()),
585            "algorithm" => algorithm = value.into_owned().to_uppercase(),
586            "digits" => {
587                if let Ok(num) = value.parse::<u32>() {
588                    if num > 0 && num < 10 {
589                        digits = num;
590                    } else {
591                        return Err(KSMRError::TOTPError(
592                            "TOTP Digits may only be 6, 7, or 8".to_string(),
593                        ));
594                    }
595                }
596            }
597            "period" => {
598                if let Ok(num) = value.parse::<u32>() {
599                    if num > 0 {
600                        period = num;
601                    }
602                }
603            }
604            "counter" => {
605                if let Ok(num) = value.parse::<u32>() {
606                    if num > 0 {
607                        counter = num;
608                    }
609                }
610            }
611            _ => {}
612        }
613    }
614
615    // Validate parameters
616    let secret = secret
617        .ok_or(KSMRError::TOTPError(
618            "TOTP secret not found in URI".to_string(),
619        ))?
620        .to_ascii_uppercase();
621    let decoded_key_option = BASE32.decode(secret.as_bytes());
622    let key = match decoded_key_option {
623        Ok(decoded_key) => decoded_key,
624        Err(err) => Err(KSMRError::DecodeError(format!(
625            "Invalid TOTP secret: {}",
626            err
627        )))?,
628    };
629
630    let tm_base = if counter > 0 {
631        counter
632    } else {
633        Utc::now().timestamp() as u32
634    };
635    let tm = tm_base / period;
636    let msg = (tm as u64).to_be_bytes();
637
638    let digest: Vec<u8> = match algorithm.as_str() {
639        "SHA1" => {
640            let mut hmac = Hmac::<Sha1>::new_from_slice(&key)
641                .map_err(|_| KSMRError::TOTPError("Failed to create HMAC".to_string()))?;
642            hmac.update(&msg);
643            hmac.finalize().into_bytes().to_vec()
644        }
645        "SHA256" => {
646            let mut hmac = Hmac::<Sha256>::new_from_slice(&key)
647                .map_err(|_| KSMRError::TOTPError("Failed to create HMAC".to_string()))?;
648            hmac.update(&msg);
649            hmac.finalize().into_bytes().to_vec()
650        }
651        "SHA512" => {
652            let mut hmac = Hmac::<Sha512>::new_from_slice(&key)
653                .map_err(|_| KSMRError::TOTPError("Failed to create HMAC".to_string()))?;
654            hmac.update(&msg);
655            hmac.finalize().into_bytes().to_vec()
656        }
657        _ => {
658            return Err(KSMRError::TOTPError(format!(
659                "Invalid algorithm: {}",
660                algorithm
661            )))
662        }
663    };
664
665    let offset = (digest.last().unwrap() & 0x0f) as usize;
666    let base = &digest[offset..offset + 4];
667    let code_int = ((base[0] & 0x7f) as u32) << 24
668        | (base[1] as u32) << 16
669        | (base[2] as u32) << 8
670        | (base[3] as u32);
671    let code = format!(
672        "{:0width$}",
673        code_int % 10u32.pow(digits),
674        width = digits as usize
675    );
676
677    let elapsed = tm_base % period; // time elapsed in current period in seconds
678    let ttl = period - elapsed; // time to live in seconds
679
680    Ok(TotpCode::new(code, ttl as u64, period as u64))
681}
682
683pub fn get_otp_url_from_value_obj(val: serde_json::Value) -> Result<String, KSMRError> {
684    let otp_value = match val.is_array() {
685        true => val.as_array().unwrap()[0][0].clone(),
686        false => {
687            return Err(KSMRError::RecordDataError(
688                "otpCode or otp field is not an array".to_string(),
689            ))
690        }
691    };
692
693    let url_retrieved = match otp_value.is_string() {
694        true => otp_value.as_str().unwrap().to_string(),
695        false => {
696            return Err(KSMRError::RecordDataError(
697                "otpCode or otp field is not a string".to_string(),
698            ))
699        }
700    };
701
702    Ok(url_retrieved)
703}
704/// Generates a random sample of characters from a given string.
705///
706/// # Parameters
707/// - `sample_length`: The number of characters to sample.
708/// - `sample_string`: The string from which to sample characters.
709///
710/// # Returns
711/// A `String` containing the sampled characters.
712///
713/// # Errors
714/// Returns an error if `sample_length` is negative or if `sample_string` is empty.
715///
716/// # Example
717/// ```
718/// use keeper_secrets_manager_core::utils::random_sample;
719/// let result = random_sample(10, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789").unwrap();
720/// println!("Random Sample: {}", result);
721/// ```
722pub fn random_sample(
723    sample_length: usize,
724    sample_string: &str,
725) -> Result<String, Box<dyn std::error::Error>> {
726    // Validate inputs
727    if sample_length == 0 {
728        return Ok(String::new());
729    }
730
731    if sample_string.is_empty() {
732        return Err("sample_string must not be empty".into());
733    }
734
735    let mut rng = rand::thread_rng();
736    let mut sample = String::new();
737
738    for _ in 0..sample_length {
739        let char = sample_string
740            .chars()
741            .choose(&mut rng)
742            .ok_or("Failed to choose a character")?;
743        sample.push(char);
744    }
745
746    Ok(sample)
747}
748
749/// Gets the current Windows user SID and name.
750///
751/// # Returns
752/// A tuple containing the SID and username, or (None, None) if there was an error.
753///
754/// # Example
755/// ```
756/// use std::process::{Command, Output};
757/// use std::option::Option;
758/// use keeper_secrets_manager_core::utils::get_windows_user_sid_and_name;
759/// let (sid, username) = get_windows_user_sid_and_name::<fn() -> Output>(None);
760/// println!("SID: {:?}, Username: {:?}", sid, username);
761/// ```
762#[cfg(target_os = "windows")]
763pub fn get_windows_user_sid_and_name<F>(command: Option<F>) -> (Option<String>, Option<String>)
764where
765    F: Fn() -> Output,
766{
767    let output = match command {
768        Some(comm) => comm(),
769        None => _default_command(),
770    };
771
772    if output.status.success() {
773        let stdout = String::from_utf8_lossy(&output.stdout);
774        let lines: Vec<&str> = stdout.lines().collect();
775        if let Some(last_line) = lines.last() {
776            let parts: Vec<&str> = last_line.split('\\').collect();
777            if let Some(username_sid) = parts.last() {
778                let username_sid_string = username_sid.to_string();
779                let split_parts: Vec<&str> = username_sid_string.split(" ").collect();
780                // let mut parts_split  = username_sid.split_ascii_whitespace();
781                // let username = parts_split.next().unwrap_or("");
782                // let sid = parts_split.next().unwrap_or("");
783                let username = split_parts[0].to_string();
784                let sid = split_parts[1].to_string();
785                return (Some(sid), Some(username));
786            }
787        }
788    } else {
789        eprintln!("Failed to execute 'whoami.exe'");
790    }
791
792    (None, None)
793}
794
795#[cfg(target_os = "windows")]
796fn _default_command() -> Output {
797    Command::new("whoami.exe")
798        .arg("/user")
799        .output()
800        .expect("Failed to execute whoami.exe")
801}
802
803#[cfg(not(target_os = "windows"))]
804fn _default_command() -> Output {
805    // This can be a dummy output or an error for non-Windows platforms.
806
807    use std::os::unix::process::ExitStatusExt;
808    Output {
809        status: std::process::ExitStatus::from_raw(1),
810        stdout: Vec::new(),
811        stderr: Vec::new(),
812    }
813}
814
815/**
816Sets the configuration mode for the specified file, adjusting its permissions
817according to the operating system's conventions. On Windows, it uses `icacls`
818to set file permissions, while on Linux/MacOS, it sets the permissions to 0600.
819
820# Arguments
821
822* `file` - A string slice that holds the path to the configuration file.
823
824# Returns
825
826This function returns `Ok(())` if the mode is set successfully, or an `io::Error`
827if an error occurs.
828
829*/
830pub fn set_config_mode(
831    file: &str,
832) -> Result<(), io::Error> {
833    // Check if we should skip setting the mode
834    if let Ok(skip_mode) = env::var("KSM_CONFIG_SKIP_MODE") {
835        if skip_mode.to_lowercase() == "true" {
836            return Ok(());
837        }
838    }
839
840    // For Windows, use icacls commands
841    #[cfg(target_os = "windows")]
842    {
843        let sid = match get_windows_user_sid_and_name::<fn() -> Output>(None) {
844            (Some(sid), _) => sid,
845            _ => {
846                return Err(io::Error::new(
847                    io::ErrorKind::Other,
848                    "Failed to get user SID",
849                ))
850            }
851        };
852
853        // Commands to set the file permissions
854        let commands = vec![
855            format!(r#"icacls "{}" /reset"#, file),
856            format!(r#"icacls "{}" /inheritance:r"#, file),
857            format!(r#"icacls "{}" /remove:g Everyone:F"#, file),
858            format!(r#"icacls "{}" /grant:r Administrators:F"#, file),
859            format!(r#"icacls "{}" /grant:r "{}:F""#, file, sid),
860        ];
861
862        for command in commands {
863
864            let output = Command::new("cmd").args(&["/C", &command]).output()?;
865
866            match output.status.code() {
867                Some(2) => {
868                    return Err(io::Error::new(
869                        io::ErrorKind::NotFound,
870                        format!("Cannot find configuration file {}", file),
871                    ))
872                }
873                Some(5) => {
874                    return Err(io::Error::new(
875                        io::ErrorKind::PermissionDenied,
876                        format!("Access denied to configuration file {}", file),
877                    ))
878                }
879                Some(1332) => {
880                    debug!("{} {}", "Failed to set some ACL permissions: {}", command);
881                    continue; // Skip localized group/user names error
882                }
883                Some(_) if !output.status.success() => {
884                    let message = format!(
885                        "Could not change the ACL for file '{}'. Set the environmental variable 'KSM_CONFIG_SKIP_MODE' to 'TRUE' to skip setting the ACL mode.",
886                        file
887                    );
888                    let stderr = String::from_utf8_lossy(&output.stderr);
889                    let full_message = if !stderr.is_empty() {
890                        format!("{}: {}", message, stderr.trim())
891                    } else {
892                        format!("{}.", message)
893                    };
894                    return Err(io::Error::new(
895                        io::ErrorKind::PermissionDenied,
896                        full_message,
897                    ));
898                }
899                _ => {}
900            }
901        }
902    }
903    #[cfg(not(target_os = "windows"))]
904    {
905        // On Linux/MacOS, set file permissions to 0600
906        let permissions = fs::metadata(file)?.permissions();
907        let mut new_permissions = permissions;
908        new_permissions.set_mode(0o600);
909        fs::set_permissions(file, new_permissions)?;
910    }
911
912    Ok(())
913}
914
915//This function runs only on windows and this unit test runs only on windows
916/// Retrieves localized account names for known administrative accounts on Windows.
917///
918/// This function uses the Win32 API to fetch account names for the local system
919/// and built-in administrators. It returns a vector of strings containing the
920/// localized names.
921///
922/// # Errors
923///
924/// This function returns an error if it fails to create a well-known SID,
925/// look up the account names, or execute the command to convert the names
926/// to the console's code page.
927///
928/// # Example
929///
930/// ```ignore
931/// fn main() -> Result<(), u32> {
932///     match _populate_windows_localized_admin_names_win32api() {
933///         Ok(localized_admins) => {
934///             for admin in localized_admins {
935///                 println!("Localized Admin: {}", admin);
936///             }
937///         },
938///         Err(err) => eprintln!("Error retrieving localized admin names: {}", err),
939///     }
940///     Ok(())
941/// }
942/// ```
943///
944/// # Test
945///
946/// This test will run if the target operating system is Windows and the
947/// function is expected to return a non-empty list of localized admin names.
948///
949/// ```ignore
950/// #[cfg(test)]
951/// mod tests {
952///     use super::*; // Ensure the test module has access to the function
953///
954///     #[test]
955///     #[cfg(target_os = "windows")] // Only run this test on Windows
956///     fn test_populate_windows_localized_admin_names_win32api() {
957///         let result = _populate_windows_localized_admin_names_win32api();
958///         assert!(result.is_ok(), "Function should return Ok on success");
959///         let localized_admins = result.unwrap();
960///         assert!(!localized_admins.is_empty(), "Should return at least one localized admin name");
961///     }
962/// }
963/// ```
964#[cfg(target_os = "windows")]
965fn _populate_windows_localized_admin_names_win32api() -> Result<Vec<String>, u32> {
966    use std::ffi::OsString; // Make sure to import OsString
967    use std::os::windows::ffi::OsStringExt; // Import the OsStringExt trait
968    use std::ptr;
969    use winapi::shared::winerror::{ERROR_INSUFFICIENT_BUFFER, ERROR_INVALID_PARAMETER};
970    use winapi::um::errhandlingapi::GetLastError;
971    use winapi::um::securitybaseapi::CreateWellKnownSid;
972    use winapi::um::winbase::LookupAccountSidW;
973
974    // Define WellKnownSidType manually
975    #[allow(non_camel_case_types)]
976    #[repr(u32)]
977    #[derive(Clone)]
978    pub enum WellKnownSidType {
979        WinLocalSystemSid = 22,
980        WinBuiltinAdministratorsSid = 26,
981    }
982
983    // Helper function to convert a wide string (UTF-16) to a Rust String
984    fn wide_to_string(wide: &[u16]) -> String {
985        let os_string = OsString::from_wide(wide);
986        os_string.to_string_lossy().into_owned()
987    }
988
989    // Function to get account name for a specific SID type
990    fn get_account_name(sid_type: WellKnownSidType) -> Result<(String, String), u32> {
991        let mut sid_size = 256;
992        let mut sid = vec![0u8; sid_size as usize];
993
994        unsafe {
995            // CreateWellKnownSid
996            if CreateWellKnownSid(
997                sid_type.clone() as u32,
998                ptr::null_mut(),
999                sid.as_mut_ptr() as *mut _,
1000                &mut sid_size,
1001            ) == 0
1002            {
1003                let error = GetLastError();
1004                if error == ERROR_INSUFFICIENT_BUFFER || error == ERROR_INVALID_PARAMETER {
1005                    sid = vec![0u8; sid_size as usize];
1006                    if CreateWellKnownSid(
1007                        sid_type as u32,
1008                        ptr::null_mut(),
1009                        sid.as_mut_ptr() as *mut _,
1010                        &mut sid_size,
1011                    ) == 0
1012                    {
1013                        return Err(GetLastError());
1014                    }
1015                } else {
1016                    return Err(error);
1017                }
1018            }
1019
1020            // LookupAccountSidW to get the size needed for the name and domain
1021            let mut name_size = 0;
1022            let mut domain_size = 0;
1023            let mut sid_name_use = 0;
1024            LookupAccountSidW(
1025                ptr::null(),
1026                sid.as_mut_ptr() as *mut _,
1027                ptr::null_mut(),
1028                &mut name_size,
1029                ptr::null_mut(),
1030                &mut domain_size,
1031                &mut sid_name_use,
1032            );
1033
1034            let error = GetLastError();
1035            if error != ERROR_INSUFFICIENT_BUFFER {
1036                return Err(error);
1037            }
1038
1039            // Allocate buffers for the account name and domain name
1040            let mut name = vec![0u16; name_size as usize];
1041            let mut domain = vec![0u16; domain_size as usize];
1042
1043            // LookupAccountSidW to actually get the name and domain
1044            if LookupAccountSidW(
1045                ptr::null(),
1046                sid.as_mut_ptr() as *mut _,
1047                name.as_mut_ptr(),
1048                &mut name_size,
1049                domain.as_mut_ptr(),
1050                &mut domain_size,
1051                &mut sid_name_use,
1052            ) == 0
1053            {
1054                return Err(GetLastError());
1055            }
1056
1057            // Convert wide strings to Rust Strings
1058            let domain_str = wide_to_string(&domain);
1059            let name_str = wide_to_string(&name);
1060
1061            Ok((domain_str, name_str))
1062        }
1063    }
1064
1065    let mut localized_admins = Vec::new();
1066    let mut admins = Vec::new();
1067
1068    // Retrieve account names for specific well-known SIDs
1069    if let Ok((_, name)) = get_account_name(WellKnownSidType::WinLocalSystemSid) {
1070        admins.push(name);
1071    }
1072
1073    if let Ok((_, name)) = get_account_name(WellKnownSidType::WinBuiltinAdministratorsSid) {
1074        admins.push(name);
1075    }
1076
1077    // Convert WMI names (admins) to the console's code page using a shell command (like "cmd /c")
1078    if !admins.is_empty() {
1079        let mut cmd = String::from("echo.");
1080        for admin in &admins {
1081            cmd.push_str(&format!(" & echo {}", admin));
1082        }
1083
1084        // Execute the command in the shell
1085        let output = std::process::Command::new("cmd")
1086            .args(&["/C", &cmd])
1087            .output()
1088            .expect("Failed to execute command");
1089
1090        if output.status.success() {
1091            let output_lines = output.stdout.split(|&b| b == b'\n');
1092            for line in output_lines {
1093                if !line.is_empty() {
1094                    localized_admins.push(String::from_utf8_lossy(line).trim().to_string());
1095                }
1096            }
1097        }
1098    }
1099
1100    Ok(localized_admins)
1101}
1102
1103#[derive(Debug)]
1104pub enum ConfigError {
1105    PermissionDenied(String),
1106    FileNotFound(String),
1107    GeneralError(String),
1108}
1109
1110/// This function checks the permissions of a given configuration file.
1111/// On Windows, it uses the `icacls` command to verify permissions.
1112/// On Unix-like systems (Linux, macOS), it checks the file mode and ensures that
1113/// only the owner has access.
1114///
1115/// The function will skip permission checking if the `KSM_CONFIG_SKIP_MODE` environment
1116/// variable is set to `TRUE`.
1117///
1118/// On Windows, if access is denied or the file is missing, specific errors are returned.
1119/// The function also checks for warnings about overly permissive access modes.
1120///
1121/// # Errors
1122///
1123/// Returns:
1124/// - `ConfigError::PermissionDenied` if the file is accessible by users other than the owner.
1125/// - `ConfigError::FileNotFound` if the file does not exist.
1126/// - `ConfigError::GeneralError` if there are other issues, such as executing the `icacls` command.
1127///
1128/// # Example (Unix-like systems)
1129/// ```ignore
1130/// use std::fs;
1131/// #[cfg(unix)]
1132/// use std::os::unix::fs::PermissionsExt;
1133/// use keeper_secrets_manager_core::utils::check_config_mode;
1134/// use keeper_secrets_manager_core::utils::ConfigError;
1135///
1136/// // Create a file and set restrictive permissions (only owner can access).
1137/// let file_path = "client-config.json";
1138/// fs::File::create(file_path).unwrap();
1139/// let mut perms = fs::metadata(file_path).unwrap().permissions();
1140/// perms.set_mode(0o600);  // Owner can read/write
1141/// fs::set_permissions(file_path, perms).unwrap();
1142///
1143/// // Run the function
1144/// match check_config_mode(file_path) {
1145///     Ok(true) => println!("Permissions are correctly set."),
1146///     Ok(false) => println!("Permissions are too open."),
1147///     Err(ConfigError::PermissionDenied(err)) => eprintln!("Permission denied: {}", err),
1148///     Err(ConfigError::FileNotFound(err)) => eprintln!("File not found: {}", err),
1149///     Err(ConfigError::GeneralError(err)) => eprintln!("General error: {}", err),
1150///     _ => eprintln!("Unknown error."),
1151/// }
1152/// ```
1153///
1154/// # Example (Windows)
1155/// ```rust,ignore
1156/// use keeper_secrets_manager_core::utils::check_config_mode;
1157/// use keeper_secrets_manager_core::utils::ConfigError;
1158///
1159/// let file_path = "client-config.json";
1160///
1161/// // Run the function
1162/// match check_config_mode(file_path) {
1163///     Ok(true) => println!("Permissions are correctly set."),
1164///     Ok(false) => println!("Permissions are too open."),
1165///     Err(ConfigError::PermissionDenied(err)) => eprintln!("Permission denied: {}", err),
1166///     Err(ConfigError::FileNotFound(err)) => eprintln!("File not found: {}", err),
1167///     Err(ConfigError::GeneralError(err)) => eprintln!("General error: {}", err),
1168///     _ => eprintln!("Unknown error."),
1169/// }
1170/// ```
1171pub fn check_config_mode(file: &str) -> Result<bool, ConfigError> {
1172    let skip_mode_check = env::var("KSM_CONFIG_SKIP_MODE")
1173        .unwrap_or("FALSE".to_string())
1174        .eq_ignore_ascii_case("TRUE");
1175
1176    if skip_mode_check {
1177        return Ok(true);
1178    }
1179
1180    #[cfg(target_os = "windows")]
1181    return check_windows_permissions(file);
1182
1183    #[cfg(not(target_os = "windows"))]
1184    return check_unix_permissions(file);
1185}
1186
1187#[cfg(target_os = "windows")]
1188fn check_windows_permissions(file: &str) -> Result<bool, ConfigError> {
1189    use std::process::Command;
1190
1191    // Execute the `icacls` command to check file permissions
1192    let output = Command::new("icacls")
1193        .arg(file)
1194        .output()
1195        .map_err(|e| ConfigError::GeneralError(format!("Error executing icacls: {}", e)))?;
1196
1197    if !output.status.success() {
1198        return match output.status.code() {
1199            Some(2) => Err(ConfigError::FileNotFound(file.to_string())),
1200            Some(5) => Err(ConfigError::PermissionDenied(file.to_string())),
1201            _ => Err(ConfigError::GeneralError(
1202                "Unknown error in icacls".to_string(),
1203            )),
1204        };
1205    }
1206
1207    // Additional checks for user permissions
1208    if !is_file_accessible(file) {
1209        return Err(ConfigError::PermissionDenied(format!(
1210            "Access denied to {}",
1211            file
1212        )));
1213    }
1214
1215    Ok(true)
1216}
1217
1218#[cfg(not(target_os = "windows"))]
1219fn check_unix_permissions(file: &str) -> Result<bool, ConfigError> {
1220    // Check if the file exists first
1221
1222    use std::path::Path;
1223    let file_path = Path::new(file);
1224    if !file_path.exists() {
1225        return Err(ConfigError::FileNotFound(file.to_string()));
1226    }
1227
1228    // Attempt to open the file to verify access permissions
1229    let metadata =
1230        fs::metadata(file_path).map_err(|_| ConfigError::FileNotFound(file.to_string()))?;
1231    if !is_file_accessible(file) {
1232        return Err(ConfigError::PermissionDenied(file.to_string()));
1233    }
1234    // Retrieve file mode and permissions for validation
1235    let permissions = metadata.permissions().mode();
1236    if permissions & 0o077 != 0 {
1237        eprintln!(
1238            "Warning: File permissions for {} are too open ({:o}). Consider setting to 0600.",
1239            file, permissions
1240        );
1241        return Err(ConfigError::PermissionDenied(format!(
1242            "File permissions too open for {}",
1243            file
1244        )));
1245    }
1246
1247    Ok(true)
1248}
1249
1250// Check if file is accessible
1251fn is_file_accessible(file: &str) -> bool {
1252    File::open(file).is_ok()
1253}
1254
1255#[derive(Debug)]
1256pub struct PasswordOptions {
1257    length: usize,
1258    lowercase: Option<i32>,
1259    uppercase: Option<i32>,
1260    digits: Option<i32>,
1261    special_characters: Option<i32>,
1262    special_characterset: String,
1263}
1264
1265impl PasswordOptions {
1266    /// Creates a new PasswordOptions with default values.
1267    pub fn new() -> Self {
1268        PasswordOptions {
1269            length: DEFAULT_PASSWORD_LENGTH,
1270            lowercase: None,
1271            uppercase: None,
1272            digits: None,
1273            special_characters: None,
1274            special_characterset: String::from(SPECIAL_CHARACTERS),
1275        }
1276    }
1277
1278    /// Set the password length.
1279    pub fn length(mut self, length: usize) -> Self {
1280        if length > 0 {
1281            self.length = length;
1282        } else {
1283            self.length = 32
1284        }
1285        self
1286    }
1287
1288    /// Set the minimum number of lowercase characters.
1289    pub fn lowercase(mut self, count: i32) -> Self {
1290        self.lowercase = Some(count);
1291        self
1292    }
1293
1294    /// Set the minimum number of uppercase characters.
1295    pub fn uppercase(mut self, count: i32) -> Self {
1296        self.uppercase = Some(count);
1297        self
1298    }
1299
1300    /// Set the minimum number of digits.
1301    pub fn digits(mut self, count: i32) -> Self {
1302        self.digits = Some(count);
1303        self
1304    }
1305
1306    /// Set the minimum number of special characters.
1307    pub fn special_characters(mut self, count: i32) -> Self {
1308        self.special_characters = Some(count);
1309        self
1310    }
1311
1312    /// Set the custom set of special characters.
1313    pub fn special_characterset(mut self, charset: String) -> Self {
1314        self.special_characterset = charset;
1315        self
1316    }
1317}
1318
1319impl Default for PasswordOptions {
1320    fn default() -> Self {
1321        Self::new()
1322    }
1323}
1324
1325/// Generates a new password based on the specified options.
1326///
1327/// The generated password will adhere to the constraints set by the
1328/// provided `PasswordOptions`. If the specified character counts exceed
1329/// the total desired password length, an error will be returned.
1330///
1331/// # Parameters
1332///
1333/// - `options`: An instance of `PasswordOptions` that defines the desired
1334///   characteristics of the password, such as length and minimum character
1335///   counts for lowercase, uppercase, digits, and special characters.
1336///
1337/// # Returns
1338///
1339/// - `Ok(String)`: A randomly generated password that meets the specified
1340///   criteria.
1341/// - `Err(String)`: An error message if the specified character counts
1342///   exceed the total password length or if there are any issues during
1343///   password generation.
1344///
1345/// # Example
1346///
1347/// ```rust
1348/// use keeper_secrets_manager_core::utils::generate_password_with_options;
1349/// use keeper_secrets_manager_core::utils::PasswordOptions;
1350///
1351/// let options = PasswordOptions::new()
1352///     .length(16)
1353///     .lowercase(4)
1354///     .uppercase(4)
1355///     .digits(4);
1356///
1357/// match generate_password_with_options(options) {
1358///     Ok(password) => println!("Generated Password: {}", password),
1359///     Err(err) => eprintln!("Error: {}", err),
1360/// }
1361/// ```
1362///
1363/// # Panics
1364///
1365/// This function does not panic, but will return an error if constraints are not met.
1366///
1367/// # Errors
1368///
1369/// - If the specified lowercase, uppercase, digits, and special characters
1370///   exceed the total length of the password, an error will be returned.
1371pub fn generate_password_with_options(options: PasswordOptions) -> Result<String, KSMRError> {
1372    let mut rng = thread_rng();
1373
1374    // Collect the counts for each character type
1375    let lowercase_count = options.lowercase.unwrap_or(0).max(0);
1376    let uppercase_count = options.uppercase.unwrap_or(0).max(0);
1377    let digits_count = options.digits.unwrap_or(0).max(0);
1378    let special_count = options.special_characters.unwrap_or(0).max(0);
1379
1380    // Calculate the total number of specified characters
1381    let total_specified = lowercase_count + uppercase_count + digits_count + special_count;
1382
1383    // Check if specified characters exceed the total length
1384    if total_specified > options.length as i32 {
1385        return Err(KSMRError::PasswordCreationError(format!(
1386            "The specified character counts ({}) exceed the total password length ({})!",
1387            total_specified, options.length
1388        )));
1389    }
1390
1391    let extra_count = options.length as i32 - total_specified;
1392
1393    // Build the extra character pool based on available options
1394    let mut extra_chars = String::new();
1395    if options.lowercase.is_some() || lowercase_count > 0 {
1396        extra_chars.push_str("abcdefghijklmnopqrstuvwxyz");
1397    }
1398    if options.uppercase.is_some() || uppercase_count > 0 {
1399        extra_chars.push_str("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
1400    }
1401    if options.digits.is_some() || digits_count > 0 {
1402        extra_chars.push_str("0123456789");
1403    }
1404    if options.special_characters.is_some() || special_count > 0 {
1405        extra_chars.push_str(&options.special_characterset);
1406    }
1407    if extra_chars.is_empty() {
1408        extra_chars.push_str("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
1409        extra_chars.push_str(SPECIAL_CHARACTERS);
1410    }
1411
1412    // Initialize the category map
1413    let category_map = vec![
1414        (lowercase_count as usize, "abcdefghijklmnopqrstuvwxyz"),
1415        (uppercase_count as usize, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"),
1416        (digits_count as usize, "0123456789"),
1417        (special_count as usize, &options.special_characterset),
1418        (extra_count.max(0) as usize, &extra_chars),
1419    ];
1420
1421    let mut password_list = Vec::new();
1422    for (count, chars) in category_map {
1423        let char_slice: Vec<char> = chars.chars().collect();
1424        let mut repeated_chars = char_slice.iter().cycle(); // Infinite repetition
1425        for _ in 0..count {
1426            if let Some(&sample) = repeated_chars.next() {
1427                password_list.push(sample);
1428            }
1429        }
1430    }
1431
1432    let mut remaining_length = options.length - password_list.len();
1433
1434    while remaining_length > 0 {
1435        // Randomly select additional characters from the extra characters
1436        let extra_char_slice: Vec<char> = extra_chars.chars().collect();
1437        let additional_samples: Vec<char> = extra_char_slice
1438            .choose_multiple(&mut rng, remaining_length)
1439            .cloned()
1440            .collect();
1441
1442        password_list.extend(additional_samples);
1443        remaining_length = options.length - password_list.len()
1444    }
1445    password_list.shuffle(&mut rng);
1446
1447    Ok(password_list.into_iter().collect())
1448}
1449
1450pub fn generate_password() -> Result<String, KSMRError> {
1451    let password_options_default = PasswordOptions::new();
1452    generate_password_with_options(password_options_default)
1453}