12
12
namespace chillerlan \JOSE \Algorithms \Signature ;
13
13
14
14
use chillerlan \JOSE \Algorithms \OpenSSLAbstract ;
15
- use chillerlan \JOSE \Key \JWK ;
16
15
use InvalidArgumentException ;
17
16
use function dechex ;
18
17
use function hexdec ;
36
35
*/
37
36
final class ECDSA extends OpenSSLAbstract implements SignatureAlgorithm{
38
37
38
+ public const ALGO_ES256 = 'ES256 ' ;
39
+ public const ALGO_ES256K = 'ES256K ' ;
40
+ public const ALGO_ES384 = 'ES384 ' ;
41
+ public const ALGO_ES512 = 'ES512 ' ;
42
+
39
43
public const SUPPORTED_ALGOS = [
40
- ' ES256 ' => OPENSSL_ALGO_SHA256 ,
41
- ' ES256K ' => OPENSSL_ALGO_SHA256 ,
42
- ' ES384 ' => OPENSSL_ALGO_SHA384 ,
43
- ' ES512 ' => OPENSSL_ALGO_SHA512 ,
44
+ self :: ALGO_ES256 => OPENSSL_ALGO_SHA256 ,
45
+ self :: ALGO_ES256K => OPENSSL_ALGO_SHA256 ,
46
+ self :: ALGO_ES384 => OPENSSL_ALGO_SHA384 ,
47
+ self :: ALGO_ES512 => OPENSSL_ALGO_SHA512 ,
44
48
];
45
49
46
- private const META = [
47
- 'ES256 ' => [256 , 64 , 'P-256 ' ],
48
- 'ES256K ' => [256 , 64 , 'P-256K ' ],
49
- 'ES384 ' => [384 , 96 , 'P-384 ' ],
50
- 'ES512 ' => [521 , 132 , 'P-521 ' ],
50
+ protected const KEYTYPE = OPENSSL_KEYTYPE_EC ;
51
+
52
+ private const KEY_LENGTH = [
53
+ self ::ALGO_ES256 => 256 ,
54
+ self ::ALGO_ES256K => 256 ,
55
+ self ::ALGO_ES384 => 384 ,
56
+ self ::ALGO_ES512 => 521 ,
51
57
];
52
58
53
- protected const KEYTYPE = OPENSSL_KEYTYPE_EC ;
59
+ private const OCTET_LENGTH = [
60
+ self ::ALGO_ES256 => 64 ,
61
+ self ::ALGO_ES256K => 64 ,
62
+ self ::ALGO_ES384 => 96 ,
63
+ self ::ALGO_ES512 => 132 ,
64
+ ];
54
65
55
66
private const BYTE_SIZE = 2 ;
56
67
@@ -60,16 +71,6 @@ final class ECDSA extends OpenSSLAbstract implements SignatureAlgorithm{
60
71
private const ASN1_BIG_INTEGER_LIMIT = '7f ' ;
61
72
private const ASN1_NEGATIVE_INTEGER = '00 ' ;
62
73
63
- private int $ keyLength ;
64
- private int $ signaturePartLength ;
65
- # private string $crv;
66
-
67
- public function __construct (JWK $ jwk , string $ algo , string |null $ passphrase = null ){
68
- parent ::__construct ($ jwk , $ algo , $ passphrase );
69
-
70
- [$ this ->keyLength , $ this ->signaturePartLength , /* $this->crv */ ] = self ::META [$ this ->algo ];
71
- }
72
-
73
74
public function sign (string $ message ):string {
74
75
return $ this ->fromAsn1 ($ this ->signMessage ($ message ));
75
76
}
@@ -79,18 +80,18 @@ public function verify(string $message, string $signature):bool{
79
80
}
80
81
81
82
protected function checkKeyLength (int $ bits ):bool {
82
- return $ bits === $ this ->keyLength ;
83
+ return $ bits === self :: KEY_LENGTH [ $ this ->algo ] ;
83
84
}
84
85
85
86
private function toAsn1 (string $ signature ):string {
86
87
$ signature = sodium_bin2hex ($ signature );
87
88
88
- if ($ this ->octetLength ($ signature ) !== $ this ->signaturePartLength ){
89
+ if ($ this ->octetLength ($ signature ) !== self :: OCTET_LENGTH [ $ this ->algo ] ){
89
90
throw new InvalidArgumentException ('Invalid signature length. ' );
90
91
}
91
92
92
- $ pointR = $ this ->preparePositiveInteger (substr ($ signature , 0 , $ this ->signaturePartLength ));
93
- $ pointS = $ this ->preparePositiveInteger (substr ($ signature , $ this ->signaturePartLength ));
93
+ $ pointR = $ this ->preparePositiveInteger (substr ($ signature , 0 , self :: OCTET_LENGTH [ $ this ->algo ] ));
94
+ $ pointS = $ this ->preparePositiveInteger (substr ($ signature , self :: OCTET_LENGTH [ $ this ->algo ] ));
94
95
$ lengthR = $ this ->octetLength ($ pointR );
95
96
$ lengthS = $ this ->octetLength ($ pointS );
96
97
@@ -114,26 +115,6 @@ private function toAsn1(string $signature):string{
114
115
);
115
116
}
116
117
117
- private function octetLength (string $ data ):int {
118
- return intdiv (strlen ($ data ), self ::BYTE_SIZE );
119
- }
120
-
121
- private function preparePositiveInteger (string $ data ):string {
122
-
123
- if (substr ($ data , 0 , self ::BYTE_SIZE ) > self ::ASN1_BIG_INTEGER_LIMIT ){
124
- return self ::ASN1_NEGATIVE_INTEGER .$ data ;
125
- }
126
-
127
- while (
128
- str_starts_with ($ data , self ::ASN1_NEGATIVE_INTEGER )
129
- && substr ($ data , 2 , self ::BYTE_SIZE ) <= self ::ASN1_BIG_INTEGER_LIMIT
130
- ){
131
- $ data = substr ($ data , 2 );
132
- }
133
-
134
- return $ data ;
135
- }
136
-
137
118
private function fromAsn1 (string $ signature ):string {
138
119
$ message = sodium_bin2hex ($ signature );
139
120
$ position = 0 ;
@@ -150,8 +131,8 @@ private function fromAsn1(string $signature):string{
150
131
$ pointS = $ this ->retrievePositiveInteger ($ message , $ position );
151
132
152
133
return sodium_hex2bin (
153
- str_pad ($ pointR , $ this ->signaturePartLength , '0 ' , STR_PAD_LEFT ).
154
- str_pad ($ pointS , $ this ->signaturePartLength , '0 ' , STR_PAD_LEFT ),
134
+ str_pad ($ pointR , self :: OCTET_LENGTH [ $ this ->algo ] , '0 ' , STR_PAD_LEFT ).
135
+ str_pad ($ pointS , self :: OCTET_LENGTH [ $ this ->algo ] , '0 ' , STR_PAD_LEFT ),
155
136
);
156
137
}
157
138
@@ -162,6 +143,26 @@ private function readAsn1Content(string $message, int &$position, int $length):s
162
143
return $ content ;
163
144
}
164
145
146
+ private function octetLength (string $ data ):int {
147
+ return intdiv (strlen ($ data ), self ::BYTE_SIZE );
148
+ }
149
+
150
+ private function preparePositiveInteger (string $ data ):string {
151
+
152
+ if (substr ($ data , 0 , self ::BYTE_SIZE ) > self ::ASN1_BIG_INTEGER_LIMIT ){
153
+ return self ::ASN1_NEGATIVE_INTEGER .$ data ;
154
+ }
155
+
156
+ while (
157
+ str_starts_with ($ data , self ::ASN1_NEGATIVE_INTEGER )
158
+ && substr ($ data , 2 , self ::BYTE_SIZE ) <= self ::ASN1_BIG_INTEGER_LIMIT
159
+ ){
160
+ $ data = substr ($ data , 2 );
161
+ }
162
+
163
+ return $ data ;
164
+ }
165
+
165
166
private function retrievePositiveInteger (string $ message , int &$ position ):string {
166
167
167
168
if ($ this ->readAsn1Content ($ message , $ position , self ::BYTE_SIZE ) !== self ::ASN1_INTEGER ){
0 commit comments