Skip to content

TRC-7913: Signature Verifiers #903

Description

@yanghang8612
tip: 7913
title: Signature Verifiers
description: Interface for the verification of signatures by address-less keys
author: yanghang8612@gmail.com
discussions-to: https://github.com/tronprotocol/tips/issues/903
status: Draft
type: Standards Track
category: TRC
created: 2026-07-02
requires: 712, 1271

Simple Summary

A standard interface for verifying signatures from cryptographic keys that have no on-chain address of their own — such as secp256r1/passkeys, RSA keys, or ZK-proven Web2 credentials — through reusable, stateless verifier contracts.

Abstract

Externally Owned Accounts (EOA) can sign messages with their associated private keys. Additionally TRC-1271 defines a method for signature verification by smart accounts such as multisig. In both cases the identity of the signer is a TRON address. We propose a standard to extend this concept of signer description, and signature verification, to keys that do not have a TRON identity of their own, in the sense that they don't have their own address to represent them.

This new mechanism can be used to integrate new signers such as non-secp256k1 cryptographic curves, hardware devices, or even email addresses. This is particularly relevant when dealing with things like social recovery of smart accounts.

Motivation

With the development of account abstraction, there is an increasing need for signature verification beyond the natively supported secp256k1. Cryptographic algorithms are being used for controlling smart accounts. In particular, curves such as secp256r1 (supported by many mobile devices, and available on TVM as a precompile via TIP-7951) and RSA keys (that are distributed by traditional institutions, and verifiable on TVM through the MODEXP precompile as bounded and repriced by TIP-7823 and TIP-7883) are widely available. Beyond these two examples, we also see the emergence of ZK solutions for signing with emails or JWT from big Web2 services.

All these signature mechanisms have one thing in common: they do not have a canonical TRON address to represent them onchain. While users could deploy TRC-1271 compatible contracts for each key individually, this would be cumbersome and expensive. As account abstraction tries to separate account addresses (that hold assets) from the key that controls them, giving fixed on-chain addresses to keys (and possibly sending assets to these addresses by mistake) is not the right approach. Instead, using a small number of verifier contracts that can process signatures in a standard way, and having the accounts rely on these verifiers, feels like the correct approach. This has the advantage that once the verifier is deployed, any key can be represented using a (verifier, key) pair without requiring any setup cost.

The (verifier, key) pairs can be given permission to control a smart account, perform social recovery, or do any other operation without ever having a dedicated on-chain address. Systems that want to adopt this approach need to transition away from the model where signers are identified by their address to a new model where signers may not have an address, and are identified by a bytes object.

This definition is backward compatible with EOA and TRC-1271 contracts: in that case, we use the address of the identity (EOA or contract) as the verifier and the key is empty.

Specification

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.

Nomenclature

  • Keys are represented using bytes objects of arbitrary length. For example a P256 or RSA public key. Keys MUST NOT be empty.
  • Verifiers are smart contracts in charge of signature verification for a given type of key. They have a TRON address.

Signature Verifier interface

Verifiers MUST implement the following interface:

interface ITRC7913SignatureVerifier {
  /**
   * @dev Verifies `signature` as a valid signature of `hash` by `key`.
   *
   * MUST return the bytes4 magic value 0x024ad318 (ITRC7913SignatureVerifier.verify.selector) if the signature is valid.
   * SHOULD return 0xffffffff or revert if the signature is not valid.
   * SHOULD return 0xffffffff or revert if the key is empty
   */
  function verify(bytes calldata key, bytes32 hash, bytes calldata signature) external view returns (bytes4);
}

Verifiers SHOULD be stateless.

The magic value 0x024ad318 is the selector of verify(bytes,bytes32,bytes) and is therefore unchanged from the original standard, allowing a verifier written for another chain to be reused on TVM without modification. A natural TRON verifier for secp256r1/passkey keys wraps the TIP-7951 precompile, which verifies a P-256 signature against an explicit public key (key = the 64-byte uncompressed point, signature = r || s) and, unlike ecrecover, does not recover an address — exactly the shape this interface expects. A WebAuthn/passkey verifier additionally parses authenticatorData and clientDataJSON before delegating to that primitive.

