用于输出4个随机单词的最短Perl解决方案

时间:2017-01-26 09:22:26

标签: perl

我有这个单行Unix shell脚本

  

for i in 1 2 3 4;做sed“$(tr -dc'0-9'< / dev / urandom | fold -w 5 |   awk'$ 0> = 35&& $ 0< = 65570'| head -1)q; d“”$ 0“;完成| perl -p00e   's / \ n(?!\ Z)/ / g'

脚本中有65K个单词,每行一个,从第35行到65570.代码和数据在同一个文件中。

此脚本从此列表中输出4个以空格分隔的随机单词,并在末尾添加换行符。例如

first fourth third second

如何使用Perl使这个单线程更短,保持

tr -dc '0-9' < /dev/urandom

部分?

保持它非常重要,因为它为所有Unix操作系统提供了密码安全伪随机数(CSPRN)。当然,如果Perl可以从/dev/urandom获取数字,那么tr也可以用Perl替换,但urandom中的数字需要保留。

为方便起见,我用65K字共享了基本脚本 65kwords.txt 要么 65kwords.txt

请使用仅核心模块。它将用于生成“人类令人难忘的密码”。

稍后,我们将使用它来存储密码的(散列)迭代计数非常高,因此即使有很多GPU / FPGA,暴力也会很慢。

5 个答案:

答案 0 :(得分:3)

你提到需要一个CSPRN,这使得这是一个非常重要的练习 - 如果你需要加密随机性,那么使用内置的东西(如rand)不是一个好的选择,因为实现在各个平台上变化很大。

但是你有Rand::Urandom看起来像是诀窍:

  

默认情况下,它使用getentropy()(仅在&gt; Linux 3.17中可用)并返回/ dev / arandom然后/ dev / urandom。

#!/usr/bin/env perl
use strict;
use warnings;
use Rand::Urandom;

chomp ( my @words  = <DATA> ); 

print $words[rand @words], " " for 1..4;
print "\n";

__DATA__
yarn
yard
wound
worst
worry
work
word
wool
wolf
wish
wise
wipe
winter
wing
wind
wife
whole
wheat
water
watch
walk
wake
voice

虽然失败了 - 你可以直接从/dev/urandom读取字节:

#!/usr/bin/env perl

use strict;
use warnings;

my @number_of_words = 4;

chomp ( my @words  = <DATA> ); 

open ( my $urandom, '<:raw', '/dev/urandom' ) or die $!;
my $bytes;
read ( $urandom, $bytes, 2 * $number_of_words );  #2 bytes 0 - 65535

#for testing
#unpack 'n' is n  An unsigned short (16-bit)
# unpack 'n*' in a list context returns a list of these. 
foreach my $value ( unpack ( "n*", $bytes ) ) {
   print $value,"\n"; 
}

#actually print the words. 
#note - this assumes that you have the right number in your list. 
# you could add a % @words to the map, e.g. $words[$_ % @words]
#but that will mean wrapping occurs, and will alter the frequency distribution. 
#a more robust solution would be to fetch additional bytes if the 'slot' is 
#empty. 
print join " ", ( map { $words[$_] } unpack ( "n*", $bytes )),"\n";

__DATA__
yarn
yard
wound
worst
#etc.

注意 - 上面的内容依赖于你的wordlist与两个字节(16位)相同的大小 - 如果这个假设不是是真的,你需要处理&#39;错过&#39;话。一个粗略的方法是采用模数,但这意味着一些包装,因此不是真正均匀分布的单词选择。否则,您可以进行位掩码和重新滚动,如下所示:

在一个相关的观点上 - 您是否考虑使用词汇表,而是使用辅音 - 元音 - 辅音分组?

E.g:

#!/usr/bin/env perl

use strict;
use warnings;

#uses /dev/urandom to fetch bytes.
#generates consonant-vowel-consonant groupings.
#each are 11.22 bits of entropy, meaning a 4-group is 45 bits. 
#( 20 * 6 * 20 = 2400, which is 11.22 bits of entropy log2 2400
#log2(2400 ^ 4) = 44.91
#but because it's generated 'true random' it's a know entropy string.

my $num    = 4;
my $format = "CVC";

my %letters = (
    V => [qw ( a e i o u y )],
    C => [ grep { not /[aeiouy]/ } "a" .. "z" ], );

my %bitmask_for;
foreach my $type ( keys %letters ) { 
   #find the next power of 2 for the number of 'letters' in the set.
   #So - for the '20' letter group, that's 31. (0x1F)
   #And for the 6 letter group that's 7.  (0x07)
   $bitmask_for{$type} =  ( 2 << log ( @{$letters{$type}} ) / log 2 ) - 1 ; 
}

open( my $urandom, '<:raw', '/dev/urandom' ) or die $!;

