1212namespace chillerlan \JOSE \Algorithms \Signature ;
1313
1414use chillerlan \JOSE \Algorithms \OpenSSLAbstract ;
15- use chillerlan \JOSE \Key \JWK ;
1615use InvalidArgumentException ;
1716use function dechex ;
1817use function hexdec ;
3635 */
3736final class ECDSA extends OpenSSLAbstract implements SignatureAlgorithm{
3837
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+
3943 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 ,
4448 ];
4549
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 ,
5157 ];
5258
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+ ];
5465
5566 private const BYTE_SIZE = 2 ;
5667
@@ -60,16 +71,6 @@ final class ECDSA extends OpenSSLAbstract implements SignatureAlgorithm{
6071 private const ASN1_BIG_INTEGER_LIMIT = '7f ' ;
6172 private const ASN1_NEGATIVE_INTEGER = '00 ' ;
6273
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-
7374 public function sign (string $ message ):string {
7475 return $ this ->fromAsn1 ($ this ->signMessage ($ message ));
7576 }
@@ -79,18 +80,18 @@ public function verify(string $message, string $signature):bool{
7980 }
8081
8182 protected function checkKeyLength (int $ bits ):bool {
82- return $ bits === $ this ->keyLength ;
83+ return $ bits === self :: KEY_LENGTH [ $ this ->algo ] ;
8384 }
8485
8586 private function toAsn1 (string $ signature ):string {
8687 $ signature = sodium_bin2hex ($ signature );
8788
88- if ($ this ->octetLength ($ signature ) !== $ this ->signaturePartLength ){
89+ if ($ this ->octetLength ($ signature ) !== self :: OCTET_LENGTH [ $ this ->algo ] ){
8990 throw new InvalidArgumentException ('Invalid signature length. ' );
9091 }
9192
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 ] ));
9495 $ lengthR = $ this ->octetLength ($ pointR );
9596 $ lengthS = $ this ->octetLength ($ pointS );
9697
@@ -114,26 +115,6 @@ private function toAsn1(string $signature):string{
114115 );
115116 }
116117
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-
137118 private function fromAsn1 (string $ signature ):string {
138119 $ message = sodium_bin2hex ($ signature );
139120 $ position = 0 ;
@@ -150,8 +131,8 @@ private function fromAsn1(string $signature):string{
150131 $ pointS = $ this ->retrievePositiveInteger ($ message , $ position );
151132
152133 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 ),
155136 );
156137 }
157138
@@ -162,6 +143,26 @@ private function readAsn1Content(string $message, int &$position, int $length):s
162143 return $ content ;
163144 }
164145
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+
165166 private function retrievePositiveInteger (string $ message , int &$ position ):string {
166167
167168 if ($ this ->readAsn1Content ($ message , $ position , self ::BYTE_SIZE ) !== self ::ASN1_INTEGER ){
0 commit comments