Search code examples
pythonsocketspycryptopycryptodome

PyCryptodome RSA signature and verification


I am working on a program which transfers data over the internet in python using the socket module. I am now trying to implement encryption using the pycryptodome module. I am using Salsa20 to transfer normal messages and to transfer the Salsa20 key I am using RSA. The problem is that the code is raising a ValueError when verifying the hash. This is the protocol:

  1. The client connects to the server (and generates a Salsa20 key)
  2. The server generates an RSA key pair and sends the public key to the client
  3. The client generates its own key pair and sends the public key
  4. The client encrypts the Salsa20 key with the public key
  5. The client signs and sends a hash of the encrypted Salsa20 key
  6. The client sends the encrypted Salsa20 key
  7. The server hashes the unsigned encrypted key, and verifies that it is the same as the signed one (this is the line where the problem happens)
  8. The server decrypts the unsigned key

I have checked and printed repeatedly, and the hashes are the same. The sockets are not the problem.

The recv_message and send_message functions are the functions I am using to package up the sending and receiving protocol. In the program the sockets are set up beforehand, so here are the important parts of the server.

import socket
from Crypto.Cipher import Salsa20, PKCS1_OAEP
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA

def send_message(message_to_send, connection):
    if type(message_to_send) is bytes:
        message = message_to_send
    else:
        message = str(message_to_send).encode()
    length_of_msg = str(len(message))
    length_of_msg = ('0' * (5-len(length_of_msg))) + length_of_msg  # This part is adding zeros as padding so that the first message is always 5 chars
    connection.send(length_of_msg.encode())
    connection.send(message)

def recv_message(connection, no_decode=False):
    length_of_msg = int(connection.recv(5))
    message = connection.recv(length_of_msg)
    if not no_decode:
        message = message.decode('utf-8')
    return message

def get_key(connection):
    rsa_server_key = RSA.generate(2048)
    send_message(rsa_server_key.publickey().exportKey(), connection)
    rsa_client_key = RSA.import_key(recv_message(connection, no_decode=True))
    key_signed = recv_message(connection, no_decode=True)
    key_unsigned = recv_message(connection, no_decode=True)
    hash_verify = pkcs1_15.new(rsa_client_key)
    hash_verify.verify(SHA256.new(data=key_unsigned), key_signed)
    rsa_cipher = PKCS1_OAEP.new(rsa_server_key)
    key = rsa_cipher.decrypt(key_unsigned)
    return key

The client has the same socket related functions and imported modules.

def share_key(key_to_send, connection):
    rsa_server_key = RSA.import_key(recv_message(connection, no_decode=True))
    rsa_client_key = RSA.generate(2048)
    send_message(rsa_server_key.publickey().exportKey(), connection)
    rsa_cipher = PKCS1_OAEP.new(rsa_server_key)
    salsa_key_unsigned = rsa_cipher.encrypt(key_to_send)
    key_signer = pkcs1_15.new(rsa_client_key)
    send_message(key_signer.sign(SHA256.new(data=salsa_key_unsigned)), connection)
    send_message(salsa_key_unsigned, connection)

The traceback is:

Traceback (most recent call last):
  File "0,9Serverside.py", line 686, in <module>
    main()
  File "0,9Serverside.py", line 670, in main
    key_input = get_key(server)
  File "0,9Serverside.py", line 243, in get_key
    hash_verify.verify(SHA256.new(data=salsa_key_unsigned), salsa_key_signature)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/Crypto/Signature/pkcs1_15.py", line 139, in verify
    raise ValueError("Invalid signature")
ValueError: Invalid signature

What do you think could be the reason the verification isn't working. Thank you for the help.


Solution

  • The client is sending back the server's public key instead of its own public key. The line:

     send_message(rsa_server_key.publickey().exportKey(), connection)
    

    should be:

     send_message(rsa_client_key.publickey().exportKey(), connection)