1.0
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: haslo gen|get|put name\n haslo 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) = split /\n/, encode_base64 $buf;
54 writernppassword $target, $buf;
55 print "$buf\n";
56}
57
58sub doget
59{
60 my $target = $ARGV[1] or usage;
61 $target = encode_base32 $target;
62 open RNP, '-|', 'rnp', '--decrypt', "$haslodir/$target.rnp"
63 or die "couldn't open pipe to rnp: $!\n";
64 my $password = <RNP>;
65 close RNP or die "rnp failed with exit code $?\n";
66 # this dance is needed because Perl prints some garbage at the end
67 # otherwise??
68 chomp $password;
69 print "$password\n";
70}
71
72sub dolist
73{
74 opendir DIR, $haslodir or die "couldn't open $haslodir: $!\n";
75 for(readdir DIR){
76 next unless s/.rnp$//;
77 my $decoded = decode_base32 $_;
78 print "$decoded\n";
79 }
80 close DIR;
81}
82
83sub doput
84{
85 my $target = $ARGV[1] or usage;
86 $target = encode_base32 $target;
87 my($password, $confirm);
88 do{
89 ReadMode 'noecho';
90 print STDERR "enter password for $ARGV[1]: ";
91 $password = ReadLine 0;
92 print STDERR "\n";
93 print STDERR "confirm password: ";
94 $confirm = ReadLine 0;
95 print STDERR "\n";
96 ReadMode 'restore';
97 }while($password ne $confirm);
98 writernppassword $target, $password;
99}
100
101my %commands = (
102 del => \&dodel,
103 gen => \&dogen,
104 get => \&doget,
105 list => \&dolist,
106 put => \&doput
107);
108my $argv0 = $ARGV[0] or usage;
109my $command = $commands{$argv0} or usage;
110$command->();