#!perl -w # (C) 2003-2007 Willem Jan Hengeveld # Web: http://www.xs4all.nl/~itsme/ # http://wiki.xda-developers.com/ # # $Id: $ # use strict; use IO::File; use Getopt::Long; use Digest::SHA; use Math::BigInt lib=>'GMP'; if (Math::BigInt->config()->{lib} !~ /GMP/) { warn "no Math::BigInt::GMP installed - this may take quite some time\n"; } # perl nbh2dbh.pl -e -pub 0x -priv 0x -exp 0x10001 -b 0x100000 nk.dbh nk2.nbh # perl nbh2dbh.pl -v nk2.nbh nk2.dbh > nk2.txt sub usage { return <<__EOF__ Usage: nbh2dbh [-v] [-pub PUBKEY] nksigned.nbh [filename.dbh] nbh2dbh -e [-b BLOCKSIZE] [-priv PRIVKEY] [-exp EXPONENT] filename.dbh nksigned.nbh __EOF__ } my $verbose= 0; my $privatekey; my $modulus; my $exponent; my $blocksize=0x100000; my $doencode; GetOptions( "v" => \$verbose, "e" => \$doencode, "priv=s" => sub { $privatekey=Math::BigInt->new($_[1]) }, "pub=s" => sub { $modulus=Math::BigInt->new($_[1]) }, "exp=s" => sub { $exponent=Math::BigInt->new($_[1]) }, "b=s" => sub { $blocksize=eval($_[1]) }, ) or die usage(); my $ifn= shift or die usage(); my $ifh= IO::File->new($ifn, "r") or die "open $ifn: $!\n"; binmode $ifh; my $ofn= shift; my $ofh= IO::File->new($ofn, "w") or die "open $ofn: $!\n" if ($ofn); binmode $ofh if ($ofh); if ($doencode) { dbhencode($ifh, $ofh); } else { dbhdecode($ifh, $ofh); } $ofh->close() if ($ofh); $ifh->close(); # the signdata is: # "\x01" + ("\xFF" x 90) + asn1encode( [ [ oid-sha1, undef ], hash ] ) # "003021300906052B0E03021A05000414"+hash # modexp(hash, privkey, modulus) sub calc_hash { # 0 = blockdata # 1 = blkflags # 2 = magic2 # 3 = seqnr my $sha= Digest::SHA->new(1); $sha->add($_[0]); $sha->add(pack("VVV", 0,0,0)); $sha->add(pack("C", $_[1])); $sha->add($_[2]); $sha->add(pack("V", $_[3])); return $sha->digest; } sub dbhdecode { my ($ifh, $ofh)= @_; my $hdrdata; $ifh->read($hdrdata, 0x17); my ( $magic1, $magic2, ) = unpack("a7a16", $hdrdata); $magic1 =~ s/\n/\\n/; printf("magic1='%s'\n", $magic1); printf("magic2= %s\n", unpack("H*", $magic2)); my $seqnr= 1; while (!$ifh->eof()) { my $ofs = $ifh->tell(); my $blkhdrdata; $ifh->read($blkhdrdata, 9) or die "read blkhdr: $!\n"; my ($blksize, $sigsize, $blkflags)= unpack("VVC", $blkhdrdata); printf("%08lx : %08lx %08lx %x", $ofs, $blksize, $sigsize, $blkflags) if ($verbose); my $blockdata=""; if ($blksize) { $ifh->read($blockdata, $blksize) or die "read blk: $!\n"; } my $sigdata=""; if ($sigsize) { $ifh->read($sigdata, $sigsize) or die "read sig: $!\n"; } $ofh->write($blockdata) if ($ofh); my $hash= calc_hash($blockdata, $blkflags, $magic2, $seqnr++); if ($modulus && $exponent) { my $plaindata= lc Math::BigInt->new('0x'.unpack("H*", $sigdata))->bmodpow($exponent, $modulus)->as_hex; $plaindata =~ s/0x/0x0/ if (length($plaindata)&1); my $hashhex = lc unpack("H*", $hash); $plaindata =~ s/^((?:..)*?)((?:ff)+)/sprintf("%s + 0xff x %d + 0x", $1, length($2)\/2)/e; printf(" %s = %s : %s\n", $hashhex, $hashhex eq substr($plaindata, -length($hashhex)) ? "OK" : "ERR", $plaindata); } else { printf(" %s %s\n", unpack("H*", $hash), unpack("H*", $sigdata)) if ($verbose); } if ($blkflags==2) { last; } } if ((-s $ifh) != $ifh->tell()) { printf("end at %08lx, remaining: %08lx\n", $ifh->tell(), (-s $ifh)-$ifh->tell()); } } sub sign_hash { my ($hash)= @_; my $plaindata= "\x01" . ("\xFF" x 90) . pack("H*", "003021300906052b0e03021a05000414") . $hash; my $hexcryptdata= Math::BigInt->new('0x'.unpack("H*", $plaindata))->bmodpow($privatekey, $modulus)->as_hex; $hexcryptdata =~ s/0x/0x0/ if (length($hexcryptdata)&1); my $sigdata= pack('H*', substr($hexcryptdata, 2)); $sigdata = ( "\x00" x (128-length($sigdata)) ) . $sigdata if (length($sigdata)<128); return $sigdata; } sub dbhencode { my ($ifh, $ofh)= @_; my $randomseed= "\x00" x 16; # "testtesttesttest"; # write file header $ofh->write(pack("a7a16", "R000FF\n", $randomseed)); my $seqnr= 1; while (!$ifh->eof()) { my $blockdata; $ifh->read($blockdata, $blocksize) or die "error reading: $!\n"; my $hash= calc_hash($blockdata, 1, $randomseed, $seqnr++); my $sigdata= sign_hash($hash); $ofh->write(pack("VVC", length($blockdata), length($sigdata), 1)) or die "write blkhdr: $!\n"; $ofh->write($blockdata) or die "write blk: $!\n"; $ofh->write($sigdata) or die "write sig: $!\n"; } my $hash= calc_hash("", 2, $randomseed, $seqnr++); my $sigdata= sign_hash($hash); $ofh->write(pack("VVC", 0, length($sigdata), 2)) or die "write last blkhdr: $!\n"; $ofh->write($sigdata) or die "write last sig: $!\n"; }