fips_canister.c: add support for embedded ppc linux.
authorAndy Polyakov <appro@openssl.org>
Fri, 22 Jul 2011 09:42:11 +0000 (09:42 +0000)
committerAndy Polyakov <appro@openssl.org>
Fri, 22 Jul 2011 09:42:11 +0000 (09:42 +0000)
fips/fips_canister.c
util/incore [new file with mode: 0755]

index 08d07e4b0084dc2448fe497caeb662152545f25f..a1f84004793fe3441678346b7af31dce7c50d3f9 100644 (file)
@@ -29,7 +29,8 @@ const void         *FIPS_text_end(void);
 
 #if !defined(FIPS_REF_POINT_IS_CROSS_COMPILER_AWARE)
 # if   (defined(__ANDROID__) && (defined(__arm__) || defined(__arm)))  || \
-       (defined(__vxworks)   && (defined(__ppc__) || defined(__ppc)))
+       (defined(__vxworks)   && (defined(__ppc__) || defined(__ppc)))  || \
+       (defined(__linux)     && defined(__PPC__) && !defined(__PPC64__))
 #  define FIPS_REF_POINT_IS_CROSS_COMPILER_AWARE
 # endif
 #endif
@@ -46,7 +47,10 @@ static void *instruction_pointer_xlc(void);
 #endif
 
 #ifdef FIPS_START
