def DeriveECDHSecretFromKey(privateKey, publicKey): """Generate a shared secret from keys in ecdsa.Keys format.""" derive = ECDH(curve=NIST256p) derive.load_private_key(privateKey) derive.load_received_public_key(publicKey) secret = derive.generate_sharedsecret_bytes() return secret
def DecryptMessage(privateKey_hex, encrypted_hex): if privateKey_hex[:2] == '0x': privateKey_hex = privateKey_hex[2:] if encrypted_hex[:2] == '0x': encrypted_hex = encrypted_hex[2:] # get the components encrypted = bytearray.fromhex(encrypted_hex) iv = encrypted[:16] ephemPubKeyEncoded = encrypted[17:81] mac = encrypted[81:113] ciphertext = encrypted[113:] # recover the temporary public key ephemPubKey = VerifyingKey.from_string(ephemPubKeyEncoded, curve=SECP256k1) # load the private key priv_key = SigningKey.from_secret_exponent(int(privateKey_hex, 16), curve=SECP256k1) ecdh = ECDH(curve=SECP256k1, private_key=priv_key) # ECDH => get the shared secret ecdh.load_received_public_key(ephemPubKey) px = ecdh.generate_sharedsecret_bytes() # compute the encription and MAC keys hash_px = SHA512.new(data=px).digest() encryptionKey = hash_px[:32] macKey = hash_px[32:] # check the MAC dataToMac = iv + bytearray([4]) + ephemPubKeyEncoded + ciphertext computed_mac = hmac.new(macKey, dataToMac, 'sha256').digest() if computed_mac != mac: raise ValueError("MAC missmatch") #decipher the text plaintext = AES256CbcDecrypt(ciphertext.hex(), encryptionKey, iv) return plaintext.decode("utf-8")
def EncryptMessage(publicKey_hex, plainText_string): if publicKey_hex[:2] == '0x': publicKey_hex = publicKey_hex[2:] publicKey_bin = bytearray.fromhex(publicKey_hex) # Generate the temporary key ecdh = ECDH(curve=SECP256k1) ecdh.generate_private_key() ephemPubKeyEncoded = bytearray.fromhex( ecdh.get_public_key().to_string().hex()) # Load the public key publicKey = VerifyingKey.from_string(publicKey_bin, curve=SECP256k1) # ECDH => get the shared secret ecdh.load_received_public_key(publicKey) px = ecdh.generate_sharedsecret_bytes() # compute the encription and MAC keys hash_px = SHA512.new(data=px).digest() encryptionKey = hash_px[:32] macKey = hash_px[32:] # cipher the plain text iv = Random.get_random_bytes(16) plaintext = plainText_string.encode(encoding='utf_8') ciphertext = AES256CbcEncrypt(plaintext, encryptionKey, iv) # compute the MAC dataToMac = iv + bytearray([4]) + ephemPubKeyEncoded + ciphertext mac = hmac.new(macKey, dataToMac, 'sha256').digest() #build the output serializedCiphertext = iv + bytearray( [4]) + ephemPubKeyEncoded + mac + ciphertext return serializedCiphertext.hex()
class SecureChannel: def __init__(self, loglevel=logging.WARNING): logger.setLevel(loglevel) logger.debug("In __init__") self.initialized_secure_channel = False self.sc_privkey = None self.sc_pubkey = None self.sc_peer_pubkey = None self.sc_IV = None self.sc_IVcounter = None self.shared_key = None self.derived_key = None self.mac_key = None self.ecdh = ECDH(curve=SECP256k1) self.ecdh.generate_private_key() self.sc_pubkey = self.ecdh.get_public_key() self.sc_pubkey_serialized = self.sc_pubkey.to_string( encoding='uncompressed') def initiate_secure_channel(self, peer_pubkey_bytes): logger.debug("In initiate_secure_channel()") self.sc_IVcounter = 1 #using ecdsa self.sc_peer_pubkey = VerifyingKey.from_string(peer_pubkey_bytes, curve=SECP256k1) self.ecdh.load_received_public_key(self.sc_peer_pubkey) self.shared_key = self.ecdh.generate_sharedsecret_bytes() #logger.debug("Shared key:"+ self.shared_key.hex()) #debug mac = hmac.new(self.shared_key, "sc_key".encode('utf-8'), sha1) self.derived_key = mac.digest()[:16] mac = hmac.new(self.shared_key, "sc_mac".encode('utf-8'), sha1) self.mac_key = mac.digest() # alternative derivation # tmp_key= sha256(self.shared_key).digest() # self.derived_key = tmp_key[:16] # tmp_key= sha256(tmp_key).digest() # self.mac_key= tmp_key[:20] # logger.debug("Derived_key key:"+ self.derived_key.hex()) #debug # logger.debug("Mac_key key:"+ self.mac_key.hex()) #debug self.initialized_secure_channel = True def encrypt_secure_channel(self, data_bytes): logger.debug("In encrypt_secure_channel()") if not self.initialized_secure_channel: raise UninitializedSecureChannelError( 'Secure channel is not initialized') ## for encryption, the data is padded with PKCS#7 (done by PADDING_DEFAULT) # blocksize= 16 # size=len(apdu) # padsize= blocksize - (size%blocksize) # apdu= apdu+ [padsize]*padsize key = self.derived_key iv = urandom(12) + (self.sc_IVcounter).to_bytes(4, byteorder='big') aes_cbc = pyaes.AESModeOfOperationCBC(key, iv=iv) aes = pyaes.Encrypter(aes_cbc, padding=pyaes.PADDING_DEFAULT) ciphertext = aes.feed(data_bytes) + aes.feed() self.sc_IVcounter += 2 data_to_mac = iv + len(ciphertext).to_bytes( 2, byteorder='big') + ciphertext mac = hmac.new(self.mac_key, data_to_mac, sha1).digest() return (iv, ciphertext, mac) def decrypt_secure_channel(self, iv, ciphertext): logger.debug("In decrypt_secure_channel()") if not self.initialized_secure_channel: raise UninitializedSecureChannelError( 'Secure channel is not initialized') key = self.derived_key aes_cbc = pyaes.AESModeOfOperationCBC(key, iv=iv) aes = pyaes.Decrypter(aes_cbc, padding=pyaes.PADDING_DEFAULT) decrypted = aes.feed(ciphertext) + aes.feed() # #remove padding (already done with PADDING_DEFAULT) # #logger.debug(f'Plaintext with padding: {toHexString(plaintext)}') # plaintext= list(plaintext) # pad= plaintext[-1] # plaintext=plaintext[0:-pad] # #logger.debug(f'Plaintext without padding: {toHexString(plaintext)}') return list(decrypted)