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