Skip to content

Feature Request: Add SEP-53 arbitrary message signing and verification #2345

@tomerweller

Description

@tomerweller

Summary

Add support for signing and verifying arbitrary messages per SEP-53. This capability is already implemented in Freighter and enables proving Stellar address ownership, off-chain agreement verification, and cross-chain integrations.

Motivation

Currently, the Stellar CLI can sign transactions but lacks the ability to sign arbitrary messages. This is a common need for:

  • Proof of ownership: Proving control of a Stellar address on social platforms, Discord bots, etc.
  • Off-chain agreements: Signing messages for legal/contractual purposes without on-chain transactions
  • Cross-chain integrations: Verifying Stellar address ownership in multi-chain applications
  • Authentication: Using Stellar keys as an authentication mechanism

Freighter wallet already supports this via their signMessage API, but CLI users currently have no equivalent capability.

Proposed Command Structure

Following the existing CLI architecture patterns (hierarchical subcommands, consistent signing options), I propose a new message subcommand:

stellar message sign    Sign an arbitrary message using SEP-53
stellar message verify  Verify a SEP-53 signed message

stellar message sign

stellar message sign [OPTIONS] <MESSAGE>

Arguments:
  <MESSAGE>  The message to sign (use --file for binary/file input)

Options:
  -f, --file <FILE>           Read message from file instead of argument
      --binary                Treat input as binary data (not UTF-8 text)
      --sign-with-key <KEY>   Sign with a local key or identity
      --sign-with-lab         Sign using https://lab.stellar.org
      --sign-with-ledger      Sign using a Ledger hardware wallet
      --hd-path <HD_PATH>     HD derivation path (if using seed phrase)
  -o, --output <FORMAT>       Output format [default: text] [possible: text, json]

Example usage:

# Sign a simple message
stellar message sign "Hello, World!" --sign-with-key alice

# Sign with JSON output
stellar message sign "Verify I own this address" --sign-with-key alice --output json

# Sign binary file
stellar message sign --file document.pdf --binary --sign-with-key alice

# Sign using Ledger
stellar message sign "Proof of ownership" --sign-with-ledger

Output (text):

Signer: GBXFXNDLV4LSWA4VB7YIL5GBD7BVNR22SGBTDKMO2SBZZHDXSKZYCP7L
Signature: fO5dbYhXUhBMhe6kId/cuVq/AfEnHRHEvsP8vXh03M1uLpi5e46yO2Q8rEBzu3feXQewcQE5GArp88u6ePK6BA==

Output (json):

{
  "signer": "GBXFXNDLV4LSWA4VB7YIL5GBD7BVNR22SGBTDKMO2SBZZHDXSKZYCP7L",
  "message": "Hello, World!",
  "signature": "fO5dbYhXUhBMhe6kId/cuVq/AfEnHRHEvsP8vXh03M1uLpi5e46yO2Q8rEBzu3feXQewcQE5GArp88u6ePK6BA=="
}

stellar message verify

stellar message verify [OPTIONS] <MESSAGE> <SIGNATURE>

Arguments:
  <MESSAGE>    The original message
  <SIGNATURE>  The base64-encoded signature to verify

Options:
  -f, --file <FILE>         Read message from file instead of argument
      --binary              Treat input as binary data (not UTF-8 text)
  -p, --public-key <KEY>    Public key or identity to verify against
  -o, --output <FORMAT>     Output format [default: text] [possible: text, json]

Example usage:

# Verify a signature
stellar message verify "Hello, World!" \
  "fO5dbYhXUhBMhe6kId/cuVq/AfEnHRHEvsP8vXh03M1uLpi5e46yO2Q8rEBzu3feXQewcQE5GArp88u6ePK6BA==" \
  --public-key GBXFXNDLV4LSWA4VB7YIL5GBD7BVNR22SGBTDKMO2SBZZHDXSKZYCP7L

# Verify using an identity name
stellar message verify "Hello, World!" "$SIGNATURE" --public-key alice

# JSON output for scripting
stellar message verify "Hello" "$SIG" --public-key "$PUBKEY" --output json

Output (text - success):

✓ Signature valid

Output (text - failure):

✗ Signature invalid

Output (json):

{
  "valid": true,
  "signer": "GBXFXNDLV4LSWA4VB7YIL5GBD7BVNR22SGBTDKMO2SBZZHDXSKZYCP7L",
  "message": "Hello, World!"
}

Implementation Notes

Per SEP-53, the signing process is:

  1. Convert message to bytes (UTF-8 if string, raw if binary)
  2. Prepend the fixed prefix: "Stellar Signed Message:\n"
  3. Compute hash = SHA256(prefix + message)
  4. Sign hash with ed25519 private key → 64-byte signature
  5. Return signature as base64

Test Vectors

SEP-53 provides test vectors that should be used for validation:

Message Signature (base64)
Hello, World! fO5dbYhXUhBMhe6kId/cuVq/AfEnHRHEvsP8vXh03M1uLpi5e46yO2Q8rEBzu3feXQewcQE5GArp88u6ePK6BA==
こんにちは、世界! CDU265Xs8y3OWbB/56H9jPgUss5G9A0qFuTqH2zs2YDgTm+++dIfmAEceFqB7bhfN3am59lCtDXrCtwH2k1GBA==

(Using seed SAKICEOVQLYWGSOJS4WW7HZJWAHZVEEBS527LHK5V4MLJALYKICQCJXMW)

Alternatives Considered

  1. stellar keys sign-message / stellar keys verify-message: Places commands under keys, but makes the subcommand less focused on key management
  2. stellar sign / stellar verify: Top-level commands, but could conflict with future additions
  3. stellar tx sign-message: Under tx, but messages aren't transactions

The stellar message subcommand provides clear separation and room for future message-related features.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    Backlog (Not Ready)

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions