Gmac.php 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. <?php
  2. namespace Aws\Crypto\Polyfill;
  3. /**
  4. * Class Gmac
  5. *
  6. * @package Aws\Crypto\Polyfill
  7. */
  8. class Gmac
  9. {
  10. use NeedsTrait;
  11. const BLOCK_SIZE = 16;
  12. /** @var ByteArray $buf */
  13. protected $buf;
  14. /** @var int $bufLength */
  15. protected $bufLength = 0;
  16. /** @var ByteArray $h */
  17. protected $h;
  18. /** @var ByteArray $hf */
  19. protected $hf;
  20. /** @var Key $key */
  21. protected $key;
  22. /** @var ByteArray $x */
  23. protected $x;
  24. /**
  25. * Gmac constructor.
  26. *
  27. * @param Key $aesKey
  28. * @param string $nonce
  29. * @param int $keySize
  30. */
  31. public function __construct(Key $aesKey, $nonce, $keySize = 256)
  32. {
  33. $this->buf = new ByteArray(16);
  34. $this->h = new ByteArray(
  35. \openssl_encrypt(
  36. \str_repeat("\0", 16),
  37. "aes-{$keySize}-ecb",
  38. $aesKey->get(),
  39. OPENSSL_RAW_DATA | OPENSSL_NO_PADDING
  40. )
  41. );
  42. $this->key = $aesKey;
  43. $this->x = new ByteArray(16);
  44. $this->hf = new ByteArray(
  45. \openssl_encrypt(
  46. $nonce,
  47. "aes-{$keySize}-ecb",
  48. $aesKey->get(),
  49. OPENSSL_RAW_DATA | OPENSSL_NO_PADDING
  50. )
  51. );
  52. }
  53. /**
  54. * Update the object with some data.
  55. *
  56. * This method mutates this Gmac object.
  57. *
  58. * @param ByteArray $blocks
  59. * @return self
  60. */
  61. public function update(ByteArray $blocks)
  62. {
  63. if (($blocks->count() + $this->bufLength) < self::BLOCK_SIZE) {
  64. // Write to internal buffer until we reach enough to write.
  65. $this->buf->set($blocks, $this->bufLength);
  66. $this->bufLength += $blocks->count();
  67. return $this;
  68. }
  69. // Process internal buffer first.
  70. if ($this->bufLength > 0) {
  71. // 0 <= state.buf_len < BLOCK_SIZE is an invariant
  72. $tmp = new ByteArray(self::BLOCK_SIZE);
  73. $tmp->set($this->buf->slice(0, $this->bufLength));
  74. $remainingBlockLength = self::BLOCK_SIZE - $this->bufLength;
  75. $tmp->set($blocks->slice(0, $remainingBlockLength), $this->bufLength);
  76. $blocks = $blocks->slice($remainingBlockLength);
  77. $this->bufLength = 0;
  78. $this->x = $this->blockMultiply($this->x->exclusiveOr($tmp), $this->h);
  79. }
  80. // Process full blocks.
  81. $numBlocks = $blocks->count() >> 4;
  82. for ($i = 0; $i < $numBlocks; ++$i) {
  83. $tmp = $blocks->slice($i << 4, self::BLOCK_SIZE);
  84. $this->x = $this->blockMultiply($this->x->exclusiveOr($tmp), $this->h);
  85. }
  86. $last = $numBlocks << 4;
  87. // Zero-fill buffer
  88. for ($i = 0; $i < 16; ++$i) {
  89. $this->buf[$i] = 0;
  90. }
  91. // Feed leftover into buffer.
  92. if ($last < $blocks->count()) {
  93. $tmp = $blocks->slice($last);
  94. $this->buf->set($tmp);
  95. $this->bufLength += ($blocks->count() - $last);
  96. }
  97. return $this;
  98. }
  99. /**
  100. * Finish processing the authentication tag.
  101. *
  102. * This method mutates this Gmac object (effectively resetting it).
  103. *
  104. * @param int $aadLength
  105. * @param int $ciphertextLength
  106. * @return ByteArray
  107. */
  108. public function finish($aadLength, $ciphertextLength)
  109. {
  110. $lengthBlock = new ByteArray(16);
  111. $state = $this->flush();
  112. // AES-GCM expects bit lengths, not byte lengths.
  113. $lengthBlock->set(ByteArray::enc32be($aadLength >> 29), 0);
  114. $lengthBlock->set(ByteArray::enc32be($aadLength << 3), 4);
  115. $lengthBlock->set(ByteArray::enc32be($ciphertextLength >> 29), 8);
  116. $lengthBlock->set(ByteArray::enc32be($ciphertextLength << 3), 12);
  117. $state->update($lengthBlock);
  118. $output = $state->x->exclusiveOr($state->hf);
  119. // Zeroize the internal values as a best-effort.
  120. $state->buf->zeroize();
  121. $state->x->zeroize();
  122. $state->h->zeroize();
  123. $state->hf->zeroize();
  124. return $output;
  125. }
  126. /**
  127. * Get a specific bit from the provided array, at the given index.
  128. *
  129. * [01234567], 8+[01234567], 16+[01234567], ...
  130. *
  131. * @param ByteArray $x
  132. * @param int $i
  133. * @return int
  134. */
  135. protected function bit(ByteArray $x, $i)
  136. {
  137. $byte = $i >> 3;
  138. return ($x[$byte] >> ((7 - $i) & 7)) & 1;
  139. }
  140. /**
  141. * Galois Field Multiplication
  142. *
  143. * This function is the critical path that must be constant-time in order to
  144. * avoid timing side-channels against AES-GCM.
  145. *
  146. * The contents of each are always calculated, regardless of the branching
  147. * condition, to prevent another kind of timing leak.
  148. *
  149. * @param ByteArray $x
  150. * @param ByteArray $y
  151. * @return ByteArray
  152. */
  153. protected function blockMultiply(ByteArray $x, ByteArray $y)
  154. {
  155. static $fieldPolynomial = null;
  156. if (!$fieldPolynomial) {
  157. $fieldPolynomial = new ByteArray([
  158. 0xe1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  159. ]);
  160. }
  161. self::needs($x->count() === 16, 'Argument 1 must be a ByteArray of exactly 16 bytes');
  162. self::needs($y->count() === 16, 'Argument 2 must be a ByteArray of exactly 16 bytes');
  163. $v = clone $y;
  164. $z = new ByteArray(16);
  165. for ($i = 0; $i < 128; ++$i) {
  166. // if ($b) $z = $z->exclusiveOr($v);
  167. $b = $this->bit($x, $i);
  168. $z = ByteArray::select(
  169. $b,
  170. $z->exclusiveOr($v),
  171. $z
  172. );
  173. // if ($b) $v = $v->exclusiveOr($fieldPolynomial);
  174. $b = $v[15] & 1;
  175. $v = $v->rshift();
  176. $v = ByteArray::select(
  177. $b,
  178. $v->exclusiveOr($fieldPolynomial),
  179. $v
  180. );
  181. }
  182. return $z;
  183. }
  184. /**
  185. * Finish processing any leftover bytes in the internal buffer.
  186. *
  187. * @return self
  188. */
  189. public function flush()
  190. {
  191. if ($this->bufLength !== 0) {
  192. $this->x = $this->blockMultiply(
  193. $this->x->exclusiveOr($this->buf),
  194. $this->h
  195. );
  196. $this->bufLength = 0;
  197. }
  198. return $this;
  199. }
  200. }