55from pathlib import Path
66
77import click
8+
89from pyencrypt import __description__ , __version__
910from pyencrypt .decrypt import decrypt_file
10- from pyencrypt .encrypt import (can_encrypt , encrypt_file , encrypt_key ,
11- generate_so_file )
11+ from pyencrypt .encrypt import (can_encrypt , encrypt_file , encrypt_key , generate_so_file )
1212from pyencrypt .generate import generate_aes_key
13+ from pyencrypt .license import generate_license_file , MIN_DATETIME , MAX_DATETIME
1314
1415VERSION = f"""\
1516 _
3334PYTHON_MAJOR , PYTHON_MINOR = sys .version_info [:2 ]
3435LAODER_FILE_NAME = click .style (
3536 "encrypted/loader.cpython-{major}{minor}{abi}-{platform}.so" .format (
36- major = PYTHON_MAJOR ,
37- minor = PYTHON_MINOR ,
38- abi = sys .abiflags ,
39- platform = sys .platform ),
37+ major = PYTHON_MAJOR , minor = PYTHON_MINOR , abi = sys .abiflags , platform = sys .platform
38+ ),
4039 blink = True ,
41- fg = 'blue' )
40+ fg = 'blue'
41+ )
42+ LICENSE_FILE_NAME = click .style ("license.lic" , blink = True , fg = 'blue' )
4243
4344SUCCESS_ANSI = click .style ('successfully' , fg = 'green' )
4445INVALID_KEY_MSG = click .style ('Your encryption key is invalid.' , fg = 'red' )
4546
47+ INVALID_EXPIRED_MSG = 'Expired before date must be less than expired after date.'
48+
4649FINISH_ENCRYPT_MSG = f"""
4750Encryption completed { SUCCESS_ANSI } .
4851Please copy { LAODER_FILE_NAME } into your encrypted directory.
5457Decryption completed { SUCCESS_ANSI } . Your origin source code has be put: %s
5558"""
5659
57- FINISH_GENERATE_MSG = f"""
60+ FINISH_GENERATE_LOADER_MSG = f"""
5861Generate loader file { SUCCESS_ANSI } . Your loader file is located in { LAODER_FILE_NAME }
5962"""
6063
64+ FINISH_GENERATE_LICENSE_MSG = f"""
65+ Generate license file { SUCCESS_ANSI } . Your license file is located in { LICENSE_FILE_NAME }
66+ """
67+
68+ DATETIME_FORMATS = ['%Y-%m-%dT%H:%M:%S %z' , '%Y-%m-%d %H:%M:%S' , '%Y-%m-%d' ]
69+
6170
6271def _check_key (key : str ) -> bool :
6372 return not (len (key ) % 4 or len (base64 .b64decode (key )) % 16 )
@@ -72,33 +81,26 @@ def cli():
7281
7382@cli .command (name = 'encrypt' )
7483@click .argument ('pathname' , type = click .Path (exists = True , resolve_path = True ))
75- @click .option ('-i' ,
76- '--in-place' ,
77- 'replace' ,
78- default = False ,
79- help = 'make changes to files in place' ,
80- is_flag = True )
81- @click .option ('-k' ,
82- '--key' ,
83- default = None ,
84- help = KEY_OPTION_HELP ,
85- type = click .STRING )
86- @click .confirmation_option (
87- '-y' ,
88- '--yes' ,
89- prompt = 'Are you sure you want to encrypt your python file?' ,
90- help = 'Automatically answer yes for confirm questions.' )
84+ @click .option ('-i' , '--in-place' , 'replace' , default = False , help = 'make changes to files in place' , is_flag = True )
85+ @click .option ('-k' , '--key' , default = None , help = KEY_OPTION_HELP , type = click .STRING )
86+ @click .option ('--with-license' , default = False , help = 'Add license to encrypted file' , is_flag = True )
87+ @click .option ('-m' , '--bind-mac' , 'mac' , default = None , help = 'Bind mac address to encrypted file' , type = click .STRING )
88+ @click .option ('-4' , '--bind-ipv4' , 'ipv4' , default = None , help = 'Bind ipv4 address to encrypted file' , type = click .STRING )
89+ @click .option ('-b' , '--before' , default = MAX_DATETIME , help = 'License is invalid before this date.' , type = click .DateTime (formats = DATETIME_FORMATS ))
90+ @click .option ('-a' , '--after' , default = MIN_DATETIME , help = 'License is invalid after this date.' , type = click .DateTime (formats = DATETIME_FORMATS ))
91+ @click .confirmation_option ('-y' , '--yes' , prompt = 'Are you sure you want to encrypt your python file?' , help = 'Automatically answer yes for confirm questions.' )
9192@click .help_option ('-h' , '--help' )
9293@click .pass_context
93- def encrypt_command (ctx , pathname , replace , key ):
94+ def encrypt_command (ctx , pathname , replace , key , with_license , mac , ipv4 , before , after ):
9495 """Encrypt your python code"""
9596 if key is not None and not _check_key (key ):
9697 ctx .fail (INVALID_KEY_MSG )
9798 if key is None :
9899 key = generate_aes_key ().decode ()
99- click .echo (
100- f'Your randomly encryption 🔑 is { click .style (key ,underline = True , fg = "yellow" )} '
101- )
100+ click .echo (f'Your randomly encryption 🔑 is { click .style (key ,underline = True , fg = "yellow" )} ' )
101+
102+ if before > after :
103+ ctx .fail (INVALID_EXPIRED_MSG )
102104
103105 path = Path (pathname )
104106
@@ -126,22 +128,16 @@ def encrypt_command(ctx, pathname, replace, key):
126128
127129 cipher_key , d , n = encrypt_key (key .encode ()) # 需要放进导入器中
128130 generate_so_file (cipher_key , d , n )
131+ if with_license is True :
132+ generate_license_file (key , Path (os .getcwd ()), after , before , mac , ipv4 )
133+ ctx .echo (FINISH_GENERATE_LICENSE_MSG )
129134 click .echo (FINISH_ENCRYPT_MSG )
130135
131136
132137@cli .command (name = 'decrypt' )
133138@click .argument ('pathname' , type = click .Path (exists = True , resolve_path = True ))
134- @click .option ('-i' ,
135- '--in-place' ,
136- 'replace' ,
137- default = False ,
138- help = 'make changes to files in place' ,
139- is_flag = True )
140- @click .option ('-k' ,
141- '--key' ,
142- required = True ,
143- help = 'Your encryption key.' ,
144- type = click .STRING )
139+ @click .option ('-i' , '--in-place' , 'replace' , default = False , help = 'make changes to files in place' , is_flag = True )
140+ @click .option ('-k' , '--key' , required = True , help = 'Your encryption key.' , type = click .STRING )
145141@click .help_option ('-h' , '--help' )
146142@click .pass_context
147143def decrypt_command (ctx , pathname , replace , key ):
@@ -177,20 +173,35 @@ def decrypt_command(ctx, pathname, replace, key):
177173
178174
179175@cli .command (name = 'generate' )
180- @click .option ('-k' ,
181- '--key' ,
182- required = True ,
183- help = 'Your encryption key.' ,
184- type = click .STRING )
176+ @click .option ('-k' , '--key' , required = True , help = 'Your encryption key.' , type = click .STRING )
185177@click .help_option ('-h' , '--help' )
186178@click .pass_context
187179def generate_loader (ctx , key ):
188180 """Generate loader file using specified key"""
189181 if not _check_key (key ):
190182 ctx .fail (INVALID_KEY_MSG )
191183 cipher_key , d , n = encrypt_key (key .encode ())
192- generate_so_file (cipher_key , d , n )
193- click .echo (FINISH_GENERATE_MSG )
184+ generate_so_file (cipher_key , d , n , Path (os .getcwd ()))
185+ click .echo (FINISH_GENERATE_LOADER_MSG )
186+
187+
188+ @cli .command (name = 'license' )
189+ @click .help_option ('-h' , '--help' )
190+ @click .option ('-k' , '--key' , required = True , help = 'Your encryption key.' , type = click .STRING )
191+ @click .option ('-m' , '--bind-mac' , help = 'Your mac address.' , type = click .STRING )
192+ @click .option ('-4' , '--bind-ipv4' , help = 'Your ipv4 address.' , type = click .STRING )
193+ @click .option ('-b' , '--before' , default = MAX_DATETIME , help = 'License is invalid before this date.' , type = click .DateTime (formats = DATETIME_FORMATS ))
194+ @click .option ('-a' , '--after' , default = MIN_DATETIME , help = 'License is invalid after this date.' , type = click .DateTime (formats = DATETIME_FORMATS ))
195+ @click .pass_context
196+ def generate_license (ctx , key , mac , ipv4 , before , after ):
197+ """Generate license file using specified key"""
198+ if not _check_key (key ):
199+ ctx .fail (INVALID_KEY_MSG )
200+ if before > after :
201+ ctx .fail (INVALID_EXPIRED_MSG )
202+
203+ generate_license_file (key , Path (os .getcwd ()), after , before , mac , ipv4 )
204+ click .echo (FINISH_GENERATE_LICENSE_MSG )
194205
195206
196207if __name__ == '__main__' :
0 commit comments