for ( 1 .. $num ) {
    for my $type ( split //, $format ) {
        my $value;
        while ( not defined $value or $value >= @{ $letters{$type} } ) {
            my $byte;
            read( $urandom, $byte, 1 );
            #byte is 0-255. Our key space is 20 or 6. 
            #So rather than modulo, which would lead to an uneven distribution,
            #we just bitmask and discard and 'too high'. 
            $value = (unpack "C", $byte ) & $bitmask_for{$type};
        }
        print $letters{$type}[$value];
    }
    print " ";
}
print "\n";
close($urandom);

这会产生3个字符的CVC符号,具有已知的熵级别(每组&#39;组为11.22),用于制作相当健壮的密码。 (45位而不是原始的64位,但显然你可以添加额外的&#39;组以获得每次11.22位)。

答案 1 :(得分:2)

这个答案不是加密安全的!

我会在Perl中完全做到这一点。不需要单线。只需抓住你的单词列表并将其放入Perl程序即可。

rand @words

这会从列表中抓取四个随机单词并输出它们。

@words在标量上下文中计算int,它给出了元素的数量,并创建了一个介于0和小于该数字的随机浮点值。 @words切断小数。这用作从map中抓取元素的索引。我们使用1 .. 4语句重复此操作四次,其中(1, 2, 3, 4)与将map列表作为参数传递给map相同。这个参数被忽略了,而是选择了我们的随机单词。 join返回一个列表,我们qw()在一个空格中。最后,我们打印生成的字符串和换行符。

单词列表是使用引用的单词'运算符创建的,它返回引用单词列表。它是简写,因此您无需键入所有引号,和逗号qw()

如果您想在底部添加单词列表,可以将sub放在__DATA__中并在顶部调用,或使用id_token部分像文件句柄一样从中读取。

答案 2 :(得分:1)

tr上使用fold/dev/urandom的特定方法的效率要低得多,所以让我们稍微修复一下,同时保持/dev/urandom部分。

假设可用内存足以包含您的脚本(包括wordlist):

chomp(@words = <DATA>);
open urandom, "/dev/urandom" or die;
read urandom, $randbytes, 4 * 2 or die;
print join(" ", map $words[$_], unpack "S*", $randbytes), "\n";
__DATA__
word
list
goes
here

这是为了简洁和简洁而没有彻底的混淆 - 当然你可以通过删除空格等来缩短它,但是没有理由。它是独立的,可以使用几十年的perls(是的,那些裸字文件句是故意的:-P)

它仍然需要单词列表中的65536个条目,因为我们不必担心使用模数运算符将偏差引入随机数选择。稍微更雄心勃勃的方法可能是从每个字的urandom读取48个字节,将其转换为0到1之间的浮点值(可移植到大多数系统)并将其乘以大小单词列表,允许任何合理大小的单词列表。

答案 3 :(得分:0)

很多废话都谈到密码强度,我认为你高估了你的几个要求的价值

  • 我不明白您对使用perl“”使代码“更短”的关注。 (你为什么选择Perl?)这里的节省只能使脚本更快地进行读取和编译,但是它们会因代码之后的半兆字节数据而变得相形见绌

  • 在这种情况下,对可怜的随机数生成器的黑客的有用性取决于密码构造的先验知识以及最近生成的密码。只有65,000个单词的样本,即使是最差的随机数生成器也会在连续密码之间显示无关紧要的相关性

    通常,如果密码更长,则密码更安全,无论其内容如何。用一系列英文单词形成一个长密码纯粹是一种让序列更难忘的方式

  • “当然后来,(哈希)迭代计数......会非常高,所以蛮力[黑客攻击?]会非常慢”

    这根本不遵循。破解算法不会试图猜测你选择的四个单词:它们只能看到一个由小写字母和空格组成的三十个字符(左右)字符串,其起源无关紧要。与具有相同字符集的相同长度的任何其他密码相比,它不会或多或少可以破解

我建议您重新考虑自己的要求,这样可以让自己更轻松。我觉得很难想到四个英文单词,并且不需要一个程序来为我做。 提示: pilchard 是一个很好的人:他们从不这么想!

如果你仍然坚持,那么我会在Perl中写这样的东西。

我只使用了数据的前18行
use strict;
use warnings 'all';

use List::Util 'shuffle';

my @s = map /\S+/g, ( shuffle( <DATA> ) )[ 0 .. 3 ];
print "@s\n";

__DATA__
yarn
yard
wound
worst
worry
work
word
wool
wolf
wish
wise
wipe
winter
wing
wind
wife
whole
wheat

输出

wind wise winter yarn

答案 4 :(得分:-2)

您可以使用Data::Random::rand_words()

perl -MData::Random -E 'say join $/, Data::Random::rand_words(size => 4)'