Skip to content

Instantly share code, notes, and snippets.

@ShimmerFairy
Created August 16, 2011 04:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ShimmerFairy/1148462 to your computer and use it in GitHub Desktop.
Save ShimmerFairy/1148462 to your computer and use it in GitHub Desktop.
SHA1 calculator
use v6;
module Digest::SHA::One:auth<lue>:version<0>;
# This sub takes a string and calculates its SHA-1 hash
our sub calculate_sha1(Str $input) is export {
my $message = $input;
if ($message.bytes * 8).Num >= (2**64.Num) {
# checks if number of bits in $message is more than 2**64 bits
# Num used 'cos default type Int is signed
fail "The string you provided is {$message.bytes/(1024**6)} EiB, which exceeds the length limit (2 EiB) for SHA-1.";
}
# blergh. Buf might be more preferable, but is NYI in nom. So,
# things are modified on a bit level!
# on the plus side, the C<* 8> in ($message.bytes * 8) isn't needed!
$message = $message.split('')».ord».fmt('%08b').join('');
if $message.bytes % 512 > 0 { # is the message size (in bits) a multiple of 512? If not, we pad!
# now each byte of the string is a bit of the original. * 8 isn't needed!
my $origlength = ($message.bytes).fmt('%064b'); # length of string used at end of padding. Must be 8 B (64 b) long
my $lastblocksize = $message.bytes % 512; # how large is the last block of data?
# Start padding!
$message ~= '1'; # first, pad with a single 1.
$message ~= ('0' xx ($lastblocksize - 65)); # pad with 0's until we get to where the original length is stored.
$message ~= $origlength; # final 64 bits are the length of the original message.
fail "Something in the padding process went wrong. Can't continue." if $message.bytes % 512 > 0;
}
# Time for calculating the hash!
# Not really. First we must define some things.
# This sub simply performs some bitwise operations based on what 'round' we're on
sub function(Int $t where 0..79, Int $b, Int $c, Int $d) { # various operations done by throughout the 80 rounds
# Int[uint32] for $b, $c, and $d would probably avoid this next line.
fail "Arguments not 32-bit words" if ($b | $c | $d) > 0xFFFFFFFF;
# calculate, based on which round we're on.
my $result;
given $t {
when 0..19 {
$result = ($b +& $c) +| (($b +^ 0xFFFFFFFF) +& $d);
}
when 20..39 {
$result = $b +^ $c +^ $d;
}
when 40..59 {
$result = ($b +& $c) +| ($b +& $d) +| ($b +& $d);
}
when 60..79 {
$result = $b +^ $c +^ $d;
}
}
$result .= fmt('%032b');
return $result;
}
# Returns a constant
sub k($t) {
given $t {
when 0..19 {
return 0x5A827999;
}
when 20..39 {
return 0x6ED9EBA1;
}
when 40..59 {
return 0x8F1BBCDC;
}
when 60..79 {
return 0xCA62C1D6;
}
}
}
# Does circular shift on 32-bit word
sub circshift($word, $n where 0..^32) {
die "The circular shift operation wasn't fed a 32 bit word." if $word > 0xFFFFFFFF;
return (($word +< $n) +| ($word +> 32-$n));
}
# defining all the variables before the loop to avoid creating them all the time
my @M = $message.comb(/<digit>**512/); # get array of 512 bit blocks
my @H = 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0; # initialize the H's buffer!
my @ABCDE; # The other buffer (come back, user-defined indexing!)
my @W; # this is where the split-up @M elements go, must be done in loop.
my $temp;
for 0..^@M.elems { # Process the blocks
@W = ("0b" «~» @M[$_].comb(/<digit>**32/))».Int; # separate the words
die "An error occured while calculating the hash" if @W.elems != 16; # sanity check
@ABCDE = @H; # set each of the five buffers (A, B, C, D, E) to the H values in buffer (H0, H1...H5, respectively)
for 0..79 { # Process the words!
# the rest of @W is generated
if 16..79 {
@W.push(circshift((@W[$_-3] +^ @W[$_-8] +^ @W[$_-14] +^ @W[$_-16]),1)); # Blame the NSA, not me!
}
$temp = circshift(@ABCDE[0],5) + function($_, @ABCDE[1], @ABCDE[2], @ABCDE[3]) + @ABCDE[4] + @W[$_] + k($_);
@ABCDE[4] = @ABCDE[3];
@ABCDE[3] = @ABCDE[2];
@ABCDE[2] = circshift(@ABCDE[1],30);
@ABCDE[1] = @ABCDE[0];
@ABCDE[0] = $temp;
}
@H = @H «+» @ABCDE;
}
# now, to concatenate all the @H elems
return [~] @H».fmt('%04X');
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment