Source: BRCs-248/peer-to-peer/0248.md · sha256 57e94623478b

BRC-248: Offline Issuer Identity Packages

Brandon Cryderman / HandCash (brandongcryderman@gmail.com)

Status: Draft (proposed number, not a BSVA assignment)

Abstract

BRC-247 stamps a 1Sat or 1Color issuance with the issuer's BRC-100 identity key. This BRC lets a holder turn that identity key into a name and an image from data it already holds, without querying an indexer, an overlay or a web server at display time.

This BRC adds no identity format. The identity is the identity key; certificates are BRC-52, handles are BRC-169, and BAP profiles are BAP.

Motivation

A bare public key means nothing to a person, so wallets want a name and a picture for each issuer, and each common source of one falls short:

Signed statements about identity keys already exist. A BRC-169 ecosystem certifies each handle to an identity key; the identity overlay (tm_identity) publishes BRC-52 certificates whose fields are revealed to anyone; 1Sat wallets publish BAP ALIAS profiles. Offline use still needs four rules: which of these count, which label wins, how image bytes travel, and how a BAP profile attaches to an identity key without becoming a second identity.

Relationship to other documents

Specification

The key words MUST, SHOULD and MAY are used as in RFC 2119.

1. Identity package

{
  "v": 2,
  "identityKey": "<compressed public key, lowercase hex>",
  "certificates": [ <certificate>, … ],
  "images": [ { "url": "<UHRP URL>", "contentType": "<media type>", "bytesB64": "<base64>" }, … ],
  "bap": { "bapId": "<BAP ID>", "beefB64": "<base64 BEEF>" }
}

Packages with v: 1, written by earlier drafts, carried only BAP records keyed by a BAP ID. A reader MAY use one as the bap member of whichever identity key a valid link (section 5) names, and otherwise MUST ignore it.

2. Certificates

Each entry is a BRC-52 certificate whose fields are revealed to anyone: the core certificate with a keyring addressed to the anyone key (private key 1), as the identity overlay publishes it. A reader:

  1. MUST verify the certifier's signature over the core certificate, and drop the certificate if it fails.
  2. MUST require subject to equal the package's identityKey.
  3. MUST decrypt the revealed fields with the keyring; fields that are not revealed are absent.
  4. MUST verify a BRC-169 handle certificate as BRC-169 §4.1 requires, except for revocation (below), with a certifier key the reader already holds for that domain. It MUST NOT trust a certifier key carried in the package.

A certificate is a claim by its certifier. A reader decides which certifiers it trusts, and MUST show the certifier beside any label taken from a certificate. A certificate whose certifier is its subject is self-published.

Revocation. Whether a certificate's revocationOutpoint is spent cannot be proven from the package. A reader that can reach a revocation source SHOULD check it, as BRC-169 §4.2 describes, and MUST drop a revoked certificate. Without one, a reader MAY show the label as of the package and MUST NOT use a package certificate for a value-moving decision.

Fields. A reader takes a name and an image from a certificate's revealed fields:

Certificate Name Image
BRC-169 handle The handle, displayed per BRC-169 §2.4 —
Any other name; else userName; else firstName and lastName joined by a space profilePhoto; else icon; else avatar

A name MUST be 1 to 80 characters, with no control characters and no leading or trailing whitespace; otherwise the certificate supplies no name. Email addresses and phone numbers MUST NOT be shown as names.

3. Images

Certificate image fields are URLs. Readers MUST NOT fetch an http(s) URL. A UHRP URL names content by its SHA-256 (BRC-26), so the package can carry the bytes:

Writers SHOULD re-encode the source image, removing EXIF and other metadata, and SHOULD use a square image of at most 512 px.

4. Choosing a label

A reader shows one name for an identity key, taken from the first source that supplies one:

  1. A verified handle certificate from a certifier the reader trusts.
  2. Another verified certificate from a certifier the reader trusts.
  3. A linked BAP profile (section 5).
  4. A self-published certificate.
  5. None: the reader shows the identity key, for example as a short fingerprint.

Where one source holds several candidates, the reader's certifier preference decides; package order MUST NOT. The image comes from the first of sources 2 to 4 that supplies a valid one. A reader MUST show where the label came from: the certifier, "BAP profile" or "self-published". Labels from sources 3 and 4 are claims by the issuer about itself.

5. Linked BAP profile

A BAP identity is shown as an identity key's profile only when both sign one link record:

OP_FALSE OP_RETURN
  1PuQa7K62MiKCtssSLKy1kh56WWU7MtUR5 SET type identity-link identityKey <identity key> bapId <BAP ID>
  | SIGMA BRC77 <identity key address> <signature> <vin>
  | SIGMA BSM <BAP signing address> <signature> <vin>

The bap member's BEEF contains the link record, the records section 6 builds the key chain from, the ALIAS section 7 selects, the B file it names, any unlink record and any revocation. Each mined transaction MUST carry its merkle path. Heights decide rotation, links and revocation, so before storing a package received from someone else a reader MUST check every merkle root in it against block headers, for example with an SPV chain tracker, and MUST NOT store a package whose roots it cannot check.

A stamp from an earlier draft of BRC-247, signed by a BAP key, is attributed to an identity key only when that BAP identity is linked to it and the stamp's signer is active at the asset's mined height.

6. BAP records and key chain

BAP records are OP_FALSE OP_RETURN data outputs, so they are unspendable. Writers SHOULD create them with 0 satoshis.

ID     OP_FALSE OP_RETURN 1BAPSuaPnfGnSBM3GLV9yhxUdYe4vGbdMT ID <bapId> <address>
       | 15PciHG22SNLQJXMoSUaWVi7WSqc7hCfva BITCOIN_ECDSA <signer address> <signature>
