Skip to content

Commit f25395d

Browse files
authored
SEP-53: Add Sign and Verify Messages SEP (#1690)
1 parent 3cc43c6 commit f25395d

2 files changed

Lines changed: 202 additions & 0 deletions

File tree

ecosystem/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
| [SEP-0050](sep-0050.md) | Non-Fungible Tokens | OpenZeppelin, Boyan Barakov, Özgün Özerk | Standard | Draft |
9494
| [SEP-0051](sep-0051.md) | XDR-JSON | Leigh McCulloch | Standard | Draft |
9595
| [SEP-0052](sep-0052.md) | Key Sharing Method for Stellar Keys | Pamphile Roy, Jun Luo | Standard | Draft |
96+
| [SEP-0053](sep-0053.md) | Sign and Verify Messages | Jun Luo, Pamphile Roy, OrbitLens, Piyal Basu | Standard | Draft |
9697

9798
### Rejected and Deprecated Proposals
9899

ecosystem/sep-0053.md

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
## Preamble
2+
3+
```
4+
SEP: 0053
5+
Title: Sign and Verify Messages
6+
Author: Jun Luo (@overcat), Pamphile Roy (@tupui), OrbitLens (@orbitlens), Piyal Basu (@piyalbasu)
7+
Track: Standard
8+
Status: Draft
9+
Created: 2025-02-01
10+
Updated: 2025-02-01
11+
Version: 0.0.1
12+
Discussion: https://github.com/stellar/stellar-protocol/discussions/1641
13+
```
14+
15+
## Simple Summary
16+
17+
This SEP proposes a canonical method for signing and verifying arbitrary
18+
messages using Stellar key pairs. It aims to standardize message signing
19+
functionality across various Stellar wallets, libraries, and services,
20+
preventing ecosystem fragmentation and ensuring interoperability.
21+
22+
## Abstract
23+
24+
Stellar uses ed25519 keys for transaction signatures by design, but there is
25+
currently no canonical specification for signing arbitrary messages outside the
26+
normal transaction flow. This proposal defines:
27+
28+
- A message format supporting user-supplied data in various encodings
29+
- SHA-256 as the standard hashing function
30+
- Standardized procedures for signing and verifying messages off-chain
31+
32+
By adopting this SEP, developers can seamlessly incorporate message signing
33+
capabilities for multi-lingual text or arbitrary binary data, enabling
34+
proof-of-ownership and authentication in off-chain scenarios such as social
35+
platform verification, cross-chain operations, and general data validation.
36+
37+
## Motivation
38+
39+
Many blockchain ecosystems provide "Sign Message" capabilities for proving key
40+
ownership outside of normal transactions. While Stellar has the fundamental
41+
cryptographic primitives, there is no official, widely adopted protocol for
42+
signing arbitrary messages. Without standardization of how bytes are composed,
43+
hashed, and verified, different implementations risk incompatibility.
44+
45+
This functionality is particularly useful for:
46+
47+
- Proving control of a Stellar address on social platforms
48+
- Verifying off-chain agreements or terms
49+
- Integrating with cross-chain or multi-chain dApps that require address proof
50+
51+
## Specification
52+
53+
### Prefix and Encodings
54+
55+
To prevent confusion with raw transactions and to mitigate replay attacks, a
56+
fixed prefix string is used: `"Stellar Signed Message:\n"`,
57+
58+
The implementation MUST handle the user-provided message in the way it is
59+
supplied:
60+
61+
- If the message is provided as a string, it SHOULD be interpreted as UTF-8
62+
text.
63+
- If the message is provided as raw bytes, it SHOULD be processed as binary
64+
data.
65+
66+
A signed message can be any data, but human-readable text is generally
67+
recommended as it is easier for users to verify visually. Wallets and libraries
68+
MUST clearly display or otherwise confirm the content being signed, especially
69+
for complex or user-supplied data.
70+
71+
### Message Format
72+
73+
The canonical signing payload is constructed by concatenating:
74+
`<prefixBytes><message>`.
75+
76+
Where:
77+
78+
- `<prefixBytes>`: Fixed UTF-8 encoded string `"Stellar Signed Message:\n"`.
79+
- `<messageBytes>`: The byte representation of the message. If the input was a
80+
string, it should be UTF-8 encoded. If the input was already bytes, no
81+
further conversion is needed.
82+
83+
#### Why `"Stellar Signed Message:\n"`
84+
85+
Bitcoin uses the prefix `"Bitcoin Signed Message:\n"` and Ethereum uses
86+
`"Ethereum Signed Message:\n"` for their respective message signing
87+
implementations. By adopting a similar format, Stellar's off-chain message
88+
signing maintains consistency with established approaches in other blockchains,
89+
making it easier for developers with multi-chain experience to understand and
90+
implement.
91+
92+
### Hashing Algorithm
93+
94+
This proposal standardizes on single-round `SHA-256` for hashing:
95+
`messageHash = SHA256(encodedMessage)`. This approach is widely regarded as
96+
secure and efficient.
97+
98+
### Signing Procedure
99+
100+
1. Convert the message to a byte array if it is a string, using UTF-8 encoding.
101+
2. Serialize the prefix (bytes) and the user’s message (bytes) as described
102+
(`prefix + message`).
103+
3. Compute `messageHash = SHA256(encodedMessage)`.
104+
4. Sign `messageHash` using the Stellar private key (ed25519). This yields a
105+
64-byte signature.
106+
107+
### Verification Procedure
108+
109+
1. Convert the message to a byte array if it is a string (same UTF-8 method
110+
used during signing).
111+
2. Reconstruct the same canonical payload using the known prefix and the byte
112+
array message.
113+
3. Compute `messageHash = SHA256(encodedMessage)`.
114+
4. Use the corresponding public key to verify the 64-byte ed25519 signature.
115+
5. If the signature matches, verification is successful; otherwise, it fails.
116+
117+
### Handling Multi-language and Binary Data
118+
119+
- Libraries SHOULD accept either a string or a byte array for the message.
120+
- If a string is provided, libraries MUST use UTF-8 encoding internally to get
121+
the byte array.
122+
- If binary data is provided, libraries MUST preserve the raw byte sequence (no
123+
extra encoding steps).
124+
- Wallet UIs can choose how to display non-ASCII or non-printable data (e.g.,
125+
by rendering hex or base64). They SHOULD clearly communicate the data format
126+
to the user.
127+
- Visual verification for end users is critically important, we generally
128+
recommend using human-readable text (e.g., UTF-8–encoded strings) for most
129+
use cases.
130+
131+
### Reference Implementation (Pseudo-Code)
132+
133+
```pseudo
134+
function encodeMessage(message):
135+
prefix = "Stellar Signed Message:\n"
136+
# Convert message to bytes (if needed)
137+
if isString(message):
138+
messageBytes = utf8Encode(message)
139+
else:
140+
messageBytes = message
141+
return prefixBytes + messageBytes
142+
143+
function stellarSignMessage(privateKey, message):
144+
signedMessageBase = encodeMessage(message)
145+
messageHash = SHA256(encodedPayload)
146+
signature = ed25519_sign(privateKey, messageHash)
147+
return signature
148+
149+
function stellarVerifyMessage(publicKey, message, signature):
150+
signedMessageBase = encodeMessage(message)
151+
messageHash = SHA256(encodedPayload)
152+
return ed25519_verify(publicKey, messageHash, signature)
153+
```
154+
155+
### Test cases
156+
157+
1. Sign a simple ASCII message
158+
159+
- Message: `Hello, World!`
160+
- Seed: `SAKICEVQLYWGSOJS4WW7HZJWAHZVEEBS527LHK5V4MLJALYKICQCJXMW`
161+
- Address: `GBXFXNDLV4LSWA4VB7YIL5GBD7BVNR22SGBTDKMO2SBZZHDXSKZYCP7L`
162+
- Signature (base64 encoded):
163+
`fO5dbYhXUhBMhe6kId/cuVq/AfEnHRHEvsP8vXh03M1uLpi5e46yO2Q8rEBzu3feXQewcQE5GArp88u6ePK6BA==`
164+
165+
2. Sign a Japanese message
166+
167+
- Message: `こんにちは、世界!`
168+
- Seed: `SAKICEVQLYWGSOJS4WW7HZJWAHZVEEBS527LHK5V4MLJALYKICQCJXMW`
169+
- Address: `GBXFXNDLV4LSWA4VB7YIL5GBD7BVNR22SGBTDKMO2SBZZHDXSKZYCP7L`
170+
- Signature (base64 encoded):
171+
`CDU265Xs8y3OWbB/56H9jPgUss5G9A0qFuTqH2zs2YDgTm+++dIfmAEceFqB7bhfN3am59lCtDXrCtwH2k1GBA==`
172+
173+
3. Sign a binary message
174+
175+
- Message (base64 encoded): `2zZDP1sa1BVBfLP7TeeMk3sUbaxAkUhBhDiNdrksaFo=`
176+
- Seed: `SAKICEVQLYWGSOJS4WW7HZJWAHZVEEBS527LHK5V4MLJALYKICQCJXMW`
177+
- Address: `GBXFXNDLV4LSWA4VB7YIL5GBD7BVNR22SGBTDKMO2SBZZHDXSKZYCP7L`
178+
- Signature (base64 encoded):
179+
`VA1+7hefNwv2NKScH6n+Sljj15kLAge+M2wE7fzFOf+L0MMbssA1mwfJZRyyrhBORQRle10X1Dxpx+UOI4EbDQ==`
180+
181+
## Limitations
182+
183+
- Ownership of a private key does not imply control of the account. Even though
184+
signatures prove that a signer controls a private key corresponding to a
185+
particular address, that does not necessarily mean they control the
186+
corresponding Stellar account. In multi-signer scenarios, possession of just
187+
one key may not grant full control over the account.
188+
189+
## Backwards Compatibility
190+
191+
This is a new standard that does not conflict with existing SEPs, though it
192+
provides off-chain signature functionality distinct from transaction signing
193+
methods.
194+
195+
## Acknowledgments
196+
197+
- Inspired by message signing patterns in Bitcoin, Ethereum, etc.
198+
199+
## References
200+
201+
- https://github.com/bitcoin/bips/blob/master/bip-0137.mediawiki

0 commit comments

Comments
 (0)