Rich Salz | 8d1ebff | 2016-11-28 12:26:05 -0500 | [diff] [blame] | 1 | #! /usr/bin/env perl |
| 2 | # Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved. |
| 3 | # |
| 4 | # Licensed under the OpenSSL license (the "License"). You may not use |
| 5 | # this file except in compliance with the License. You can obtain a copy |
| 6 | # in the file LICENSE in the source distribution or at |
| 7 | # https://www.openssl.org/source/license.html |
| 8 | |
| 9 | # Run the tests specified in bntests.txt, as a check against OpenSSL. |
| 10 | use strict; |
| 11 | use warnings; |
| 12 | use Math::BigInt; |
| 13 | |
| 14 | my $EXPECTED_FAILURES = 0; |
| 15 | my $failures = 0; |
| 16 | |
| 17 | sub bn |
| 18 | { |
| 19 | my $x = shift; |
| 20 | my ($sign, $hex) = ($x =~ /^([+\-]?)(.*)$/); |
| 21 | |
| 22 | $hex = '0x' . $hex if $hex !~ /^0x/; |
| 23 | return Math::BigInt->from_hex($sign.$hex); |
| 24 | } |
| 25 | |
| 26 | sub evaluate |
| 27 | { |
| 28 | my $lineno = shift; |
| 29 | my %s = @_; |
| 30 | |
| 31 | if ( defined $s{'Sum'} ) { |
| 32 | # Sum = A + B |
| 33 | my $sum = bn($s{'Sum'}); |
| 34 | my $a = bn($s{'A'}); |
| 35 | my $b = bn($s{'B'}); |
| 36 | return if $sum == $a + $b; |
| 37 | } elsif ( defined $s{'LShift1'} ) { |
| 38 | # LShift1 = A * 2 |
| 39 | my $lshift1 = bn($s{'LShift1'}); |
| 40 | my $a = bn($s{'A'}); |
| 41 | return if $lshift1 == $a->bmul(2); |
| 42 | } elsif ( defined $s{'LShift'} ) { |
| 43 | # LShift = A * 2**N |
| 44 | my $lshift = bn($s{'LShift'}); |
| 45 | my $a = bn($s{'A'}); |
| 46 | my $n = bn($s{'N'}); |
| 47 | return if $lshift == $a->blsft($n); |
| 48 | } elsif ( defined $s{'RShift'} ) { |
| 49 | # RShift = A / 2**N |
| 50 | my $rshift = bn($s{'RShift'}); |
| 51 | my $a = bn($s{'A'}); |
| 52 | my $n = bn($s{'N'}); |
| 53 | return if $rshift == $a->brsft($n); |
| 54 | } elsif ( defined $s{'Square'} ) { |
| 55 | # Square = A * A |
| 56 | my $square = bn($s{'Square'}); |
| 57 | my $a = bn($s{'A'}); |
| 58 | return if $square == $a->bmul($a); |
| 59 | } elsif ( defined $s{'Product'} ) { |
| 60 | # Product = A * B |
| 61 | my $product = bn($s{'Product'}); |
| 62 | my $a = bn($s{'A'}); |
| 63 | my $b = bn($s{'B'}); |
| 64 | return if $product == $a->bmul($b); |
| 65 | } elsif ( defined $s{'Quotient'} ) { |
| 66 | # Quotient = A / B |
| 67 | # Remainder = A - B * Quotient |
| 68 | my $quotient = bn($s{'Quotient'}); |
| 69 | my $remainder = bn($s{'Remainder'}); |
| 70 | my $a = bn($s{'A'}); |
| 71 | my $b = bn($s{'B'}); |
| 72 | |
| 73 | # First the remainder test. |
| 74 | $b->bmul($quotient); |
| 75 | my $rempassed = $remainder == $a->bsub($b) ? 1 : 0; |
| 76 | |
| 77 | # Math::BigInt->bdiv() is documented to do floored division, |
| 78 | # i.e. 1 / -4 = -1, while OpenSSL BN_div does truncated |
| 79 | # division, i.e. 1 / -4 = 0. We need to make the operation |
| 80 | # work like OpenSSL's BN_div to be able to verify. |
| 81 | $a = bn($s{'A'}); |
| 82 | $b = bn($s{'B'}); |
| 83 | my $neg = $a->is_neg() ? !$b->is_neg() : $b->is_neg(); |
| 84 | $a->babs(); |
| 85 | $b->babs(); |
| 86 | $a->bdiv($b); |
| 87 | $a->bneg() if $neg; |
| 88 | return if $rempassed && $quotient == $a; |
| 89 | } elsif ( defined $s{'ModMul'} ) { |
| 90 | # ModMul = (A * B) mod M |
| 91 | my $modmul = bn($s{'ModMul'}); |
| 92 | my $a = bn($s{'A'}); |
| 93 | my $b = bn($s{'B'}); |
| 94 | my $m = bn($s{'M'}); |
| 95 | $a->bmul($b); |
| 96 | return if $modmul == $a->bmod($m); |
| 97 | } elsif ( defined $s{'ModExp'} ) { |
| 98 | # ModExp = (A ** E) mod M |
| 99 | my $modexp = bn($s{'ModExp'}); |
| 100 | my $a = bn($s{'A'}); |
| 101 | my $e = bn($s{'E'}); |
| 102 | my $m = bn($s{'M'}); |
| 103 | return if $modexp == $a->bmodpow($e, $m); |
| 104 | } elsif ( defined $s{'Exp'} ) { |
| 105 | my $exp = bn($s{'Exp'}); |
| 106 | my $a = bn($s{'A'}); |
| 107 | my $e = bn($s{'E'}); |
| 108 | return if $exp == $a ** $e; |
| 109 | } elsif ( defined $s{'ModSqrt'} ) { |
| 110 | # (ModSqrt * ModSqrt) mod P = A mod P |
| 111 | my $modsqrt = bn($s{'ModSqrt'}); |
| 112 | my $a = bn($s{'A'}); |
| 113 | my $p = bn($s{'P'}); |
| 114 | $modsqrt->bmul($modsqrt); |
| 115 | $modsqrt->bmod($p); |
| 116 | $a->bmod($p); |
| 117 | return if $modsqrt == $a; |
| 118 | } else { |
| 119 | print "# Unknown test: "; |
| 120 | } |
| 121 | $failures++; |
| 122 | print "# #$failures Test (before line $lineno) failed\n"; |
| 123 | foreach ( keys %s ) { |
| 124 | print "$_ = $s{$_}\n"; |
| 125 | } |
| 126 | print "\n"; |
| 127 | } |
| 128 | |
| 129 | my $infile = shift || 'bntests.txt'; |
| 130 | die "No such file, $infile" unless -f $infile; |
| 131 | open my $IN, $infile || die "Can't read $infile, $!\n"; |
| 132 | |
| 133 | my %stanza = (); |
| 134 | my $l = 0; |
| 135 | while ( <$IN> ) { |
| 136 | $l++; |
| 137 | s|\R$||; |
| 138 | next if /^#/; |
| 139 | if ( /^$/ ) { |
| 140 | if ( keys %stanza ) { |
| 141 | evaluate($l, %stanza); |
| 142 | %stanza = (); |
| 143 | } |
| 144 | next; |
| 145 | } |
| 146 | # Parse 'key = value' |
| 147 | if ( ! /\s*([^\s]*)\s*=\s*(.*)\s*/ ) { |
| 148 | print "Skipping $_\n"; |
| 149 | next; |
| 150 | } |
| 151 | $stanza{$1} = $2; |
| 152 | }; |
| 153 | evaluate($l, %stanza) if keys %stanza; |
| 154 | die "Got $failures, expected $EXPECTED_FAILURES" |
| 155 | if $infile eq 'bntests.txt' and $failures != $EXPECTED_FAILURES; |
| 156 | close($IN) |