CC-20200217

Crypto Core Intermediate February 17, 2020

Back to All Tasks

Problem Statement

You are tasked with developing a simple digital signature system for verifying the authenticity of messages. Your system should use asymmetric cryptography, specifically RSA key pairs to sign and verify messages. Develop functions to generate an RSA key pair, sign a message using the private key, and verify the signature using the public key. Ensure that your system can handle different types of data inputs (e.g., strings, binary files) and discuss how you manage these varying data types within your implementation. Include error handling for scenarios such as attempting to verify a signature with an incorrect or mismatched key.

Concepts

  • Hash Functions
  • Digital Signatures
  • Public Key Cryptography

Constraints

  • Use RSA algorithm
  • Implement functions for key generation, signing, and verification
  • Handle at least two different types of data inputs

Security Notes

  • Ensure that the private key is kept secure and never exposed publicly
  • Be aware of potential attacks like chosen-ciphertext attacks in real-world applications
  • Consider the performance implications of using RSA for large data sets

Solutions

Python Solution

from Crypto.PublicKey import RSA
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256
import binascii

def generate_rsa_key_pair():
    	# Generate an RSA key pair of size 2048 bits
    	key = RSA.generate(2048)
    	private_key = key.export_key()
    	public_key = key.publickey().export_key()
    	return private_key, public_key

def sign_message(private_key, message):
    	# Import the private key from bytes format
    	key = RSA.import_key(private_key)
    	if isinstance(message, str):
    		# If the message is a string, encode it to bytes
    		message = message.encode('utf-8')
    	elif not isinstance(message, bytes):
    		# Raise an error if the message is neither a string nor bytes
    		raise ValueError('Message must be either a string or bytes.')
    	# Create a SHA256 hash of the message
    	hash_obj = SHA256.new(message)
    	# Sign the hash with the private key using PKCS1 v1.5 padding
    	signature = pkcs1_15.new(key).sign(hash_obj)
    	return binascii.hexlify(signature).decode('utf-8')

def verify_signature(public_key, message, signature):
    	# Import the public key from bytes format
    	key = RSA.import_key(public_key)
    	if isinstance(message, str):
    		# If the message is a string, encode it to bytes
    		message = message.encode('utf-8')
    	elif not isinstance(message, bytes):
    		# Raise an error if the message is neither a string nor bytes
    		raise ValueError('Message must be either a string or bytes.')
    	# Convert the signature from hex to bytes
    	signature = binascii.unhexlify(signature)
    	# Create a SHA256 hash of the message
    	hash_obj = SHA256.new(message)
    	try:
    		# Verify the signature with the public key using PKCS1 v1.5 padding
    		pkcs1_15.new(key).verify(hash_obj, signature)
    		return True
    	except (ValueError, TypeError):
    		# Return False if verification fails due to an incorrect or mismatched key
    		return False

This solution implements a simple digital signature system using the RSA algorithm. The system includes functions for generating an RSA key pair, signing a message with the private key, and verifying a signature with the public key.

The `generate_rsa_key_pair` function generates a new RSA key pair of size 2048 bits and returns the private and public keys in bytes format. It uses PyCryptodome's RSA module for this purpose.

The `sign_message` function takes a private key and a message as inputs. The message can be either a string or bytes. If it's a string, the function encodes it to bytes using UTF-8 encoding. It then creates a SHA256 hash of the message and signs the hash with the private key using PKCS1 v1.5 padding. The signature is returned in hexadecimal format.

The `verify_signature` function takes a public key, a message, and a signature as inputs. Similar to the signing function, it handles messages that are either strings or bytes. It converts the hexadecimal signature back to bytes and creates a SHA256 hash of the message. The function then attempts to verify the signature using the public key. If verification is successful, it returns True; otherwise, it catches exceptions (ValueError or TypeError) and returns False, indicating that the signature is invalid or the keys do not match.

This implementation ensures that the private key remains secure by never exposing it publicly. It also handles different data types for messages and includes error handling for mismatched or incorrect keys. However, users should be aware of potential security risks such as chosen-ciphertext attacks in real-world applications.