• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mlocati / ocsp / 3931153084

pending completion
3931153084

push

github

GitHub
Merge pull request #10 from mlocati/phpseclib3

15 of 15 new or added lines in 3 files covered. (100.0%)

452 of 677 relevant lines covered (66.77%)

1.61 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

37.17
/src/Asn1/Der/Encoder.php
1
<?php
2

3
namespace Ocsp\Asn1\Der;
4

5
use DateTime;
6
use DateTimeImmutable;
7
use DateTimeZone;
8
use Ocsp\Asn1\Element;
9
use Ocsp\Asn1\Encoder as EncoderInterface;
10
use Ocsp\Asn1\Tag;
11
use Ocsp\Asn1\TaggableElement;
12
use Ocsp\Exception\Asn1EncodingException;
13
use Ocsp\Service\Math;
14

15
/**
16
 * Encoder from ASN.1 to DER.
17
 */
18
class Encoder implements EncoderInterface
19
{
20
    /**
21
     * {@inheritdoc}
22
     *
23
     * @see \Ocsp\Asn1\Encoder::getEncodingHandle()
24
     */
25
    public function getEncodingHandle()
26
    {
27
        return 'der';
×
28
    }
29

30
    /**
31
     * {@inheritdoc}
32
     *
33
     * @see \Ocsp\Asn1\Encoder::encodeElement()
34
     */
35
    public function encodeElement(Element $element)
36
    {
37
        $tag = null;
2✔
38
        if ($element instanceof TaggableElement) {
2✔
39
            $tag = $element->getTag();
2✔
40
        }
41
        if ($tag === null) {
2✔
42
            return $this->doEncodeElement($element);
2✔
43
        }
44
        switch ($tag->getEnvironment()) {
×
45
            case Tag::ENVIRONMENT_EXPLICIT:
×
46
                $elementBytes = $this->doEncodeElement($element);
×
47

48
                return $this->encodeType($tag->getTagID(), $tag->getClass(), true) . $this->encodeLength($elementBytes) . $elementBytes;
×
49
            case Tag::ENVIRONMENT_IMPLICIT:
×
50
                return $this->doEncodeElement($element, $tag);
×
51
            default:
52
                throw Asn1EncodingException::create(sprintf('Invalid ASN.1 tag environment: %s', $tag->getEnvironment()));
×
53
        }
54
    }
55

56
    /**
57
     * {@inheritdoc}
58
     *
59
     * @see \Ocsp\Asn1\Encoder::encodeInteger()
60
     */
61
    public function encodeInteger($value)
62
    {
63
        if (is_int($value)) {
2✔
64
            if ($value === 0) {
×
65
                return "\x00";
×
66
            }
67
            if ($value > 0) {
×
68
                if (PHP_INT_SIZE === 4 || $value < 0xFFFFFFFF) {
×
69
                    return ltrim(pack('N', $value), "\x00");
×
70
                }
71
                if (PHP_VERSION_ID >= 50603) {
×
72
                    return ltrim(pack('J', $value), "\x00");
×
73
                }
74
            }
75
            $value = Math::createBigInteger((string) $value);
×
76
        } elseif (is_string($value)) {
2✔
77
            $value = Math::createBigInteger($value);
2✔
78
        }
79

80
        return $value->toBytes(true);
2✔
81
    }
82

83
    /**
84
     * {@inheritdoc}
85
     *
86
     * @see \Ocsp\Asn1\Encoder::encodeIdentifier()
87
     */
88
    public function encodeIdentifier($value)
89
    {
90
        $parts = explode('.', $value);
2✔
91
        $result = chr((int) array_shift($parts) * 40 + (int) array_shift($parts));
2✔
92
        while (($part = array_shift($parts)) !== null) {
2✔
93
            $result .= $this->encodeIdentifierPart($part);
2✔
94
        }
95

96
        return $result;
2✔
97
    }
98

99
    /**
100
     * {@inheritdoc}
101
     *
102
     * @see \Ocsp\Asn1\Encoder::encodeOctetString()
103
     */
104
    public function encodeOctetString($value)
105
    {
106
        return $value;
2✔
107
    }
108

109
    /**
110
     * {@inheritdoc}
111
     *
112
     * @see \Ocsp\Asn1\Encoder::encodePrintableString()
113
     */
114
    public function encodePrintableString($value)
115
    {
116
        return $value;
2✔
117
    }
118

119
    /**
120
     * {@inheritdoc}
121
     *
122
     * @see \Ocsp\Asn1\Encoder::encodeBitString()
123
     */
124
    public function encodeBitString($bytes, $unusedBitsInLastByte)
125
    {
126
        return chr($unusedBitsInLastByte) . $bytes;
×
127
    }
128

129
    /**
130
     * {@inheritdoc}
131
     *
132
     * @see \Ocsp\Asn1\Encoder::encodeGeneralizedTime()
133
     */
134
    public function encodeGeneralizedTime(DateTimeImmutable $value)
135
    {
136
        $datetime = new DateTime('now', new DateTimeZone('UTC'));
×
137
        $datetime->setTimestamp($value->getTimestamp());
×
138

139
        $result = $datetime->format('YmdHis');
×
140
        $useconds = ltrim($value->format('u'), '0');
×
141
        if ($useconds !== '') {
×
142
            $result .= '.' . $useconds;
×
143
        }
144
        $result .= 'Z';
×
145

146
        return $result;
×
147
    }
148

149
    /**
150
     * @param \Ocsp\Asn1\Element $element
151
     * @param \Ocsp\Asn1\Tag|null $implicitTag
152
     *
153
     * @throws \Ocsp\Exception\Asn1EncodingException when the element or the tag are defined in invalid classes
154
     *
155
     * @return string
156
     */
157
    protected function doEncodeElement(Element $element, Tag $implicitTag = null)
158
    {
159
        if ($implicitTag === null) {
2✔
160
            $result = $this->encodeType($element->getTypeID(), $element->getClass(), $element->isConstructed());
2✔
161
        } else {
162
            $result = $this->encodeType($implicitTag->getTagID(), $implicitTag->getClass(), $element->isConstructed());
×
163
        }
164
        $elementBytes = $element->getEncodedValue($this);
2✔
165

166
        return $result . $this->encodeLength($elementBytes) . $elementBytes;
2✔
167
    }
168

169
    /**
170
     * Encode a part of the value of an IDENTIFIER element.
171
     *
172
     * @param string $part
173
     *
174
     * @return string
175
     */
176
    protected function encodeIdentifierPart($part)
177
    {
178
        $part = ltrim($part, '0');
2✔
179
        if ($part === '') {
2✔
180
            return "\x00";
×
181
        }
182
        $bytes = [];
2✔
183
        if (strlen($part) < strlen(PHP_INT_MAX)) {
2✔
184
            $int = (int) $part;
2✔
185
            if ($int <= 127) {
2✔
186
                return chr($int);
2✔
187
            }
188
            $bits = decbin($int);
×
189
        } else {
190
            $bits = Math::createBigInteger($part)->toBits();
×
191
        }
192
        do {
193
            array_unshift($bytes, bindec(substr($bits, -7)));
×
194
            $bits = substr($bits, 0, -7);
×
195
        } while ($bits !== '' && $bits !== false);
×
196
        $result = '';
×
197
        foreach (array_splice($bytes, 0, -1) as $byte) {
×
198
            $result .= chr(0x80 | $byte);
×
199
        }
200
        $result .= chr(reset($bytes));
×
201

202
        return $result;
×
203
    }
204

205
    /**
206
     * Encode the type ID.
207
     *
208
     * @param int|string|\phpseclib\Math\BigInteger|\phpseclib3\Math\BigInteger $typeID the type ID
209
     * @param string $class the class (the value of one of the Element::CLASS_... constants)
210
     * @param bool $isConstructed is the element a constructed element?
211
     *
212
     * @throws \Ocsp\Exception\Asn1EncodingException when $class contains an invalid value
213
     *
214
     * @return string
215
     */
216
    protected function encodeType($typeID, $class, $isConstructed)
217
    {
218
        switch ($class) {
219
            case Element::CLASS_UNIVERSAL:
2✔
220
                $firstByte = 0b00000000;
2✔
221
                break;
2✔
222
            case Element::CLASS_APPLICATION:
×
223
                $firstByte = 0b01000000;
×
224
                break;
×
225
            case Element::CLASS_CONTEXTSPECIFIC:
×
226
                $firstByte = 0b10000000;
×
227
                break;
×
228
            case Element::CLASS_PRIVATE:
×
229
                $firstByte = 0b11000000;
×
230
                break;
×
231
            default:
232
                throw Asn1EncodingException::create(sprintf('Invalid ASN.1 class: %s', $class));
×
233
        }
234
        if ($isConstructed) {
2✔
235
            $firstByte |= 0b00100000;
2✔
236
        }
237
        $typeIDBits = $this->getBits($typeID);
2✔
238
        if (!isset($typeIDBits[5])) {
2✔
239
            $typeIDInt = bindec($typeIDBits);
2✔
240
            if ($typeIDInt <= 30) {
2✔
241
                return chr($firstByte | $typeIDInt);
2✔
242
            }
243
        }
244
        $result = chr($firstByte | 0b00011111);
×
245
        while (isset($typeIDBits[7])) {
×
246
            $result .= chr(bindec('1' . substr($typeIDBits, -7)));
×
247
            $typeIDBits = substr($typeIDBits, 0, -7);
×
248
        }
249
        $result .= chr(bindec($typeIDBits));
×
250

251
        return $result;
×
252
    }
253

254
    /**
255
     * Encode the length of the encoded value of an element.
256
     *
257
     * @param string $encodedElementValue the encoded value of an element
258
     *
259
     * @return string
260
     */
261
    protected function encodeLength($encodedElementValue)
262
    {
263
        $length = strlen($encodedElementValue);
2✔
264
        if ($length < 127) {
2✔
265
            return chr($length);
2✔
266
        }
267
        $lengthHex = dechex($length);
×
268
        $lengthHexLength = strlen($lengthHex);
×
269
        if (($lengthHexLength % 2) !== 0) {
×
270
            $lengthHex = '0' . $lengthHex;
×
271
            $lengthHexLength++;
×
272
        }
273
        $lengthNumBytes = strlen($lengthHex) >> 1;
×
274
        $result = chr($lengthNumBytes | 0x80);
×
275
        for ($index = 0; $index < $lengthHexLength; $index += 2) {
×
276
            $result .= chr(hexdec($lengthHex[$index] . $lengthHex[$index + 1]));
×
277
        }
278

279
        return $result;
×
280
    }
281

282
    /**
283
     * Get the bits representing a number.
284
     *
285
     * @param int|string|\phpseclib\Math\BigInteger|\phpseclib3\Math\BigInteger $number
286
     *
287
     * @return string
288
     */
289
    protected function getBits($number)
290
    {
291
        if (is_int($number)) {
2✔
292
            return decbin($number);
2✔
293
        }
294
        if (is_string($number)) {
×
295
            $number = ltrim($number, '0');
×
296
            if ($number === '') {
×
297
                return '0';
×
298
            }
299
            if (strlen($number) < strlen((string) PHP_INT_MAX)) {
×
300
                return decbin((int) $number);
×
301
            }
302
            $number = Math::createBigInteger($number);
×
303
        }
304

305
        return $number->toBits(true);
×
306
    }
307
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc