Skip to content

Commit

Permalink
Merge pull request #86 from paragonie/optimize
Browse files Browse the repository at this point in the history
32-bit curve25519 speedup (important for WordPress)
  • Loading branch information
paragonie-scott authored May 9, 2019
2 parents 87125d5 + 82072a7 commit d30e880
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 6 deletions.
149 changes: 145 additions & 4 deletions src/Core32/Int64.php
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,9 @@ public function mask64($hi = 0, $lo = 0)
*/
public function mulInt($int = 0, $size = 0)
{
if (ParagonIE_Sodium_Compat::$fastMult) {
return $this->mulIntFast($int);
}
ParagonIE_Sodium_Core32_Util::declareScalarType($int, 'int', 1);
ParagonIE_Sodium_Core32_Util::declareScalarType($size, 'int', 2);
/** @var int $int */
Expand Down Expand Up @@ -268,11 +271,11 @@ public function mulInt($int = 0, $size = 0)
$a3 &= 0xffff;

$int >>= 1;
$return->limbs[0] = $ret0;
$return->limbs[1] = $ret1;
$return->limbs[2] = $ret2;
$return->limbs[3] = $ret3;
}
$return->limbs[0] = $ret0;
$return->limbs[1] = $ret1;
$return->limbs[2] = $ret2;
$return->limbs[3] = $ret3;
return $return;
}

Expand Down Expand Up @@ -317,6 +320,141 @@ public static function ctSelect(
);
}

/**
* @param array<int, int> $a
* @param array<int, int> $b
* @param int $baseLog2
* @return array<int, int>
*/
public function multiplyLong(array $a, array $b, $baseLog2 = 16)
{
$a_l = count($a);
$b_l = count($b);
$r = array_fill(0, $a_l + $b_l + 1, 0);
$base = 1 << $baseLog2;
for ($i = 0; $i < $a_l; ++$i) {
$a_i = $a[$i];
for ($j = 0; $j < $a_l; ++$j) {
$b_j = $b[$j];
$product = ($a_i * $b_j) + $r[$i + $j];
$carry = ($product >> $baseLog2 & 0xffff);
$r[$i + $j] = ($product - (int) ($carry * $base)) & 0xffff;
$r[$i + $j + 1] += $carry;
}
}
return array_slice($r, 0, 5);
}

/**
* @param int $int
* @return ParagonIE_Sodium_Core32_Int64
*/
public function mulIntFast($int)
{
// Handle negative numbers
$aNeg = ($this->limbs[0] >> 15) & 1;
$bNeg = ($int >> 31) & 1;
$a = array_reverse($this->limbs);
$b = array(
$int & 0xffff,
($int >> 16) & 0xffff,
-$bNeg & 0xffff,
-$bNeg & 0xffff
);
if ($aNeg) {
for ($i = 0; $i < 4; ++$i) {
$a[$i] = ($a[$i] ^ 0xffff) & 0xffff;
}
++$a[0];
}
if ($bNeg) {
for ($i = 0; $i < 4; ++$i) {
$b[$i] = ($b[$i] ^ 0xffff) & 0xffff;
}
++$b[0];
}
// Multiply
$res = $this->multiplyLong($a, $b);

// Re-apply negation to results
if ($aNeg !== $bNeg) {
for ($i = 0; $i < 4; ++$i) {
$res[$i] = (0xffff ^ $res[$i]) & 0xffff;
}
// Handle integer overflow
$c = 1;
for ($i = 0; $i < 4; ++$i) {
$res[$i] += $c;
$c = $res[$i] >> 16;
$res[$i] &= 0xffff;
}
}

// Return our values
$return = new ParagonIE_Sodium_Core32_Int64();
$return->limbs = array(
$res[3] & 0xffff,
$res[2] & 0xffff,
$res[1] & 0xffff,
$res[0] & 0xffff
);
if (count($res) > 4) {
$return->overflow = $res[4] & 0xffff;
}
$return->unsignedInt = $this->unsignedInt;
return $return;
}

