Integer.php 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. <?php
  2. /*
  3. * This file is part of the PHPASN1 library.
  4. *
  5. * Copyright © Friedrich Große <friedrich.grosse@gmail.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace FG\ASN1\Universal;
  11. use Exception;
  12. use FG\Utility\BigInteger;
  13. use FG\ASN1\Exception\ParserException;
  14. use FG\ASN1\ASNObject;
  15. use FG\ASN1\Parsable;
  16. use FG\ASN1\Identifier;
  17. class Integer extends ASNObject implements Parsable
  18. {
  19. /** @var int */
  20. private $value;
  21. /**
  22. * @param int $value
  23. *
  24. * @throws Exception if the value is not numeric
  25. */
  26. public function __construct($value)
  27. {
  28. if (is_numeric($value) == false) {
  29. throw new Exception("Invalid VALUE [{$value}] for ASN1_INTEGER");
  30. }
  31. $this->value = $value;
  32. }
  33. public function getType()
  34. {
  35. return Identifier::INTEGER;
  36. }
  37. public function getContent()
  38. {
  39. return $this->value;
  40. }
  41. protected function calculateContentLength()
  42. {
  43. return strlen($this->getEncodedValue());
  44. }
  45. protected function getEncodedValue()
  46. {
  47. $value = BigInteger::create($this->value, 10);
  48. $negative = $value->compare(0) < 0;
  49. if ($negative) {
  50. $value = $value->absoluteValue();
  51. $limit = 0x80;
  52. } else {
  53. $limit = 0x7f;
  54. }
  55. $mod = 0xff+1;
  56. $values = [];
  57. while($value->compare($limit) > 0) {
  58. $values[] = $value->modulus($mod)->toInteger();
  59. $value = $value->shiftRight(8);
  60. }
  61. $values[] = $value->modulus($mod)->toInteger();
  62. $numValues = count($values);
  63. if ($negative) {
  64. for ($i = 0; $i < $numValues; $i++) {
  65. $values[$i] = 0xff - $values[$i];
  66. }
  67. for ($i = 0; $i < $numValues; $i++) {
  68. $values[$i] += 1;
  69. if ($values[$i] <= 0xff) {
  70. break;
  71. }
  72. assert($i != $numValues - 1);
  73. $values[$i] = 0;
  74. }
  75. if ($values[$numValues - 1] == 0x7f) {
  76. $values[] = 0xff;
  77. }
  78. }
  79. $values = array_reverse($values);
  80. $r = pack("C*", ...$values);
  81. return $r;
  82. }
  83. private static function ensureMinimalEncoding($binaryData, $offsetIndex)
  84. {
  85. // All the first nine bits cannot equal 0 or 1, which would
  86. // be non-minimal encoding for positive and negative integers respectively
  87. if ((ord($binaryData[$offsetIndex]) == 0x00 && (ord($binaryData[$offsetIndex+1]) & 0x80) == 0) ||
  88. (ord($binaryData[$offsetIndex]) == 0xff && (ord($binaryData[$offsetIndex+1]) & 0x80) == 0x80)) {
  89. throw new ParserException("Integer not minimally encoded", $offsetIndex);
  90. }
  91. }
  92. public static function fromBinary(&$binaryData, &$offsetIndex = 0)
  93. {
  94. $parsedObject = new static(0);
  95. self::parseIdentifier($binaryData[$offsetIndex], $parsedObject->getType(), $offsetIndex++);
  96. $contentLength = self::parseContentLength($binaryData, $offsetIndex, 1);
  97. if ($contentLength > 1) {
  98. self::ensureMinimalEncoding($binaryData, $offsetIndex);
  99. }
  100. $isNegative = (ord($binaryData[$offsetIndex]) & 0x80) != 0x00;
  101. $number = BigInteger::create(ord($binaryData[$offsetIndex++]) & 0x7F);
  102. for ($i = 0; $i < $contentLength - 1; $i++) {
  103. $number = $number->multiply(0x100)->add(ord($binaryData[$offsetIndex++]));
  104. }
  105. if ($isNegative) {
  106. $number = $number->subtract(BigInteger::create(2)->toPower(8 * $contentLength - 1));
  107. }
  108. $parsedObject = new static((string)$number);
  109. $parsedObject->setContentLength($contentLength);
  110. return $parsedObject;
  111. }
  112. }