Alert Source Discuss
⚠️ Review Standards Track: ERC

ERC-7627: Secure Messaging Protocol

End-to-end encryption for sending messages between users.

Authors Chen Liaoyuan (@chenly) <cly@kip.pro>
Created 2024-02-19

Abstract

This proposal implements the capability to securely exchange encrypted messages on-chain. Users can register their public keys and encryption algorithms by registration and subsequently send encrypted messages to other users using their addresses. The interface also includes enumerations for public key algorithms and a structure for user information to support various encryption algorithms and user information management.

Motivation

With the emergence of Layer 2 chains featuring sub-second block times and the introduction of account abstraction, the use of end-to-end encrypted communication has facilitated the proliferation of real-time communication and online chat dApps. Providing a unified interface enables easy integration of encrypted communication into smart contracts, thereby fostering innovation. Standardization promotes interoperability, facilitating seamless communication across platforms.

Specification

Objectives

  • Provide a standardized interface for implementing messaging systems in smart contracts, including user registration and message sending functionalities.
  • Enhance flexibility and scalability for messaging systems by defining enumerations for public key algorithms and a structure for user information.
  • Define events for tracking message sending to enhance the observability and auditability of the contract.
  • Using a custom sessionId allows messages to be organized into a conversation.
  • Encrypt message content using the recipient’s public key during message transmission.

Interface

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119 and RFC 8174.

Implementers of this standard MUST have all of the following functions:

pragma solidity ^0.8.0;

interface IERC7627 {

    enum PublicKeyAlgorithm { ECDSA, ED25519, X25519 }

    struct PublicKey {
        bytes public_key; 
        uint64 valid_before;
        PublicKeyAlgorithm algorithm; 
    }
    
    // Events
	/**
     * @notice Event emitted when a message is sent between users.
     * @param from The address of the sender
     * @param to The address of the recipient
     * @param keyIndex The index of the public key used to encrypt the message
     * @param sessionId The session ID for the communication
     * @param encryptedMessage The encrypted message in bytes
	*/
    event MessageSent(address indexed from, address indexed to, bytes32 indexed keyIndex, bytes32 sessionId, bytes encryptedMessage);

	/**
     * @notice Event emitted when a user's public key is updated.
     * @param user The address of the user whose public key is updated
     * @param keyIndex The index of the public key being updated
     * @param newPublicKey The new public key data
	*/
    event PublicKeyUpdated(address indexed user, bytes32 indexed keyIndex, PublicKey newPublicKey);

    // Functions

	/**
     * @notice Updates the public key for the sender.
     * @param _keyIndex The index of the key to be updated
     * @param _publicKey The new public key data
	*/
    function updatePublicKey(bytes32 _keyIndex, PublicKey memory _publicKey) external;

	/**
     * @notice Sends an encrypted message to a specified address.
     * @param _to The recipient's address
     * @param _keyIndex The index of the public key used to encrypt the message
     * @param _sessionId The session ID for the communication
     * @param _encryptedMessage The encrypted message in bytes
	*/
    function sendMessage(address _to, bytes32 _keyIndex, bytes32 _sessionId, bytes calldata _encryptedMessage) external;

	/**
     * @notice Retrieves a public key for a specific user and key index.
     * @param _user The address of the user
     * @param _keyIndex The index of the key to retrieve
     * @return The public key data associated with the user and key index
	*/
    function getUserPublicKey(address _user, bytes32 _keyIndex) external view returns (PublicKey memory);
}

Rationale

Event Emission for Off-Chain Integration

By emitting events when messages are sent or public keys are updated, the implementation facilitates seamless integration with off-chain dApps. This enables these dApps to easily track and display the latest messages and updates, ensuring real-time responsiveness and enhancing user interaction.

End-to-End Encryption Security

The design ensures that only the owner of an address can update their public key. This restriction preserves the integrity of the end-to-end encryption, making sure that only the intended recipient can decrypt and read the messages, thereby securing communication.

Session ID for Conversation Organization

The use of session IDs in message transactions allows multiple messages to be grouped under specific conversations. This feature is crucial for organizing and managing discussions within a dApp, providing users with a coherent and structured messaging experience.

Reference Implementation

pragma solidity ^0.8.0;

contract ERC7627 {

    /// @dev Enum to specify the algorithm used for the public key.
    enum PublicKeyAlgorithm { ECDSA, ED25519, X25519 }

    /// @dev Structure to represent a user's public key.
    struct PublicKey {
        bytes public_key; 
        uint64 valid_before;
        PublicKeyAlgorithm algorithm; 
    }

    /// @dev Mapping to store public keys for each address. The mapping is by user address and a unique key index.
    mapping(address => mapping(bytes32 => PublicKey)) public pk;

    event MessageSent(address indexed from, address indexed to, bytes32 indexed keyIndex, bytes32 sessionId, bytes encryptedMessage);

    event PublicKeyUpdated(address indexed user, bytes32 indexed keyIndex, PublicKey newPublicKey);

    function updatePublicKey(bytes32 _keyIndex, PublicKey memory _publicKey) external {
        pk[msg.sender][_keyIndex] = _publicKey;
        emit PublicKeyUpdated(msg.sender, _keyIndex, _publicKey);
    }

    function sendMessage(address _to, bytes32 _keyIndex, bytes32 _sessionId, bytes calldata _encryptedMessage) external {
        emit MessageSent(msg.sender, _to, _keyIndex, _sessionId, _encryptedMessage);
    }

    function getUserPublicKey(address _user, bytes32 _keyIndex) external view returns (PublicKey memory) {
        return pk[_user][_keyIndex];
    }
}

Security Considerations

Utilization of Latest Secure Encryption Algorithms

When selecting encryption algorithms, it is essential to stay informed about the latest security news and recommendations. Avoid using asymmetric encryption algorithms with known vulnerabilities or those not recommended to ensure the confidentiality and integrity of messages. Regularly update encryption algorithms to address evolving security threats.

Strict Encryption Using Public Keys for Message Content

To maintain message confidentiality, the content of sent messages must be strictly encrypted using the recipient’s public key. Any plaintext information transmitted could lead to information leakage and security risks. Encrypt message content at all times during transmission and storage to prevent unauthorized access to sensitive information.

Key Management and Protection

Robust key management and protection measures are necessary for both user public and private keys. Ensure secure storage and transmission of keys to prevent leakage and tampering. Employ multi-factor authentication and key rotation strategies to enhance key security and regularly assess key management processes to mitigate potential security risks.

Copyright and related rights waived via CC0

Citation

Please cite this document as:

Chen Liaoyuan (@chenly) <cly@kip.pro>, "ERC-7627: Secure Messaging Protocol [DRAFT]," Ethereum Improvement Proposals, no. 7627, February 2024. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7627.