Skip to content
Sonr Crypto
Esc
navigateopen⌘Jpreview
On this page

Signatures

Choosing between BLS aggregation, BBS+ selective disclosure, ECDSA canonicalization, verifiable random functions, and the chain-specific Schnorr variants.

Five very different things live under this heading, and they are not interchangeable. Before you pick one, decide which property you actually need: aggregation (many signatures collapse into one), selective disclosure (a holder proves a subset of signed attributes), determinism and canonical encoding (the same message always yields the same bytes), verifiable randomness (an output nobody can predict but everybody can check), or wire compatibility with a specific blockchain.

Every package here is a distinct construction with its own key type. There is no shared Signer interface across them, and keys from one scheme are never valid in another.

Pick a scheme

Goal Package Page
Collapse N signatures over N messages into one 96-byte object signatures/bls/bls_sig BLS
Multi-signature: N signers, one message, one aggregate check signatures/bls/bls_sig (SigPop) BLS
Split a signing key into t-of-n shares with no interaction signatures/bls/bls_sig BLS
Sign a vector of attributes; let the holder reveal only some signatures/bbs BBS+
Issue a credential over messages the issuer must not see signatures/bbs BBS+
Kill ECDSA signature malleability before storing or comparing ecdsa ECDSA utilities
Sign with ECDSA without depending on runtime entropy ecdsa ECDSA utilities
Unpredictable-but-verifiable per-message output (leader election, lotteries) vrf VRF
Sign a Mina payment or delegation transaction signatures/schnorr/mina Chain schemes
Produce a NEM/Symbol Keccak-flavoured Ed25519 signature signatures/schnorr/nem Chain schemes

Some adjacent things are documented elsewhere:

  • The interactive Schnorr proof of knowledge (zkp/schnorr) is a ZKP, not a signature scheme — see zero-knowledge/schnorr.
  • Threshold ECDSA and threshold Ed25519 (FROST) produce ordinary ECDSA / Ed25519 signatures from distributed shares — see threshold ECDSA and threshold Ed25519. BLS threshold signing on this page is a different, much simpler construction: it needs no rounds of interaction.

What these packages assume about curves

signatures/bbs and the Mina scheme are written against the core/curves Curve / Point / Scalar abstraction — BBS+ specifically requires a *curves.PairingCurve (curves.BLS12381(...)). signatures/bls/bls_sig bypasses the abstraction entirely and calls the low-level core/curves/native/bls12381 backend directly, so it is hard-wired to BLS12-381. The ecdsa package operates on stdlib crypto/ecdsa and crypto/elliptic types, and vrf on a vendored Edwards25519 implementation.

The shared proof toolkit: signatures/common

signatures/common holds the sigma-protocol plumbing that BBS+ (and code composing proofs with BBS+) builds on. It is a building-block package — you rarely import it alone, but you will import it to construct BBS+ proof messages.

Symbol Kind Purpose
Challenge = curves.Scalar Fiat-Shamir challenge value
Commitment = curves.Point Pedersen commitment to one or more scalars
Nonce = curves.Scalar Freshness / replay protection in a proof
SignatureBlinding = curves.PairingScalar Blinding factor for blind signing
HmacDrbg struct HMAC deterministic random bit generator, any hash, auto-reseeding
ProofCommittedBuilder struct Accumulates (point, scalar) commitments into Schnorr proofs
ProofMessage interface Classifies a signed message as revealed or hidden

The four aliases are Go type aliases, not defined types: a common.Nonce is a curves.Scalar, so no conversion is needed and the compiler will not stop you passing a challenge where a nonce belongs. Treat the names as documentation, not as type safety.

ProofMessage and its three implementations

ProofMessage is how a BBS+ prover declares, per message, whether it is disclosed:

type ProofMessage interface {
	IsHidden() bool
	GetBlinding(reader io.Reader) curves.Scalar
	GetMessage() curves.Scalar
}
PropType
RevealedMessage?struct { Message curves.Scalar }

IsHidden() == false. The verifier learns this message. GetBlinding returns nil.

Typestruct { Message curves.Scalar }
ProofSpecificMessage?struct { Message curves.Scalar }

IsHidden() == true. A fresh random blinding factor is drawn from the reader, used only by this proof.

Typestruct { Message curves.Scalar }
SharedBlindingMessage?struct { Message, Blinding curves.Scalar }

IsHidden() == true, but you supply the blinding factor so the same hidden value can be linked across several proofs (e.g. a BBS+ proof plus a range proof over the same attribute).

Typestruct { Message, Blinding curves.Scalar }

ProofCommittedBuilder

A small accumulator for Schnorr-style proofs of knowledge of a linear combination:

import "github.com/sonr-io/crypto/signatures/common"

builder := common.NewProofCommittedBuilder(curve)
_ = builder.CommitRandom(basePoint, crand.Reader) // blinding for a secret you know
_ = builder.Commit(otherPoint, knownScalar)       // fixed scalar

bytes := builder.GetChallengeContribution() // feed into your transcript
proofs, err := builder.GenerateProof(challenge, secrets)

GetChallengeContribution returns the compressed encoding of SumOfProducts(points, scalars) — the aggregate commitment. GenerateProof then returns one response scalar per commitment, computed as secret*challenge + blinding, and errors if len(secrets) does not match the number of commitments. Get(index) retrieves the (point, scalar) pair at a position, returning (nil, nil) out of range. The builder caps out at roughly 65535 commitments.

HmacDrbg

drbg := common.NewHmacDrbg(entropy, nonce, personalization, sha256.New)
buf := make([]byte, 64)
_, _ = drbg.Read(buf)
drbg.Reseed(moreEntropy)

It satisfies io.Reader, so it can be handed to any API here that takes a reader — which is how you make an otherwise randomised proof reproducible in a test.

Caveats that apply across this section

Where to next

Last updated on September 2, 2026

Was this page helpful?