| #! /usr/bin/env perl |
| # Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved. |
| # |
| # Licensed under the OpenSSL license (the "License"). You may not use |
| # this file except in compliance with the License. You can obtain a copy |
| # in the file LICENSE in the source distribution or at |
| # https://www.openssl.org/source/license.html |
| |
| # 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); |
| |
| $!=42; # signal fipsld to revert to two-step link |
| 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; |
| |
| # new cross-compile support |
| $FIPS_text_startX = $exe->Lookup("FIPS_text_startX"); |
| $FIPS_text_endX = $exe->Lookup("FIPS_text_endX"); |
| |
| if (!$legacy_mode) { |
| if (!$FIPS_text_startX || !$FIPS_text_endX) { |
| print STDERR "@ARGV[$#ARGV] is not cross-compiler aware.\n"; |
| exit(42); # signal fipsld to revert to two-step link |
| } |
| |
| $FINGERPRINT_ascii_value |
| = $exe->Lookup("FINGERPRINT_ascii_value"); |
| |
| } |
| if ($FIPS_text_startX && $FIPS_text_endX) { |
| $FIPS_text_start = $FIPS_text_startX; |
| $FIPS_text_end = $FIPS_text_endX; |
| } |
| |
| 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); |
| } elsif (defined($FINGERPRINT_ascii_value)) { |
| seek(FD,$FINGERPRINT_ascii_value->{st_offset},0) or die "$!"; |
| print FD unpack("H*",$fingerprint) or die "$!"; |
| } else { |
| seek(FD,$FIPS_signature->{st_offset},0) or die "$!"; |
| print FD $fingerprint or die "$!"; |
| } |
| |
| close (FD); |