Skip to content
Permalink
master
Switch branches/tags
Go to file
### What
Introduces a new strkey type to represent ed25519 signed payload signers introduced by [CAP-40].

### Why
[CAP-40] introduces a new signer for disclosing transaction signatures in transactions. Some ecosystem services, such as Horizon, only refer to signers in string form. Stellar SDKs also use strings to pass around signers. A string form for the signer introduced by CAP-40 is required for these services.

The leading `P` prefix for the new strkey was chosen to indicate that the signer is a signed payload because the word payload begins with p.

The 4-byte length prefix is to ensure that a truncated signed payload that has a valid CRC is not considered valid.

The 4-byte length prefix and the payload zero padding to a multiple of 4 bytes makes the raw bytes encoded in the strkey identical to the signer key when XDR encoded, a property of existing strkeys, excluding muxed accounts that match their XDR encoding with fields reversed.

[CAP-40]: https://stellar.org/protocol/cap-40
4 contributors

Users who have contributed to this file

@leighmcculloch @tomerweller @stanford-scs @2opremio

Preamble

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

Simple Summary

Strkey is an ASCII format for representing Stellar account IDs and addresses.

Motivation

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.

Abstract

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.

Specification

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:

  1. Start with the appropriate version byte computed by the OR of a key type base value and algorithm selector from the tables above.

  2. Append the binary bytes of the key (e.g., 32-bytes for ED25519).

  3. If we are encoding a multiplexed address, append an 8-byte memo ID in network byte order (most significant byte first).

  4. 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.

  5. 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).

  6. 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.

Tests

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 test cases

  1. 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,
    }
    
  2. 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,
     }
    
  3. 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,
     }
    
  4. 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)
  5. 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)

Invalid test cases

  1. Invalid length (Ed25519 should be 32 bytes, not 5)

    • Strkey: GAAAAAAAACGC6
  2. 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
  3. Invalid length (congruent to 1 mod 8)

    • Strkey: GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZA
  4. Invalid length (base-32 decoding should yield 35 bytes, not 36)

    • Strkey: GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUACUSI
  5. Invalid algorithm (low 3 bits of version byte are 7)

    • Strkey: G47QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVP2I
  6. Invalid length (congruent to 6 mod 8)

    • Strkey: MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLKA
  7. Invalid length (base-32 decoding should yield 43 bytes, not 44)

    • Strkey: MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAAV75I
  8. Invalid algorithm (low 3 bits of version byte are 7)

    • Strkey: M47QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUQ
  9. Padding bytes are not allowed

    • Strkey: MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUK===
  10. Invalid checksum

    • Strkey: MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUO
  11. Length prefix specifies length that is shorter than payload in signed payload

    • Strkey: PA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAQACAQDAQCQMBYIBEFAWDANBYHRAEISCMKBKFQXDAMRUGY4DUPB6IAAAAAAAAPM
  12. Length prefix specifies length that is longer than payload in signed payload

    • Strkey: PA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAOQCAQDAQCQMBYIBEFAWDANBYHRAEISCMKBKFQXDAMRUGY4Z2PQ
  13. No zero padding in signed payload

    • Strkey: PA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAOQCAQDAQCQMBYIBEFAWDANBYHRAEISCMKBKFQXDAMRUGY4DXFH6

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",
}

Implementation

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

Security Concerns

None.