b-open-io avatar

message-signing

This skill should be used when the user asks to "sign a message", "verify a signature", "use BSM", "

by b-open-io|Open Source

BSV Message Signing

Three approaches for message signing on BSV, from simple to full-featured.

Quick Reference

ApproachPackageUse Case
Direct SDK@bsv/sdkSimple message signing (BSM, BRC-77)
Templates@1sat/templatesScript parsing/creation for protocols
Sigma Protocolsigma-protocolTransaction-bound signatures

1. Direct SDK Signing

For simple message authentication without transaction context.

BSM (Bitcoin Signed Message)

import { BSM, PrivateKey, Signature, BigNumber } from "@bsv/sdk";

const privateKey = PrivateKey.fromWif("L1...");
const message = [/* byte array */];

// Sign
const signature = BSM.sign(message, privateKey, "raw") as Signature;
const h = new BigNumber(BSM.magicHash(message));
const recovery = signature.CalculateRecoveryFactor(privateKey.toPublicKey(), h);
const compactSig = signature.toCompact(recovery, true, "base64");

// Verify - recover public key and check address
const sig = Signature.fromCompact(compactSig, "base64");
for (let r = 0; r < 4; r++) {
  try {
    const pubKey = sig.RecoverPublicKey(r, new BigNumber(BSM.magicHash(message)));
    if (BSM.verify(message, sig, pubKey)) {
      console.log("Signer:", pubKey.toAddress());
      break;
    }
  } catch { /* try next */ }
}

BRC-77 (SignedMessage)

import { SignedMessage, PrivateKey, PublicKey } from "@bsv/sdk";

const signer = PrivateKey.fromWif("L1...");
const message = [/* byte array */];

// Public signature (anyone can verify)
const sig = SignedMessage.sign(message, signer);
const valid = SignedMessage.verify(message, sig);

// Private signature (only recipient can verify)
const recipientPubKey = PublicKey.fromString("02...");
const privateSig = SignedMessage.sign(message, signer, recipientPubKey);

const recipientKey = PrivateKey.fromWif("K1...");
const privateValid = SignedMessage.verify(message, privateSig, recipientKey);

See: examples/bsm-sign-verify.ts, examples/brc77-private-sig.ts

2. Template-Based Signing

For working with BitCom protocols and script parsing. Uses @1sat/templates.

Sigma Template

import { Sigma, SigmaAlgorithm, BitCom } from "@1sat/templates";
import { PrivateKey, Hash } from "@bsv/sdk";

// Decode from existing script
const bitcom = BitCom.decode(script);
const sigmas = Sigma.decode(bitcom);

// Create signature (given pre-computed hashes)
const inputHash = Array.from(Hash.sha256(outpointBytes));
const dataHash = Array.from(Hash.sha256(scriptDataBytes));

const sigma = Sigma.sign(inputHash, dataHash, privateKey, {
  algorithm: SigmaAlgorithm.BRC77,  // or SigmaAlgorithm.BSM
  vin: 0
});

// Verify
const valid = sigma.verifyWithHashes(inputHash, dataHash);

// Generate locking script
const lockingScript = sigma.lock();

AIP Template

AIP.sign() accepts a Signer — either PrivateKeySigner (raw key) or WalletSigner (BRC-100 wallet).

import { AIP, BitCom, PrivateKeySigner, WalletSigner } from "@1sat/templates";

// Decode AIP from script
const bitcom = BitCom.decode(script);
const aips = AIP.decode(bitcom);

// Sign with raw PrivateKey
const signer = new PrivateKeySigner(privateKey);
const aip = await AIP.sign(dataBytes, signer);

// Sign with BRC-100 wallet (uses BAP identity key derivation)
const walletSigner = new WalletSigner(wallet, [1, "bapid"], "identity", "self");
const aip2 = await AIP.sign(dataBytes, walletSigner);

const valid = aip.verify();

AIP via @1sat/actions

The applyAip helper signs OP_RETURN data and appends the AIP suffix to a locking script:

import { applyAip } from '@1sat/actions'

// ctx is a OneSatContext with a BRC-100 wallet
const signedScript = await applyAip(ctx, lockingScript)
// Appends: | AIP_PREFIX BITCOIN_ECDSA <address> <compactSig>

Uses the wallet's BAP identity key (protocolID: [1, "bapid"], keyID: "identity"). Delegates signing to AIP.sign() from templates via WalletSigner.