ALIAS  OP_FALSE OP_RETURN 1BAPSuaPnfGnSBM3GLV9yhxUdYe4vGbdMT ALIAS <bapId> <profile JSON>
       | 15PciHG22SNLQJXMoSUaWVi7WSqc7hCfva BITCOIN_ECDSA <signer address> <signature>
B      OP_FALSE OP_RETURN 19HxigV4QyBv3tHpQVcUEQyq1pzZVdoAut <image bytes> <content type> binary

AIP signature

BAP ID and root address. bapId is base58(ripemd160(sha256(utf8(rootAddress)))), as BAP defines. The root address is the signer of an ID record whose signer address hashes to bapId. Readers MUST NOT accept a root address that does not hash to the BAP ID.

Key chain. From the records for one BAP ID:

  1. Find the root address.
  2. Let current be the root. Among ID records signed by current that declare a valid address other than 0, take the first on chain (below). That address is the next signing key. Set current to it and repeat.
  3. Stop when there is no candidate or when an address repeats. Readers MUST follow at least 64 keys and MAY stop after that.
  4. The last key in the chain is the current key. A chain with no declared key is not an identity.

First on chain. A record has a mined height when the package proves it with a merkle path; otherwise it is unmined. If any candidate is mined, the lowest height wins, and two mined candidates at the lowest height have no winner, so the chain stops there. If no candidate is mined, a single unmined candidate wins; two or more have no winner.

Revocation. As BAP defines, an ID record signed by the root address that declares the address 0 destroys the identity. Readers use the first-on-chain revocation, or any revocation when none wins. ID records not signed by the root that declare 0 are ignored.

Signer verdict. Whether a key spoke for the BAP identity in a block at height h (unknown when unmined or unproven) is the first row that applies:

Verdict When
unknown-key The key is not in the chain.
revoked A revocation exists, and it is unmined, h is unknown, or h ≥ the revocation's height.
active The key is the current key, its successor is unmined, or h is known and lower than its successor's height.
retired-key Otherwise.

Only active counts. A rotation does not undo links signed before it was mined, and an unconfirmed revocation takes effect at once.

7. BAP profile

The ALIAS body is a JSON object using schema.org Person or Organization attributes. name follows section 2's rule; description is OPTIONAL, at most 280 characters, and an invalid one is dropped. image SHOULD be b://<txid>, naming a B output in the BEEF; a B file follows section 3's type and size limits.

An identity can have many ALIAS records. Readers consider only those whose signer is active at the ALIAS's own height, and rank them in order:

  1. An ALIAS signed by a later key in the chain ranks above one signed by an earlier key.
  2. For the same key, an unmined ALIAS ranks above a mined one.
  3. Between two mined records, the higher block ranks first.
  4. In the same block, an ALIAS in the same transaction as the ID record declaring its signer ranks lower.
  5. Otherwise, the higher transaction offset within the block ranks first.

If the top ALIAS has no valid name, the reader tries the next one. BEEF serialization may reorder transactions, so readers MUST NOT use package order to choose a profile.

8. Store once

9. Delivery beside an item

No BRC defines the message a wallet sends with an item or token over a BRC-33 messagebox. When that message body is a JSON object, its meta object (created if absent) MAY carry packages for the identity keys the delivered assets name. Receivers that do not implement this BRC ignore the member.

{ "meta": { "identities": [ <identity package>, … ] } }

10. Wallet baskets

A wallet keeps the BAP records of its own BAP identity in basket bap, as 1Sat wallets do:

Record Tags customInstructions
ID type:id, bapId:<id>, seq:<N> {"protocolID":[1,"sigma"],"keyID":"identity-<N>"}
ALIAS type:alias, bapId:<id>, publishedAt:<ms> —
B file type:image, bapId:<id> —
Link or unlink type:link, bapId:<id>, identityKey:<key> —

BAP key N is the BRC-42 child at protocol [1, "sigma"], key ID identity-N, counterparty self; key 0 is the root. 1Sat wallets take the ID record with the highest seq: in bap as declaring the wallet's current BAP key, so bap MUST hold only the BAP identity derived from the wallet's own root; records of any other BAP identity go in another basket, with the same tags and no customInstructions. Before signing a link, a wallet merges the records other apps added to bap, so it never signs with a BAP key another app has rotated away.

Tags are filters, not records. Reference BRC-100 clients lowercase tags, and a BAP ID is case-sensitive base58, so readers MUST match bapId: case-insensitively and MUST take the BAP ID from the record script. These are data outputs, not collectables, and wallets MUST NOT list them in a collectable view.

Rationale

The identity key, labelled by certificates. The identity key is what BRC-247 stamps and what BRC-100 applications already address. Handles and identity certificates are existing signed statements about identity keys, so labelling needs no new identity object, only rules for carrying and ranking them.

Who vouches is always shown. A certificate is only as good as its certifier, and a self-published name is only a claim. Showing the source beside the label lets a holder tell a certified handle from a name anyone could write.

A two-way BAP link. A link signed only by the identity key would let anyone claim someone else's BAP profile; one signed only by the BAP key would let a BAP identity attach itself to someone else's assets. Requiring both keeps BAP a profile of the identity key, never a second issuer.

Image bytes by hash. UHRP URLs already name content by hash, so carrying the bytes lets the image render offline and lets the reader prove they are the bytes the certifier signed.

Package beside items, not inside them. Copying an image into each item wastes space, and putting the package inside Atomic BEEF is forbidden by BRC-95.

Security considerations

Reference implementation

HandCash Desktop (@handcash/wallet-ui):

Version 2 packages, publicly revealed certificates and link records are in progress there.