Skip to content

Commit

Permalink
Merge pull request #170 from paragonie/pre-v1.22-fixes
Browse files Browse the repository at this point in the history
Pre v1.21 fixes
  • Loading branch information
paragonie-security authored Apr 19, 2024
2 parents 3fa914e + eef0ebe commit 3daf997
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 22 deletions.
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,10 @@ insightful technical information you may find helpful.
* `crypto_sign()`
* `crypto_sign_open()`
* PECL Libsodium Features
* `crypto_aead_aegis128l_encrypt()`
* `crypto_aead_aegis128l_decrypt()`
* `crypto_aead_aegis256_encrypt()`
* `crypto_aead_aegis256_decrypt()`
* `crypto_aead_aes256gcm_encrypt()`
* `crypto_aead_aes256gcm_decrypt()`
* `crypto_aead_chacha20poly1305_encrypt()`
Expand Down Expand Up @@ -343,13 +347,13 @@ insightful technical information you may find helpful.

### Features Excluded from this Polyfill

* `\Sodium\memzero()` - Although we expose this API endpoint, we can't reliably
* `sodium_memzero()` - Although we expose this API endpoint, we can't reliably
zero buffers from PHP.

If you have the PHP extension installed, sodium_compat
will use the native implementation to zero out the string provided. Otherwise
it will throw a `SodiumException`.
* `\Sodium\crypto_pwhash()` - It's not feasible to polyfill scrypt or Argon2
* `sodium_crypto_pwhash()` - It's not feasible to polyfill scrypt or Argon2
into PHP and get reasonable performance. Users would feel motivated to select
parameters that downgrade security to avoid denial of service (DoS) attacks.

