PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.3.1
Defender Security – Malware Scanner, Login Security & Firewall v5.3.1
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-auth / cose-lib / src / Algorithm / Signature / ECDSA / ECSignature.php

ECSignature.php in Defender Security – Malware Scanner, Login Security & Firewall 5.3.1, at vendor/web-auth/cose-lib/src/Algorithm/Signature/ECDSA/ECSignature.php

145 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-2021 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 Cose\Algorithm\Signature\ECDSA;
15
16 use function bin2hex;
17 use function dechex;
18 use function hexdec;
19 use InvalidArgumentException;
20 use function mb_strlen;
21 use function mb_substr;
22 use function str_pad;
23 use const STR_PAD_LEFT;
24
25 /**
26 * @internal
27 */
28 final class ECSignature
29 {
30 private const ASN1_SEQUENCE = '30';
31 private const ASN1_INTEGER = '02';
32 private const ASN1_MAX_SINGLE_BYTE = 128;
33 private const ASN1_LENGTH_2BYTES = '81';
34 private const ASN1_BIG_INTEGER_LIMIT = '7f';
35 private const ASN1_NEGATIVE_INTEGER = '00';
36 private const BYTE_SIZE = 2;
37
38 public static function toAsn1(string $signature, int $length): string
39 {
40 $signature = bin2hex($signature);
41
42 if (self::octetLength($signature) !== $length) {
43 throw new InvalidArgumentException('Invalid signature length.');
44 }
45
46 $pointR = self::preparePositiveInteger(mb_substr($signature, 0, $length, '8bit'));
47 $pointS = self::preparePositiveInteger(mb_substr($signature, $length, null, '8bit'));
48
49 $lengthR = self::octetLength($pointR);
50 $lengthS = self::octetLength($pointS);
51
52 $totalLength = $lengthR + $lengthS + self::BYTE_SIZE + self::BYTE_SIZE;
53 $lengthPrefix = $totalLength > self::ASN1_MAX_SINGLE_BYTE ? self::ASN1_LENGTH_2BYTES : '';
54
55 $bin = hex2bin(
56 self::ASN1_SEQUENCE
57 .$lengthPrefix.dechex($totalLength)
58 .self::ASN1_INTEGER.dechex($lengthR).$pointR
59 .self::ASN1_INTEGER.dechex($lengthS).$pointS
60 );
61 if (false === $bin) {
62 throw new InvalidArgumentException('Unable to convert into ASN.1');
63 }
64
65 return $bin;
66 }
67
68 public static function fromAsn1(string $signature, int $length): string
69 {
70 $message = bin2hex($signature);
71 $position = 0;
72
73 if (self::ASN1_SEQUENCE !== self::readAsn1Content($message, $position, self::BYTE_SIZE)) {
74 throw new InvalidArgumentException('Invalid data. Should start with a sequence.');
75 }
76
77 // @phpstan-ignore-next-line
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 (false === $bin) {
87 throw new InvalidArgumentException('Unable to convert from ASN.1');
88 }
89
90 return $bin;
91 }
92
93 private static function octetLength(string $data): int
94 {
95 return intdiv(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 (
105 self::ASN1_NEGATIVE_INTEGER === mb_substr($data, 0, self::BYTE_SIZE, '8bit')
106 && mb_substr($data, 2, self::BYTE_SIZE, '8bit') <= self::ASN1_BIG_INTEGER_LIMIT
107 ) {
108 $data = mb_substr($data, 2, null, '8bit');
109 }
110
111 return $data;
112 }
113
114 private static function readAsn1Content(string $message, int &$position, int $length): string
115 {
116 $content = mb_substr($message, $position, $length, '8bit');
117 $position += $length;
118
119 return $content;
120 }
121
122 private static function readAsn1Integer(string $message, int &$position): string
123 {
124 if (self::ASN1_INTEGER !== self::readAsn1Content($message, $position, self::BYTE_SIZE)) {
125 throw new InvalidArgumentException('Invalid data. Should contain an integer.');
126 }
127
128 $length = (int) hexdec(self::readAsn1Content($message, $position, self::BYTE_SIZE));
129
130 return self::readAsn1Content($message, $position, $length * self::BYTE_SIZE);
131 }
132
133 private static function retrievePositiveInteger(string $data): string
134 {
135 while (
136 self::ASN1_NEGATIVE_INTEGER === mb_substr($data, 0, self::BYTE_SIZE, '8bit')
137 && mb_substr($data, 2, self::BYTE_SIZE, '8bit') > self::ASN1_BIG_INTEGER_LIMIT
138 ) {
139 $data = mb_substr($data, 2, null, '8bit');
140 }
141
142 return $data;
143 }
144 }
145