Gmac.php 6.2 KB

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