PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 6.1.0
Defender Security – Malware Scanner, Login Security & Firewall v6.1.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / vendor / web-token / jwt-core / Util / ECSignature.php

ECSignature.php in Defender Security – Malware Scanner, Login Security & Firewall 6.1.0, at vendor/web-token/jwt-core/Util/ECSignature.php

144 lines 4.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 /*
6 * The MIT License (MIT)
7 *
8 * Copyright (c) 2014-2020 Spomky-Labs
9 *
10 * This software may be modified and distributed under the terms
11 * of the MIT license. See the LICENSE file for details.
12 */
13
14 namespace Jose\Component\Core\Util;
15
16 use InvalidArgumentException;
17 use function is_string;
18 use const STR_PAD_LEFT;
19
20 /**
21 * @internal
22 */
23 final class ECSignature
24 {
25 private const ASN1_SEQUENCE = '30';
26 private const ASN1_INTEGER = '02';
27 private const ASN1_MAX_SINGLE_BYTE = 128;
28 private const ASN1_LENGTH_2BYTES = '81';
29 private const ASN1_BIG_INTEGER_LIMIT = '7f';
30 private const ASN1_NEGATIVE_INTEGER = '00';
31 private const BYTE_SIZE = 2;
32
33 /**
34 * @throws InvalidArgumentException if the length of the signature is invalid
35 */
36 public static function toAsn1(string $signature, int $length): string
37 {
38 $signature = bin2hex($signature);
39
40 if (self::octetLength($signature) !== $length) {
41 throw new InvalidArgumentException('Invalid signature length.');
42 }
43
44 $pointR = self::preparePositiveInteger(mb_substr($signature, 0, $length, '8bit'));
45 $pointS = self::preparePositiveInteger(mb_substr($signature, $length, null, '8bit'));
46
47 $lengthR = self::octetLength($pointR);
48 $lengthS = self::octetLength($pointS);
49
50 $totalLength = $lengthR + $lengthS + self::BYTE_SIZE + self::BYTE_SIZE;
51 $lengthPrefix = $totalLength > self::ASN1_MAX_SINGLE_BYTE ? self::ASN1_LENGTH_2BYTES : '';
52
53 $bin = hex2bin(
54 self::ASN1_SEQUENCE
55 .$lengthPrefix.dechex($totalLength)
56 .self::ASN1_INTEGER.dechex($lengthR).$pointR
57 .self::ASN1_INTEGER.dechex($lengthS).$pointS
58 );
59 if (!is_string($bin)) {
60 throw new InvalidArgumentException('Unable to parse the data');
61 }
62
63 return $bin;
64 }
65
66 /**
67 * @throws InvalidArgumentException if the signature is not an ASN.1 sequence
68 */
69 public static function fromAsn1(string $signature, int $length): string
70 {
71 $message = bin2hex($signature);
72 $position = 0;
73
74 if (self::ASN1_SEQUENCE !== self::readAsn1Content($message, $position, self::BYTE_SIZE)) {
75 throw new InvalidArgumentException('Invalid data. Should start with a sequence.');
76 }
77
78 if (self::ASN1_LENGTH_2BYTES === self::readAsn1Content($message, $position, self::BYTE_SIZE)) {
79 $position += self::BYTE_SIZE;
80 }
81
82 $pointR = self::retrievePositiveInteger(self::readAsn1Integer($message, $position));
83 $pointS = self::retrievePositiveInteger(self::readAsn1Integer($message, $position));
84
85 $bin = hex2bin(str_pad($pointR, $length, '0', STR_PAD_LEFT).str_pad($pointS, $length, '0', STR_PAD_LEFT));
86 if (!is_string($bin)) {
87 throw new InvalidArgumentException('Unable to parse the data');
88 }
89
90 return $bin;
91 }
92
93 private static function octetLength(string $data): int
94 {
95 return (int) (mb_strlen($data, '8bit') / self::BYTE_SIZE);
96 }
97
98 private static function preparePositiveInteger(string $data): string
99 {
100 if (mb_substr($data, 0, self::BYTE_SIZE, '8bit') > self::ASN1_BIG_INTEGER_LIMIT) {
101 return self::ASN1_NEGATIVE_INTEGER.$data;
102 }
103
104 while (0 === mb_strpos($data, self::ASN1_NEGATIVE_INTEGER, 0, '8bit')
105 && mb_substr($data, 2, self::BYTE_SIZE, '8bit') <= self::ASN1_BIG_INTEGER_LIMIT) {
106 $data = mb_substr($data, 2, null, '8bit');
107 }
108
109 return $data;
110 }
111
112 private static function readAsn1Content(string $message, int &$position, int $length): string
113 {
114 $content = mb_substr($message, $position, $length, '8bit');
115 $position += $length;
116
117 return $content;
118 }
119
120 /**
121 * @throws InvalidArgumentException if the data is not an integer
122 */
123 private static function readAsn1Integer(string $message, int &$position): string
124 {
125 if (self::ASN1_INTEGER !== self::readAsn1Content($message, $position, self::BYTE_SIZE)) {
126 throw new InvalidArgumentException('Invalid data. Should contain an integer.');
127 }
128
129 $length = (int) hexdec(self::readAsn1Content($message, $position, self::BYTE_SIZE));
130
131 return self::readAsn1Content($message, $position, $length * self::BYTE_SIZE);
132 }
133
134 private static function retrievePositiveInteger(string $data): string
135 {
136 while (0 === mb_strpos($data, self::ASN1_NEGATIVE_INTEGER, 0, '8bit')
137 && mb_substr($data, 2, self::BYTE_SIZE, '8bit') > self::ASN1_BIG_INTEGER_LIMIT) {
138 $data = mb_substr($data, 2, null, '8bit');
139 }
140
141 return $data;
142 }
143 }
144