Expand All @@ -361,6 +365,8 @@ insightful technical information you may find helpful.
To detect support for Argon2i at runtime, use
`ParagonIE_Sodium_Compat::crypto_pwhash_is_available()`, which returns a
boolean value (`TRUE` or `FALSE`).
* Libsodium's HKDF API (`crypto_kdf_hkdf_*()`) is not included because PHP has
its own [HMAC features](https://php.met/hash_hmac) amd it was not deemed necessary.

### PHPCompatibility Ruleset

Expand Down
33 changes: 21 additions & 12 deletions src/Core/AEGIS/State128L.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
<?php

if (class_exists('ParagonIE_Sodium_Core_AEGIS_State128L', false)) {
return;
}

if (!defined('SODIUM_COMPAT_AEGIS_C0')) {
define('SODIUM_COMPAT_AEGIS_C0', "\x00\x01\x01\x02\x03\x05\x08\x0d\x15\x22\x37\x59\x90\xe9\x79\x62");
}
Expand Down Expand Up @@ -237,20 +242,24 @@ public function update($m0, $m1)
S'6 = AESRound(S5, S6)
S'7 = AESRound(S6, S7)
*/
$s_0 = ParagonIE_Sodium_Core_AES::aesRound(
$this->state[7],
$this->state[0] ^ $m0
list($s_0, $s_1) = ParagonIE_Sodium_Core_AES::doubleRound(
$this->state[7], $this->state[0] ^ $m0,
$this->state[0], $this->state[1]
);

list($s_2, $s_3) = ParagonIE_Sodium_Core_AES::doubleRound(
$this->state[1], $this->state[2],
$this->state[2], $this->state[3]
);

list($s_4, $s_5) = ParagonIE_Sodium_Core_AES::doubleRound(
$this->state[3], $this->state[4] ^ $m1,
$this->state[4], $this->state[5]
);
$s_1 = ParagonIE_Sodium_Core_AES::aesRound($this->state[0], $this->state[1]);
$s_2 = ParagonIE_Sodium_Core_AES::aesRound($this->state[1], $this->state[2]);
$s_3 = ParagonIE_Sodium_Core_AES::aesRound($this->state[2], $this->state[3]);
$s_4 = ParagonIE_Sodium_Core_AES::aesRound(
$this->state[3],
$this->state[4] ^ $m1
list($s_6, $s_7) = ParagonIE_Sodium_Core_AES::doubleRound(
$this->state[5], $this->state[6],
$this->state[6], $this->state[7]
);
$s_5 = ParagonIE_Sodium_Core_AES::aesRound($this->state[4], $this->state[5]);
$s_6 = ParagonIE_Sodium_Core_AES::aesRound($this->state[5], $this->state[6]);
$s_7 = ParagonIE_Sodium_Core_AES::aesRound($this->state[6], $this->state[7]);

/*
S0 = S'0
Expand Down
24 changes: 16 additions & 8 deletions src/Core/AEGIS/State256.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<?php

if (class_exists('ParagonIE_Sodium_Core_AEGIS_State256', false)) {
return;
}

if (!defined('SODIUM_COMPAT_AEGIS_C0')) {
define('SODIUM_COMPAT_AEGIS_C0', "\x00\x01\x01\x02\x03\x05\x08\x0d\x15\x22\x37\x59\x90\xe9\x79\x62");
}
Expand Down Expand Up @@ -203,15 +207,19 @@ public function update($m)
S'4 = AESRound(S3, S4)
S'5 = AESRound(S4, S5)
*/
$s_0 = ParagonIE_Sodium_Core_AES::aesRound(
$this->state[5],
$this->state[0] ^ $m
list($s_0, $s_1) = ParagonIE_Sodium_Core_AES::doubleRound(
$this->state[5],$this->state[0] ^ $m,
$this->state[0], $this->state[1]
);

list($s_2, $s_3) = ParagonIE_Sodium_Core_AES::doubleRound(
$this->state[1], $this->state[2],
$this->state[2], $this->state[3]
);
list($s_4, $s_5) = ParagonIE_Sodium_Core_AES::doubleRound(
$this->state[3], $this->state[4],
$this->state[4], $this->state[5]
);
$s_1 = ParagonIE_Sodium_Core_AES::aesRound($this->state[0], $this->state[1]);
$s_2 = ParagonIE_Sodium_Core_AES::aesRound($this->state[1], $this->state[2]);
$s_3 = ParagonIE_Sodium_Core_AES::aesRound($this->state[2], $this->state[3]);
$s_4 = ParagonIE_Sodium_Core_AES::aesRound($this->state[3], $this->state[4]);
$s_5 = ParagonIE_Sodium_Core_AES::aesRound($this->state[4], $this->state[5]);

/*
S0 = S'0
Expand Down
54 changes: 54 additions & 0 deletions src/Core/AES.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<?php

if (class_exists('ParagonIE_Sodium_Core_AES', false)) {
return;
}

/**
* Bitsliced implementation of the AES block cipher.
*
Expand Down Expand Up @@ -441,6 +445,56 @@ public static function aesRound($x, $y)
self::store32_le($q[6]);
}

/**
* Process two AES blocks in one shot.
*
* @param string $b0 First AES block
* @param string $rk0 First round key
* @param string $b1 Second AES block
* @param string $rk1 Second round key
* @return string[]
*/
public static function doubleRound($b0, $rk0, $b1, $rk1)
{
$q = ParagonIE_Sodium_Core_AES_Block::init();
// First block
$q[0] = self::load_4(self::substr($b0, 0, 4));
$q[2] = self::load_4(self::substr($b0, 4, 4));
$q[4] = self::load_4(self::substr($b0, 8, 4));
$q[6] = self::load_4(self::substr($b0, 12, 4));
// Second block
$q[1] = self::load_4(self::substr($b1, 0, 4));
$q[3] = self::load_4(self::substr($b1, 4, 4));
$q[5] = self::load_4(self::substr($b1, 8, 4));
$q[7] = self::load_4(self::substr($b1, 12, 4));;

$rk = ParagonIE_Sodium_Core_AES_Block::init();
// First round key
$rk[0] = self::load_4(self::substr($rk0, 0, 4));
$rk[2] = self::load_4(self::substr($rk0, 4, 4));
$rk[4] = self::load_4(self::substr($rk0, 8, 4));
$rk[6] = self::load_4(self::substr($rk0, 12, 4));
// Second round key
$rk[1] = self::load_4(self::substr($rk1, 0, 4));
$rk[3] = self::load_4(self::substr($rk1, 4, 4));
$rk[5] = self::load_4(self::substr($rk1, 8, 4));
$rk[7] = self::load_4(self::substr($rk1, 12, 4));

$q->orthogonalize();
self::sbox($q);
$q->shiftRows();
$q->mixColumns();
$q->orthogonalize();
// add round key without key schedule:
for ($i = 0; $i < 8; ++$i) {
$q[$i] ^= $rk[$i];
}
return array(
self::store32_le($q[0]) . self::store32_le($q[2]) . self::store32_le($q[4]) . self::store32_le($q[6]),
self::store32_le($q[1]) . self::store32_le($q[3]) . self::store32_le($q[5]) . self::store32_le($q[7]),
);
}

/**
* @param ParagonIE_Sodium_Core_AES_Expanded $skey
* @param ParagonIE_Sodium_Core_AES_Block $q
Expand Down
4 changes: 4 additions & 0 deletions src/Core/AES/Expanded.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<?php

if (class_exists('ParagonIE_Sodium_Core_AES_Expanded', false)) {
return;
}

/**
* @internal This should only be used by sodium_compat
*/
Expand Down
4 changes: 4 additions & 0 deletions src/Core/AES/KeySchedule.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<?php

if (class_exists('ParagonIE_Sodium_Core_AES_KeySchedule', false)) {
return;
}

/**
* @internal This should only be used by sodium_compat
*/
Expand Down
30 changes: 30 additions & 0 deletions tests/unit/AESTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,36 @@ public function testAesRound()
);
}

public function testAesDoubleRound()
{
$in = ParagonIE_Sodium_Core_Util::hex2bin('000102030405060708090a0b0c0d0e0f');
$rk = ParagonIE_Sodium_Core_Util::hex2bin('101112131415161718191a1b1c1d1e1f');
$this->assertSame(
'7a7b4e5638782546a8c0477a3b813f437a7b4e5638782546a8c0477a3b813f43',
ParagonIE_Sodium_Core_Util::bin2hex(
implode('', ParagonIE_Sodium_Core_AES::doubleRound($in, $rk, $in, $rk))
)
);

// Let's randomize this to test equivalence.
$in0 = random_bytes(16);
$in1 = random_bytes(16);
$rk0 = random_bytes(16);
$rk1 = random_bytes(16);

$c0 = ParagonIE_Sodium_Core_AES::aesRound($in0, $rk0);
$c1 = ParagonIE_Sodium_Core_AES::aesRound($in1, $rk1);
list($c2, $c3) = ParagonIE_Sodium_Core_AES::doubleRound($in0, $rk0, $in1, $rk1);
$this->assertSame(
ParagonIE_Sodium_Core_Util::bin2hex($c0),
ParagonIE_Sodium_Core_Util::bin2hex($c2)
);
$this->assertSame(
ParagonIE_Sodium_Core_Util::bin2hex($c1),
ParagonIE_Sodium_Core_Util::bin2hex($c3)
);
}

/**
* @dataProvider aes128ecbProvider
* @covers ParagonIE_Sodium_Core_AES::encryptBlockECB
Expand Down

0 comments on commit 3daf997

Please sign in to comment.