| #! /usr/bin/env perl |
| # Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved. |
| # |
| # Licensed under the Apache License 2.0 (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 |
| |
| # Run the tests specified in bntests.txt, as a check against OpenSSL. |
| use strict; |
| use warnings; |
| use Math::BigInt; |
| |
| my $EXPECTED_FAILURES = 0; |
| my $failures = 0; |
| |
| sub bn |
| { |
| my $x = shift; |
| my ($sign, $hex) = ($x =~ /^([+\-]?)(.*)$/); |
| |
| $hex = '0x' . $hex if $hex !~ /^0x/; |
| return Math::BigInt->from_hex($sign.$hex); |
| } |
| |
| sub evaluate |
| { |
| my $lineno = shift; |
| my %s = @_; |
| |
| if ( defined $s{'Sum'} ) { |
| # Sum = A + B |
| my $sum = bn($s{'Sum'}); |
| my $a = bn($s{'A'}); |
| my $b = bn($s{'B'}); |
| return if $sum == $a + $b; |
| } elsif ( defined $s{'LShift1'} ) { |
| # LShift1 = A * 2 |
| my $lshift1 = bn($s{'LShift1'}); |
| my $a = bn($s{'A'}); |
| return if $lshift1 == $a->bmul(2); |
| } elsif ( defined $s{'LShift'} ) { |
| # LShift = A * 2**N |
| my $lshift = bn($s{'LShift'}); |
| my $a = bn($s{'A'}); |
| my $n = bn($s{'N'}); |
| return if $lshift == $a->blsft($n); |
| } elsif ( defined $s{'RShift'} ) { |
| # RShift = A / 2**N |
| my $rshift = bn($s{'RShift'}); |
| my $a = bn($s{'A'}); |
| my $n = bn($s{'N'}); |
| return if $rshift == $a->brsft($n); |
| } elsif ( defined $s{'Square'} ) { |
| # Square = A * A |
| my $square = bn($s{'Square'}); |
| my $a = bn($s{'A'}); |
| return if $square == $a->bmul($a); |
| } elsif ( defined $s{'Product'} ) { |
| # Product = A * B |
| my $product = bn($s{'Product'}); |
| my $a = bn($s{'A'}); |
| my $b = bn($s{'B'}); |
| return if $product == $a->bmul($b); |
| } elsif ( defined $s{'Quotient'} ) { |
| # Quotient = A / B |
| # Remainder = A - B * Quotient |
| my $quotient = bn($s{'Quotient'}); |
| my $remainder = bn($s{'Remainder'}); |
| my $a = bn($s{'A'}); |
| my $b = bn($s{'B'}); |
| |
| # First the remainder test. |
| $b->bmul($quotient); |
| my $rempassed = $remainder == $a->bsub($b) ? 1 : 0; |
| |
| # Math::BigInt->bdiv() is documented to do floored division, |
| # i.e. 1 / -4 = -1, while OpenSSL BN_div does truncated |
| # division, i.e. 1 / -4 = 0. We need to make the operation |
| # work like OpenSSL's BN_div to be able to verify. |
| $a = bn($s{'A'}); |
| $b = bn($s{'B'}); |
| my $neg = $a->is_neg() ? !$b->is_neg() : $b->is_neg(); |
| $a->babs(); |
| $b->babs(); |
| $a->bdiv($b); |
| $a->bneg() if $neg; |
| return if $rempassed && $quotient == $a; |
| } elsif ( defined $s{'ModMul'} ) { |
| # ModMul = (A * B) mod M |
| my $modmul = bn($s{'ModMul'}); |
| my $a = bn($s{'A'}); |
| my $b = bn($s{'B'}); |
| my $m = bn($s{'M'}); |
| $a->bmul($b); |
| return if $modmul == $a->bmod($m); |
| } elsif ( defined $s{'ModExp'} ) { |
| # ModExp = (A ** E) mod M |
| my $modexp = bn($s{'ModExp'}); |
| my $a = bn($s{'A'}); |
| my $e = bn($s{'E'}); |
| my $m = bn($s{'M'}); |
| return if $modexp == $a->bmodpow($e, $m); |
| } elsif ( defined $s{'Exp'} ) { |
| my $exp = bn($s{'Exp'}); |
| my $a = bn($s{'A'}); |
| my $e = bn($s{'E'}); |
| return if $exp == $a ** $e; |
| } elsif ( defined $s{'ModSqrt'} ) { |
| # (ModSqrt * ModSqrt) mod P = A mod P |
| my $modsqrt = bn($s{'ModSqrt'}); |
| my $a = bn($s{'A'}); |
| my $p = bn($s{'P'}); |
| $modsqrt->bmul($modsqrt); |
| $modsqrt->bmod($p); |
| $a->bmod($p); |
| return if $modsqrt == $a; |
| } else { |
| print "# Unknown test: "; |
| } |
| $failures++; |
| print "# #$failures Test (before line $lineno) failed\n"; |
| foreach ( keys %s ) { |
| print "$_ = $s{$_}\n"; |
| } |
| print "\n"; |
| } |
| |
| my $infile = shift || 'bntests.txt'; |
| die "No such file, $infile" unless -f $infile; |
| open my $IN, $infile || die "Can't read $infile, $!\n"; |
| |
| my %stanza = (); |
| my $l = 0; |
| while ( <$IN> ) { |
| $l++; |
| s|\R$||; |
| next if /^#/; |
| if ( /^$/ ) { |
| if ( keys %stanza ) { |
| evaluate($l, %stanza); |
| %stanza = (); |
| } |
| next; |
| } |
| # Parse 'key = value' |
| if ( ! /\s*([^\s]*)\s*=\s*(.*)\s*/ ) { |
| print "Skipping $_\n"; |
| next; |
| } |
| $stanza{$1} = $2; |
| }; |
| evaluate($l, %stanza) if keys %stanza; |
| die "Got $failures, expected $EXPECTED_FAILURES" |
| if $infile eq 'bntests.txt' and $failures != $EXPECTED_FAILURES; |
| close($IN) |