-#define FIPS_ref_point FIPS_text_start
+# define FIPS_ref_point FIPS_text_start
+# ifdef FIPS_REF_POINT_IS_CROSS_COMPILER_AWARE
+#  define instruction_pointer  FIPS_text_startX
+# endif
 /* Some compilers put string literals into a separate segment. As we
  * are mostly interested to hash AES tables in .rodata, we declare
  * reference points accordingly. In case you wonder, the values are
@@ -55,7 +59,10 @@ static void *instruction_pointer_xlc(void);
 const unsigned int FIPS_rodata_start[]=
        { 0x46495053, 0x5f726f64, 0x6174615f, 0x73746172 };
 #else
-#define FIPS_ref_point FIPS_text_end
+# define FIPS_ref_point FIPS_text_end
+# ifdef FIPS_REF_POINT_IS_CROSS_COMPILER_AWARE
+#  define instruction_pointer  FIPS_text_endX
+# endif
 const unsigned int FIPS_rodata_end[]=
        { 0x46495053, 0x5f726f64, 0x6174615f, 0x656e645b };
 #endif
@@ -150,7 +157,7 @@ static void *instruction_pointer(void)
 const void *FIPS_ref_point()
 {
 #if    defined(FIPS_REF_POINT_IS_CROSS_COMPILER_AWARE)
-    return (void *)FIPS_ref_point;
+    return (void *)instruction_pointer;
 #elif  defined(INSTRUCTION_POINTER_IMPLEMENTED)
     return instruction_pointer();
 /* Below we essentially cover vendor compilers which do not support
diff --git a/util/incore b/util/incore
new file mode 100755 (executable)
index 0000000..ee495ee
--- /dev/null
@@ -0,0 +1,440 @@
+#!/usr/bin/env perl
+#
+# Copyrifgr (c) 2011 The OpenSSL Project.
+#
+# The script embeds fingerprint into ELF executable object, either
+# application binary or shared library.
+
+######################################################################
+#
+# ELF symbol table parser by <appro@openssl.org>. The table entries
+# are extended with offset within executable file...
+#
+{ package ELF;
+  use FileHandle;
+
+    sub dup  { my %copy=map {$_} @_; return \%copy; }
+
+    sub Load {
+       my $class = shift;
+       my $self  = {};
+       my $FD    = FileHandle->new();  # autoclose
+
+       bless $self,$class;
+
+       sysopen($FD,shift,0) or die "$!";
+       binmode($FD);
+
+       #################################################
+       # read and parse elf_ehdr.e_ident...
+       #
+       read($FD,my $elf,16) or die "$!";
+
+       my %e_ident;
+       @e_ident{magic,class,data,version,osabi,abiver,pad}=
+               unpack("a4C*",$elf);
+
+       die "not ELF file" if ($e_ident{magic} ne chr(0177)."ELF");
+
+       my $elf_bits   = $e_ident{class}*32;    # 32 or 64
+       my $big_endian = $e_ident{data}-1;      # 0 or 1
+
+       if ($elf_bits==64) {
+           if (!(((1<<31)<<1) && $big_endian==(unpack("L",pack("N",1))==1))) {
+               die "ELF64 is supported only natively";
+           }
+       }
+
+       #################################################
+       # read and parse remainder of elf_ehdr...
+       #
+       read($FD,my $elfhdr,64) or die "$!";
+
+       my %elf_ehdr;
+       @elf_ehdr{e_type,e_machine,e_version,
+               e_entry,e_phoff,e_shoff,e_flags,e_ehsize,
+               e_phentsize,e_phnum,e_shentsize,e_shnum,e_shstrndx} =
+       $elf_bits==32 ?
+               unpack($big_endian?"nnN5n6":"vvV5v6",$elfhdr)
+       :       unpack("SSLQ3LS6",$elfhdr);
+
+       # put aside e_machine in case one has to treat specific
+       # platforms differently, see EM_ constants in elf.h for
+       # assortment... 
+       $self->{e_machine} = $elf_ehdr{e_machine};
+
+       #################################################
+       # read and parse elf_shdr table...
+       #
+       my ($i,$sz,$symtab_idx,$blob,$strings);
+
+       seek($FD,$elf_ehdr{e_shoff},0) or die "$!";
+       read($FD,$blob,$elf_ehdr{e_shentsize}*$elf_ehdr{e_shnum}) or die "$!";
+
+       my @sections;
+       my $elf_shdr_struct=($elf_bits==32?($big_endian?"N10":"V10"):"L2Q4L2Q2");
+       for ($sz=$elf_ehdr{e_shentsize},$i=0;$i<length($blob);$i+=$sz) {
+           my %elf_shdr;
+
+           @elf_shdr{sh_name,sh_type,sh_flags,
+                       sh_addr,sh_offset,sh_size,
+                       sh_link,sh_info,sh_addalign,sh_entsize} =
+               unpack($elf_shdr_struct,substr($blob,$i,$sz));
+
+           push(@sections,dup(%elf_shdr));
+
+           # note SHT_SYMTAB or SHT_DYNSYM for future reference
+           if ($elf_shdr{sh_type}==2 || $elf_shdr{sh_type}==11) {
+               $symtab_idx = $#sections;
+           }
+       }
+
+       # read strings table and map section names...
+       seek($FD,@sections[$elf_ehdr{e_shstrndx}]->{sh_offset},0)       or die "$!";
+       read($FD,$strings,@sections[$elf_ehdr{e_shstrndx}]->{sh_size})  or die "$!";
+       for (@sections) {
+           $_->{sh_name}=(split(chr(0),substr($strings,$_->{sh_name},64)))[0];
+       }
+
+       #################################################
+       # read symbol strings table...
+       #
+       $i=@sections[$symtab_idx]->{sh_link};
+       seek($FD,@sections[$i]->{sh_offset},0)          or die "$!";
+       read($FD,$strings,@sections[$i]->{sh_size})     or die "$!";
+
+       #################################################
+       # read and parse elf_sym table...
+       #
+       seek($FD,@sections[$symtab_idx]->{sh_offset},0)         or die "$!";
+       read($FD,my $blob,@sections[$symtab_idx]->{sh_size})    or die "$!";
+
+       for ($sz=@sections[$symtab_idx]->{sh_entsize},$i=0;$i<length($blob);$i+=$sz) {
+           my %elf_sym;
+
+           if ($elf_bits==32) {
+               @elf_sym{st_name,st_value,st_size,st_info,st_other,st_shndx} =
+                       unpack($big_endian?"N3CCn":"V3CCv",substr($blob,$i,$sz));
+           } else {
+               @elf_sym{st_name,st_info,st_other,st_shndx,st_value,st_size} =
+                       unpack("LCCSQQ",substr($blob,$i,$sz));
+           }
+
+           my $st_type = $elf_sym{st_info}&0xf;
+           my $st_bind = $elf_sym{st_info}>>4;
+           my $st_secn = $elf_sym{st_shndx};
+           my $name;
+           #                 (STT_OBJECT  || STT_FUNC)
+           if ($st_bind<3 && ($st_type==1 || $st_type==2)
+               && $st_secn <= $#sections               # sane st_shndx
+               && @sections[$st_secn]->{sh_type}       # not SHN_UNDEF
+               && ($name=(split(chr(0),substr($strings,$elf_sym{st_name},128)))[0])
+               ) {
+               # synthesize st_offset, ...
+               $elf_sym{st_offset}  = $elf_sym{st_value}
+                               - @sections[$st_secn]->{sh_addr}
+                               + @sections[$st_secn]->{sh_offset};
+               $elf_sym{st_name}    = $name;
+               $elf_sym{st_section} = @sections[$st_secn]->{sh_name};
+               # ... and add to lookup table
+               $self->{symbols}{$name} = dup(%elf_sym);
+           }
+       }
+
+       return $self;
+    }
+
+    sub Lookup {
+       my $self = shift;
+       my $name = shift;
+       return $self->{symbols}{$name};
+    }
+
+    sub Traverse {
+       my $self = shift;
+       my $code = shift;
+
+       if (ref($code) eq 'CODE') {
+           for (keys(%{$self->{symbols}})) { &$code($self->{symbols}{$_}); }
+       }
+    }
+}
+
+######################################################################
+#
+# SHA1 and HMAC in Perl by <appro@openssl.org>.
+#
+{ package SHA1;
+  use integer;
+
+    {
+    ################################### SHA1 block code generator
+    my @V = ('$A','$B','$C','$D','$E');
+    my $i;
+
+    sub XUpdate {
+      my $ret;
+       $ret="(\$T=\$W[($i-16)%16]^\$W[($i-14)%16]^\$W[($i-8)%16]^\$W[($i-3)%16],\n\t";
+       if ((1<<31)<<1) {
+           $ret.="    \$W[$i%16]=((\$T<<1)|(\$T>>31))&0xffffffff)\n\t  ";
+       } else {
+           $ret.="    \$W[$i%16]=(\$T<<1)|((\$T>>31)&1))\n\t  ";
+       }
+    }
+    sub tail {
+      my ($a,$b,$c,$d,$e)=@V;
+      my $ret;
+       if ((1<<31)<<1) {
+           $ret.="(($a<<5)|($a>>27));\n\t";
+           $ret.="$b=($b<<30)|($b>>2); $e&=0xffffffff; #$b&=0xffffffff;\n\t";
+       } else {
+           $ret.="(($a<<5)|($a>>27)&0x1f);\n\t";
+           $ret.="$b=($b<<30)|($b>>2)&0x3fffffff;\n\t";
+       }
+      $ret;
+    }
+    sub BODY_00_15 {
+       my ($a,$b,$c,$d,$e)=@V;
+       "$e+=\$W[$i]+0x5a827999+((($c^$d)&$b)^$d)+".tail();
+    }
+    sub BODY_16_19 {
+       my ($a,$b,$c,$d,$e)=@V;
+       "$e+=".XUpdate()."+0x5a827999+((($c^$d)&$b)^$d)+".tail();
+    }
+    sub BODY_20_39 {
+       my ($a,$b,$c,$d,$e)=@V;
+       "$e+=".XUpdate()."+0x6ed9eba1+($b^$c^$d)+".tail();
+    }
+    sub BODY_40_59 {
+       my ($a,$b,$c,$d,$e)=@V;
+       "$e+=".XUpdate()."+0x8f1bbcdc+(($b&$c)|(($b|$c)&$d))+".tail();
+    }
+    sub BODY_60_79 {
+       my ($a,$b,$c,$d,$e)=@V;
+       "$e+=".XUpdate()."+0xca62c1d6+($b^$c^$d)+".tail();
+    }
+
+    my $sha1_impl =
+    'sub block {
+       my $self = @_[0];
+       my @W    = unpack("N16",@_[1]);
+       my ($A,$B,$C,$D,$E,$T) = @{$self->{H}};
+       ';
+
+       $sha1_impl.='
+       $A &= 0xffffffff;
+       $B &= 0xffffffff;
+       ' if ((1<<31)<<1);
+
+       for($i=0;$i<16;$i++){ $sha1_impl.=BODY_00_15(); unshift(@V,pop(@V)); }
+       for(;$i<20;$i++)    { $sha1_impl.=BODY_16_19(); unshift(@V,pop(@V)); }
+       for(;$i<40;$i++)    { $sha1_impl.=BODY_20_39(); unshift(@V,pop(@V)); }
+       for(;$i<60;$i++)    { $sha1_impl.=BODY_40_59(); unshift(@V,pop(@V)); }
+       for(;$i<80;$i++)    { $sha1_impl.=BODY_60_79(); unshift(@V,pop(@V)); }
+
+       $sha1_impl.='
+       $self->{H}[0]+=$A;      $self->{H}[1]+=$B;      $self->{H}[2]+=$C;
+       $self->{H}[3]+=$D;      $self->{H}[4]+=$E;      }';
+
+    #print $sha1_impl,"\n";
+    eval($sha1_impl);          # generate code
+    }
+
+    sub Init {
+       my $class = shift;      # multiple instances...
+       my $self  = {};
+
+       bless $self,$class;
+       $self->{H} = [0x67452301,0xefcdab89,0x98badcfe,0x10325476,0xc3d2e1f0];
+       $self->{N} = 0;
+       return $self;
+    }
+
+    sub Update {
+       my $self = shift;
+       my $msg;
+
+       foreach $msg (@_) {
+           my $len  = length($msg);
+           my $num  = length($self->{buf});
+           my $off  = 0;
+
+           $self->{N} += $len;
+
+           if (($num+$len)<64)
+           {   $self->{buf} .= $msg; next;     }
+           elsif ($num)
+           {   $self->{buf} .= substr($msg,0,($off=64-$num));
+               $self->block($self->{buf});
+           }
+
+           while(($off+64) <= $len)
+           {   $self->block(substr($msg,$off,64));
+               $off += 64;
+           }
+
+           $self->{buf} = substr($msg,$off);
+       }
+       return $self;
+    }
+
+    sub Final {
+       my $self = shift;
+       my $num  = length($self->{buf});
+
+       $self->{buf} .= chr(0x80); $num++;
+       if ($num>56)
+       {   $self->{buf} .= chr(0)x(64-$num);
+           $self->block($self->{buf});
+           $self->{buf}=undef;
+           $num=0;
+       }
+       $self->{buf} .= chr(0)x(56-$num);
+       $self->{buf} .= pack("N2",($self->{N}>>29)&0x7,$self->{N}<<3);
+       $self->block($self->{buf});
+
+       return pack("N*",@{$self->{H}});
+    }
+
+    sub Selftest {
+       my $hash;
+
+       $hash=SHA1->Init()->Update('abc')->Final();
+       die "SHA1 test#1" if (unpack("H*",$hash) ne 'a9993e364706816aba3e25717850c26c9cd0d89d');
+
+       $hash=SHA1->Init()->Update('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')->Final();
+       die "SHA1 test#2" if (unpack("H*",$hash) ne '84983e441c3bd26ebaae4aa1f95129e5e54670f1');
+
+       #$hash=SHA1->Init()->Update('a'x1000000)->Final();
+       #die "SHA1 test#3" if (unpack("H*",$hash) ne '34aa973cd4c4daa4f61eeb2bdbad27316534016f');
+    }
+}
+
+{ package HMAC;
+
+    sub Init {
+       my $class = shift;
+       my $key   = shift;
+       my $self  = {};
+
+       bless $self,$class;
+
+       if (length($key)>64) {
+           $key = SHA1->Init()->Update($key)->Final();
+       }
+       $key .= chr(0x00)x(64-length($key));
+
+       my @ikey = map($_^=0x36,unpack("C*",$key));
+       ($self->{hash} = SHA1->Init())->Update(pack("C*",@ikey));
+        $self->{okey} = pack("C*",map($_^=0x36^0x5c,@ikey));
+
+       return $self;
+    }
+
+    sub Update {
+       my $self = shift;
+       $self->{hash}->Update(@_);
+       return $self;
+    }
+
+    sub Final {
+       my $self  = shift;
+       my $ihash = $self->{hash}->Final();
+       return SHA1->Init()->Update($self->{okey},$ihash)->Final();
+    }
+
+    sub Selftest {
+       my $hmac;
+
+       $hmac = HMAC->Init('0123456789:;<=>?@ABC')->Update('Sample #2')->Final();
+       die "HMAC test" if (unpack("H*",$hmac) ne '0922d3405faa3d194f82a45830737d5cc6c75d24');
+    }
+}
+
+######################################################################
+#
+# main()
+#
+my $legacy_mode;
+
+if ($ARGV<0 || ($#ARGV>0 && !($legacy_mode=(@ARGV[0] =~ /^\-(dso|exe)$/)))) {
+       print STDERR "usage: $0 [-dso|-exe] elfbinary\n";
+       exit(1);
+}
+
+$exe = ELF->Load(@ARGV[$#ARGV]);
+
+$FIPS_text_start       = $exe->Lookup("FIPS_text_start")               or die;
+$FIPS_text_end         = $exe->Lookup("FIPS_text_end")                 or die;
+$FIPS_rodata_start     = $exe->Lookup("FIPS_rodata_start")             or die;
+$FIPS_rodata_end       = $exe->Lookup("FIPS_rodata_end")               or die;
+$FIPS_signature                = $exe->Lookup("FIPS_signature")                or die;
+
+if (!$legacy_mode) {
+    $FIPS_text_start   = $exe->Lookup("FIPS_text_startX");
+    $FIPS_text_end     = $exe->Lookup("FIPS_text_endX");
+
+    if (!$FIPS_text_start || !$FIPS_text_end) {
+       print STDERR "@ARGV[$#ARGV] is not cross-compiler aware.\n";
+       exit(1);
+    }
+
+    $FINGERPRINT_ascii_value
+                       = $exe->Lookup("FINGERPRINT_ascii_value")       or die;
+}
+
+sysopen(FD,@ARGV[$#ARGV],$legacy_mode?0:2) or die "$!";        # 2 is read/write
+binmode(FD);
+
+sub HMAC_Update {
+  my ($hmac,$off,$len) = @_;
+  my $blob;
+
+    seek(FD,$off,0)    or die "$!";
+    read(FD,$blob,$len)        or die "$!";
+    $$hmac->Update($blob);
+}
+
+# fips/fips.c:FIPS_incore_fingerprint's Perl twin
+#
+sub FIPS_incore_fingerprint {
+  my $p1  = $FIPS_text_start->{st_offset};
+  my $p2  = $FIPS_text_end->{st_offset};
+  my $p3  = $FIPS_rodata_start->{st_offset};
+  my $p4  = $FIPS_rodata_end->{st_offset};
+  my $sig = $FIPS_signature->{st_offset};
+  my $ctx = HMAC->Init("etaonrishdlcupfm");
+
+    # detect overlapping regions
+    if ($p1<=$p3 && $p2>=$p3) {
+       $p3 = $p1; $p4 = $p2>$p4?$p2:$p4; $p1 = 0; $p2 = 0;
+    } elsif ($p3<=$p1 && $p4>=$p1) {
+       $p3 = $p3; $p4 = $p2>$p4?$p2:$p4; $p1 = 0; $p2 = 0;
+    }
+
+    if ($p1) {
+       HMAC_Update (\$ctx,$p1,$p2-$p1);
+    }
+
+    if ($sig>=$p3 && $sig<$p4) {
+       # "punch" hole
+       HMAC_Update(\$ctx,$p3,$sig-$p3);
+       $p3 = $sig+20;
+       HMAC_Update(\$ctx,$p3,$p4-$p3);
+    } else {
+       HMAC_Update(\$ctx,$p3,$p4-$p3);
+    }
+
+    return $ctx->Final();
+}
+
+$fingerprint = FIPS_incore_fingerprint();
+
+if ($legacy_mode) {
+    print unpack("H*",$fingerprint);
+} else {
+    seek(FD,$FINGERPRINT_ascii_value->{st_offset},0)   or die "$!";
+    print FD unpack("H*",$fingerprint)                 or die "$!";
+}
+
+close (FD);