1
- import os
1
+ import secrets
2
2
import base64
3
+ import secp256k1
3
4
from cffi import FFI
4
- from secp256k1 import PrivateKey , PublicKey
5
5
from cryptography .hazmat .primitives .ciphers import Cipher , algorithms , modes
6
6
from cryptography .hazmat .primitives import padding
7
7
from . import bech32
8
8
9
- def generate_private_key () -> str :
10
- private_key = PrivateKey ()
11
- public_key = private_key .pubkey .serialize ().hex ()
12
- while not public_key .startswith ("02" ):
13
- private_key = PrivateKey ()
14
- public_key = private_key .pubkey .serialize ().hex ()
15
- return private_key .serialize ()
9
+ class PublicKey :
10
+ def __init__ (self , raw_bytes : bytes ) -> None :
11
+ self .raw_bytes = raw_bytes
16
12
17
- def get_public_key (secret : str ) -> str :
18
- private_key = PrivateKey (bytes .fromhex (secret ))
19
- public_key = private_key .pubkey .serialize ().hex ()
20
- return public_key [2 :] # chop off sign byte
13
+ def bech32 (self ) -> str :
14
+ converted_bits = bech32 .convertbits (self .raw_bytes , 8 , 5 )
15
+ return bech32 .bech32_encode ("npub" , converted_bits , bech32 .Encoding .BECH32 )
21
16
22
- def get_key_pair () -> tuple :
23
- private_key = PrivateKey ()
24
- public_key = private_key .pubkey .serialize ().hex ()
25
- return (private_key .serialize (), public_key [2 :])
17
+ def hex (self ) -> str :
18
+ return self .raw_bytes .hex ()
26
19
27
- def bech32_encode_private_key ( private_key : str ) -> str :
28
- converted_bits = bech32 . convertbits ( bytes . fromhex ( private_key ), 8 , 5 )
29
- return bech32 . bech32_encode ( "nsec" , converted_bits , bech32 . Encoding . BECH32 )
20
+ def verify_signed_message_hash ( self , hash : str , sig : str ) -> bool :
21
+ pk = secp256k1 . PublicKey ( self . raw_bytes , True )
22
+ return pk . schnorr_verify ( bytes . fromhex ( hash ), bytes . fromhex ( sig ), None , True )
30
23
31
- def bech32_decode_private_key (private_key_bech32 : str ) -> str :
32
- data = bech32 .bech32_decode (private_key_bech32 )[1 ]
33
- return bytes (bech32 .convertbits (data , 5 , 8 , False )).hex ()
24
+ class PrivateKey :
25
+ def __init__ (self , raw_secret : bytes = None ) -> None :
26
+ if not raw_secret is None :
27
+ self .raw_secret = raw_secret
28
+ else :
29
+ self .raw_secret = secrets .token_bytes (32 )
34
30
35
- def bech32_encode_public_key (public_key : str ) -> str :
36
- converted_bits = bech32 .convertbits (bytes .fromhex (public_key ), 8 , 5 )
37
- return bech32 .bech32_encode ("npub" , converted_bits , bech32 .Encoding .BECH32 )
31
+ sk = secp256k1 .PrivateKey (self .raw_secret )
32
+ self .public_key = PublicKey (sk .pubkey .serialize ()[1 :])
38
33
39
- def bech32_decode_public_key ( public_key_bech32 : str ) -> str :
40
- data = bech32 .bech32_decode ( public_key_bech32 )[ 1 ]
41
- return bytes ( bech32 .convertbits ( data , 5 , 8 , False )). hex ( )
34
+ def bech32 ( self ) -> str :
35
+ converted_bits = bech32 .convertbits ( self . raw_secret , 8 , 5 )
36
+ return bech32 .bech32_encode ( "nsec" , converted_bits , bech32 . Encoding . BECH32 )
42
37
43
- def tweak_add_private_key (private_key : str , scalar : bytes ) -> str :
44
- sk = PrivateKey (bytes .fromhex (private_key ))
45
- tweaked_secret = sk .tweak_add (scalar )
46
- new_sk = PrivateKey (tweaked_secret )
47
- return new_sk .serialize ()
38
+ def hex (self ) -> str :
39
+ return self .raw_secret .hex ()
48
40
49
- def compute_shared_secret ( sender_private_key : str , receiver_public_key : str ) -> str :
50
- public_key = PublicKey ( bytes . fromhex ( "02" + receiver_public_key ), True )
51
- return public_key . ecdh ( bytes . fromhex ( sender_private_key ), hashfn = copy_x ). hex ()
41
+ def tweak_add ( self , scalar : bytes ) -> bytes :
42
+ sk = secp256k1 . PrivateKey ( self . raw_secret )
43
+ return sk . tweak_add ( scalar )
52
44
53
- def encrypt_message (content : str , shared_secret : str ) -> str :
54
- iv = os .urandom (16 )
45
+ def compute_shared_secret (self , public_key_hex : str ) -> bytes :
46
+ pk = secp256k1 .PublicKey (bytes .fromhex ("02" + public_key_hex ), True )
47
+ return pk .ecdh (self .raw_secret , hashfn = copy_x )
55
48
56
- cipher = Cipher ( algorithms . AES ( bytes . fromhex ( shared_secret )), modes . CBC ( iv ))
57
- padder = padding .PKCS7 (128 ).padder ()
58
- padded_data = padder .update (content .encode ()) + padder .finalize ()
49
+ def encrypt_message ( self , message : str , public_key_hex : str ) -> str :
50
+ padder = padding .PKCS7 (128 ).padder ()
51
+ padded_data = padder .update (message .encode ()) + padder .finalize ()
59
52
60
- encryptor = cipher . encryptor ( )
61
- encrypted_message = encryptor . update ( padded_data ) + encryptor . finalize ( )
53
+ iv = secrets . token_bytes ( 16 )
54
+ cipher = Cipher ( algorithms . AES ( self . compute_shared_secret ( public_key_hex )), modes . CBC ( iv ) )
62
55
63
- return f"{ base64 .b64encode (encrypted_message ).decode ()} ?iv={ base64 .b64encode (iv ).decode ()} "
56
+ encryptor = cipher .encryptor ()
57
+ encrypted_message = encryptor .update (padded_data ) + encryptor .finalize ()
64
58
65
- def decrypt_message (encoded_message : str , shared_secret : str ) -> str :
66
- encoded_data = encoded_message .split ('?iv=' )
67
- encoded_content , encoded_iv = encoded_data [0 ], encoded_data [1 ]
59
+ return f"{ base64 .b64encode (encrypted_message ).decode ()} ?iv={ base64 .b64encode (iv ).decode ()} "
68
60
69
- encrypted_content = base64 .b64decode (encoded_content )
70
- iv = base64 .b64decode (encoded_iv )
61
+ def decrypt_message (self , encoded_message : str , public_key_hex : str ) -> str :
62
+ encoded_data = encoded_message .split ('?iv=' )
63
+ encoded_content , encoded_iv = encoded_data [0 ], encoded_data [1 ]
71
64
72
- cipher = Cipher ( algorithms . AES ( bytes . fromhex ( shared_secret )), modes . CBC ( iv ) )
73
- decryptor = cipher . decryptor ( )
74
- decrypted_message = decryptor . update ( encrypted_content ) + decryptor . finalize ( )
65
+ iv = base64 . b64decode ( encoded_iv )
66
+ cipher = Cipher ( algorithms . AES ( self . compute_shared_secret ( public_key_hex )), modes . CBC ( iv ) )
67
+ encrypted_content = base64 . b64decode ( encoded_content )
75
68
76
- unpadder = padding . PKCS7 ( 128 ). unpadder ()
77
- unpadded_data = unpadder .update (decrypted_message ) + unpadder .finalize ()
69
+ decryptor = cipher . decryptor ()
70
+ decrypted_message = decryptor .update (encrypted_content ) + decryptor .finalize ()
78
71
79
- return unpadded_data .decode ()
72
+ unpadder = padding .PKCS7 (128 ).unpadder ()
73
+ unpadded_data = unpadder .update (decrypted_message ) + unpadder .finalize ()
80
74
81
- def sign_message (hash : str , private_key : str ) -> str :
82
- sk = PrivateKey (bytes .fromhex (private_key ))
83
- sig = sk .schnorr_sign (bytes .fromhex (hash ), None , raw = True )
84
- return sig .hex ()
75
+ return unpadded_data .decode ()
85
76
86
- def verify_message (hash : str , sig : str , public_key : str ) -> bool :
87
- pk = PublicKey (bytes .fromhex ("02" + public_key ), True )
88
- return pk .schnorr_verify (bytes .fromhex (hash ), bytes .fromhex (sig ), None , True )
77
+ def sign_message_hash (self , hash : bytes ) -> str :
78
+ sk = secp256k1 .PrivateKey (self .raw_secret )
79
+ sig = sk .schnorr_sign (hash , None , raw = True )
80
+ return sig .hex ()
89
81
90
82
ffi = FFI ()
91
83
@ffi .callback ("int (unsigned char *, const unsigned char *, const unsigned char *, void *)" )
92
84
def copy_x (output , x32 , y32 , data ):
93
85
ffi .memmove (output , x32 , 32 )
94
- return 1
95
-
86
+ return 1
0 commit comments