HighSide Open Sources its Encryption Code
Here we open source the encryption modules used in the ClearChat client. We share it under the MIT license in the hope that it will be of use to the public.
AESEncryption.py does authenticated AES256 encryption and decryption.
highlevelcrypto.py does public key operations like encryption and signing.
example.py shows how to use them. It runs under Python 2.7 and there are two prerequisites: pyelliptic and OpenSSL.
AESEncryption.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | import os import hashlib import hmac import pyelliptic def AES_CTRWithHMACEncrypt(keySeedMaterial, data): assert len (keySeedMaterial) = = 32 keys = hashlib.sha512(hashlib.sha512(keySeedMaterial).digest()).digest() HMACKey = keys[: 32 ] encryptionKey = keys[ 32 :] iv = pyelliptic.Cipher.gen_IV( 'aes-256-ctr' ) ctx = pyelliptic.Cipher(encryptionKey, iv, 1 , ciphername = 'aes-256-ctr' ) ciphertext = ctx.update(data) ciphertext + = ctx.final() hmacDigest = hmac.new(HMACKey, iv + ciphertext, hashlib.sha512).digest() return hmacDigest + iv + ciphertext def AES_CTRWithHMACDecrypt(keySeedMaterial, data): assert len (keySeedMaterial) = = 32 keys = hashlib.sha512(hashlib.sha512(keySeedMaterial).digest()).digest() HMACKey = keys[: 32 ] encryptionKey = keys[ 32 :] includedHMACDigest = data[: 64 ] iv = data[ 64 : 80 ] ciphertext = data[ 80 :] calculatedHMACDigest = hmac.new(HMACKey, iv + ciphertext, hashlib.sha512).digest() if not hmac.compare_digest(includedHMACDigest, calculatedHMACDigest): # constant time comparison raise Exception( "HMAC failed. Cannot decrypt data. The data has been modified." ) ctx2 = pyelliptic.Cipher(encryptionKey, iv, 0 , ciphername = 'aes-256-ctr' ) return ctx2.ciphering(ciphertext) |
highlevelcrypto.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | import pyelliptic from pyelliptic import OpenSSL def makeCryptor(private_key): public_key = pointMult(private_key) privkey_bin = '\x02\xca\x00\x20' + private_key pubkey_bin = '\x02\xca\x00\x20' + public_key[ 1 : 33 ] + '\x00\x20' + public_key[ 33 :] cryptor = pyelliptic.ECC(curve = 'secp256k1' ,privkey = privkey_bin,pubkey = pubkey_bin) return cryptor def addOpenSSLKeyFormatting(pubkey): return '\x02\xca\x00\x20' + pubkey[ 1 : 33 ] + '\x00\x20' + pubkey[ 33 :] def makePubCryptor(pubkey): pubkey = addOpenSSLKeyFormatting(pubkey) return pyelliptic.ECC(curve = 'secp256k1' ,pubkey = pubkey) # Converts private key into public key def privToPub(private_key): return pointMult(private_key) def encrypt(msg,pubkey): return pyelliptic.ECC(curve = 'secp256k1' ).encrypt(msg,addOpenSSLKeyFormatting(pubkey)) def decrypt(msg,privkey): return makeCryptor(privkey).decrypt(msg) def sign(msg,privKey): return makeCryptor(privKey).sign(msg) def verify(msg,sig,pubkey): try : return makePubCryptor(pubkey).verify(sig,msg) except : return False # Does an EC point multiplication; turns a private key into a public key. def pointMult(secret): while True : try : k = OpenSSL.EC_KEY_new_by_curve_name(OpenSSL.get_curve( 'secp256k1' )) priv_key = OpenSSL.BN_bin2bn(secret, 32 , None ) group = OpenSSL.EC_KEY_get0_group(k) pub_key = OpenSSL.EC_POINT_new(group) OpenSSL.EC_POINT_mul(group, pub_key, priv_key, None , None , None ) OpenSSL.EC_KEY_set_private_key(k, priv_key) OpenSSL.EC_KEY_set_public_key(k, pub_key) size = OpenSSL.i2o_ECPublicKey(k, None ) mb = OpenSSL.create_string_buffer(size) OpenSSL.i2o_ECPublicKey(k, OpenSSL.byref(OpenSSL.pointer(mb))) OpenSSL.EC_POINT_free(pub_key) OpenSSL.BN_free(priv_key) OpenSSL.EC_KEY_free(k) return mb.raw except Exception as e: """ Evidently, this type of error can occur very rarely. If we just try again it will work. File "highlevelcrypto.py", line 35, in pointMult group = OpenSSL.EC_KEY_get0_group(k) WindowsError: exception: access violation reading 0x0000000000000008 """ import traceback import time traceback.print_exc() time.sleep( 0.2 ) |
example.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | import os import sys import struct from AESEncryption import AES_CTRWithHMACEncrypt, AES_CTRWithHMACDecrypt import highlevelcrypto # First let's try a simple symmetric encryption operation. key = os.urandom( 32 ) message = """And Saint Attila raised the hand grenade up on high, saying, "O Lord, bless this Thy hand grenade that, with it, Thou mayest blow Thine enemies to tiny bits in Thy mercy." And the Lord did grin, and the people did feast upon the lambs and sloths and carp and anchovies and orangutans and breakfast cereals and fruit bats and large chu--\"\n""" encryptedData = AES_CTRWithHMACEncrypt(key, message) decryptedData = AES_CTRWithHMACDecrypt(key, encryptedData) print "Decrypted data:" , decryptedData # Very simple! Now let's try asymetric encryption. Alice and Bob each generate # a private and public key for themselves. alicePrivateKey = os.urandom( 32 ) bobPrivateKey = os.urandom( 32 ) alicePublicKey = highlevelcrypto.privToPub(alicePrivateKey) bobPublicKey = highlevelcrypto.privToPub(bobPrivateKey) # Alice and Bob acquire each other's public keys. # Suppose Alice wants to send a message to Bob. messageSender = "Alice" message = """And the Lord spake, saying, "First shalt thou take out the Holy Pin. Then, shalt thou count to three. No more. No less. Three shalt be the number thou shalt count, and the number of the counting shall be three. Four shalt thou not count, nor either count thou two, excepting that thou then proceed to three. Five is right out. Once the number three, being the third number, be reached, then, lobbest thou thy Holy Hand Grenade of Antioch towards thy foe, who, being naughty in My sight, shall snuff it." Amen""" # In order to prevent forwarding attacks, it is important to include the # message sender in the encrypted message so that Bob can verify it later. message = struct.pack( '>L' , len (messageSender)) + messageSender + message encryptedMessage = highlevelcrypto.encrypt(message, bobPublicKey) signature = highlevelcrypto.sign(encryptedMessage, alicePrivateKey) # Alice sends Bob the encrypted message and the signature. if highlevelcrypto.verify(encryptedMessage, signature, alicePublicKey): print "Signature verification passed." else : print "Signature verification failed!" sys.exit( 1 ) # Bob is now sure that this message came from Alice. decryptedData = highlevelcrypto.decrypt(encryptedMessage, bobPrivateKey) lengthOfWritersName = struct.unpack( '>L' , decryptedData[: 4 ])[ 0 ] writersName = decryptedData[ 4 : 4 + lengthOfWritersName] decryptedMessage = decryptedData[ 4 + lengthOfWritersName:] # Bob must verify that the sender of this message matches the person who # apparently sent it and signed it. if writersName ! = "Alice" : print "Sender is not who I expected! Ignoring message completely." # Do not feel tempted to show the message. Always block messages when # security checks fail. sys.exit( 1 ) # Bob is now sure that this message came from Alice *and* that Alice wrote it. print "Message was written by" , writersName print "Decrypted message:" , decryptedMessage |