diff --git a/composer.json b/composer.json index 5ef6dd8..a64d74a 100644 --- a/composer.json +++ b/composer.json @@ -8,10 +8,10 @@ "email": "cody@phillipsdata.com" }], "autoload": { - "psr-4": {"Phpaes\\": "src/"} + "psr-4": {"PhpAes\\": "src/"} }, "autoload-dev": { - "psr-4": {"Phpaes\\Test\\": "tests/"} + "psr-4": {"PhpAes\\Test\\": "tests/"} }, "require": { "php": ">=5.3" diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..3a70d62 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,23 @@ + + + + + tests/Unit + + + tests/Integration + + + + + + src/ + + + diff --git a/src/Aes.php b/src/Aes.php index 07bafe1..84b8d49 100755 --- a/src/Aes.php +++ b/src/Aes.php @@ -1,676 +1,694 @@ -mode = strtoupper($mode); - $this->iv = $iv; - $this->Nk = strlen($z)/4; - $this->Nr = $this->Nk + self::$Nb + 2; - - if ($this->mode != "ECB" && strlen($this->iv) != 16) { - die("The initialization vector must be 128 bits (or 16 characters) long."); - } - - if ($this->Nk != 4 && $this->Nk != 6 && $this->Nk != 8) { - die("Key is " . ($this->Nk*32) . " bits long. *not* 128, 192, or 256."); - } - - $this->Nr = $this->Nk+self::$Nb+2; - - $this->keyExpansion($z); // places expanded key in w - } - - public function __destruct() { - unset($this->w); - unset($this->s); - } - - /** Encrypts an aribtrary length String. - * @params plaintext string - * @returns ciphertext string - **/ - public function encrypt($x) { - $t = ''; // 16-byte block to hold the temporary input of the cipher - $y = ''; // returned cipher text; - $y_block = $this->iv; // 16-byte block to hold the temporary output of the cipher - $xsize = strlen($x); - - switch($this->mode) { - case "ECB": - // put a 16-byte block into t, ecnrypt it and add it to the result - for ($i=0; $i<$xsize; $i+=16) { - for ($j=0; $j<16; $j++) { - if (($i+$j)<$xsize) { - $t[$j] = $x[$i+$j]; - } else { - $t[$j] = chr(0); - } - } - - $y_block = $this->encryptBlock($t); - $y .= $y_block; - } - break; - case "CBC": - // put a 16-byte block into t, ecnrypt it and add it to the result - for ($i=0; $i<$xsize; $i+=16) { - for ($j=0; $j<16; $j++) { - // XOR this block of plaintext with the initialization vector - $t[$j] = chr(ord(($i+$j)<$xsize ? $x[$i+$j] : chr(0)) ^ ord($y_block[$j])); - } - - $y_block = $this->encryptBlock($t); - $y .= $y_block; - } - break; - case "CFB": - for ($i=0; $i<$xsize; $i+=16) { - // Encrypt the initialization vector/cipher output then XOR with the plaintext - $y_block = $this->encryptBlock($y_block); - - for ($j=0; $j<16; $j++) { - // XOR the cipher output with the plaintext. - $y_block[$j] = chr(ord(($i+$j)<$xsize ? $x[$i+$j] : chr(0)) ^ ord($y_block[$j])); - } - - $y .= $y_block; - } - - break; - case "OFB": - for ($i=0; $i<$xsize; $i+=16) { - // Encrypt the initialization vector/cipher output then XOR with the plaintext - $t = $this->encryptBlock($y_block); - - for ($j=0; $j<16; $j++) { - // XOR the cipher output with the plaintext. - $y_block[$j] = chr(ord(($i+$j)<$xsize ? $x[$i+$j] : chr(0)) ^ ord($t[$j])); - } - - $y .= $y_block; - $y_block = $t; - } - break; - } - return $y; - } - - /** Decrypts an aribtrary length String. - * @params ciphertext string - * @returns plaintext string - **/ - public function decrypt($y) { - $t = array(); // 16-byte block - $x = ''; // returned plain text; - $y_block = $this->iv; - $x_block = ''; - - // put a 16-byte block into t - $ysize = strlen($y); - - switch($this->mode) { - case "ECB": - for ($i=0; $i<$ysize; $i+=16) { - for ($j=0; $j<16; $j++) { - if (($i+$j)<$ysize) { - $t[$j] = $y[$i+$j]; - } else { - $t[$j] = chr(0); - } - } - - $x_block = $this->decryptBlock($t); - $x .= $x_block; - } - break; - case "CBC": - for ($i=0; $i<$ysize; $i+=16) { - for ($j=0; $j<16; $j++) { - if (($i+$j)<$ysize) { - $t[$j] = $y[$i+$j]; - } else { - $t[$j] = chr(0); - } - } - - $x_block = $this->decryptBlock($t); - - // XOR the iv/previous cipher block with this decrypted cipher block - for ($j=0; $j<16; $j++) { - $x_block[$j] = chr(ord($x_block[$j]) ^ ord($y_block[$j])); - } - - $y_block = $t; - $x .= $x_block; - } - break; - case "CFB": - for ($i=0; $i<$ysize; $i+=16) { - // Encrypt the initialization vector/cipher output then XOR with the ciphertext - $x_block = $this->encryptBlock($y_block); - - for ($j=0; $j<16; $j++) { - // XOR the cipher output with the ciphertext. - $x_block[$j] = chr(ord(($i+$j)<$ysize ? $y[$i+$j] : chr(0)) ^ ord($x_block[$j])); - $y_block[$j] = $y[$i+$j]; - } - - $x .= $x_block; - } - break; - case "OFB": - $x = $this->encrypt($y); - break; - } - return rtrim($x, chr(0)); // Remove any buffer residue on return. - } - - /** Encrypts the 16-byte plain text. - * @params 16-byte plaintext string - * @returns 16-byte ciphertext string - **/ - public function encryptBlock($x) { - $y = ''; // 16-byte string - - // place input x into the initial state matrix in column order - for ($i=0; $i<4*self::$Nb; $i++) { - // we want integerger division for the second index - $this->s[$i%4][($i-$i%self::$Nb)/self::$Nb] = ord($x[$i]); - } - - // add round key - $this->addRoundKey(0); - - for ($i=1; $i<$this->Nr; $i++) { - // substitute bytes - $this->subBytes(); - - // shift rows - $this->shiftRows(); - - // mix columns - $this->mixColumns(); - - // add round key - $this->addRoundKey($i); - } - - // substitute bytes - $this->subBytes(); - - // shift rows - $this->shiftRows(); - - // add round key - $this->addRoundKey($i); - - // place state matrix s into y in column order - for ($i=0; $i<4*self::$Nb; $i++) { - $y .= chr($this->s[$i%4][($i-$i%self::$Nb)/self::$Nb]); - } - return $y; - } - - /** Decrypts the 16-byte cipher text. - * @params 16-byte ciphertext string - * @returns 16-byte plaintext string - **/ - public function decryptBlock($y) { - $x = ''; // 16-byte string - - // place input y into the initial state matrix in column order - for ($i=0; $i<4*self::$Nb; $i++) { - $this->s[$i%4][($i-$i%self::$Nb)/self::$Nb] = ord($y[$i]); - } - - // add round key - $this->addRoundKey($this->Nr); - - for ($i=$this->Nr-1; $i>0; $i--) { - // inverse shift rows - $this->invShiftRows(); - - // inverse sub bytes - $this->invSubBytes(); - - // add round key - $this->addRoundKey($i); - - // inverse mix columns - $this->invMixColumns(); - } - - // inverse shift rows - $this->invShiftRows(); - - // inverse sub bytes - $this->invSubBytes(); - - // add round key - $this->addRoundKey($i); - - // place state matrix s into x in column order - for ($i=0; $i<4*self::$Nb; $i++) { - // Used to remove filled null characters. - $x .= chr($this->s[$i%4][($i-$i%self::$Nb)/self::$Nb]); - } - - return $x; - } - - /** makes a big key out of a small one - * @returns void - **/ - private function keyExpansion($z) { - // Rcon is the round constant - static $Rcon = array( - 0x00000000, - 0x01000000, - 0x02000000, - 0x04000000, - 0x08000000, - 0x10000000, - 0x20000000, - 0x40000000, - 0x80000000, - 0x1b000000, - 0x36000000, - 0x6c000000, - 0xd8000000, - 0xab000000, - 0x4d000000, - 0x9a000000, - 0x2f000000 - ); - - $temp = 0; // temporary 32-bit word - - // the first Nk words of w are the cipher key z - for ($i=0; $i<$this->Nk; $i++) { - $this->w[$i] = 0; - // fill an entire word of expanded key w - // by pushing 4 bytes into the w[i] word - $this->w[$i] = ord($z[4*$i]); // add a byte in - $this->w[$i] <<= 8; // make room for the next byte - $this->w[$i] += ord($z[4*$i+1]); - $this->w[$i] <<= 8; - $this->w[$i] += ord($z[4*$i+2]); - $this->w[$i] <<= 8; - $this->w[$i] += ord($z[4*$i+3]); - } - - - for (; $iNr+1); $i++) { - $temp = $this->w[$i-1]; - - if ($i%$this->Nk == 0) { - $temp = $this->subWord($this->rotWord($temp)) ^ $Rcon[$i/$this->Nk]; - } else if ($this->Nk > 6 && $i%$this->Nk == 4) { - $temp = $this->subWord($temp); - } - - $this->w[$i] = $this->w[$i-$this->Nk] ^ $temp; - - self::make32BitWord($this->w[$i]); - } - } - - /** adds the key schedule for a round to a state matrix. - * @returns void - **/ - private function addRoundKey($round) { - $temp = ""; - - for ($i=0; $i<4; $i++) { - for ($j=0; $jw[$round*self::$Nb+$j] >> (3-$i)*8; - // Cast temp from a 32-bit word into an 8-bit byte. - $temp %= 256; - // Can't do unsigned shifts, so we need to make this temp positive - $temp = ($temp < 0 ? (256 + $temp) : $temp); - - $this->s[$i][$j] ^= $temp; // xor temp with the byte at location (i,j) of the state - } - } - } - - /** unmixes each column of a state matrix. - * @returns void - **/ - private function invMixColumns() { - $s0 = $s1 = $s2 = $s3= ''; - - // There are Nb columns - for ($i=0; $is[0][$i]; - $s1 = $this->s[1][$i]; - $s2 = $this->s[2][$i]; - $s3 = $this->s[3][$i]; - - $this->s[0][$i] = $this->mult(0x0e, $s0) - ^ $this->mult(0x0b, $s1) - ^ $this->mult(0x0d, $s2) - ^ $this->mult(0x09, $s3); - $this->s[1][$i] = $this->mult(0x09, $s0) - ^ $this->mult(0x0e, $s1) - ^ $this->mult(0x0b, $s2) - ^ $this->mult(0x0d, $s3); - $this->s[2][$i] = $this->mult(0x0d, $s0) - ^ $this->mult(0x09, $s1) - ^ $this->mult(0x0e, $s2) - ^ $this->mult(0x0b, $s3); - $this->s[3][$i] = $this->mult(0x0b, $s0) - ^ $this->mult(0x0d, $s1) - ^ $this->mult(0x09, $s2) - ^ $this->mult(0x0e, $s3); - } - } - - /** applies an inverse cyclic shift to the last 3 rows of a state matrix. - * @returns void - **/ - private function invShiftRows() { - $temp = array(); - for ($i=1; $i<4; $i++) { - for ($j=0; $js[$i][$j]; - } - for ($j=0; $js[$i][$j] = $temp[$j]; - } - } - } - - /** applies inverse S-Box substitution to each byte of a state matrix. - * @returns void - **/ - private function invSubBytes() { - for ($i=0; $i<4; $i++) { - for ($j=0; $js[$i][$j] = self::$invSBox[$this->s[$i][$j]]; - } - } - } - - /** mixes each column of a state matrix. - * @returns void - **/ - private function mixColumns() { - $s0 = $s1 = $s2 = $s3= ''; - - // There are Nb columns - for ($i=0; $is[0][$i]; - $s1 = $this->s[1][$i]; - $s2 = $this->s[2][$i]; - $s3 = $this->s[3][$i]; - - $this->s[0][$i] = $this->mult(0x02, $s0) - ^ $this->mult(0x03, $s1) - ^ $this->mult(0x01, $s2) - ^ $this->mult(0x01, $s3); - $this->s[1][$i] = $this->mult(0x01, $s0) - ^ $this->mult(0x02, $s1) - ^ $this->mult(0x03, $s2) - ^ $this->mult(0x01, $s3); - $this->s[2][$i] = $this->mult(0x01, $s0) - ^ $this->mult(0x01, $s1) - ^ $this->mult(0x02, $s2) - ^ $this->mult(0x03, $s3); - $this->s[3][$i] = $this->mult(0x03, $s0) - ^ $this->mult(0x01, $s1) - ^ $this->mult(0x01, $s2) - ^ $this->mult(0x02, $s3); - } - } - - /** applies a cyclic shift to the last 3 rows of a state matrix. - * @returns void - **/ - private function shiftRows() { - $temp = array(); - for ($i=1; $i<4; $i++) { - for ($j=0; $js[$i][($j+$i)%self::$Nb]; - } - for ($j=0; $js[$i][$j] = $temp[$j]; - } - } - } - /** applies S-Box substitution to each byte of a state matrix. - * @returns void - **/ - private function subBytes() { - - for ($i=0; $i<4; $i++) { - for ($j=0; $js[$i][$j] = self::$sBox[$this->s[$i][$j]]; - } - } - } - - /** multiplies two polynomials a(x), b(x) in GF(2^8) modulo the irreducible polynomial m(x) = x^8+x^4+x^3+x+1 - * @returns 8-bit value - **/ - private static function mult($a, $b) { - $sum = self::$ltable[$a] + self::$ltable[$b]; - $sum %= 255; - // Get the antilog - $sum = self::$atable[$sum]; - return ($a == 0 ? 0 : ($b == 0 ? 0 : $sum)); - } - - /** applies a cyclic permutation to a 4-byte word. - * @returns 32-bit int - **/ - private static function rotWord($w) { - $temp = $w >> 24; // put the first 8-bits into temp - $w <<= 8; // make room for temp to fill the lower end of the word - self::make32BitWord($w); - // Can't do unsigned shifts, so we need to make this temp positive - $temp = ($temp < 0 ? (256 + $temp) : $temp); - $w += $temp; - - return $w; - } - - /** applies S-box substitution to each byte of a 4-byte word. - * @returns 32-bit int - **/ - private static function subWord($w) { - $temp = 0; - // loop through 4 bytes of a word - for ($i=0; $i<4; $i++) { - $temp = $w >> 24; // put the first 8-bits into temp - // Can't do unsigned shifts, so we need to make this temp positive - $temp = ($temp < 0 ? (256 + $temp) : $temp); - $w <<= 8; // make room for the substituted byte in w; - self::make32BitWord($w); - $w += self::$sBox[$temp]; // add the substituted byte back - } - - self::make32BitWord($w); - - return $w; - } - - /** reduces a 64-bit word to a 32-bit word - * @returns void - **/ - private static function make32BitWord(&$w) { - // Reduce this 64-bit word to 32-bits on 64-bit machines - $w &= 0x00000000FFFFFFFF; - } -} +mode = strtoupper($mode); + $this->iv = $iv; + $this->Nk = strlen($z)/4; + $this->Nr = $this->Nk + self::$Nb + 2; + + if ($this->mode != "ECB" && strlen($this->iv) != 16) { + die('The initialization vector must be 128 bits (or 16 characters) long.'); + } + + if ($this->Nk != 4 && $this->Nk != 6 && $this->Nk != 8) { + die('Key is ' . ($this->Nk*32) . ' bits long. *not* 128, 192, or 256.'); + } + + $this->Nr = $this->Nk+self::$Nb+2; + + $this->keyExpansion($z); // places expanded key in w + } + + public function __destruct() + { + unset($this->w); + unset($this->s); + } + + /** Encrypts an aribtrary length String. + * @params plaintext string + * @returns ciphertext string + **/ + public function encrypt($x) + { + $t = ''; // 16-byte block to hold the temporary input of the cipher + $y = ''; // returned cipher text; + $y_block = $this->iv; // 16-byte block to hold the temporary output of the cipher + $xsize = strlen($x); + + switch ($this->mode) { + case 'ECB': + // put a 16-byte block into t, ecnrypt it and add it to the result + for ($i = 0; $i < $xsize; $i += 16) { + for ($j = 0; $j < 16; $j++) { + if (($i+$j)<$xsize) { + $t[$j] = $x[$i+$j]; + } else { + $t[$j] = chr(0); + } + } + + $y_block = $this->encryptBlock($t); + $y .= $y_block; + } + break; + case 'CBC': + // put a 16-byte block into t, ecnrypt it and add it to the result + for ($i = 0; $i < $xsize; $i += 16) { + for ($j=0; $j<16; $j++) { + // XOR this block of plaintext with the initialization vector + $t[$j] = chr(ord(($i+$j)<$xsize ? $x[$i+$j] : chr(0)) ^ ord($y_block[$j])); + } + + $y_block = $this->encryptBlock($t); + $y .= $y_block; + } + break; + case 'CFB': + for ($i = 0; $i < $xsize; $i += 16) { + // Encrypt the initialization vector/cipher output then XOR with the plaintext + $y_block = $this->encryptBlock($y_block); + + for ($j = 0; $j < 16; $j++) { + // XOR the cipher output with the plaintext. + $y_block[$j] = chr(ord(($i+$j)<$xsize ? $x[$i+$j] : chr(0)) ^ ord($y_block[$j])); + } + + $y .= $y_block; + } + + break; + case 'OFB': + for ($i = 0; $i < $xsize; $i += 16) { + // Encrypt the initialization vector/cipher output then XOR with the plaintext + $t = $this->encryptBlock($y_block); + + for ($j = 0; $j < 16; $j++) { + // XOR the cipher output with the plaintext. + $y_block[$j] = chr(ord(($i+$j)<$xsize ? $x[$i+$j] : chr(0)) ^ ord($t[$j])); + } + + $y .= $y_block; + $y_block = $t; + } + break; + } + return $y; + } + + /** Decrypts an aribtrary length String. + * @params ciphertext string + * @returns plaintext string + **/ + public function decrypt($y) + { + $t = array(); // 16-byte block + $x = ''; // returned plain text; + $y_block = $this->iv; + $x_block = ''; + + // put a 16-byte block into t + $ysize = strlen($y); + + switch ($this->mode) { + case 'ECB': + for ($i=0; $i<$ysize; $i+=16) { + for ($j=0; $j<16; $j++) { + if (($i+$j)<$ysize) { + $t[$j] = $y[$i+$j]; + } else { + $t[$j] = chr(0); + } + } + + $x_block = $this->decryptBlock($t); + $x .= $x_block; + } + break; + case 'CBC': + for ($i = 0; $i < $ysize; $i += 16) { + for ($j = 0; $j < 16; $j++) { + if (($i+$j)<$ysize) { + $t[$j] = $y[$i+$j]; + } else { + $t[$j] = chr(0); + } + } + + $x_block = $this->decryptBlock($t); + + // XOR the iv/previous cipher block with this decrypted cipher block + for ($j = 0; $j < 16; $j++) { + $x_block[$j] = chr(ord($x_block[$j]) ^ ord($y_block[$j])); + } + + $y_block = $t; + $x .= $x_block; + } + break; + case 'CFB': + for ($i = 0; $i < $ysize; $i += 16) { + // Encrypt the initialization vector/cipher output then XOR with the ciphertext + $x_block = $this->encryptBlock($y_block); + + for ($j = 0; $j < 16; $j++) { + // XOR the cipher output with the ciphertext. + $x_block[$j] = chr(ord(($i+$j)<$ysize ? $y[$i+$j] : chr(0)) ^ ord($x_block[$j])); + $y_block[$j] = $y[$i+$j]; + } + + $x .= $x_block; + } + break; + case 'OFB': + $x = $this->encrypt($y); + break; + } + return rtrim($x, chr(0)); // Remove any buffer residue on return. + } + + /** Encrypts the 16-byte plain text. + * @params 16-byte plaintext string + * @returns 16-byte ciphertext string + **/ + public function encryptBlock($x) + { + $y = ''; // 16-byte string + + // place input x into the initial state matrix in column order + for ($i = 0; $i <4*self::$Nb; $i++) { + // we want integerger division for the second index + $this->s[$i%4][($i-$i%self::$Nb)/self::$Nb] = ord($x[$i]); + } + + // add round key + $this->addRoundKey(0); + + for ($i = 1; $i < $this->Nr; $i++) { + // substitute bytes + $this->subBytes(); + + // shift rows + $this->shiftRows(); + + // mix columns + $this->mixColumns(); + + // add round key + $this->addRoundKey($i); + } + + // substitute bytes + $this->subBytes(); + + // shift rows + $this->shiftRows(); + + // add round key + $this->addRoundKey($i); + + // place state matrix s into y in column order + for ($i = 0; $i < 4*self::$Nb; $i++) { + $y .= chr($this->s[$i%4][($i-$i%self::$Nb)/self::$Nb]); + } + return $y; + } + + /** Decrypts the 16-byte cipher text. + * @params 16-byte ciphertext string + * @returns 16-byte plaintext string + **/ + public function decryptBlock($y) + { + $x = ''; // 16-byte string + + // place input y into the initial state matrix in column order + for ($i = 0; $i < 4*self::$Nb; $i++) { + $this->s[$i%4][($i-$i%self::$Nb)/self::$Nb] = ord($y[$i]); + } + + // add round key + $this->addRoundKey($this->Nr); + + for ($i = $this->Nr-1; $i > 0; $i--) { + // inverse shift rows + $this->invShiftRows(); + + // inverse sub bytes + $this->invSubBytes(); + + // add round key + $this->addRoundKey($i); + + // inverse mix columns + $this->invMixColumns(); + } + + // inverse shift rows + $this->invShiftRows(); + + // inverse sub bytes + $this->invSubBytes(); + + // add round key + $this->addRoundKey($i); + + // place state matrix s into x in column order + for ($i = 0; $i < 4*self::$Nb; $i++) { + // Used to remove filled null characters. + $x .= chr($this->s[$i%4][($i-$i%self::$Nb)/self::$Nb]); + } + + return $x; + } + + /** makes a big key out of a small one + * @returns void + **/ + private function keyExpansion($z) + { + // Rcon is the round constant + static $Rcon = array( + 0x00000000, + 0x01000000, + 0x02000000, + 0x04000000, + 0x08000000, + 0x10000000, + 0x20000000, + 0x40000000, + 0x80000000, + 0x1b000000, + 0x36000000, + 0x6c000000, + 0xd8000000, + 0xab000000, + 0x4d000000, + 0x9a000000, + 0x2f000000 + ); + + $temp = 0; // temporary 32-bit word + + // the first Nk words of w are the cipher key z + for ($i = 0; $i < $this->Nk; $i++) { + $this->w[$i] = 0; + // fill an entire word of expanded key w + // by pushing 4 bytes into the w[i] word + $this->w[$i] = ord($z[4*$i]); // add a byte in + $this->w[$i] <<= 8; // make room for the next byte + $this->w[$i] += ord($z[4*$i+1]); + $this->w[$i] <<= 8; + $this->w[$i] += ord($z[4*$i+2]); + $this->w[$i] <<= 8; + $this->w[$i] += ord($z[4*$i+3]); + } + + + for (; $i < self::$Nb*($this->Nr+1); $i++) { + $temp = $this->w[$i-1]; + + if ($i%$this->Nk == 0) { + $temp = $this->subWord($this->rotWord($temp)) ^ $Rcon[$i/$this->Nk]; + } elseif ($this->Nk > 6 && $i%$this->Nk == 4) { + $temp = $this->subWord($temp); + } + + $this->w[$i] = $this->w[$i-$this->Nk] ^ $temp; + + self::make32BitWord($this->w[$i]); + } + } + + /** adds the key schedule for a round to a state matrix. + * @returns void + **/ + private function addRoundKey($round) + { + $temp = ''; + + for ($i = 0; $i < 4; $i++) { + for ($j = 0; $j < self::$Nb; $j++) { + // place the i-th byte of the j-th word from expanded key w into temp + $temp = $this->w[$round*self::$Nb+$j] >> (3-$i)*8; + // Cast temp from a 32-bit word into an 8-bit byte. + $temp %= 256; + // Can't do unsigned shifts, so we need to make this temp positive + $temp = ($temp < 0 ? (256 + $temp) : $temp); + + $this->s[$i][$j] ^= $temp; // xor temp with the byte at location (i,j) of the state + } + } + } + + /** unmixes each column of a state matrix. + * @returns void + **/ + private function invMixColumns() + { + $s0 = $s1 = $s2 = $s3= ''; + + // There are Nb columns + for ($i = 0; $i < self::$Nb; $i++) { + $s0 = $this->s[0][$i]; + $s1 = $this->s[1][$i]; + $s2 = $this->s[2][$i]; + $s3 = $this->s[3][$i]; + + $this->s[0][$i] = $this->mult(0x0e, $s0) + ^ $this->mult(0x0b, $s1) + ^ $this->mult(0x0d, $s2) + ^ $this->mult(0x09, $s3); + $this->s[1][$i] = $this->mult(0x09, $s0) + ^ $this->mult(0x0e, $s1) + ^ $this->mult(0x0b, $s2) + ^ $this->mult(0x0d, $s3); + $this->s[2][$i] = $this->mult(0x0d, $s0) + ^ $this->mult(0x09, $s1) + ^ $this->mult(0x0e, $s2) + ^ $this->mult(0x0b, $s3); + $this->s[3][$i] = $this->mult(0x0b, $s0) + ^ $this->mult(0x0d, $s1) + ^ $this->mult(0x09, $s2) + ^ $this->mult(0x0e, $s3); + } + } + + /** applies an inverse cyclic shift to the last 3 rows of a state matrix. + * @returns void + **/ + private function invShiftRows() + { + $temp = array(); + for ($i = 1; $i < 4; $i++) { + for ($j = 0; $j < self::$Nb; $j++) { + $temp[($i+$j)%self::$Nb] = $this->s[$i][$j]; + } + for ($j = 0; $j < self::$Nb; $j++) { + $this->s[$i][$j] = $temp[$j]; + } + } + } + + /** applies inverse S-Box substitution to each byte of a state matrix. + * @returns void + **/ + private function invSubBytes() + { + for ($i = 0; $i < 4; $i++) { + for ($j = 0; $j < self::$Nb; $j++) { + $this->s[$i][$j] = self::$invSBox[$this->s[$i][$j]]; + } + } + } + + /** mixes each column of a state matrix. + * @returns void + **/ + private function mixColumns() + { + $s0 = $s1 = $s2 = $s3= ''; + + // There are Nb columns + for ($i = 0; $i < self::$Nb; $i++) { + $s0 = $this->s[0][$i]; + $s1 = $this->s[1][$i]; + $s2 = $this->s[2][$i]; + $s3 = $this->s[3][$i]; + + $this->s[0][$i] = $this->mult(0x02, $s0) + ^ $this->mult(0x03, $s1) + ^ $this->mult(0x01, $s2) + ^ $this->mult(0x01, $s3); + $this->s[1][$i] = $this->mult(0x01, $s0) + ^ $this->mult(0x02, $s1) + ^ $this->mult(0x03, $s2) + ^ $this->mult(0x01, $s3); + $this->s[2][$i] = $this->mult(0x01, $s0) + ^ $this->mult(0x01, $s1) + ^ $this->mult(0x02, $s2) + ^ $this->mult(0x03, $s3); + $this->s[3][$i] = $this->mult(0x03, $s0) + ^ $this->mult(0x01, $s1) + ^ $this->mult(0x01, $s2) + ^ $this->mult(0x02, $s3); + } + } + + /** applies a cyclic shift to the last 3 rows of a state matrix. + * @returns void + **/ + private function shiftRows() + { + $temp = array(); + for ($i = 1; $i < 4; $i++) { + for ($j = 0; $j < self::$Nb; $j++) { + $temp[$j] = $this->s[$i][($j+$i)%self::$Nb]; + } + for ($j = 0; $j < self::$Nb; $j++) { + $this->s[$i][$j] = $temp[$j]; + } + } + } + /** applies S-Box substitution to each byte of a state matrix. + * @returns void + **/ + private function subBytes() + { + + for ($i = 0; $i < 4; $i++) { + for ($j = 0; $j < self::$Nb; $j++) { + $this->s[$i][$j] = self::$sBox[$this->s[$i][$j]]; + } + } + } + + /** multiplies two polynomials a(x), b(x) in GF(2^8) modulo the irreducible polynomial m(x) = x^8+x^4+x^3+x+1 + * @returns 8-bit value + **/ + private static function mult($a, $b) + { + $sum = self::$ltable[$a] + self::$ltable[$b]; + $sum %= 255; + // Get the antilog + $sum = self::$atable[$sum]; + return ($a == 0 ? 0 : ($b == 0 ? 0 : $sum)); + } + + /** applies a cyclic permutation to a 4-byte word. + * @returns 32-bit int + **/ + private static function rotWord($w) + { + $temp = $w >> 24; // put the first 8-bits into temp + $w <<= 8; // make room for temp to fill the lower end of the word + self::make32BitWord($w); + // Can't do unsigned shifts, so we need to make this temp positive + $temp += $temp < 0 ? 256 : 0; + $w += $temp; + + return $w; + } + + /** applies S-box substitution to each byte of a 4-byte word. + * @returns 32-bit int + **/ + private static function subWord($w) + { + $temp = 0; + // loop through 4 bytes of a word + for ($i = 0; $i < 4; $i++) { + $temp = $w >> 24; // put the first 8-bits into temp + // Can't do unsigned shifts, so we need to make this temp positive + $temp += $temp < 0 ? 256 : 0; + $w <<= 8; // make room for the substituted byte in w; + self::make32BitWord($w); + $w += self::$sBox[$temp]; // add the substituted byte back + } + + self::make32BitWord($w); + + return $w; + } + + /** reduces a 64-bit word to a 32-bit word + * @returns void + **/ + private static function make32BitWord(&$w) + { + // Reduce this 64-bit word to 32-bits on 64-bit machines + $w &= 0x00000000FFFFFFFF; + } +} diff --git a/tests/Integration/AesTest.php b/tests/Integration/AesTest.php new file mode 100755 index 0000000..1283ba3 --- /dev/null +++ b/tests/Integration/AesTest.php @@ -0,0 +1,63 @@ +encrypt($input); + $this->assertNotEquals($cipherText, $input); + + $result = $aes->decrypt($cipherText); + $this->assertEquals($result, $input); + } + + /** + * Data provider + * + * @return array + */ + public function cipherProvider() + { + $iv = '1234567890abcdef'; + + $keys = array( + 'abcdefgh01234567', + 'abcdefghijkl012345678901', + 'abcdefghijuklmno0123456789012345' + ); + $modes = array('ECB', 'CBC', 'CFB', 'OFB'); + + $input = array(file_get_contents('./fixtures/example.txt'), 'hello world!', ''); + + $params = array(); + foreach ($modes as $mode) { + foreach ($keys as $key) { + foreach ($input as $data) { + $params[] = array($key, $mode, $iv, $data); + } + } + } + return $params; + } +} diff --git a/tests/Integration/aes_demo.php b/tests/Integration/aes_demo.php deleted file mode 100755 index 4e8c4ca..0000000 --- a/tests/Integration/aes_demo.php +++ /dev/null @@ -1,38 +0,0 @@ -encrypt($data); - $result = $aes->decrypt($cipherText); - echo "\n\nPlain-Text:\n" . $result . "\n"; - echo "Cipher-Text (base64): " - . chunk_split(base64_encode($cipherText)) . "\n"; - $end = microtime(true); - - echo "\n\nExecution time: " . ($end - $start) . "\n"; - - if ($result !== $data) { - fwrite(STDERR, "[ERROR] Unexpected output\n"); - exit(1); - } - } - } -} diff --git a/example.txt b/tests/Integration/fixtures/example.txt similarity index 100% rename from example.txt rename to tests/Integration/fixtures/example.txt