11import base64
2+ import ipaddress
23import os
4+ import re
35import shutil
46import sys
57from pathlib import Path
810
911from pyencrypt import __description__ , __version__
1012from pyencrypt .decrypt import decrypt_file
11- from pyencrypt .encrypt import (can_encrypt , encrypt_file , encrypt_key , generate_so_file )
13+ from pyencrypt .encrypt import (can_encrypt , encrypt_file , encrypt_key ,
14+ generate_so_file )
1215from pyencrypt .generate import generate_aes_key
13- from pyencrypt .license import generate_license_file , MIN_DATETIME , MAX_DATETIME
16+ from pyencrypt .license import MAX_DATETIME , MIN_DATETIME , generate_license_file
1417
1518VERSION = f"""\
1619 _
4245LICENSE_FILE_NAME = click .style ("license.lic" , blink = True , fg = 'blue' )
4346
4447SUCCESS_ANSI = click .style ('successfully' , fg = 'green' )
45- INVALID_KEY_MSG = click .style ('Your encryption key is invalid.' , fg = 'red' )
48+ INVALID_KEY_MSG = click .style ('Your encryption 🔑 is invalid.' , fg = 'red' )
4649
4750INVALID_EXPIRED_MSG = 'Expired before date must be less than expired after date.'
4851
6871DATETIME_FORMATS = ['%Y-%m-%dT%H:%M:%S %z' , '%Y-%m-%d %H:%M:%S' , '%Y-%m-%d' ]
6972
7073
71- def _check_key (key : str ) -> bool :
72- return not (len (key ) % 4 or len (base64 .b64decode (key )) % 16 )
74+ class KeyParamType (click .ParamType ):
75+ name = 'key'
76+
77+ def _check_key (self , key : str ) -> bool :
78+ return not (len (key ) % 4 or len (base64 .b64decode (key )) % 16 )
79+
80+ def convert (self , value , param , ctx ) -> str :
81+ value = click .STRING .convert (value , param , ctx )
82+ if not self ._check_key (value ):
83+ self .fail (INVALID_KEY_MSG , param , ctx )
84+ return value
85+
86+ def get_metavar (self , param ):
87+ return '🔑'
88+
89+ def __repr__ (self ) -> str :
90+ return "KEY"
91+
92+
93+ class MacAddressParamType (click .ParamType ):
94+ name = 'mac_address'
95+ pattern = re .compile (r'^([0-9a-fA-F]{2}[:-]){5}([0-9a-fA-F]{2})$' )
96+
97+ def convert (self , value , param , ctx ) -> str :
98+ value = click .STRING .convert (value , param , ctx )
99+ if not self .pattern .match (value ):
100+ self .fail (f'{ value } is not a valid mac address' , param , ctx )
101+ return value
102+
103+ def get_metavar (self , param ):
104+ return '01:23:45:67:89:AB'
105+
106+ def __repr__ (self ) -> str :
107+ return "MacAddress"
108+
109+
110+ class IPv4AddressParamType (click .ParamType ):
111+ name = 'ipv4_address'
112+
113+ def convert (self , value , param , ctx ) -> str :
114+ value = click .STRING .convert (value , param , ctx )
115+ try :
116+ return str (ipaddress .IPv4Address (value ))
117+ except ValueError :
118+ self .fail (f'{ value } is not a valid IPv4 Address' , param , ctx )
119+
120+ def get_metavar (self , param ):
121+ return '192.168.0.1'
122+
123+ def __repr__ (self ) -> str :
124+ return "Ipv4Address"
125+
126+ class CustomParamType :
127+ KEY = KeyParamType ()
128+ MAC_ADDR = MacAddressParamType ()
129+ IPV4_ADDR = IPv4AddressParamType ()
73130
74131
75132@click .group ()
@@ -82,19 +139,17 @@ def cli():
82139@cli .command (name = 'encrypt' )
83140@click .argument ('pathname' , type = click .Path (exists = True , resolve_path = True ))
84141@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 )
142+ @click .option ('-k' , '--key' , default = None , help = KEY_OPTION_HELP , type = CustomParamType . KEY )
86143@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 )
144+ @click .option ('-m' , '--bind-mac' , 'mac' , default = None , help = 'Bind mac address to encrypted file' , type = CustomParamType . MAC_ADDR )
145+ @click .option ('-4' , '--bind-ipv4' , 'ipv4' , default = None , help = 'Bind ipv4 address to encrypted file' , type = CustomParamType . IPV4_ADDR )
89146@click .option ('-b' , '--before' , default = MAX_DATETIME , help = 'License is invalid before this date.' , type = click .DateTime (formats = DATETIME_FORMATS ))
90147@click .option ('-a' , '--after' , default = MIN_DATETIME , help = 'License is invalid after this date.' , type = click .DateTime (formats = DATETIME_FORMATS ))
91148@click .confirmation_option ('-y' , '--yes' , prompt = 'Are you sure you want to encrypt your python file?' , help = 'Automatically answer yes for confirm questions.' )
92149@click .help_option ('-h' , '--help' )
93150@click .pass_context
94151def encrypt_command (ctx , pathname , replace , key , with_license , mac , ipv4 , before , after ):
95152 """Encrypt your python code"""
96- if key is not None and not _check_key (key ):
97- ctx .fail (INVALID_KEY_MSG )
98153 if key is None :
99154 key = generate_aes_key ().decode ()
100155 click .echo (f'Your randomly encryption 🔑 is { click .style (key ,underline = True , fg = "yellow" )} ' )
@@ -130,21 +185,19 @@ def encrypt_command(ctx, pathname, replace, key, with_license, mac, ipv4, before
130185 generate_so_file (cipher_key , d , n )
131186 if with_license is True :
132187 generate_license_file (key , Path (os .getcwd ()), after , before , mac , ipv4 )
133- ctx .echo (FINISH_GENERATE_LICENSE_MSG )
188+ click .echo (FINISH_GENERATE_LICENSE_MSG )
134189 click .echo (FINISH_ENCRYPT_MSG )
135190
136191
137192@cli .command (name = 'decrypt' )
138193@click .argument ('pathname' , type = click .Path (exists = True , resolve_path = True ))
139194@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 )
195+ @click .option ('-k' , '--key' , required = True , help = 'Your encryption key.' , type = CustomParamType . KEY )
141196@click .help_option ('-h' , '--help' )
142197@click .pass_context
143198def decrypt_command (ctx , pathname , replace , key ):
144199 """Decrypt encrypted pye file"""
145200 path = Path (pathname )
146- if not _check_key (key ):
147- ctx .fail (INVALID_KEY_MSG )
148201
149202 if path .is_file ():
150203 if replace :
@@ -173,30 +226,26 @@ def decrypt_command(ctx, pathname, replace, key):
173226
174227
175228@cli .command (name = 'generate' )
176- @click .option ('-k' , '--key' , required = True , help = 'Your encryption key.' , type = click . STRING )
229+ @click .option ('-k' , '--key' , required = True , help = 'Your encryption key.' , type = CustomParamType . KEY )
177230@click .help_option ('-h' , '--help' )
178231@click .pass_context
179232def generate_loader (ctx , key ):
180233 """Generate loader file using specified key"""
181- if not _check_key (key ):
182- ctx .fail (INVALID_KEY_MSG )
183234 cipher_key , d , n = encrypt_key (key .encode ())
184235 generate_so_file (cipher_key , d , n , Path (os .getcwd ()))
185236 click .echo (FINISH_GENERATE_LOADER_MSG )
186237
187238
188239@cli .command (name = 'license' )
189240@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 )
241+ @click .option ('-k' , '--key' , required = True , help = 'Your encryption key.' , type = CustomParamType . KEY )
242+ @click .option ('-m' , '--bind-mac' , help = 'Your mac address.' , type = CustomParamType . MAC_ADDR )
243+ @click .option ('-4' , '--bind-ipv4' , help = 'Your ipv4 address.' , type = CustomParamType . IPV4_ADDR )
193244@click .option ('-b' , '--before' , default = MAX_DATETIME , help = 'License is invalid before this date.' , type = click .DateTime (formats = DATETIME_FORMATS ))
194245@click .option ('-a' , '--after' , default = MIN_DATETIME , help = 'License is invalid after this date.' , type = click .DateTime (formats = DATETIME_FORMATS ))
195246@click .pass_context
196247def generate_license (ctx , key , mac , ipv4 , before , after ):
197248 """Generate license file using specified key"""
198- if not _check_key (key ):
199- ctx .fail (INVALID_KEY_MSG )
200249 if before > after :
201250 ctx .fail (INVALID_EXPIRED_MSG )
202251
0 commit comments