├── LICENSE ├── README.md ├── cryptocode.py └── dist ├── cryptocode-0.1-py3-none-any.whl └── cryptocode-0.1.tar.gz /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 gdavid7 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cryptocode 2 | [![Downloads](https://static.pepy.tech/badge/cryptocode)](https://pepy.tech/project/cryptocode) 3 | 4 | Python library used to encrypt and decrypt strings in the simplest possible way, while also being incredibly secure. 5 | ## Requirements 6 | 7 | 8 | - **Python** 3 or later. 9 | 10 | ## Installation 11 | 12 | Install some Python utilities along with some libraries and other stuff: 13 | 14 | ~~~ 15 | python -m pip install cryptocode 16 | ~~~ 17 | 18 | ## Basic usage 19 | Encrypting a message: 20 | 21 | ~~~ 22 | >>> import cryptocode 23 | >>> myEncryptedMessage = cryptocode.encrypt("I like trains", "password123") 24 | >>> print(myEncryptedMessage) 25 | M+Wykmlub0z7FhEdmA==*PvAbXRNx0SiSDHHxLsKZ5w==*ihQM/fdkgrX3G+yOItyAUQ==*QFNDmuUP1ysgo01/P2MNpg== 26 | ~~~ 27 | 28 | The first parameter is the string you want to encrypt. The second parameter is the password, which will be used for decrypting the string. 29 | 30 | Decrypting a message" 31 | ~~~ 32 | >>> import cryptocode 33 | >>> myDecryptedMessage = cryptocode.decrypt("M+Wykmlub0z7FhEdmA==*PvAbXRNx0SiSDHHxLsKZ5w==*ihQM/fdkgrX3G+yOItyAUQ==*QFNDmuUP1ysgo01/P2MNpg==", "password123") 34 | >>> print(myDecryptedMessage) 35 | I like trains 36 | ~~~ 37 | The first parameter is the encrypted string and the second parameter is the password. If the password is incorrect, decrypt function will return `False`. 38 | 39 | ## Example 40 | Here, we will be creating a simple "trial product key". This is useful if you have software that you would like people to use temporarily. 41 | In this example, we will be letting the user use the product for 2 hours. The password we will be using is ``cryptocode is amazing``. 42 | 43 | Code on the server side: 44 | ~~~ 45 | import cryptocode 46 | import time 47 | hours = 2 48 | messageToEncrypt = str(time.time() + hours * 60 * 60) 49 | ## Hours * 60 * 60 is necessary because we need to turn the hours into seconds, since the timestamp is in seconds. 50 | cryptocode.encrypt(messageToEncrypt, "cryptocode is amazing") 51 | ~~~ 52 | 53 | Code on the client side: 54 | ~~~ 55 | import cryptocode 56 | import time 57 | import sys 58 | #Function to verify that the key is valid: 59 | def check_valid(key): 60 | message = cryptocode.decrypt(key, 'cryptocode is amazing') 61 | if message == False: 62 | #The key is incorrect! 63 | return False 64 | if float(message) >= time.time(): 65 | return True 66 | else: 67 | #The key has expired! 68 | return False 69 | userKeyInput = input("Please enter your product key.") 70 | keyChecked = check_valid(userKeyInput) 71 | if keyChecked == True: 72 | print("You are good to go!") 73 | if keyChecked == False: 74 | print("You have either entered an invalid key or your time has expired. Sorry!") 75 | sys.exit() 76 | ~~~ 77 | 78 | ## Reasons for using this library 79 | 80 | There are countless python libraries for encoding and decoding messages through various means. Why is this one better? 81 | 82 | Cryptocode is meant for people who simply want an abstraction. This library is by far the easiest to use out of any cryptography library because there are only two functions, encode and decode. 83 | This is an example of using AES-GCM encryption to provide encryption and integrity with a regular cryptography library: 84 | ~~~ 85 | import binascii, time 86 | from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d 87 | 88 | from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes 89 | from cryptography.hazmat.backends import default_backend 90 | from cryptography.exceptions import InvalidTag 91 | 92 | backend = default_backend() 93 | 94 | def aes_gcm_encrypt(message: bytes, key: bytes) -> bytes: 95 | current_time = int(time.time()).to_bytes(8, 'big') 96 | algorithm = algorithms.AES(key) 97 | iv = secrets.token_bytes(algorithm.block_size // 8) 98 | cipher = Cipher(algorithm, modes.GCM(iv), backend=backend) 99 | encryptor = cipher.encryptor() 100 | encryptor.authenticate_additional_data(current_time) 101 | ciphertext = encryptor.update(message) + encryptor.finalize() 102 | return b64e(current_time + iv + ciphertext + encryptor.tag) 103 | 104 | def aes_gcm_decrypt(token: bytes, key: bytes, ttl=None) -> bytes: 105 | algorithm = algorithms.AES(key) 106 | try: 107 | data = b64d(token) 108 | except (TypeError, binascii.Error): 109 | raise InvalidToken 110 | timestamp, iv, tag = data[:8], data[8:algorithm.block_size // 8 + 8], data[-16:] 111 | if ttl is not None: 112 | current_time = int(time.time()) 113 | time_encrypted, = int.from_bytes(data[:8], 'big') 114 | if time_encrypted + ttl < current_time or current_time + 60 < time_encrypted: 115 | # too old or created well before our current time + 1 h to account for clock skew 116 | raise InvalidToken 117 | cipher = Cipher(algorithm, modes.GCM(iv, tag), backend=backend) 118 | decryptor = cipher.decryptor() 119 | decryptor.authenticate_additional_data(timestamp) 120 | ciphertext = data[8 + len(iv):-16] 121 | return decryptor.update(ciphertext) + decryptor.finalize() 122 | ~~~ 123 | 124 | As you can see, this can be unnecessary if you only need the encoding for basic reasons. In summary, cryptocode is better for most people because it is an abstraction: it provides a simple input and output without the user needing to know how it works. 125 | -------------------------------------------------------------------------------- /cryptocode.py: -------------------------------------------------------------------------------- 1 | from base64 import b64encode, b64decode 2 | import hashlib 3 | from Cryptodome.Cipher import AES 4 | from Cryptodome.Random import get_random_bytes 5 | def encrypt(message , password): 6 | plain_text = message 7 | 8 | # generate a random salt 9 | 10 | salt = get_random_bytes(AES.block_size) 11 | 12 | # use the Scrypt KDF to get a private key from the password 13 | 14 | private_key = hashlib.scrypt( 15 | password.encode(), 16 | salt=salt, 17 | n=2 ** 14, 18 | r=8, 19 | p=1, 20 | dklen=32, 21 | ) 22 | 23 | # create cipher config 24 | 25 | cipher_config = AES.new(private_key, AES.MODE_GCM) 26 | 27 | # return a dictionary with the encrypted text 28 | 29 | (cipher_text, tag) = \ 30 | cipher_config.encrypt_and_digest(bytes(plain_text, 'utf-8')) 31 | encryptedDict = { 32 | 'cipher_text': b64encode(cipher_text).decode('utf-8'), 33 | 'salt': b64encode(salt).decode('utf-8'), 34 | 'nonce': b64encode(cipher_config.nonce).decode('utf-8'), 35 | 'tag': b64encode(tag).decode('utf-8'), 36 | } 37 | encryptedString = encryptedDict['cipher_text'] + '*' \ 38 | + encryptedDict['salt'] + '*' + encryptedDict['nonce'] + '*' \ 39 | + encryptedDict['tag'] 40 | return encryptedString 41 | 42 | 43 | def decrypt(enc_dict, password): 44 | enc_dict = enc_dict.split('*') 45 | try: 46 | enc_dict = { 47 | 'cipher_text': enc_dict[0], 48 | 'salt': enc_dict[1], 49 | 'nonce': enc_dict[2], 50 | 'tag': enc_dict[3], 51 | } 52 | 53 | # decode the dictionary entries from base64 54 | 55 | salt = b64decode(enc_dict['salt']) 56 | cipher_text = b64decode(enc_dict['cipher_text']) 57 | nonce = b64decode(enc_dict['nonce']) 58 | tag = b64decode(enc_dict['tag']) 59 | 60 | # generate the private key from the password and salt 61 | 62 | private_key = hashlib.scrypt( 63 | password.encode(), 64 | salt=salt, 65 | n=2 ** 14, 66 | r=8, 67 | p=1, 68 | dklen=32, 69 | ) 70 | 71 | # create the cipher config 72 | 73 | cipher = AES.new(private_key, AES.MODE_GCM, nonce=nonce) 74 | 75 | # decrypt the cipher text 76 | 77 | decrypted = cipher.decrypt_and_verify(cipher_text, tag) 78 | except: 79 | return False 80 | 81 | return decrypted.decode('UTF-8') 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /dist/cryptocode-0.1-py3-none-any.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdavid7/cryptocode/3f68923588e2dc98e543514b41b63b95c167614d/dist/cryptocode-0.1-py3-none-any.whl -------------------------------------------------------------------------------- /dist/cryptocode-0.1.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdavid7/cryptocode/3f68923588e2dc98e543514b41b63b95c167614d/dist/cryptocode-0.1.tar.gz --------------------------------------------------------------------------------