[SEP-53] Sign and Verify Messages #1641
Replies: 16 comments 34 replies
|
A Python Implementation Openimport base64
from stellar_sdk import Keypair
from stellar_sdk.exceptions import BadSignatureError
from stellar_sdk.utils import sha256
def encode_message(message: str | bytes):
prefix = b"Stellar Signed Message:\n"
if isinstance(message, str):
message = message.encode('utf-8')
return prefix + message
def sign_message(message: str | bytes, secret: str) -> bytes:
kp = Keypair.from_secret(secret)
signed_message_base = encode_message(message)
message_hash = sha256(signed_message_base)
return kp.sign(message_hash)
def verify_message(message: str | bytes, signature: bytes, public_key: str):
kp = Keypair.from_public_key(public_key)
signed_message_base = encode_message(message)
message_hash = sha256(signed_message_base)
return kp.verify(message_hash, signature)
if __name__ == '__main__':
kp = Keypair.from_secret("SAKICEVQLYWGSOJS4WW7HZJWAHZVEEBS527LHK5V4MLJALYKICQCJXMW")
message = "Hello, World!"
signature = sign_message(message, kp.secret)
print(f"Signature: {base64.b64encode(signature)}")
try:
verify_message(message, signature, kp.public_key)
print("Signature is valid")
except BadSignatureError:
print("Bad Signature") |
|
Very nice 👍 some thoughts (not necessarily in order, as they come 😅) I would add some text saying why we would add into the message the byte lengths. On the attack side we could elaborate a bit. You mention replay, but here to me it's not clear how the design mitigates it. I suppose this can be achieved with passing a random value as part of the message itself from the client asking for authentication. In general for the UX, I think we should push to ask that a special representation of these messages is made. Right now, some dApp show a modal explaining why they have to sign a transaction to authenticate, but I think that this should be done by the wallet. A bit like a oauth2 flow where you have the modal telling you what you are connecting to and what that means. Going a bit further in that direction, I would add the concept of scopes in the message. This way, wallets can show a clear authentication pop up with clear information about scoping (of course there is no pre authorization on these scopes with a normal address, but passkey wallets and other smart wallets can have policy signing as @kalepail demonstrated.) |
|
For These two methods are rarely used in Stellar, and I’m not sure which one people prefer. Directly using a fixed-length int encoding might be simpler. |
|
For Binary data, is it worth suggesting a specific encoding? One of the things I dislike the most about ETH blob signing is how opaque it is. For example, we could mandate that the binary format is an |
Something that's challenging for all these use cases is that it only proves control of a key, not of an address, unless the off-chain system has an intimate understanding of what it means for that key's signature in the context of an address. That's a similar challenge faced by SEP-10 and SEP-45 which is why those SEPs are focused on how to validate the account, not only the key. Something that was helpful in some past proposals was to go into more detail on the use cases. I think it would be helpful add a section for each use case with an example of how it will be used for that case, and discuss the limitations as well. The limitations might be fine, this doesn't have to support all types of accounts necessarily if not all accounts will use it. |
|
Very well written SEP 👍
function signMessage(message, keypair) {
const publicKey = keypair.publicKey()
const signedMessage = keypair.publicKey()+':'+message //protection from spoofing
const messageHash = sha256(signedMessage)
const signature = keypair.sign(messageHash).toString('hex')
return {publicKey, signature, signedMessage}
}During a conversation with the community long, long time ago (on public Stellar Slack) people voted for this prefix format (I originally also proposed a static string prefix). There were some arguments in favor of using a deterministic variable prefix (hence the pubkey) for security reasons, but I'm not a cryptography expert myself. Can we utilize our format (StrKey-encoded public key + message) to prevent breaking existing integrations? |
|
I started implementing messages signing in xBull during the weekend and I was following the approach from Albedo because that way it will be compatible with everything that already uses it... and being honest I like the idea of including the public key because that way the wallet knows what account needs to use to sign the message (SEP-43 optional Now, that being said... I believe SEP-10 serves better for everything related to account authentication because by just signing a message it doesn't directly validate the ownership of that stellar account (enough signature weight) which SEP-10 takes care of. But being compatible (or at least similar) to what other chains use is also a good argument because it makes it easy for multi chain projects to adopt stellar. So I will pause the implementation on xBull for a couple of days while I see the evolution of this conversation. |
|
In general, this proposal is looking good to me (other than my open question about message and prefix length). I also tend to agree it'd be best to try to urge devs to use 1 of a few specific encodings (but mostly text) as it would be nice to make displaying the message in a wallet UI a little more straightforward. We've had a few devs in Discord who use signing/verifying to authenticate reach out to the Freighter team recently. I'll encourage them to take a look at this so we can get some participation from some others actively using this workflow |
|
As you're asking people to use their PK, you also need to ensure proper domain separation with other blobs signed in the ecosystem. If you look at what is done for transactions, you'll also need the network ID in the prefix for example. |
|
@overcat This conversation has died down a bit. Would you mind outlining the outstanding questions that we need to agree on before moving forward with the proposal? This is a really useful SEP and I'd love to get this spec finalized |
|
The main point of contention right now lies in the format of the message body. Currently, there are two proposals:
For option 2, based on the discussion here, we've decided not to include a messageLength field. If it becomes necessary in the future, we can update this SEP accordingly. The Albedo wallet currently uses option 1, so choosing option 1 would maintain compatibility with it. We'd like to know which option everyone prefers. If you prefer option 1, please click ❤️. If you prefer option 2, please click 🚀. If you have other suggestions, please leave a comment. Thank you! |
|
Hello everyone, I've created a document and would like to invite you all to review it before I officially submit a PR. The document is open for editing, so if you have any suggestions, please feel free to make changes directly or add your comments. Thank you! |
|
Hi, based on this discussion, I’ve created a PR to add this SEP, which you can view here. Additionally, I’ve included everyone who participated in this discussion group in the list of authors. If you’d like me to remove your name from the list, please let me know. Thank you! |
|
Where does this proposal stand? I'm porting an application from Ethereum and need Stellar's equivalent of Ethereum Signed Messages. The discussion at #1641 mentions two approaches, but SEP-0053 only reflects Option 2. Did this reach consensus, or is it still open? Also: are there active projects supporting message signing and verification beyond Albedo? One constraint worth noting for anyone else porting from EVM: Stellar's ed25519 has no signature recovery function — that's secp256k1 territory (Ethereum, Bitcoin). You have to pass the public key to the verifier explicitly. It doesn't need to be embedded in the signed message itself (passing it alongside works), but you do need to account for it in your verification flow. |
|
We just entered the Final Comment Period (Final). I will make a PR to either get the SEP back to draft of promote it to Final on June 15th. |
|
I created #1963 to promote from Final Comment Period (Final) to Final. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
In SEP-43, we added the sign message API, but did not describe how to implement it. I think we should add a separate SEP to describe how to implement this feature. Suggestions for modifications, discussions, and additions are welcome.
Preamble
Simple Summary
This SEP proposes a canonical method for signing and verifying arbitrary messages using Stellar key pairs. It aims to standardize message signing functionality across various Stellar wallets, libraries, and services, preventing ecosystem fragmentation and ensuring interoperability.
Abstract
Stellar uses ed25519 keys for transaction signatures by design, but there is currently no canonical specification for signing arbitrary messages outside the normal transaction flow. This proposal defines:
By adopting this SEP, developers can seamlessly incorporate message signing capabilities for multi-lingual text or arbitrary binary data, enabling proof-of-ownership and authentication in off-chain scenarios such as social platform verification, cross-chain operations, and general data validation.
Motivation
Many blockchain ecosystems provide "Sign Message" capabilities for proving key ownership outside of normal transactions. While Stellar has the fundamental cryptographic primitives, there is no official, widely adopted protocol for signing arbitrary messages. Without standardization of how bytes are composed, hashed, and verified, different implementations risk incompatibility.
This functionality is particularly useful for:
Specification
Prefix and Encodings
To prevent confusion with raw transactions and to mitigate replay attacks, a fixed prefix string is used:
"Stellar Signed Message:\n",The implementation MUST handle the user-provided message in the way it is supplied:
A signed message can be any data, but human-readable text is generally recommended as it is easier for users to verify visually. Wallets and libraries MUST clearly display or otherwise confirm the content being signed, especially for complex or user-supplied data.
Message Format
The canonical signing payload is constructed by concatenating:
<prefixBytes><message>.Where:
<prefixBytes>: Fixed UTF-8 encoded string"Stellar Signed Message:\n".<messageBytes>: The byte representation of the message. If the input was a string, it should be UTF-8 encoded. If the input was already bytes, no further conversion is needed.Why
"Stellar Signed Message:\n"Bitcoin uses the prefix
"Bitcoin Signed Message:\n"and Ethereum uses"Ethereum Signed Message:\n"for their respective message signing implementations. By adopting a similar format, Stellar's off-chain message signing maintains consistency with established approaches in other blockchains, making it easier for developers with multi-chain experience to understand and implement.Hashing Algorithm
This proposal standardizes on single-round
SHA-256for hashing:messageHash = SHA256(encodedMessage). This approach is widely regarded as secure and efficient.Signing Procedure
prefix + message).messageHash = SHA256(encodedMessage).messageHashusing the Stellar private key (ed25519). This yields a 64-byte signature.Verification Procedure
messageHash = SHA256(encodedMessage).Handling Multi-language and Binary Data
Reference Implementation (Pseudo-Code)
Test cases
Hello, World!SAKICEVQLYWGSOJS4WW7HZJWAHZVEEBS527LHK5V4MLJALYKICQCJXMWGBXFXNDLV4LSWA4VB7YIL5GBD7BVNR22SGBTDKMO2SBZZHDXSKZYCP7LfO5dbYhXUhBMhe6kId/cuVq/AfEnHRHEvsP8vXh03M1uLpi5e46yO2Q8rEBzu3feXQewcQE5GArp88u6ePK6BA==こんにちは、世界!SAKICEVQLYWGSOJS4WW7HZJWAHZVEEBS527LHK5V4MLJALYKICQCJXMWGBXFXNDLV4LSWA4VB7YIL5GBD7BVNR22SGBTDKMO2SBZZHDXSKZYCP7LCDU265Xs8y3OWbB/56H9jPgUss5G9A0qFuTqH2zs2YDgTm+++dIfmAEceFqB7bhfN3am59lCtDXrCtwH2k1GBA==2zZDP1sa1BVBfLP7TeeMk3sUbaxAkUhBhDiNdrksaFo=SAKICEVQLYWGSOJS4WW7HZJWAHZVEEBS527LHK5V4MLJALYKICQCJXMWGBXFXNDLV4LSWA4VB7YIL5GBD7BVNR22SGBTDKMO2SBZZHDXSKZYCP7LVA1+7hefNwv2NKScH6n+Sljj15kLAge+M2wE7fzFOf+L0MMbssA1mwfJZRyyrhBORQRle10X1Dxpx+UOI4EbDQ==Limitations
Even though signatures prove that a signer controls a private key corresponding to a particular address, that does not necessarily mean they control the corresponding Stellar account. In multi-signer scenarios, possession of just one key may not grant full control over the account.
Backwards Compatibility
This is a new standard that does not conflict with existing SEPs, though it provides off-chain signature functionality distinct from transaction signing methods.
Acknowledgments
References
All reactions