I actually liked the idea to use STDIN, so if anyone ever lights upon this page, consider if you can use STDIN in your situation.
But I'd like to post my own answer, which uses the Expect Perl module, because:
- I used it and it's proven to work at least on Linux (though the question in the comments mentions that the module had problems to run on Windows);
- since the question was titled without any mention of
cryptsetup (which can accept the password on STDIN), so the Expect module is like a general solution here, which should work for other tools;
My solution is below. I didn't read the documentation much, so there may be better ways to do this. For me it was enough to grasp the idea of the object instantiation, and the expect/send methods.
You create an instance as if you just started a program in the terminal:
my $exp = Expect->new('progName', ($arg1, $arg2, $etc));
Then it's possible to interact with it through the expect (which waits/confirms the program's output to the user) and send (which allows the user to 'type') methods.
expect may be called in either scalar or list context (I used the list one) and it accepts a string or regexp of what output to expect and for how long. If no expected output occurred within the specified time it throws an error:
#sec to wait for #use regexp (don't match exactly) #regexp to match against
my @out = $exp->expect(10, '-re', 'smth');
send just accepts the input
$exp->send('some chars');
That's it, one just need to create a script, which would work as if it was a human user.
And just in case it might be handy to someone, I'll post my complete particular solution for cryptsetup (I've tested it on a dummy volume, which it was able to mount, and I've run it on a real volume with 6k combinations tried without any noticeable problems):
*I forgot to close the file descriptor here, so one should add close($fd) where appropriate
use strict;
use warnings;
use feature 'say';
# I installed Expect with its deps locally in my situation, so I had to change @INC
BEGIN {
unshift(@INC, './perl-mods/lib/perl5/');
unshift(@INC, './perl-mods/lib/perl5/x86_64-linux-thread-multi/');
}
use Expect;
my $devName = '/dev/sdb3';
my $combinationsFileName = 'combs.txt';
open(my $fd, '<', $combinationsFileName);
my $lineCount = 0;
while (<$fd>) {
my $pass = $_;
chomp($_);
say $_ . ' ' . ++$lineCount;
my $exp = Expect->new('cryptsetup', ('open', $devName, 'crypt-vol')) or die 'err 1';
my @out = $exp->expect(10, '-re', 'Enter passphrase') or die 'err 2';
$exp->send($pass);
@out = $exp->expect(10, '-re', 'No key available') or die 'err 3';
#if cryptsetup returned an error code
if ($out[1]) {
die $out[1] . ' ' . $pass;
}
}