How to protect rust CLI with password

Hello everyone, I have a CLI and i want to protect some command and only can be executed by entering a password that i will set it before build it. Thank you in advance.

What you're describing is security through obscurity. There is no amount of effort on your part that will prevent your audience from determining that password on their own.

When faced with hiding text data I usually generate two random byte streams that can be combined using exclusive-or to recover the original data. It is certainly not effective at keeping the data a secret but it does inhibit someone from finding the data using a text editor.

Oh i got it, but i need a way to do that even is not secure for example if anyone want to run start command will ask for the password before run this command

What exactly are you unsure how to do? If you aren't worried about actual security you can just compare the value to a string literal to meet your requirements (though it can't be overstated how easy it would be to circumvent that).

3 Likes

@semicoleon Thanks for your advice, Yeah i want to do that but I'm new in rust lang so i can't handle it by myself please if you can help me or provide me a reference to do this because i Google it without finding any results.

I guess if you do that, you would need to take care the optimizer doesn't do the XOR at compile time? Not sure how to achieve that in Rust.

The problem of storing the password can be solved by... not storing the password, only a representation of it that can't be inverted to find the password itself. This includes possible techniques such as:

  • storing the hash of the password
  • storing a public key and using its private counterpart as the password (I haven't seen this one used in the wild, and there may be very good reasons for that)

However, neither of these techniques actually works in practice. If you "restrict" some action the program performs by making a binary decision, then jumping or not jumping to the "restricted" part of the code, then it's trivial to overwrite the binary in a way that this condition is neutered and the action is performed nevertheless.

You can try obfuscating the code, but that won't deter any half-serious reverse engineer, either. In general, it's simply not possible to reliably and securely prevent someone from running code that they have on their computer.

3 Likes

Maybe there is one secure way. The code itself would need to be securely encrypted with the password being the key. Then the only way to execute it (without the password) would be to re-program the functionality of the code on your own (which you can always do even if you have no program at all but know what it's supposed to do).

2 Likes

I would make the command in question its own executable and then look at ways to protect that executable. For example, require sudo access. Alternatively, encrypt the entire executable and have some wrapper program to temporarily unencrypt and run it. Those would be easier approaches than encrypting a portion of the executable itself.

Depending on your goals, this may or may not be a useful approach. Most DRM schemes ultimately fail, if that's what we're talking about.

1 Like

Sample code for that or using a hash...

use bcrypt::{hash, verify};
use rand::prelude::*;

const HASH: &str = "$2b$04$ftNHyP17aUbL8ElIoSorIOPfHAPzo0uHV5AubkQoH7vF/BpCG49xK";

const LEFT: [u8; 18] = [83, 216, 179, 30, 150, 234, 28, 82, 168, 144, 115, 206, 32, 237, 151, 121, 46, 84];
const RIGHT: [u8; 18] = [35, 185, 192, 109, 225, 133, 110, 54, 133, 247, 28, 171, 83, 192, 255, 28, 92, 49];

fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!();

    let password = "password-goes-here";

    let tm1 = hash(password.as_bytes(), 4);
    println!("tm1 = {:?}", tm1);
    let tm1 = tm1.unwrap();
    let tm2 = verify(password.as_bytes(), &tm1);
    println!("tm2 = {:?}", tm2);
    let tm3 = verify(password.as_bytes(), HASH);
    println!("tm3 = {:?}", tm3);
    match tm2 {
        Ok(true) => println!("run it"),
        _ => println!("no run for you"),
    }
    println!();

    let mut lft = Vec::<u8>::new();
    let mut rgt = Vec::<u8>::new();
    let mut rng = rand::thread_rng();
    for rover in password.as_bytes() {
        let mask: u8 = rng.gen();
        let value = rover ^ mask;
        lft.push(mask);
        rgt.push(value);
    }
    println!("const LEFT: [u8; {}] = {:?};", lft.len(), lft);
    println!("const RIGHT: [u8; {}] = {:?};", rgt.len(), rgt);

    let mut good: bool = true;
    let bytes = password.as_bytes();
    if bytes.len() == LEFT.len() {
        for ((l, r), b) in LEFT.iter().zip(RIGHT.iter()).zip(bytes.iter()) {
            if l ^ r != *b {
                println!("no run for you");
                good = false;
                break;
            }
        }
    } else {
        println!("no run for you");
        good = false;
    }
    if good {
        println!("run it");
    }

    println!();

    Ok(())
}