#!/usr/bin/perl
package Razor2::Signature::Ephemeral;
use strict;
use Data::Dumper;
BEGIN {
eval { require Digest::SHA; import Digest::SHA qw(sha1_hex); 1 }
or do { require Digest::SHA1; import Digest::SHA1 qw(sha1_hex) }
}
sub new {
my ( $class, %args ) = @_;
my $self = bless {
seed => $args{seed} || 42,
separator => encode_separator( $args{separator} ) || encode_separator("10"),
}, $class;
$self;
}
sub hexdigest {
my ( $self, $content ) = @_;
# Initialize PRNG with $seed
srand( $$self{seed} );
my @content = split /$$self{separator}/, $content;
# $content =~ s/$$self{separator}//g; -- We don't do this anyore
# my $size = length($content);
my $lines = scalar @content;
debug("\nNumber of lines: $lines");
# Randomly choose relative locations and section sizes (in percent)
my $sections = 6;
my $ssize = 100 / $sections;
my @rel_lineno = map { rand($ssize) + ( $_ * $ssize ) } 0 .. ( $sections - 1 );
my @lineno = map { int( ( $_ * $lines ) / 100 ) } @rel_lineno;
debug("Relative Line Numbers (in percent): @rel_lineno");
debug("Absolute Line Numbers: @lineno");
my @rel_offset1 = map { rand(50) + ( $_ * 50 ) } qw(0 1);
my @rel_offset2 = map { rand(50) + ( $_ * 50 ) } qw(0 1);
debug("Relative Offsets for section 1: @rel_offset1");
debug("Relative Offsets for section 2: @rel_offset2");
my ( $l1, $l2 ) = ( 0, 0 );
for ( $lineno[1] .. $lineno[2] ) { $l1 += length( $content[$_] ) if $content[$_] }
for ( $lineno[3] .. $lineno[4] ) { $l2 += length( $content[$_] ) if $content[$_] }
debug("Length of the first section: $l1 bytes");
debug("Length of the second section: $l2 bytes");
my @offset1 = map { int( ( $_ * $l1 ) / 100 ) } @rel_offset1;
my @offset2 = map { int( ( $_ * $l2 ) / 100 ) } @rel_offset2;
debug( "Chunk start/end positions in Section 1: @offset1 (length: " . ( $offset1[1] - $offset1[0] ) . ") " );
debug( "Chunk start/end positions in Section 2: @offset2 (length: " . ( $offset2[1] - $offset2[0] ) . ") " );
my $x = 0;
my ( $sc, $sl, $ec, $el ) = ( 0, 0, 0, 0 );
my $section1 = picksection(
\@content,
$lineno[1], $lineno[2],
$offset1[0], $offset1[1]
);
my $section2 = picksection(
\@content,
$lineno[3], $lineno[4],
$offset2[0], $offset2[1]
);
debug("Section 1: $section1");
debug("Section 2: $section2");
my $seclength = length( $section1 . $section2 );
debug("Total length of stuff that will be hashed: $seclength");
if ( $section1 =~ /^\s+$/ && $section2 =~ /^\s+$/ ) {
debug("Both sections were whitespace only!");
$section1 = "";
$section2 = "";
}
my $digest;
if ( $seclength > 128 ) {
$digest = sha1_hex($section1, $section2);
}
else {
debug("Sections too small... reverting back to orginal content.");
$digest = sha1_hex($content);
}
debug("Computed e-hash is $digest");
return $digest;
}
sub picksection {
my ( $content, $sline, $eline, $soffset, $eoffset ) = @_;
my $x = 0;
my ( $sc, $sl, $ec, $el ) = ( 0, 0, 0, 0 );
for ( $sline .. $eline ) {
next unless $content->[$_];
$x = $x + length( $content->[$_] );
if ( ( $x > $soffset ) && ( $sc == 0 ) ) { # we come here first time
$sc = length( $content->[$_] ) - ( $x - $soffset ); # $x is greater than start
$sl = $_; # offset
}
if ( $x > $eoffset ) {
$ec = length( $content->[$_] ) - ( $x - $eoffset );
$el = $_;
}
last if $ec;
}
$sc = 0 if $sc < 0;
$ec = 0 if $ec < 0; # FIX! not verified to work correctly.
debug("Absolute chunk offsets: Line $sl charachter $sc to line $el character $ec");
my $section = "";
if ( $sl == $el ) {
if ( $content->[$sl] ) {
$section = substr( $content->[$sl], $sc, $ec - $sc + 1 );
}
else {
$section = "";
}
}
else {
$section .= substr( $content->[$sl], $sc );
for ( $sl + 1 .. $el - 1 ) {
$section .= $content->[$_];
}
$section .= substr( $content->[$el], 0, $ec );
}
return $section;
}
sub encode_separator {
my ( $self, $separator ) = @_;
my $rv;
unless ( ref $self ) { $separator = $self }
my @chars = split /-/, $separator;
push @chars, $separator unless scalar @chars;
for (@chars) { $rv .= chr($_) }
return $rv;
}
sub debug {
my $message = shift;
# print "debug: $message\n";
#open TMP, ">>/tmp/ehash";
#print TMP "$message\n";
#close TMP;
}
1;