#!/usr/bin/perl -wT # Prints out a 50% random sampling of upcoming days. # # Set the 'key' parameter to a string to tweak the RNG. # # Set the 'r' parameter to a number 1..256 to randomly # select r/256 instead of 50% . # # Set 'salt' and 'rounds' for crypt(3). # # Copyright 2020 Ken Takusagawa # # This program is free software: you can redistribute it # and/or modify it under the terms of the GNU Affero General # Public License as published by the Free Software Foundation, # either version 3 of the License, or (at your option) any # later version. # # This program is distributed in the hope that it will be # useful, but WITHOUT ANY WARRANTY; without even the implied # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU Affero General # Public License along with this program. If not, see # . use POSIX qw(mktime strftime); use CGI qw(:standard); use Digest::SHA qw(sha1 sha512_hex); use URI::Escape qw(uri_escape); use Scalar::Util(looks_like_number); print header; $r=param('r'); $r=128 unless defined$r; unless ($r =~ /^\d+$/){ &errorr(); } unless (1 <= $r and $r <= 256){ &errorr(); } die if $r<1; #prevent infinite loop die if $r>256; if($r==128){ $proportion="50%"; }else{ $proportion="$r/256"; } $andr="-$r/256"; print start_html("random $proportion of days"); print << 'EOF'; EOF $debug=param('debug'); unless(defined$debug){ $debug=0; } unless(looks_like_number$debug){ # needs to be numeric because we will compare numerically later $debug=1 } $now=time; # beware that TZ is local time zone of the web server print "\n" if $debug; # go backward one day for people in west time zones $now -= 24*60*60; @brokendowntime=localtime($now); $day=$brokendowntime[3]; $month=$brokendowntime[4]+1; $year=$brokendowntime[5]+1900; $second=$minute=0; $hour=12; # avoid weirdness of daylight saving time changes # initialization vector, perhaps interpretable as a random # seed, perhaps interpretable as a secret key $key=param('key'); unless (defined$key){ $key=""; $andkey=""; } else { if($debug){ print"\n"; } # make some attempt at avoiding length-extension attacks against sha-2 family by hashing twice, inspired by HMAC-SHA #$key=sha512_hex($key); # let's use crypt in case the user wants to keep key # secret but the output is observed by an adversary. $type=6; # sha512 $rounds=param('rounds'); $defaultrounds=200000; # avoid overloading web server $rounds=$defaultrounds unless defined($rounds); unless ($rounds =~ /^\d*$/){ print"\n" if$debug; $rounds=$defaultrounds; } unless($rounds eq ""){ #crypt will automatically clip to minimum 1000 if ($rounds>$defaultrounds){ print"\n" if$debug; $rounds=$defaultrounds; } $rounds="\$rounds=$rounds" } # passing an empty string for rounds does 5000 $salt=param('salt'); $salt='randomdays' unless defined($salt); # thwart rainbow tables # salt characters outside the range are technically accepted by # __sha512_crypt_r even though the man page says not. we follow # the principal of "be liberal in what you accept". # uncomment this to restrict salt characters $salt =~ s,[^0-9A-Za-z/.],,g; # crypt will automatically truncate salt to maximum 16 characters $full='$'.$type.$rounds.'$'.$salt; $key=crypt $key,$full; $key="-".$key; $andkey="-crypt(key)"; if($debug){ die unless ($part1,$salt,$part2)=($key =~ /-(.+\$)([^\$]*)(\$[^\$]+)$/); print"\n"; } } if($debug){ print "\n"; } print"\n"; $ct=0; while($ct<=366){ # limit number matching days to avoid excessive CPU load at r=1 #$t=mktime($second,$minute,$hour,$day,$month-1,$year-1900); # sadly srand gives chunks 4 identical days in a row # srand$t; # for(0..999){rand 1} # if(rand 1 <0.5){ # print scalar(localtime($t)),"
\n"; # $ct++; # } $iso8601=strftime('%F', $second,$minute,$hour,$day,$month-1,$year-1900); $out=strftime('%F - %B %e %Y %A',$second,$minute,$hour,$day,$month-1,$year-1900); $x=$iso8601.$andr.$key; @F=unpack("C*",sha1($x)); if($debug>1){ print "\n"; } if($F[0]<$r){ #print scalar(localtime($t))," ($iso8601)
\n"; if($debug==1){ print "\n"; } print "$out
\n"; $ct++; } # because of the way strftime is implemented, this works even if the day is outside of the range of numbers for the month. $day++; } print end_html; sub possiblyescape { # prevent XSS attacks die unless@_; my$escaped; for($_[0]){ $escaped=uri_escape($_); if($_ ne $escaped){ $escaped="uri_unescape($escaped)"; } } $escaped; } sub errorr { print start_html("ERROR, random proportion of days"); print << 'EOF';

ERROR: r parameter should be integer, 1 <= r <= 256

EOF print end_html; exit; }