Rationale

Verifiers can be used to avoid deploying many TRC-1271 "identity contracts" (one per key), which would be expensive. Using this model, new keys can be used without any deployment costs.

TRC-1271 already covers the cases where a key is not necessary. To avoid ambiguity, the verifier contracts should not support (or be expected to support) empty keys. Signers that are 20 bytes long (empty key) should be handled using TRC-1271.

Consistency with existing systems (ecrecover and TRC-1271) requires the message to be a bytes32 hash. On TRON this is usually produced following TIP-712. Cryptographic systems that use different hashing methods SHOULD see this hash as a message, and possibly rehash it following the relevant standards. Wallets producing the hash for a verifier flow MUST NOT wrap it with a TRON personal-message prefix, which would break verification.

Backwards Compatibility

A system can support TRC-7913 signers alongside EOAs and TRC-1271 in the following way.

A signer is a bytes object that is the concatenation of an address and optionally a key: verifier || key. A signer is at least 20 bytes long. The 20-byte verifier is the ABI-level TRON address (the account body with the 0x41 prefix stripped, consistent with TIP-712 and TIP-2612); wallets may display it as Base58Check.

Given a signer signer, a message hash hash, and a signature sign, verification is done as follows:

  • if signer.length < 20: verification fails;
  • split signer into (verifier, key) with verifier being the first 20 bytes and key being the rest (potentially empty);
  • if key is empty, then consider that verifier is the identity.
    • verification is done using TRC-1271's isValidSignature if there is code at verifier address, or ecrecover otherwise;
  • if key is not empty, call ITRC7913SignatureVerifier(verifier).verify(key, hash, signature)
    • if the return value is the expected magic value (0x024ad318) then verification is successful,
    • otherwise, verification fails.

Note that TRON's protocol-level weighted-multisignature permission system (TIP-16, TIP-105) is not visible to TVM code and is a separate layer: native permissions gate transaction signing, whereas TRC-7913 governs in-contract signature checks.

Reference Implementation

In Solidity, signature verification could be implemented in the following library:

import {SignatureChecker} from '@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol';

/// @dev Extension of OpenZeppelin's SignatureChecker library
library SignatureCheckerExtended {
  function isValidSignatureNow(bytes calldata signer, bytes32 hash, bytes memory signature) internal view returns (bool) {
      if (signer.length < 20 ) {
        return false;
      } else if (signer.length == 20) {
        return SignatureChecker.isValidSignatureNow(address(bytes20(signer)), hash, signature);
      } else {
        try ITRC7913SignatureVerifier(address(bytes20(signer[0:20]))).verify(signer[20:], hash, signature) returns (bytes4 magic) {
          return magic == ITRC7913SignatureVerifier.verify.selector;
        } catch {
          return false;
        }
      }
  }
}

Security Considerations

Signers may be used for anything from a smart-account session key (with a short lifetime of a few hours/days) to social-recovery "guardians" that may only be used several years after they are set up. In order to ensure that these signers remain valid "in perpetuity", the verifier contract should be trustless. This means that the verifiers should not be upgradeable contracts.

Verifiers should also not depend on any value that can be modified after deployment. In Solidity terms, the verify function should be pure. Any parameters that are involved in signature verification should either be part of the key, or part of the immutable code of the verifier. Using an immutable variable (in Solidity) would be safe. The stateless aspect of the verifier also keeps it compatible with the validation-scope rules that a future TRON account-abstraction alt-mempool would impose during the validation phase.

For the empty-key fallback path, the usual ecrecover edge cases apply: a malformed signature returns address(0), so an address(0) result MUST be treated as failure, and s/v normalization (low-s, v ∈ {27, 28}) SHOULD be enforced as in TRC-1271 and TIP-2612.

Copyright

Copyright and related rights waived via CC0.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions