-
Notifications
You must be signed in to change notification settings - Fork 142
Open
Description
Summary
Add support for signing and verifying arbitrary messages per SEP-53 to the Keypair class.
This feature is already implemented in:
- Python SDK (py-stellar-base) -
Keypair.sign_message()/Keypair.verify_message() - Java SDK (java-stellar-sdk) -
KeyPair.signMessage()/KeyPair.verifyMessage() - Stellar CLI (stellar-cli) -
stellar message sign/stellar message verify
The JavaScript SDK should have parity with other Stellar SDKs.
Motivation
SEP-53 enables proving Stellar address ownership without on-chain transactions, useful for:
- Proof of ownership: Verifying control of a Stellar address on social platforms, Discord bots, etc.
- Off-chain agreements: Signing messages for contractual purposes
- Cross-chain integrations: Verifying Stellar address ownership in multi-chain applications
- Authentication: Using Stellar keys as an authentication mechanism (similar to "Sign in with Ethereum")
Proposed API
Following the pattern established by other SDKs:
// Signing a message
const keypair = StellarBase.Keypair.fromSecret('S...');
const signature = keypair.signMessage('Hello, World!');
// Returns: Buffer containing 64-byte ed25519 signature
// Verifying a message
const publicKeypair = StellarBase.Keypair.fromPublicKey('G...');
const isValid = publicKeypair.verifyMessage('Hello, World!', signature);
// Returns: boolean
// Binary message support
const binaryMessage = Buffer.from([0x01, 0x02, 0x03]);
const sig = keypair.signMessage(binaryMessage);Implementation Details
Per SEP-53, the signing process is:
- Convert message to bytes (UTF-8 if string)
- Prepend the fixed prefix:
"Stellar Signed Message:\n" - Compute
hash = SHA256(prefix + message) - Sign
hashwith ed25519 private key - Return 64-byte signature
Suggested Implementation
In src/keypair.js:
const MESSAGE_PREFIX = Buffer.from('Stellar Signed Message:\n');
/**
* Sign an arbitrary message according to SEP-53.
* @param {string|Buffer} message - The message to sign
* @returns {Buffer} 64-byte ed25519 signature
* @see https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0053.md
*/
signMessage(message) {
if (!this.canSign()) {
throw new Error('Cannot sign: no secret key available');
}
const messageHash = this._calculateMessageHash(message);
return this.sign(messageHash);
}
/**
* Verify a SEP-53 signed message.
* @param {string|Buffer} message - The original message
* @param {Buffer} signature - The signature to verify
* @returns {boolean} True if signature is valid
*/
verifyMessage(message, signature) {
const messageHash = this._calculateMessageHash(message);
return this.verify(messageHash, signature);
}
/**
* Calculate the SEP-53 message hash.
* @private
*/
_calculateMessageHash(message) {
const messageBytes = typeof message === 'string'
? Buffer.from(message, 'utf8')
: Buffer.from(message);
const payload = Buffer.concat([MESSAGE_PREFIX, messageBytes]);
return hash(payload); // Uses existing hash function (SHA-256)
}Test Vectors
SEP-53 provides test vectors that should be used for validation:
| Message | Expected Signature (base64) |
|---|---|
Hello, World! |
fO5dbYhXUhBMhe6kId/cuVq/AfEnHRHEvsP8vXh03M1uLpi5e46yO2Q8rEBzu3feXQewcQE5GArp88u6ePK6BA== |
こんにちは、世界! |
CDU265Xs8y3OWbB/56H9jPgUss5G9A0qFuTqH2zs2YDgTm+++dIfmAEceFqB7bhfN3am59lCtDXrCtwH2k1GBA== |
(Using public key GBXFXNDLV4LSWA4VB7YIL5GBD7BVNR22SGBTDKMO2SBZZHDXSKZYCP7L)
TypeScript Types
declare class Keypair {
// ... existing methods ...
/**
* Sign an arbitrary message according to SEP-53.
* @param message - The message to sign (string or Buffer)
* @returns 64-byte signature as Buffer
* @throws Error if keypair cannot sign (no secret key)
*/
signMessage(message: string | Buffer): Buffer;
/**
* Verify a SEP-53 signed message.
* @param message - The original message
* @param signature - The signature to verify
* @returns true if valid, false otherwise
*/
verifyMessage(message: string | Buffer, signature: Buffer): boolean;
}References
Metadata
Metadata
Assignees
Labels
No labels