Beersy
BRC-69

Revealing Key Linkages

Keys derived between two parties are deliberately unlinkable, which is usually the point and occasionally a problem when someone has to audit a relationship. This describes two ways to deliberately reveal that link, and is clear about how much each one gives away.

About Key linkage, proving on purpose that two keys belong to the same person.

AlsoKey mathsPrivacy

Ty Everettchanged 20 Oct 20266 min read

Reference for an AI

Everything an assistant needs to answer questions about BRC-69 accurately, including what it depends on.

Summary

Why
Users sometimes need to prove key ownership or provide an audit trail for a transaction, but the underlying derivation scheme is designed to keep keys unlinkable, so a controlled way to reveal linkage is needed.
What
BRC-69 defines two methods for revealing the cryptographic link between a root and a derived child key used in a BRC-42/BRC-43 transaction.
How
A wallet either discloses the root shared secret between two counterparties (Method 1), which links all of their past and future derived keys, or discloses just the HMAC-based offset for one specific protocol, and (Method 2), which proves one key's provenance without exposing the whole…

What this lets you do

  • Prove ownership of a specific derived key to an auditor
  • Reveal a root shared secret to link all activity between two identity keys
  • Reveal a narrow offset to audit one protocol and key ID without broader exposure
  • Encrypt a revelation payload for a designated verifier via BRC-72
  • Understand the confidentiality limits of each disclosure before authorizing it

Written by claude-sonnet-5 from the specification text. Where the two differ, the original is correct.

root secretmethod1 or metdisclosed link

The specification

Abstract

This BRC defines two methods for revealing key linkages under the BRC-42 key derivation scheme and BRC-43. Method 1 reveals the root shared point between two counterparties, enabling linkage across their derivation universe and calculation of their BRC-2 encryption keys for known derivation . Method 2 reveals a specific child-key offset rather than the root shared point. These methods support BRC-3 signature verification and transaction auditing, with different confidentiality boundaries. Neither method is a blanket guarantee that encrypted data remains confidential after a disclosure. Correctness of linkage claims is a separate concern addressed by BRC-93 and, for Method 1, BRC-94.

Motivation

The BRC-42 key derivation architecture and BRC-43 allow for privacy-preserving transactions between parties. However, there are scenarios where users may wish to prove key associations and provide audit trails for their interactions. For example, a user may want to establish their ownership of a key used in a transaction, or authorities might need to audit transactions between specific parties. This technical standard aims to fulfill these requirements, allowing users to prove key associations and reveal transaction linkages between parties, while preserving the confidentiality of interactions with unrelated parties.

Specification

Method 1: Revealing Counterparty Shared Secrets

This method returns the BRC-42 root ECDH shared point between a user's and a specified 's key. Its compressed point encoding is the key used for child-key derivation. A recipient can compute offsets and child public keys for known or subsequently learned BRC-43 , and for that pair, linking past and future activity that uses the same identity-key pair. The disclosure does not itself enumerate unknown labels or identify every transaction.

Method 1 also removes the confidentiality of BRC-2 data from the recipient of the disclosure when the identity public keys, derivation labels and ciphertext are available. BRC-42 adds the same derivation offset to both parties' keys for a given . Consequently the disclosed root shared point, offset and identity public keys suffice to calculate the child ECDH shared point used by BRC-2; recovering either party's private key is not necessary. This applies across protocols and key IDs for that pair, rather than only to a protocol named in an application permission or audit request. It does not provide the root shared points for unrelated counterparty pairs, ciphertext not available to the recipient, or separately generated content-encryption keys merely because they belong to the same application.

Implementations MUST NOT describe Method 1 as preserving BRC-2 ciphertext confidentiality from the recipient of the root shared point, or as a cryptographically limited grant for a single protocol or time range. Wallets SHOULD explain the pair-wide linkage and encryption-key consequences before authorizing a disclosure. A BRC-94 proof verifies the disclosed point's correctness; it does not reduce these consequences.

This method must never be used where the counterparty is the user themself (counterparty=self), as that would reveal keys used internally by the user. The method is also ineffective where the counterparty is "anyone", as the anyone private key is already publicly known (it is the value 1).

Method 2: Revealing Specific Key Associations

This method reveals the offset value (the link) that connects a root identity key to one of its derived child public keys, under a specified BRC-43 protocol ID, key ID, and counterparty. By exposing this link, the user can prove their association with the specific derived key, allowing for auditing of all activity involving this key for the specified protocol and counterparty.

This method allows the user to create an audit trail for specific protocols, key identifiers, and counterparties without exposing all interactions with the particular counterparty. This is because, while the root shared secret is used to compute the HMAC, it is not revealed — only the resulting HMAC is shared.

For two distinct counterparties whose private keys remain secret, the offset alone does not disclose the root shared point or suffice to calculate their BRC-2 child ECDH point. Its narrower MUST NOT be interpreted as an unconditional confidentiality guarantee: it does not protect an encryption key already derivable from other disclosed material or from a publicly known counterparty private key, such as anyone. An offset is sensitive key-derivation material, not merely public metadata. Revelation correctness and authorization remain separate requirements.

Protection of Revelation Payloads

BRC-72 encrypts revelation payloads for a designated verifier, and BRC-100 exposes these operations as wallet methods. This protects delivery to that verifier under the encryption scheme's assumptions. Once decrypted, the verifier holds the disclosed point or offset and can retain or redistribute it. Transport encryption, a privileged flag, a permission decision or a revelation timestamp does not cryptographically constrain subsequent use, erase knowledge already received, or revoke the disclosure. Method 1 remains applicable to earlier and later ciphertext under the same identity-key pair when the necessary labels and ciphertext are available.

