mistress haslo
  1#!/usr/bin/env perl
  2
  3# Public domain
  4
  5use strict;
  6use warnings;
  7use MIME::Base32 qw(decode_base32 encode_base32);
  8use MIME::Base64 qw(encode_base64);
  9use Term::ReadKey qw(ReadMode ReadLine);
 10
 11sub usage
 12{
 13	die "usage:\thaslo gen|get|put name\n\thaslo list\n";
 14}
 15
 16my $home = $ENV{'HOME'} or die "\$HOME not set\n";
 17my $hasloemail = $ENV{'HASLOEMAIL'} or die "\$HASLOEMAIL not set\n";
 18my $haslodir = $ENV{'HASLODIR'} || "$home/.haslo";
 19
 20sub writernppassword
 21{
 22	unless(-d $haslodir){
 23		print STDERR "creating directory $haslodir\n";
 24		mkdir $haslodir
 25			or die "couldn't create directory $haslodir: $!\n";
 26	}
 27	my $target = shift;
 28	my $password = shift;
 29	open RNP, '|-', 'rnp', '--encrypt', '--recipient', $hasloemail,
 30		'--output', "$haslodir/$target.rnp"
 31		or die "couldn't open pipe to rnp: $!\n";
 32	print RNP $password;
 33	close RNP or die "rnp failed with exit code $?\n";
 34}
 35
 36sub dodel
 37{
 38	my $target = $ARGV[1] or usage;
 39	$target = encode_base32 $target;
 40	system 'rm', '-i', "$haslodir/$target.rnp"
 41		and die "rm failed with exit code $?\n";
 42}
 43
 44sub dogen
 45{
 46	my $target = $ARGV[1] or usage;
 47	$target = encode_base32 $target;
 48	open URANDOM, '<', '/dev/urandom'
 49		or die "couldn't open /dev/urandom: $!\n";
 50	read URANDOM, my $buf, 128
 51		or die "couldn't read from /dev/urandom: $!\n";
 52	close URANDOM;
 53	$buf = encode_base64 $buf;
 54	writernppassword $target, $buf;
 55}
 56
 57sub doget
 58{
 59	my $target = $ARGV[1] or usage;
 60	$target = encode_base32 $target;
 61	open RNP, '-|', 'rnp', '--decrypt', "$haslodir/$target.rnp"
 62		or die "couldn't open pipe to rnp: $!\n";
 63	my $password = <RNP>;
 64	close RNP or die "rnp failed with exit code $?\n";
 65	print $password;
 66}
 67
 68sub dolist
 69{
 70	opendir DIR, $haslodir or die "couldn't open $haslodir: $!\n";
 71	for(readdir DIR){
 72		next unless s/.rnp$//;
 73		my $decoded = decode_base32 $_;
 74		print "$decoded\n";
 75	}
 76	close DIR;
 77}
 78
 79sub doput
 80{
 81	my $target = $ARGV[1] or usage;
 82	$target = encode_base32 $target;
 83	my($password, $confirm);
 84	do{
 85		ReadMode 'noecho';
 86		print STDERR "enter password for $ARGV[1]: ";
 87		$password = ReadLine 0;
 88		print STDERR "\n";
 89		print STDERR "confirm password: ";
 90		$confirm = ReadLine 0;
 91		print STDERR "\n";
 92		ReadMode 'restore';
 93	}while($password ne $confirm);
 94	writernppassword $target, $password;
 95}
 96
 97my %commands = (
 98	del => \&dodel,
 99	gen => \&dogen,
100	get => \&doget,
101	list => \&dolist,
102	put => \&doput
103);
104my $argv0 = $ARGV[0] or usage;
105my $command = $commands{$argv0} or usage;
106$command->();