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 osimport hashlibimport hmacimport pyellipticdef 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 + ciphertextdef 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 pyellipticfrom pyelliptic import OpenSSLdef 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 cryptordef 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 keydef 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 osimport sysimport structfrom AESEncryption import AES_CTRWithHMACEncrypt, AES_CTRWithHMACDecryptimport 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, "OLord, bless this Thy hand grenade that, with it, Thou mayest blow Thine enemiesto tiny bits in Thy mercy." And the Lord did grin, and the people did feastupon the lambs and sloths and carp and anchovies and orangutans and breakfastcereals 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 HolyPin. Then, shalt thou count to three. No more. No less. Three shalt be thenumber thou shalt count, and the number of the counting shall be three. Fourshalt thou not count, nor either count thou two, excepting that thou thenproceed to three. Five is right out. Once the number three, being the thirdnumber, be reached, then, lobbest thou thy Holy Hand Grenade of Antioch towardsthy 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 + messageencryptedMessage = 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", writersNameprint "Decrypted message:", decryptedMessage |
Jonathan Warren