Implementation

The two methods in this technical standard are essential building blocks for creating protocols that support accountability and provide an added layer of security to Bitcoin and MetaNet client software implementing BRC-42 and BRC-43 protocols. Implementations should ensure that these methods are only used in ways that maintain user privacy and do not reveal linkage information — especially counterparty root shared secrets — to the public.

Compatibility Clarification

The confidentiality clarification corrects the earlier claim that both methods always preserve BRC-2 encryption confidentiality. It does not change either revelation payload, the BRC-42 derivation, or BRC-2 ciphertext. Implementations that represented Method 1 as a protocol-limited audit operation SHOULD update their permission explanations and security documentation. Encryption and proof verification remain separate from the scope of the disclosed.

The reference KeyDeriver returns the compressed root shared point for Method 1 and the invoice-specific HMAC for Method 2. ProtoWallet encrypts these disclosures for the designated verifier; that wrapper leaves their derivation scope unchanged.

Revision History

  • 2026-09-30: Corrected Method 1's BRC-2 confidentiality claim and qualified Method 2's scope. Distinguished disclosure authority from delivery encryption, correctness proofs and permissions. Revelation bytes and key derivation remain unchanged.

Examples

Below is TypeScript code that implements the described methods:

Method 1

import bsv from 'babbage-bsv'

const BN = bsv.crypto.BN
const Hash = bsv.crypto.Hash
const G = bsv.crypto.Point.getG()

/**
 * Returns the root shared secret as described by Method 1
 *
 * @param params All parameters are provided in an object
 * @param params.senderPrivateKey The private key of the sender in WIF format
 * @param params.recipientPublicKey The public key of the recipient in hexadecimal DER format
 *
 * @returns The revealed shared secret, given as a hex string
 */
export function getRootKeyLinkage(params: {
  senderPrivateKey: string | bsv.crypto.BN | bsv.PrivateKey,
  recipientPublicKey: string | bsv.PublicKey,
  invoiceNumber: string
}): string {
  let publicKey: bsv.PublicKey, privateKey: bsv.PrivateKey
  if (typeof params.recipientPublicKey === 'string') {
    publicKey = bsv.PublicKey.fromString(params.recipientPublicKey)
  } else if (params.recipientPublicKey instanceof bsv.PublicKey) {
    publicKey = params.recipientPublicKey
  } else {
    throw new Error('Unrecognized format for recipientPublicKey')
  }
  if (typeof params.senderPrivateKey === 'string') {
    privateKey = BN.fromHex(params.senderPrivateKey)
  } else if (params.senderPrivateKey instanceof BN) {
    privateKey = params.senderPrivateKey
  } else if (params.senderPrivateKey instanceof bsv.PrivateKey) {
    privateKey = params.senderPrivateKey.bn
  } else {
    throw new Error('Unrecognized format for senderPrivateKey')
  }
  const sharedSecret = publicKey.point.mul(privateKey).toBuffer()
  return sharedSecret.toString('hex')
}

Method 2

import bsv from 'babbage-bsv'

const BN = bsv.crypto.BN
const Hash = bsv.crypto.Hash
const G = bsv.crypto.Point.getG()

/**
 * Returns the child key offset as described by Method 2
 *
 * @param params All parameters are provided in an object
 * @param params.senderPrivateKey The private key of the sender in WIF format
 * @param params.recipientPublicKey The public key of the recipient in hexadecimal DER format
 * @param params.securityLevel The BRC-43 security level
 * @param params.protocolID The BRC-43 protocol ID
 * @param params.keyID The BRC-43 key ID
 *
 * @returns The revealed key offset, given as a hex string
 */
export function getKeyLinkage(params: {
  senderPrivateKey: string | bsv.crypto.BN | bsv.PrivateKey,
  recipientPublicKey: string | bsv.PublicKey,
  invoiceNumber: string,
  securityLevel: number,
  protocolID: string,
  keyID: string
}): string {
  let publicKey: bsv.PublicKey, privateKey: bsv.PrivateKey
  if (typeof params.recipientPublicKey === 'string') {
    publicKey = bsv.PublicKey.fromString(params.recipientPublicKey)
  } else if (params.recipientPublicKey instanceof bsv.PublicKey) {
    publicKey = params.recipientPublicKey
  } else {
    throw new Error('Unrecognized format for recipientPublicKey')
  }
  if (typeof params.senderPrivateKey === 'string') {
    privateKey = BN.fromHex(params.senderPrivateKey)
  } else if (params.senderPrivateKey instanceof BN) {
    privateKey = params.senderPrivateKey
  } else if (params.senderPrivateKey instanceof bsv.PrivateKey) {
    privateKey = params.senderPrivateKey.bn
  } else {
    throw new Error('Unrecognized format for senderPrivateKey')
  }
  const sharedSecret = publicKey.point.mul(privateKey).toBuffer()
  const invoiceNumber = Buffer.from(`${params.securityLevel}-${params.protocolId}-${params.keyID}`, 'utf8')
  const hmac = Hash.sha256hmac(sharedSecret, invoiceNumber)
  return hmac.toString('hex')
}
Was this helpful?

Search Beersy

Search standards by number, title, author or topic