Transaction Signing & Verification

← Back to Cryptography I: Blockchain Security and Cryptographic Foundations

Introduction

Digital signatures are the heart of XRPL's security. Every transaction must be signed with the private key corresponding to the sending account. This signature is mathematical proof that the account owner authorized the transaction. Without a valid signature, a transaction is rejected immediately.

In this chapter, we'll trace the complete signing and verification pipeline, understand the differences between secp256k1 and ed25519, and explore why canonical signatures matter.

The Signature: Mathematical Proof of Authorization

A digital signature proves three things:

  1. Authenticity: The signature was created by someone with the secret key

  2. Integrity: The signed data hasn't been modified

  3. Non-repudiation: The signer cannot deny having signed

Transaction Data + Secret Key  →  Signature
Transaction Data + Public Key + Signature  →  Valid/Invalid

Creating a Signature

The High-Level Interface

// From src/libxrpl/protocol/SecretKey.cpp
Buffer sign(
    PublicKey const& pk,
    SecretKey const& sk,
    Slice const& m)
{
    // Automatically detect key type from public key
    auto const type = publicKeyType(pk.slice());

    switch (*type)
    {
        case KeyType::ed25519:
            return signEd25519(pk, sk, m);

        case KeyType::secp256k1:
            return signSecp256k1(pk, sk, m);
    }
}

Parameters:

  • pk: Public key (for key type detection)

  • sk: Secret key (the signing key)

  • m: Message (the data to sign)

Returns:

  • A Buffer containing the signature bytes

Ed25519 Signing: Simple and Fast

How it works:

  1. Allocate 64-byte buffer

  2. Call ed25519_sign with message, keys, and output buffer

  3. Return the signature

Properties:

  • Always produces exactly 64 bytes

  • Deterministic: same message + key = same signature

  • Fast: ~50 microseconds

  • No pre-hashing needed

Signature format:

Where R and S are elliptic curve points/scalars (mathematical details abstracted by the library).

Secp256k1 Signing: More Complex

How it works:

  1. Pre-hash the message: Compute SHA-512-Half of the message

  2. Sign the digest: Use ECDSA to sign the 32-byte hash

  3. Serialize: Encode signature in DER format

Why pre-hash?

  • ECDSA works on fixed-size inputs (32 bytes)

  • Messages can be any size

  • Hashing first normalizes all inputs to 32 bytes

  • Security proof for ECDSA assumes you're signing a hash

Why DER encoding? DER (Distinguished Encoding Rules) is a standard binary format from X.509:

Deterministic Nonces (RFC 6979):

This is critical for security. ECDSA requires a random "nonce" (number used once) for each signature. If:

  • The same nonce is used twice with the same key → secret key can be extracted

  • The nonce is predictable → secret key can be extracted

RFC 6979 derives the nonce deterministically from the message and secret key, making it:

  • Different for every message

  • Unpredictable to attackers

  • Free from random number generation failures

Verifying a Signature

The High-Level Interface

Parameters:

  • publicKey: The public key to verify against

  • m: The message that was signed

  • sig: The signature to verify

  • mustBeFullyCanonical: Whether to enforce strict canonicality (important!)

Returns:

  • true if signature is valid

  • false if signature is invalid or malformed

Ed25519 Verification

Canonicality check:

Why check canonicality? Ensures the S component is in the valid range. This prevents malformed signatures from being processed.

Secp256k1 Verification

The digest verification function:

Steps:

  1. Check canonicality: Ensure signature is in canonical form

  2. Parse public key: Convert from compressed format to library format

  3. Parse signature: Decode DER encoding

  4. Verify: Check mathematical relationship between public key, message, and signature

Signature Malleability and Canonicality

The Problem: Signature Malleability

In secp256k1, a signature is a pair of numbers (R, S). Due to the mathematics of elliptic curves:

Where n is the curve order. This means one message has two valid signatures.

Why this is dangerous:

Attack scenarios:

  1. Transaction ID confusion: Applications tracking txID1 won't see the transaction confirmed (it confirms as txID2)

  2. Double-spend attempts: Submit both versions, one might get through

  3. Chain reaction: If txID is used as input to another transaction, that transaction becomes invalid

The Solution: Canonical Signatures

Require S to be in the "low" range:

This makes each signature unique—only one valid signature per message.

Checking Canonicality

Canonicality levels:

Enforcement:

In production, XRPL always sets mustBeFullyCanonical = true to prevent malleability.

Ed25519: No Malleability

Ed25519 signatures are inherently canonical—there's only one valid signature per message. The curve mathematics don't allow the kind of malleability that exists in ECDSA.

This is one of the design advantages of Ed25519 over secp256k1.

Transaction Signing in Practice

Signing a Transaction

What gets signed:

The signature is computed over:

  1. A prefix (HashPrefix::txSign)

  2. All transaction fields (except the signature itself)

Verifying a Transaction

Multi-Signing

XRPL supports multi-signature transactions where multiple parties must sign:

Each signer independently signs the transaction, and all signatures are verified.

Performance Characteristics

Signing Speed

Verification Speed

Why verification speed matters:

Every validator must verify every transaction signature. In a high-throughput system:

Ed25519's speed advantage is significant at scale.

Signature Size

Summary

Digital signatures in XRPL:

  1. Purpose: Prove authorization, ensure integrity, enable non-repudiation

  2. Two algorithms:

    • secp256k1: Hash-then-sign, DER encoding, requires canonicality checks

    • ed25519: Direct signing, fixed 64 bytes, inherently canonical

  3. Signing: Secret key + message → signature

  4. Verification: Public key + message + signature → valid/invalid

  5. Malleability: secp256k1 requires canonical signatures to prevent attacks

  6. Performance: ed25519 is faster for both signing and verification

Key takeaways:

  • Always enforce canonical signatures for secp256k1

  • Ed25519 is recommended for new accounts (faster, simpler)

  • Verification happens for every transaction in the network

  • Multi-signing allows multiple parties to authorize a transaction

In the next chapter, we'll explore hash functions and how they're used throughout XRPL for integrity and identification.

Last updated