DefuseCryptoEncoding

The WooCommerce Germanized Defuse Crypto Encoding class.

Defined (1)

The class is defined in the following location(s).

/includes/gateways/direct-debit/libraries/php-encryption/Encoding.php  
  1. final class Encoding 
  2. const CHECKSUM_BYTE_SIZE = 32; 
  3. const CHECKSUM_HASH_ALGO = 'sha256'; 
  4. const SERIALIZE_HEADER_BYTES = 4; 
  5.  
  6. /** 
  7. * Converts a byte string to a hexadecimal string without leaking 
  8. * information through side channels. 
  9. * @param string $binary_string 
  10. * @throws Defuse\Crypto\Exception\EnvironmentIsBrokenException 
  11. * @return string 
  12. */ 
  13. public static function binToHex($byte_string) 
  14. $hex = ''; 
  15. $len = Core::ourStrlen($byte_string); 
  16. for ($i = 0; $i < $len; ++$i) { 
  17. $c = \ord($byte_string[$i]) & 0xf; 
  18. $b = \ord($byte_string[$i]) >> 4; 
  19. $hex .= \pack( 
  20. 'CC',  
  21. 87 + $b + ((($b - 10) >> 8) & ~38),  
  22. 87 + $c + ((($c - 10) >> 8) & ~38) 
  23. ); 
  24. return $hex; 
  25.  
  26. /** 
  27. * Converts a hexadecimal string into a byte string without leaking 
  28. * information through side channels. 
  29. * @param string $hex_string 
  30. * @throws Defuse\Crypto\Exception\BadFormatException 
  31. * @throws Defuse\Crypto\Exception\EnvironmentIsBrokenException 
  32. * @return string 
  33. */ 
  34. public static function hexToBin($hex_string) 
  35. $hex_pos = 0; 
  36. $bin = ''; 
  37. $hex_len = Core::ourStrlen($hex_string); 
  38. $state = 0; 
  39. $c_acc = 0; 
  40.  
  41. while ($hex_pos < $hex_len) { 
  42. $c = \ord($hex_string[$hex_pos]); 
  43. $c_num = $c ^ 48; 
  44. $c_num0 = ($c_num - 10) >> 8; 
  45. $c_alpha = ($c & ~32) - 55; 
  46. $c_alpha0 = (($c_alpha - 10) ^ ($c_alpha - 16)) >> 8; 
  47. if (($c_num0 | $c_alpha0) === 0) { 
  48. throw new Ex\BadFormatException( 
  49. 'Encoding::hexToBin() input is not a hex string.' 
  50. ); 
  51. $c_val = ($c_num0 & $c_num) | ($c_alpha & $c_alpha0); 
  52. if ($state === 0) { 
  53. $c_acc = $c_val * 16; 
  54. } else { 
  55. $bin .= \pack('C', $c_acc | $c_val); 
  56. $state ^= 1; 
  57. ++$hex_pos; 
  58. return $bin; 
  59.  
  60. /** 
  61. * SECURITY NOTE ON APPLYING CHECKSUMS TO SECRETS: 
  62. * The checksum introduces a potential security weakness. For example,  
  63. * suppose we apply a checksum to a key, and that an adversary has an 
  64. * exploit against the process containing the key, such that they can 
  65. * overwrite an arbitrary byte of memory and then cause the checksum to 
  66. * be verified and learn the result. 
  67. * In this scenario, the adversary can extract the key one byte at 
  68. * a time by overwriting it with their guess of its value and then 
  69. * asking if the checksum matches. If it does, their guess was right. 
  70. * This kind of attack may be more easy to implement and more reliable 
  71. * than a remote code execution attack. 
  72. * This attack also applies to authenticated encryption as a whole, in 
  73. * the situation where the adversary can overwrite a byte of the key 
  74. * and then cause a valid ciphertext to be decrypted, and then 
  75. * determine whether the MAC check passed or failed. 
  76. * By using the full SHA256 hash instead of truncating it, I'm ensuring 
  77. * that both ways of going about the attack are equivalently difficult. 
  78. * A shorter checksum of say 32 bits might be more useful to the 
  79. * adversary as an oracle in case their writes are coarser grained. 
  80. * Because the scenario assumes a serious vulnerability, we don't try 
  81. * to prevent attacks of this style. 
  82. */ 
  83.  
  84. /** 
  85. * INTERNAL USE ONLY: Applies a version header, applies a checksum, and 
  86. * then encodes a byte string into a range of printable ASCII characters. 
  87. * @param string $header 
  88. * @param string $bytes 
  89. * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException 
  90. * @return string 
  91. */ 
  92. public static function saveBytesToChecksummedAsciiSafeString($header, $bytes) 
  93. // Headers must be a constant length to prevent one type's header from 
  94. // being a prefix of another type's header, leading to ambiguity. 
  95. if (Core::ourStrlen($header) !== self::SERIALIZE_HEADER_BYTES) { 
  96. throw new Ex\EnvironmentIsBrokenException( 
  97. 'Header must be ' . self::SERIALIZE_HEADER_BYTES . ' bytes.' 
  98. ); 
  99.  
  100. return Encoding::binToHex( 
  101. $header . 
  102. $bytes . 
  103. \hash( 
  104. self::CHECKSUM_HASH_ALGO,  
  105. $header . $bytes,  
  106. true 
  107. ); 
  108.  
  109. /** 
  110. * INTERNAL USE ONLY: Decodes, verifies the header and checksum, and returns 
  111. * the encoded byte string. 
  112. * @param string $expected_header 
  113. * @param string $string 
  114. * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException 
  115. * @throws \Defuse\Crypto\Exception\BadFormatException 
  116. * @return string 
  117. */ 
  118. public static function loadBytesFromChecksummedAsciiSafeString($expected_header, $string) 
  119. // Headers must be a constant length to prevent one type's header from 
  120. // being a prefix of another type's header, leading to ambiguity. 
  121. if (Core::ourStrlen($expected_header) !== self::SERIALIZE_HEADER_BYTES) { 
  122. throw new Ex\EnvironmentIsBrokenException( 
  123. 'Header must be 4 bytes.' 
  124. ); 
  125.  
  126. $bytes = Encoding::hexToBin($string); 
  127.  
  128. /** Make sure we have enough bytes to get the version header and checksum. */ 
  129. if (Core::ourStrlen($bytes) < self::SERIALIZE_HEADER_BYTES + self::CHECKSUM_BYTE_SIZE) { 
  130. throw new Ex\BadFormatException( 
  131. 'Encoded data is shorter than expected.' 
  132. ); 
  133.  
  134. /** Grab the version header. */ 
  135. $actual_header = Core::ourSubstr($bytes, 0, self::SERIALIZE_HEADER_BYTES); 
  136.  
  137. if ($actual_header !== $expected_header) { 
  138. throw new Ex\BadFormatException( 
  139. 'Invalid header.' 
  140. ); 
  141.  
  142. /** Grab the bytes that are part of the checksum. */ 
  143. $checked_bytes = Core::ourSubstr( 
  144. $bytes,  
  145. 0,  
  146. Core::ourStrlen($bytes) - self::CHECKSUM_BYTE_SIZE 
  147. ); 
  148.  
  149. /** Grab the included checksum. */ 
  150. $checksum_a = Core::ourSubstr( 
  151. $bytes,  
  152. Core::ourStrlen($bytes) - self::CHECKSUM_BYTE_SIZE,  
  153. self::CHECKSUM_BYTE_SIZE 
  154. ); 
  155.  
  156. /** Re-compute the checksum. */ 
  157. $checksum_b = \hash(self::CHECKSUM_HASH_ALGO, $checked_bytes, true); 
  158.  
  159. /** Check if the checksum matches. */ 
  160. if (! Core::hashEquals($checksum_a, $checksum_b)) { 
  161. throw new Ex\BadFormatException( 
  162. "Data is corrupted, the checksum doesn't match" 
  163. ); 
  164.  
  165. return Core::ourSubstr( 
  166. $bytes,  
  167. self::SERIALIZE_HEADER_BYTES,  
  168. Core::ourStrlen($bytes) - self::SERIALIZE_HEADER_BYTES - self::CHECKSUM_BYTE_SIZE 
  169. );