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