/**
* @param ParagonIE_Sodium_Core32_Int64 $right
* @return ParagonIE_Sodium_Core32_Int64
*/
public function mulInt64Fast(ParagonIE_Sodium_Core32_Int64 $right)
{
$aNeg = ($this->limbs[0] >> 15) & 1;
$bNeg = ($right->limbs[0] >> 15) & 1;

$a = array_reverse($this->limbs);
$b = array_reverse($right->limbs);
if ($aNeg) {
for ($i = 0; $i < 4; ++$i) {
$a[$i] = ($a[$i] ^ 0xffff) & 0xffff;
}
++$a[0];
}
if ($bNeg) {
for ($i = 0; $i < 4; ++$i) {
$b[$i] = ($b[$i] ^ 0xffff) & 0xffff;
}
++$b[0];
}
$res = $this->multiplyLong($a, $b);
if ($aNeg !== $bNeg) {
if ($aNeg !== $bNeg) {
for ($i = 0; $i < 4; ++$i) {
$res[$i] = ($res[$i] ^ 0xffff) & 0xffff;
}
$c = 1;
for ($i = 0; $i < 4; ++$i) {
$res[$i] += $c;
$c = $res[$i] >> 16;
$res[$i] &= 0xffff;
}
}
}
$return = new ParagonIE_Sodium_Core32_Int64();
$return->limbs = array(
$res[3] & 0xffff,
$res[2] & 0xffff,
$res[1] & 0xffff,
$res[0] & 0xffff
);
if (count($res) > 4) {
$return->overflow = $res[4];
}
return $return;
}

/**
* @param ParagonIE_Sodium_Core32_Int64 $int
* @param int $size
Expand All @@ -327,6 +465,9 @@ public static function ctSelect(
*/
public function mulInt64(ParagonIE_Sodium_Core32_Int64 $int, $size = 0)
{
if (ParagonIE_Sodium_Compat::$fastMult) {
return $this->mulInt64Fast($int);
}
ParagonIE_Sodium_Core32_Util::declareScalarType($size, 'int', 2);
if (!$size) {
$size = 63;
Expand Down
46 changes: 46 additions & 0 deletions tests/Int64TestTemp.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

/**
* Class Int64Test
*/
class Int64TestTemp extends PHPUnit_Framework_TestCase
{
public function setUp()
{
if (PHP_INT_SIZE === 8) {
$this->markTestSkipped('Only relevant to 32-bit platforms.');
}
ParagonIE_Sodium_Compat::$fastMult = true;
}


/**
* @covers ParagonIE_Sodium_Core32_Int64::mulInt()
* @covers ParagonIE_Sodium_Core32_Int64::mulInt64()
* @throws SodiumException
* @throws TypeError
*/
public function testMult()
{
for ($j = 0; $j < 64; ++$j) {
$baseSmall = random_int(1, 65536);
$base = new ParagonIE_Sodium_Core32_Int64(array(0, 0, 0, $baseSmall));
for ($i = 0; $i < 64; ++$i) {
$value = random_int(1, 65536);
$result = ($baseSmall * $value);
$expected = array(
0,
0,
($result >> 16) & 0xffff,
$result & 0xffff
);
$this->assertSame(
$expected,
$base->mulInt($value)->limbs,
$baseSmall . ' x ' . $value . ' = ' . $result
);
}
}
}

}
1 change: 1 addition & 0 deletions tests/Windows32Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public function setUp()
if (PHP_INT_SIZE !== 4) {
$this->markTestSkipped('64-bit OS');
}
ParagonIE_Sodium_Compat::$fastMult = true;
ParagonIE_Sodium_Compat::$disableFallbackForUnitTests = true;
}

Expand Down
4 changes: 2 additions & 2 deletions tests/unit/Int64Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public function setUp()
if (PHP_INT_SIZE === 8) {
$this->markTestSkipped('Only relevant to 32-bit platforms.');
}
ParagonIE_Sodium_Compat::$fastMult = true;
}

/**
Expand Down Expand Up @@ -195,7 +196,7 @@ public function testMult()

$negOne = new ParagonIE_Sodium_Core32_Int64(array(0xffff, 0xffff, 0xffff, -1 & 0xffff));
$this->assertSame(
array(0xffff, 0xffff, 0xffff, -5 & 0xffff),
array(0xffff, 0xffff, 0xffff, 0xfffb),
$negOne->mulInt(5)->limbs
);
$this->assertSame(
Expand Down Expand Up @@ -226,7 +227,6 @@ public function testMult()
($result >> 16) & 0xffff,
$result & 0xffff
);

$this->assertSame(
$expected,
$base->mulInt($value)->limbs,
Expand Down

0 comments on commit d30e880

Please sign in to comment.