Cryptography

Cryptographic systems, curves, and key types used in AT Protocol

Cryptography

AT Protocol uses public key cryptography to authenticate public data. For simplicity and consistency, a small number of cryptographic systems and signing techniques are reused through the protocol framework. Data encryption is not used directly in the protocol, though many components use secure network transport based on TLS.

Two elliptic curves are currently supported, and implementations are expected to fully support both:

  • p256 elliptic curve: aka "NIST P-256", aka secp256r1 (note the r), aka prime256v1
    • This curve is included in the WebCrypto API. It is commonly supported by personal device hardware (Trusted Platform Modules (TPMs) and mobile Secure Enclaves), and by cloud Hardware Security Modules (HSMs)
  • k256 elliptic curve: aka secp256k1 (note the k)
    • This curve is not included in the WebCrypto API. It is broadly supported by personal secret key management technologies and hardware, due to it's use in popular cryptocurrencies, including Bitcoin. It is also supported by cloud HSMs.

Because of the subtle visual distinction when the full curve names are written out, they are often referred to as p256 or k256.

The original atproto reference implementation consistently support both curve types. When generating new key pairs, it uses k256 by default.

Key points for both systems have loss-less "compressed" representations, which are useful when sharing the public keys. This is usually supported natively for k256, but sometimes requires extra methods or jumping through hoops for p256. You can read more about this at: 02, 03 or 04? So What Are Compressed and Uncompressed Public Keys?.

A common pattern when signing data in atproto is to:

  • encode the data in a strict CBOR format (DRISL)
  • hash the resulting CBOR bytes using SHA-256, resulting in raw bytes (not a hex-encoded string)
  • sign the hash bytes.

ECDSA Signature Malleability

Some ECDSA signatures can be transformed to yield a new distinct but still-valid signature. This does not require access to the private signing key or the data that was signed. The scope of attacks possible using this property is limited, but it is an unexpected property.

For k256 specifically, the distinction is between "low-S" and "high-S" signatures, as discussed in Bitcoin BIP-0062.

In atproto, use of the "low-S" signature variant is required for both p256 and k256 curves.

In atproto, signatures should always be verified using the verification routines provided by the cryptographic library, never by comparing signature values as raw bytes.

Public Key Encoding

When encoding public keys as strings, the preferred representation is the "multikey" encoding used by did:key. Because this representation includes a "codec" indicating the curve type, they can be parsed umabiguously.

The process for encoding a public key in this format is:

  • Encode the public key curve "point" as bytes. Be sure to use the smaller "compact" or "compressed" representation. This is usually easy for k256, but might require a special argument or configuration for p256 keys
  • Prepend the appropriate curve codec value, as varint-encoded bytes, in front of the key bytes:
    • p256 (compressed, 33 byte key length): p256-pub, code 0x1200, varint-encoded bytes: [0x80, 0x24]
    • k256 (compressed, 33 byte key length): secp256k1-pub, code 0xE7, varint bytes: [0xE7, 0x01]
  • Encode the combined bytes with with base58btc, and prefix with a z character, yielding string

The decoding process is the same in reverse, using the identified curve type as context.

To encode a key as a did:key identifier, use the above multikey encoding, and add the ASCII prefix did:key:. This identifier is used as an internal implementation detail in the DID PLC method.

Note that there is a variant legacy "multibase" encoding described in the atproto DID specification document, which does not include a codec type value, and uses uncompressed byte encoding of keys. This format is deprecated.

Encoded Examples

A P-256 public key, encoded as a multikey, and as did:key:

zDnaembgSGUhZULN2Caob4HLJPaxBh92N7rtH21TErzqf8HQo
did:key:zDnaembgSGUhZULN2Caob4HLJPaxBh92N7rtH21TErzqf8HQo

A K-256 public key, encoded as a multikey, and as did:key:

zQ3shqwJEJyMBsBXCWyCBpUBMqxcon9oHB7mCvx4sSpMdLJwc
did:key:zQ3shqwJEJyMBsBXCWyCBpUBMqxcon9oHB7mCvx4sSpMdLJwc

Usage and Implementation Guidelines

There is no specific recommended byte or string encoding for private keys across the atproto ecosystem. Sometimes simple hex encoding is used, sometimes multikey, and sometimes JWK.

Possible Future Changes

The set of supported cryptographic systems is expected to evolve slowly. There are significant interoperability and implementation advantages to having as few systems as possible at any point in time.