-
Notifications
You must be signed in to change notification settings - Fork 0
/
pwg
executable file
·114 lines (96 loc) · 3.04 KB
/
pwg
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#!/usr/bin/env perl
#
# Copyright (c) 2022, 2023 Omar Polo <op@omarpolo.com>
# Copyright (c) 2023 Alexander Arkhipov <aa@alearx.org>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
use strict;
use warnings;
use v5.32;
use open ":std", ":encoding(UTF-8)";
use Getopt::Long qw(:config bundling require_order);
use File::Basename;
my $urandom; # opened later
my $chars = "\x20-\x7E";
my $wordlist;
my $length = 32;
my $me = basename $0;
sub usage {
say STDERR "usage: $me [-an] [-w wordlist] [len]";
exit(1);
}
# not really arc4random but closer...
sub arc4random {
my $r = read($urandom, my $buf, 4)
or die "$me: failed to read /dev/urandom: $!\n";
die "$me: short read\n" if $r != 4;
return unpack('L', $buf);
}
# Calculate a uniformly distributed random number less than $upper_bound
# avoiding "modulo bias".
#
# Uniformity is achieved by generating new random numbers until the one
# returned is outside the range [0, 2**32 % $upper_bound). This
# guarantees the selected random number will be inside
# [2**32 % $upper_bound, 2**32) which maps back to [0, $upper_bound)
# after reduction modulo $upper_bound.
sub randline {
my $upper_bound = shift;
return 0 if $upper_bound < 2;
my $min = 2**32 % $upper_bound;
# This could theoretically loop forever but each retry has
# p > 0.5 (worst case, usually far better) of selecting a
# number inside the range we need, so it should rarely need
# to re-roll.
my $r;
while (1) {
$r = arc4random;
last if $r >= $min;
}
return $r % $upper_bound;
}
GetOptions(
"a" => sub { $chars = "0-9a-zA-Z" },
"n" => sub { $chars = "0-9" },
"w=s" => \$wordlist,
) or usage;
$length = 6 if defined $wordlist;
$length = shift if @ARGV;
die "$me: invalid length: $length\n" unless $length =~ /^\d+$/;
open($urandom, "<:raw", "/dev/urandom")
or die "$me: can't open /dev/urandom: $!\n";
if (not defined $wordlist) {
my $pass = "";
my $l = $length;
while ($l >= 0) {
read($urandom, my $t, 128)
or die "$me: failed to read /dev/urandom: $!\n";
$t =~ s/[^$chars]//g;
$l -= length($t);
$pass .= $t;
}
say substr($pass, 0, $length);
exit 0;
}
open(my $fh, "<", $wordlist) or die "$me: can't open $wordlist: $!\n";
my @lines = (0);
push @lines, tell $fh while <$fh>;
while ($length--) {
seek $fh, $lines[randline scalar(@lines)], 0
or die "$me: seek: $!\n";
my $line = <$fh>;
chomp($line);
print $line;
print " " if $length;
}
say "";