55use Casper \Util \ByteUtil ;
66
77use Mdanter \Ecc \Crypto \Key \PrivateKeyInterface ;
8- use Mdanter \Ecc \Crypto \Key \PublicKeyInterface ;
9- use Mdanter \Ecc \Crypto \Signature \Signature ;
10- use Mdanter \Ecc \Crypto \Signature \SignatureInterface ;
11- use Mdanter \Ecc \Crypto \Signature \Signer ;
12- use Mdanter \Ecc \Crypto \Signature \SignHasher ;
138use Mdanter \Ecc \EccFactory ;
14- use Mdanter \Ecc \Math \GmpMathInterface ;
15- use Mdanter \Ecc \Primitives \GeneratorPoint ;
16- use Mdanter \Ecc \Random \RandomGeneratorFactory ;
179use Mdanter \Ecc \Serializer \PrivateKey \DerPrivateKeySerializer ;
1810use Mdanter \Ecc \Serializer \PrivateKey \PemPrivateKeySerializer ;
1911use Mdanter \Ecc \Serializer \PublicKey \DerPublicKeySerializer ;
2012use Mdanter \Ecc \Serializer \PublicKey \PemPublicKeySerializer ;
21- use Phactor \Math ;
2213
2314/**
24- * Secp255K1 key implementation
15+ * Secp256K1 key implementation
2516 */
2617final class Secp256K1Key extends AsymmetricKey
2718{
28- use Math;
29-
19+ protected const RESULT_OK = 1 ;
3020 protected const HASHING_ALGORITHM = 'sha256 ' ;
3121
32- protected GmpMathInterface $ adapter ;
33-
34- protected GeneratorPoint $ generator ;
35-
36- protected PemPrivateKeySerializer $ privateKeySerializer ;
37-
38- protected PemPublicKeySerializer $ publicKeySerializer ;
39-
40- protected PrivateKeyInterface $ secpPrivateKey ;
41-
42- protected PublicKeyInterface $ secpPublicKey ;
22+ protected PrivateKeyInterface $ privateKeyObject ;
4323
44- public function __construct (PrivateKeyInterface $ privateKey = null )
24+ public function __construct (PrivateKeyInterface $ privateKeyObject = null )
4525 {
46- $ this ->adapter = EccFactory:: getAdapter ();
47- $ this -> generator = EccFactory::getSecgCurves ()-> generator256k1 ();
48- $ this -> privateKeySerializer = new PemPrivateKeySerializer ( new DerPrivateKeySerializer ( $ this -> adapter ));
49- $ this -> publicKeySerializer = new PemPublicKeySerializer ( new DerPublicKeySerializer ( $ this -> adapter ) );
26+ $ this ->privateKeyObject = $ privateKeyObject ??
27+ EccFactory::getSecgCurves ()
28+ -> generator256k1 ()
29+ -> createPrivateKey ( );
5030
51- $ this -> secpPrivateKey = $ privateKey ?? $ this ->generator -> createPrivateKey ( );
52- $ privateKeyString = ByteUtil::hexToString ($ this ->encodeHex (( string ) $ this -> secpPrivateKey -> getSecret (), false ));
31+ $ privateKey = ByteUtil:: hexToString ( gmp_strval ( $ this ->privateKeyObject -> getSecret (), 16 ) );
32+ $ publicKey = ByteUtil::hexToString ($ this ->getCompressedPublicKeyHex ( ));
5333
54- $ this ->secpPublicKey = $ this ->secpPrivateKey ->getPublicKey ();
55- $ xHex = str_pad ($ this ->encodeHex ((string ) $ this ->secpPublicKey ->getPoint ()->getX (), false ), 64 , "0 " , STR_PAD_LEFT );
56- $ compressedPublicKeyPrefix = $ this ->Modulo ((string ) $ this ->secpPublicKey ->getPoint ()->getY (), '2 ' ) === '1 ' ? '03 ' : '02 ' ;
57- $ publicKeyCompressedString = ByteUtil::hexToString ($ compressedPublicKeyPrefix . $ xHex );
58-
59- parent ::__construct ($ publicKeyCompressedString , $ privateKeyString , self ::ALGO_SECP255K1 );
34+ parent ::__construct ($ publicKey , $ privateKey , self ::ALGO_SECP255K1 );
6035 }
6136
6237 /**
@@ -65,11 +40,9 @@ public function __construct(PrivateKeyInterface $privateKey = null)
6540 */
6641 public static function createFromPrivateKeyFile (string $ pathToPrivateKey ): self
6742 {
68- $ adapter = EccFactory::getAdapter ();
69- $ privateKeySerializer = new PemPrivateKeySerializer (new DerPrivateKeySerializer ($ adapter ));
70-
7143 return new self (
72- $ privateKeySerializer ->parse (file_get_contents ($ pathToPrivateKey ))
44+ (new PemPrivateKeySerializer (new DerPrivateKeySerializer ()))
45+ ->parse (file_get_contents ($ pathToPrivateKey ))
7346 );
7447 }
7548
@@ -79,7 +52,8 @@ public static function createFromPrivateKeyFile(string $pathToPrivateKey): self
7952 */
8053 public function exportPublicKeyInPem (): string
8154 {
82- return $ this ->publicKeySerializer ->serialize ($ this ->secpPublicKey ) . PHP_EOL ;
55+ return (new PemPublicKeySerializer (new DerPublicKeySerializer ()))
56+ ->serialize ($ this ->privateKeyObject ->getPublicKey ()) . PHP_EOL ;
8357 }
8458
8559 /**
@@ -88,73 +62,94 @@ public function exportPublicKeyInPem(): string
8862 */
8963 public function exportPrivateKeyInPem (): string
9064 {
91- return $ this ->privateKeySerializer ->serialize ($ this ->secpPrivateKey ) . PHP_EOL ;
65+ return (new PemPrivateKeySerializer (new DerPrivateKeySerializer ()))
66+ ->serialize ($ this ->privateKeyObject ) . PHP_EOL ;
9267 }
9368
9469 /**
9570 * @inheritDoc
9671 * @param string $message
9772 * @return string
73+ *
74+ * @throws \Exception
9875 */
9976 public function sign (string $ message ): string
10077 {
101- $ hasher = new SignHasher (self ::HASHING_ALGORITHM , $ this ->adapter );
102- $ hash = $ hasher ->makeHash ($ message , $ this ->generator );
78+ $ context = secp256k1_context_create (SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY );
79+
80+ $ signature = null ;
81+ $ signResult = secp256k1_ecdsa_sign (
82+ $ context ,
83+ $ signature ,
84+ hash (self ::HASHING_ALGORITHM , $ message , true ),
85+ $ this ->privateKey
86+ );
10387
104- $ random = RandomGeneratorFactory::getRandomGenerator ();
105- $ randomK = $ random ->generate ($ this ->generator ->getOrder ());
88+ if ($ signResult !== self ::RESULT_OK ) {
89+ throw new \Exception ("Failed to create signature " );
90+ }
10691
107- $ signature = ( new Signer ( $ this -> adapter ))
108- -> sign ( $ this -> secpPrivateKey , $ hash , $ randomK );
92+ $ signatureSerialized = '' ;
93+ secp256k1_ecdsa_signature_serialize_compact ( $ context , $ signatureSerialized , $ signature );
10994
110- return $ this -> signatureToHex ( $ signature );
95+ return ByteUtil:: stringToHex ( $ signatureSerialized );
11196 }
11297
11398 /**
11499 * @inheritDoc
115- * @param string $signature
100+ * @param string $hexSignature
116101 * @param string $message
117102 * @return bool
103+ *
104+ * @throws \Exception
118105 */
119- public function verify (string $ signature , string $ message ): bool
106+ public function verify (string $ hexSignature , string $ message ): bool
120107 {
121- try {
122- $ signature = $ this ->hexToSignature ($ signature );
108+ $ context = secp256k1_context_create (SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY );
123109
124- $ hasher = new SignHasher ( self :: HASHING_ALGORITHM ) ;
125- $ hash = $ hasher -> makeHash ( $ message , $ this ->generator );
110+ $ publicKey = null ;
111+ $ publicKeyParseResult = secp256k1_ec_pubkey_parse ( $ context , $ publicKey , $ this ->publicKey );
126112
127- return (new Signer ($ this ->adapter ))
128- ->verify ($ this ->secpPublicKey , $ signature , $ hash );
129- } catch (\Exception $ e ) {
130- return false ;
113+ if ($ publicKeyParseResult !== self ::RESULT_OK ) {
114+ throw new \Exception ("Failed to parse public key " );
131115 }
132- }
133116
134- private function signatureToHex (SignatureInterface $ signature ): string
135- {
136- $ r = $ signature ->getR ();
137- $ s = $ signature ->getS ();
117+ $ signature = null ;
118+ $ signatureParseResult = secp256k1_ecdsa_signature_parse_compact (
119+ $ context ,
120+ $ signature ,
121+ ByteUtil::hexToString ($ hexSignature )
122+ );
123+
124+ if ($ signatureParseResult !== self ::RESULT_OK ) {
125+ throw new \Exception ("Failed to parse DER signature " );
126+ }
138127
139- return gmp_strval ($ r , 16 ) . gmp_strval ($ s , 16 );
128+ $ isVerified = secp256k1_ecdsa_verify (
129+ $ context ,
130+ $ signature ,
131+ hash (self ::HASHING_ALGORITHM , $ message , true ),
132+ $ publicKey
133+ );
134+
135+ return $ isVerified === self ::RESULT_OK ;
140136 }
141137
142- private function hexToSignature ( string $ hex ): SignatureInterface
138+ private function getCompressedPublicKeyHex ( ): string
143139 {
144- $ hex = mb_strtolower ($ hex );
140+ $ xPointValue = $ this ->privateKeyObject
141+ ->getPublicKey ()
142+ ->getPoint ()
143+ ->getX ();
145144
146- if (strpos ($ hex , '0x ' ) >= 0 ) {
147- $ count = 1 ;
148- $ hex = str_replace ('0x ' , '' , $ hex , $ count );
149- }
150-
151- if (mb_strlen ($ hex ) !== 128 ) {
152- throw new \InvalidArgumentException ('Binary string was not correct. ' );
153- }
145+ $ yPointValue = $ this ->privateKeyObject
146+ ->getPublicKey ()
147+ ->getPoint ()
148+ ->getY ();
154149
155- $ r = mb_substr ( $ hex , 0 , 64 );
156- $ s = mb_substr ( $ hex , 64 , 64 ) ;
150+ $ xPointValueHex = str_pad ( gmp_strval ( $ xPointValue , 16 ) , 64 , ' 0 ' , STR_PAD_LEFT );
151+ $ prefix = gmp_strval ( gmp_mod ( $ yPointValue , 2 )) === ' 1 ' ? ' 03 ' : ' 02 ' ;
157152
158- return new Signature ( gmp_init ( $ r , 16 ), gmp_init ( $ s , 16 )) ;
153+ return $ prefix . $ xPointValueHex ;
159154 }
160155}
0 commit comments