SEP: 0023
Title: Strkeys
Author: David Mazières, Tomer Weller <@tomerweller>, Leigh McCulloch <@leighmcculloch>, Alfonso Acosta <@2opremio>
Track: Standard
Status: Active
Created: 2019-09-16
Updated: 2021-07-23
Version: 1.1.0
Discussion: https://groups.google.com/g/stellar-dev
Strkey is an ASCII format for representing Stellar account IDs and addresses.
A canonical representation for encoding Stellar account IDs, muxed accounts, and other signers of Stellar transactions and accounts improves interoperability. Applications in the Stellar ecosystem may refer to an account, a muxed account, or a signer, using a common format and therefore understand each other. Strkeys are used widely in other SEPs.
Strkey is an ASCII encoding for Stellar account IDs, muxed accounts, and signers. Applications may reliably encode or decode a Strkey with each account having a single valid representation.
Strkey, the ASCII encoding for accounts, keys, and signers, currently covers ED25519 public keys, ED25519 private keys (also known as seeds), pre-authorized transaction hashes, and hash-x signers (which provide signing authority upon revelation of a SHA-256 preimage), muxed accounts (introduced in CAP-27), and signed payload signers (introduced in CAP-40).
Each strkey has its type encoded in the top 5 bits of the version byte, which is the first 8 bits of the strkey. The possible "base" values (top 5 bits) of the version byte, which determine the first character of the base-32 encoding of the key, are listed here:
Key type | Base value | First char | Muxed | Alg |
---|---|---|---|---|
STRKEY_PUBKEY | 6 << 3 | G | no | PK |
STRKEY_MUXED | 12 << 3 | M | yes | PK |
STRKEY_PRIVKEY | 18 << 3 | S | no | PK |
STRKEY_PRE_AUTH_TX | 19 << 3 | T | no | Hash |
STRKEY_HASH_X | 23 << 3 | X | no | Hash |
STRKEY_SIGNED_PAYLOAD | 15 << 3 | P | no | PK |
The low 3 bits of the version byte encode an algorithm specifier. Thus, a version byte becomes the bitwise OR of a base value above and one of the algorithm specifiers from the two tables below. (These tables will be extended when Stellar adds additional crypto algorithms.)
PK Algorithm | Value |
---|---|
STRKEY_ALG_ED25519 | 0 |
Hash Algorithm | Value |
---|---|
STRKEY_ALG_SHA256 | 0 |
The following steps transform a binary key into a strkey:
-
Start with the appropriate version byte computed by the OR of a key type base value and algorithm selector from the tables above.
-
Append the binary bytes of the key (e.g., 32-bytes for ED25519).
-
If we are encoding a multiplexed address, append an 8-byte memo ID in network byte order (most significant byte first).
-
If we are encoding a signed payload, append a 4-byte length in network byte order (most significant byte first) that holds the length of the payload, then append the payload, and finally zero padding of 0 to 3 zero bytes such that the total number of bytes of the payload plus the zero padding is a multiple of four.
-
Compute a 16-bit CRC16 checksum of the result of the prior step (using polynomial x16 + x12 + x5 + 1). Append the two-byte checksum to the result of the previous step (e.g., producing a 35-byte quantity for a non-multiplexed ED25519 public key, or 43 byte quantity for a multiplexed one).
-
Encode the result of the previous step using RFC4648 base-32 encoding without padding. For example, a multiplexed address yields a 43-byte quantity whose base-32 encoding is 69 bytes with no trailing
=
signs because no padding is allowed.
To transform a strkey into a binary key, the process is simply
reversed. However, a strkey is only valid if re-encoding the binary
key yields the exact same strkey. Note, in particular, that a
strkey's length must not be congruent to 1, 3, or 6 mod 8, and
unused bits of the last symbol must be zero. Some non-padding base32
libraries, such as the one in the standard go
library—base32.StdEncoding.WithPadding(base32.NoPadding)
—do
not enforce these requirements. Therefore, implementations of strkey
decoding must check and reject such invalid inputs, or perform a
round-trip and reject strkey inputs that do not re-encode to the exact
same string.
The CRC is an error detection mechanism but does not guarantee that a strkey has not been truncated. Existing strkeys have a determinible length based on their strkey type and algorithmn specifier.
Implementations of strkey must accept the following valid test cases and reject the invalid test cases. Common bugs such as forgetting to reject strkeys with length congruent to 1, 3, or 6 mod 8 before invoking base-32 decoding will cause software to accept the invalid test cases, which could in turn cause security problems.
-
Valid non-multiplexed account
- Strkey
GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ
- type:
KEY_TYPE_ED25519
- Binary
MuxedAccount
:
{ 0x00, 0x00, 0x00, 0x00, 0x3f, 0x0c, 0x34, 0xbf, 0x93, 0xad, 0x0d, 0x99, 0x71, 0xd0, 0x4c, 0xcc, 0x90, 0xf7, 0x05, 0x51, 0x1c, 0x83, 0x8a, 0xad, 0x97, 0x34, 0xa4, 0xa2, 0xfb, 0x0d, 0x7a, 0x03, 0xfc, 0x7f, 0xe8, 0x9a, }
- Strkey
-
Valid multiplexed account
- Strkey:
MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUQ
- type:
KEY_TYPE_MUXED_ED25519
- id: 0
- ed25519:
GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ
- Binary
MuxedAccount
:
{ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x0c, 0x34, 0xbf, 0x93, 0xad, 0x0d, 0x99, 0x71, 0xd0, 0x4c, 0xcc, 0x90, 0xf7, 0x05, 0x51, 0x1c, 0x83, 0x8a, 0xad, 0x97, 0x34, 0xa4, 0xa2, 0xfb, 0x0d, 0x7a, 0x03, 0xfc, 0x7f, 0xe8, 0x9a, }
- Strkey:
-
Valid multiplexed account in which unsigned id exceeds maximum signed 64-bit integer
- Strkey:
MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK
- type:
KEY_TYPE_MUXED_ED25519
- id: 9223372036854775808
- ed25519:
GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ
- Binary
MuxedAccount
:
{ 0x00, 0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x0c, 0x34, 0xbf, 0x93, 0xad, 0x0d, 0x99, 0x71, 0xd0, 0x4c, 0xcc, 0x90, 0xf7, 0x05, 0x51, 0x1c, 0x83, 0x8a, 0xad, 0x97, 0x34, 0xa4, 0xa2, 0xfb, 0x0d, 0x7a, 0x03, 0xfc, 0x7f, 0xe8, 0x9a, }
- Strkey:
-
Valid signed payload with an ed25519 public key and a 32-byte payload.
- Strkey:
PA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAQACAQDAQCQMBYIBEFAWDANBYHRAEISCMKBKFQXDAMRUGY4DUPB6IBZGM
- type:
KEY_TYPE_ED25519_SIGNED_PAYLOAD
- ed25519:
GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ
- payload: 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 (hex)
- Strkey:
-
Valid signed payload with an ed25519 public key and a 29-byte payload which becomes zero padded.
- Strkey:
PA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAOQCAQDAQCQMBYIBEFAWDANBYHRAEISCMKBKFQXDAMRUGY4DUAAAAFGBU
- type:
KEY_TYPE_ED25519_SIGNED_PAYLOAD
- ed25519:
GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ
- payload: 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d (hex)
- Strkey:
-
Invalid length (Ed25519 should be 32 bytes, not 5)
- Strkey:
GAAAAAAAACGC6
- Strkey:
-
The unused trailing bit must be zero in the encoding of the last three bytes (24 bits) as five base-32 symbols (25 bits)
- Strkey:
MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUR
- Strkey:
-
Invalid length (congruent to 1 mod 8)
- Strkey:
GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZA
- Strkey:
-
Invalid length (base-32 decoding should yield 35 bytes, not 36)
- Strkey:
GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUACUSI
- Strkey:
-
Invalid algorithm (low 3 bits of version byte are 7)
- Strkey:
G47QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVP2I
- Strkey:
-
Invalid length (congruent to 6 mod 8)
- Strkey:
MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLKA
- Strkey:
-
Invalid length (base-32 decoding should yield 43 bytes, not 44)
- Strkey:
MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAAV75I
- Strkey:
-
Invalid algorithm (low 3 bits of version byte are 7)
- Strkey:
M47QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUQ
- Strkey:
-
Padding bytes are not allowed
- Strkey:
MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUK===
- Strkey:
-
Invalid checksum
- Strkey:
MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUO
- Strkey:
-
Length prefix specifies length that is shorter than payload in signed payload
- Strkey:
PA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAQACAQDAQCQMBYIBEFAWDANBYHRAEISCMKBKFQXDAMRUGY4DUPB6IAAAAAAAAPM
- Strkey:
-
Length prefix specifies length that is longer than payload in signed payload
- Strkey:
PA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAOQCAQDAQCQMBYIBEFAWDANBYHRAEISCMKBKFQXDAMRUGY4Z2PQ
- Strkey:
-
No zero padding in signed payload
- Strkey:
PA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAOQCAQDAQCQMBYIBEFAWDANBYHRAEISCMKBKFQXDAMRUGY4DXFH6
- Strkey:
You can paste these invalid strkeys more conveniently into a unit test using the following array:
{
"GAAAAAAAACGC6",
"MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUR",
"GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZA",
"GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUACUSI",
"G47QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVP2I",
"MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLKA",
"MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAAV75I",
"M47QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUQ",
"MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUK===",
"MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUO",
"PA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAQACAQDAQCQMBYIBEFAWDANBYHRAEISCMKBKFQXDAMRUGY4DUPB6IAAAAAAAAPM",
"PA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAOQCAQDAQCQMBYIBEFAWDANBYHRAEISCMKBKFQXDAMRUGY4Z2PQ",
"PA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAOQCAQDAQCQMBYIBEFAWDANBYHRAEISCMKBKFQXDAMRUGY4DXFH6",
}
There are many implementations of strkey in Stellar SDKs. As a reference, the following implementations are kept up-to-date with this SEP.
https://github.com/xdrpp/stc
None.