in reply to Generating lists from a charset

I am working on solving a similar problem with generation of rainbow tables (tables of strings and their hashes). I didn't want recursion because I needed restartability and arbitrary length.

I have something that works, albiet it needs a bit more work:

package RainbowTable; =pod ================================================================= +========= =head1 NAME RainbowTable - A module to assist in the creation of hash-based rainbow tables =head1 VERSION This document describes RainbowTable version 0.01 =head1 SYNOPSIS Generates a range of printable character combinations (such as might be used for passwords) and converts them to hashes via a specifiable hash function, caching the results for easy and fast searching later. A hash type is required, other parameters are optional. use RainbowTable; #generate a table of MD5 hashes for all sets 1-10 chars in length my $rainbow = RainbowTable->new( hash => 'MD5', maxlength => 10, minlength=>1, ); while ($rainbow->next) { open my $fh, '>', $rainbow->hash or die($!); print $fh $rainbow->string; close $fh; } The iterative model is used, since it can take a I<very> long time to generate the tables, and the interruption should be restartable. use RainbowTable; #resume processing using the 'start_at' parameter my $rainbow = RainbowTable->new( hash => 'MD5', start_at => 'aaaba', ); You can specifiy your own character set: # process only alphanumerics (no spaces, even) my $rainbow = RainbowTable->new( hash => 'MD5', chars => [ ('A'..'Z'), ('a'..'z'), (0..9) ] ); Any hash type with a related Digest::* module can be specified by name. For example, 'MD5' will load Digest::MD5 for hashing. The module interface must have a 'hexdigest' method in OO mode. =cut use strict; use warnings; use Params::Validate qw/validate_with :types/; #_____________________________________________________________________ +___ new()_ sub new { # new ( ) my $self = bless {}, shift; # creates blessed-hash object my %args = validate_with( params => \@_, spec => { hash => 1, minlength => {type=>SCALAR, default=>1}, maxlength => {type=>SCALAR, default=>10}, start_at => {type=>SCALAR, default=>''}, chars => {type=>ARRAYREF, default=>undef}, }, ); # some extra param checking and initialization unless (defined $args{chars}) { for ( 32..126) { push @{$args{chars}}, chr($_) } for (128..168) { push @{$args{chars}}, chr($_) } } foreach (keys %args) { $self->{$_} = $args{$_}; } $self->{hash} = _loadHash($args{hash}); $self->{SET} = join('',@{$args{chars}}); die ("Must have sets of at least 1 char") if $self->{minlength} < +1; return $self; } #__________________________________________________________________ _l +oadHash()_ sub _loadHash { # _loadHash ( $name ) - loads hashing module by name # Returns codeRef to hash object or dies on failure. my ($name) = @_; my $hashobj; eval { eval "require Digest::$name;" or die($@); $hashobj = "Digest::$name"->new() or die("Unable to construct object for '$name'. $@"); die("'$name' can't 'hexdigest'") unless $hashobj->can('hexdige +st'); }; if ($@) { die "Unable to initialize hashing function '$name': $@"; } return $hashobj; } #__________________________________________________________________ sp +aceSize()_ sub spaceSize { # spaceSize ( ) - size of search space, ignoring starting location my $self = shift; my $base = @{$self->{chars}}; my $size = $base ** $self->{maxlength} - $base ** ($self->{minleng +th}-1) + 1; return $size; } #_____________________________________________________________________ +__ next()_ sub next { # next ( ) - generates the next string to deal with, updates 'star +t_at' my $self = shift; my $size = length $self->{start_at}; my $max = length($self->{SET})-1; while ( length($self->{start_at}) < $self->{minlength} ) { $self->{start_at}.=substr( $self->{SET},0,1 ); } #return if we started with a too-short string. This is because we + will #have just initialized it. return 1 if $size < $self->{minlength}; # increment my $col = $size-1; while ($col >= 0) { #find current char in set my $loc = index( $self->{SET}, substr($self->{start_at},$col,1 +) ); if ( $loc == $max ) { # roll over substr( $self->{start_at}, $col, 1 ) = substr( $self->{SET +},0,1 ); if ($col == 0) { # leftmost column rollover adds a new place $self->{start_at}= substr($self->{SET},0,1).$self->{st +art_at}; last; } $col--; } else { substr($self->{start_at},$col,1) = substr( $self->{SET},$l +oc+1,1 ); last; } } #/while # $self->next if length($self->{start_at}) < $self->{minlength}; return 0 if length($self->{start_at}) > $self->{maxlength}; return 1; } #_____________________________________________________________________ +__ hash()_ sub hash { # hash ( ) - calculates and returns hashed value my $self = shift; $self->{hash}->reset; $self->{hash}->add($self->string); my $hash = $self->{hash}->hexdigest; return $hash; } #_____________________________________________________________________ + string()_ sub string { # string ( ) - returns current working string my $self = shift; my $string = $self->{start_at}; return $string; } 1;

Basically, you could then solve your problem with:

use RainbowTable; # I know we don't need a hash, but the module requires one. my $generator = RainbowTable->new( hash => 'MD5', chars => [ ('A'..'Z'), ('a'..'z'), (0..9) ], minlength => 3, maxlength => 3, ); while ($generator->next) { print $generator->string, "\n"; }

Yeah, I know this is offtopic in an Obfu node, but I thought this might actually help you out. If you make useful modifications, I'd love to see them; my code's MIT licensed, though I haven't pasted that in yet -- basically, use to your heart's content, but give me credit.

<-radiant.matrix->
A collection of thoughts and links from the minds of geeks
The Code that can be seen is not the true Code
"In any sufficiently large group of people, most are idiots" - Kaa's Law