See: examples/sigma-template.ts

Note: Sigma template requires sigma-protocol as peer dependency for Algorithm enum.

3. Sigma Protocol (Full Transaction Signing)

For complete transaction-bound signatures. Use when:

  • Signing OP_RETURN data in transactions
  • Need multiple signatures on same output
  • Want automatic hash calculation from transaction context
bun add sigma-protocol
import { Sigma, Algorithm } from "sigma-protocol";
import { Transaction, PrivateKey } from "@bsv/sdk";

// Create Sigma instance
const sigma = new Sigma(tx, targetVout, sigmaInstance, refVin);

// Sign with BSM (default)
const { signedTx } = sigma.sign(privateKey);

// Sign with BRC-77
const { signedTx: tx2 } = sigma.sign(privateKey, Algorithm.BRC77);

// Sign with BRC-77 for specific verifier (private)
const { signedTx: tx3 } = sigma.sign(privateKey, Algorithm.BRC77, verifierPubKey);

// Verify
const valid = sigma.verify();
const privateValid = sigma.verify(recipientPrivateKey);  // for private BRC-77

Multiple Signatures

// First signature (user with BSM)
const sigma1 = new Sigma(tx, 0, 0);
const { signedTx } = sigma1.sign(userKey, Algorithm.BSM);

// Second signature (platform with BRC-77)
const sigma2 = new Sigma(signedTx, 0, 1);
sigma2.sign(platformKey, Algorithm.BRC77);

// Verify each
sigma2.setSigmaInstance(0);
sigma2.verify();  // User

sigma2.setSigmaInstance(1);
sigma2.verify();  // Platform

See: examples/sigma-multi-sig.ts

Algorithm Comparison

FeatureBSMBRC-77
Key derivationDirectChild key per message
RecoveryFrom signatureEmbedded in signature
Private verifyNoYes (with recipient key)
SDK moduleBSMSignedMessage

When to Use What

ScenarioApproach
Simple message authDirect SDK (BSM or BRC-77)
BAP identity signingbsv-bap library
Parse existing Sigma/AIP scriptsTemplates
Build BitCom transaction outputsTemplates
AIP-sign OP_RETURN with BRC-100 walletapplyAip from @1sat/actions
Sigma-sign inscription with BRC-100 walletinscribe with signWithBAP: true
Sign transaction OP_RETURN datasigma-protocol
Multiple signatures per outputsigma-protocol
Platform + user dual signingsigma-protocol

BAP Identity Signing (BSM)

BAP identities use BSM for signing. The bsv-bap library handles this:

import { BAP } from "bsv-bap";
import { Utils } from "@bsv/sdk";
const { toArray } = Utils;

const bap = new BAP({ rootPk: wif });
const identity = bap.getId(bap.listIds()[0]);

// Sign with BSM (uses derived identity signing key)
const message = toArray("Hello World", "utf8");
const { address, signature } = identity.signMessage(message);

// Verify BSM signature
const isValid = bap.verifySignature("Hello World", address, signature);

A CLI is also available: bun add -g bsv-bap (see create-bap-identity skill).

Sigma Signing via @1sat/actions

The inscribe action from @1sat/actions supports a signWithBAP parameter that applies a Sigma BSM signature using the wallet's BAP identity key. This is the recommended approach when signing inscriptions through the actions framework — it handles anchor tx construction, sigma hash computation, and broadcast automatically.

import { inscribe, createContext } from '@1sat/actions'

const result = await inscribe.execute(ctx, {
  base64Content: btoa('Hello Sigma'),
  contentType: 'text/plain',
  signWithBAP: true,  // Signs with BAP identity via Sigma protocol
})

The resulting transaction carries a Sigma signature that can be verified with sigma-protocol:

import { Sigma } from 'sigma-protocol'
const sigma = new Sigma(tx, 0, 0, 0)
sigma.verify() // true

Additional Resources

Reference Files

  • references/brc-77-spec.md - Full BRC-77 specification
  • references/sigma-advanced.md - Remote signing, hash details, advanced patterns

Examples

  • examples/bsm-sign-verify.ts - Direct BSM signing
  • examples/brc77-private-sig.ts - BRC-77 private signatures
  • examples/sigma-template.ts - Template-based Sigma operations
  • examples/sigma-multi-sig.ts - Full sigma-protocol with multi-sig