Blame test/bntests.pl

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