From 7a7760c00512e395897f5a39011d06b45da84777 Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Wed, 8 Jul 2026 14:19:25 +0200 Subject: [PATCH 1/2] fix MSP curve is hardcoded in constructor, ignoring IdemixMSPConfig.CurveId Signed-off-by: Angelo De Caro # Conflicts: # msp/idemixmsp.go --- AGENTS.md | 89 ++++++++++++++++++++++++++ CLAUDE.md | 1 + README.md | 28 ++++++++ msp/idemixmsp.go | 145 ++++++++++++++++++++++++++++++++++-------- msp/idemixmsp_test.go | 2 +- 5 files changed, 236 insertions(+), 29 deletions(-) create mode 100644 AGENTS.md create mode 100644 CLAUDE.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..cc9dfba --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,89 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What this project is + +A Go implementation of an anonymous identity stack (Identity Mixer / Idemix) for blockchain systems, originally designed for use with Hyperledger Fabric. It provides zero-knowledge proof-based credentials: users can prove possession of a CA-signed credential and selectively disclose attributes (OU, Role, EnrollmentID, RevocationHandle) without revealing their identity. + +## Commands + +```bash +# CI entry point: checks + unit-tests + unit-tests-race +make all + +# Format, vet, and license-header check +make checks # runs check-deps, gofmt, go vet, addlicense -check +make fmt # apply gofmt -l -s -w in place + +# Tests +make unit-tests # go test ./... +make unit-tests-race # go test -race -cover with GORACE=history_size=7, 960s timeout + +# Run a single package or test directly +go test ./bccsp/schemes/aries/... +go test ./bccsp/schemes/aries/ -run TestSignerSign + +# Build idemixgen tool +make idemixgen # go install ./tools/idemixgen → $GOPATH/bin +make binaries # cross-compile to bin/amd64/ (linux) and bin/arm64/ (darwin) + +# Protobuf regeneration +make installbuf # installs buf@v1.70.0 +make genprotos # buf generate --template buf.gen.yaml + +# Tidy modules +make tidy +``` + +## Architecture + +### Two cryptographic schemes + +The codebase implements two parallel cryptographic backends, both exposed through the same BCCSP interface: + +1. **Legacy / dlog** (`bccsp/schemes/dlog/`) — The original Idemix implementation using discrete log proofs. It takes a `*math.Curve` and a `Translator` (handles curve-specific serialization). Instantiated via `idemix.New(keyStore, curve, translator, exportable)`. + +2. **Aries** (`bccsp/schemes/aries/`) — A newer implementation using BBS+ signatures for credential issuance, intended to replace the dlog scheme. Instantiated via `idemix.NewAries(keyStore, curve, translator, exportable)`. + +The **legacy guard** in CI enforces that new code must not add new imports of `schemes/dlog` or `schemes/weak-bb` outside of the known files listed in `.github/workflows/go.yml`. New code should use the Aries scheme. + +### Package structure + +``` +bccsp/ + types/ # Interfaces: Issuer, User, CredRequest, Credential, SignatureScheme, etc. + handlers/ # BCCSP operation wrappers: IssuerKeyGen, UserKeyGen, Signer, Verifier, etc. + keystore/ # Key storage implementations + schemes/ + aries/ # Aries/BBS+ implementation of all bccsp/types interfaces + dlog/ + bridge/ # Adapts dlog/crypto to bccsp/types interfaces + crypto/ # Core dlog Idemix math (signature, credential, nym, revocation) + weak-bb/ # Weak Boneh-Boyen signatures (used internally for revocation) + bccsp.go # New() and NewAries() constructors; multiplexer wiring + impl.go # CSP base: type-keyed dispatch maps for KeyGen/Sign/Verify/etc. + +bbs/ # BBS+ signature implementation (used by aries/Cred) + +msp/ # Hyperledger Fabric MSP integration: credential lifecycle, + # attribute indices (OU=0, Role=1, EnrollmentID=2, RevocationHandle=3) + +tools/idemixgen/ # CLI tool to generate issuer keys and signer configs for Fabric MSP +``` + +### BCCSP dispatch pattern + +`CSP` (in `bccsp/impl.go`) is a generic dispatcher that holds `map[reflect.Type]Signer` (and similar maps for KeyGen/Verify/Import/Deriv). Operations are routed by the Go type of the key or opts argument. `bccsp.go` wires concrete scheme implementations into these maps via `AddWrapper`. The `userSecreKeySignerMultiplexer` and `issuerPublicKeyVerifierMultiplexer` types handle cases where a single key type handles multiple opt types. + +### Curve and translator + +`github.com/IBM/mathlib` provides the elliptic curve abstractions (`*math.Curve`, `*math.G1`, `*math.G2`, `*math.Zr`). The `Translator` interface (dlog scheme) handles curve-specific protobuf serialization for group elements. The `bccsp/schemes/dlog/crypto/translator/amcl/` package provides concrete translators for each supported curve family (FP256BN, BLS12-381, etc.). + +### Credential attributes + +The four fixed attributes are accessed by index: OU (0), Role (1), EnrollmentID (2), RevocationHandle (3). These indices are referenced throughout `msp/` and must match across signing and verification. + +### Protobuf + +`.proto` files live alongside their generated `.pb.go` files. Run `make genprotos` (requires `buf`) to regenerate. The main proto files are `bccsp/schemes/dlog/crypto/idemix.proto` and `bccsp/schemes/aries/cred.proto`. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..eef4bd2 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md \ No newline at end of file diff --git a/README.md b/README.md index aa62072..60cb0c0 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,34 @@ This project is a Go implementation of an anonymous identity stack for blockchai - [Signature verification](#signature-verification) - [Auditing NymEid](#auditing-nymeid) +# MSP Usage + +## Constructors + +Two MSP constructors are available, corresponding to the two cryptographic backends: + +- **`NewIdemixMsp(version)`** — uses the legacy dlog scheme. Accepts any supported curve; defaults to `FP256BN_AMCL` when `curve_id` is absent. +- **`NewIdemixMspAries(version)`** — uses the Aries/BBS+ scheme. Accepts only BBS curves (`BLS12_381_BBS` or `BLS12_381_BBS_GURVY`); defaults to `BLS12_381_BBS` when `curve_id` is absent. + +## Curve selection + +The elliptic curve is no longer hardcoded at construction time. Instead, `Setup` reads `IdemixMSPConfig.CurveId` (set by `idemixgen` when generating key material) and builds the BCCSP with the matching curve and translator. + +Supported `curve_id` values and their backends: + +| `curve_id` | Backend | Notes | +|---|---|---| +| `FP256BN_AMCL` | AMCL | Default for dlog | +| `BN254` | Gurvy | | +| `FP256BN_AMCL_MIRACL` | AMCL | Legacy compatibility | +| `BLS12_377_GURVY` | Gurvy | | +| `BLS12_381_GURVY` | Gurvy | | +| `BLS12_381` | Kilic | | +| `BLS12_381_BBS` | Kilic | Default for Aries | +| `BLS12_381_BBS_GURVY` | Gurvy | | + +The `curve_id` value is written into the MSP config by `idemixgen --curve `. An empty `curve_id` triggers the per-scheme default (backward-compatible). `Setup` errors if the config type (`IDEMIX` vs `IDEMIX_ARIES`) does not match the constructor used, or if an Aries MSP is configured with a non-BBS curve. + # Protocol Here we describe the cryptographic protocol that is implemented. diff --git a/msp/idemixmsp.go b/msp/idemixmsp.go index 9a19070..9de777d 100644 --- a/msp/idemixmsp.go +++ b/msp/idemixmsp.go @@ -18,6 +18,7 @@ import ( idemix "github.com/IBM/idemix/bccsp" "github.com/IBM/idemix/bccsp/keystore" + idemixcrypto "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" "github.com/IBM/idemix/bccsp/schemes/dlog/crypto/translator/amcl" bccsp "github.com/IBM/idemix/bccsp/types" im "github.com/IBM/idemix/msp/config" @@ -69,6 +70,51 @@ const ( const rhIndex = 3 const eidIndex = 2 +// Curve ID string constants matching the values written by idemixgen into IdemixMSPConfig.CurveId. +const ( + curveIDFP256BN_AMCL = "FP256BN_AMCL" + curveIDBN254 = "BN254" + curveIDFP256BN_AMCL_MIRACL = "FP256BN_AMCL_MIRACL" + curveIDBLS12_377_GURVY = "BLS12_377_GURVY" + curveIDBLS12_381_GURVY = "BLS12_381_GURVY" + curveIDBLS12_381 = "BLS12_381" + curveIDBLS12_381_BBS = "BLS12_381_BBS" + curveIDBLS12_381_BBS_GURVY = "BLS12_381_BBS_GURVY" +) + +// curveAndTranslator maps a curve_id string (as stored in IdemixMSPConfig) to the corresponding +// math.Curve and dlog Translator. Returns an error for unknown curve IDs. +func curveAndTranslator(curveID string) (*math.Curve, idemixcrypto.Translator, error) { + switch curveID { + case curveIDFP256BN_AMCL: + c := math.Curves[math.FP256BN_AMCL] + return c, &amcl.Fp256bn{C: c}, nil + case curveIDBN254: + c := math.Curves[math.BN254] + return c, &amcl.Gurvy{C: c}, nil + case curveIDFP256BN_AMCL_MIRACL: + c := math.Curves[math.FP256BN_AMCL_MIRACL] + return c, &amcl.Fp256bnMiracl{C: c}, nil + case curveIDBLS12_377_GURVY: + c := math.Curves[math.BLS12_377_GURVY] + return c, &amcl.Gurvy{C: c}, nil + case curveIDBLS12_381_GURVY: + c := math.Curves[math.BLS12_381_GURVY] + return c, &amcl.Gurvy{C: c}, nil + case curveIDBLS12_381: + c := math.Curves[math.BLS12_381] + return c, &amcl.Gurvy{C: c}, nil + case curveIDBLS12_381_BBS: + c := math.Curves[math.BLS12_381_BBS] + return c, &amcl.Gurvy{C: c}, nil + case curveIDBLS12_381_BBS_GURVY: + c := math.Curves[math.BLS12_381_BBS_GURVY] + return c, &amcl.Gurvy{C: c}, nil + default: + return nil, nil, fmt.Errorf("unknown curve id %q", curveID) + } +} + // Logger defines the logging interface required by Idemixmsp. // This interface is compatible with the Go SDK log package and common logging facades. type Logger interface { @@ -87,6 +133,8 @@ type Idemixmsp struct { revocationPK bccsp.Key epoch int logger Logger + aries bool + exportable bool } // defaultLogger is a simple logger implementation that wraps zap.SugaredLogger @@ -141,38 +189,43 @@ func (l *stdLogger) IsEnabledFor(level zapcore.Level) bool { return true // Standard logger always logs } -// NewIdemixMsp creates a new instance of idemixmsp +// NewIdemixMsp creates a new instance of idemixmsp using the dlog scheme. +// The curve is determined at Setup time from IdemixMSPConfig.CurveId (default: FP256BN_AMCL). func NewIdemixMsp(version MSPVersion) (MSP, error) { - logger := newDefaultLogger("idemix") - logger.Debugf("Creating Idemix-based MSP instance") + return NewIdemixMspWithLogger(version, newDefaultLogger("idemix")) +} - curve := math.Curves[math.FP256BN_AMCL] - csp, err := idemix.New(&keystore.Dummy{}, curve, &amcl.Fp256bn{C: curve}, true) - if err != nil { - panic(fmt.Sprintf("unexpected condition, error received [%s]", err)) +// NewIdemixMspWithLogger creates a new instance of idemixmsp using the dlog scheme with a custom logger. +// The curve is determined at Setup time from IdemixMSPConfig.CurveId (default: FP256BN_AMCL). +// If logger is nil, the default logger is used. +func NewIdemixMspWithLogger(version MSPVersion, logger Logger) (MSP, error) { + if logger == nil { + logger = newDefaultLogger("idemix") } - - msp := Idemixmsp{csp: csp, logger: logger} - msp.version = version - + logger.Debugf("Creating Idemix-based MSP instance") + msp := Idemixmsp{logger: logger, version: version +, aries: false, exportable: true} return &msp, nil } -// NewIdemixMspAries creates a new -// instance of idemixmsp +// NewIdemixMspAries creates a new instance of idemixmsp using the Aries/BBS+ scheme. +// The curve is determined at Setup time from IdemixMSPConfig.CurveId; only BLS12_381_BBS +// and BLS12_381_BBS_GURVY are accepted (default: BLS12_381_BBS). func NewIdemixMspAries(version MSPVersion) (MSP, error) { - logger := newDefaultLogger("idemix") - logger.Debugf("Creating Idemix-based MSP instance") + return NewIdemixMspAriesWithLogger(version, newDefaultLogger("idemix")) +} - curve := math.Curves[math.BLS12_381_BBS] - csp, err := idemix.NewAries(&keystore.Dummy{}, curve, &amcl.Gurvy{C: curve}, true) - if err != nil { - panic(fmt.Sprintf("unexpected condition, error received [%s]", err)) +// NewIdemixMspAriesWithLogger creates a new instance of idemixmsp using the Aries/BBS+ scheme with a custom logger. +// The curve is determined at Setup time from IdemixMSPConfig.CurveId; only BLS12_381_BBS +// and BLS12_381_BBS_GURVY are accepted (default: BLS12_381_BBS). +// If logger is nil, the default logger is used. +func NewIdemixMspAriesWithLogger(version MSPVersion, logger Logger) (MSP, error) { + if logger == nil { + logger = newDefaultLogger("idemix") } - - msp := Idemixmsp{csp: csp, logger: logger} - msp.version = version - + logger.Debugf("Creating Idemix-based MSP instance") + msp := Idemixmsp{logger: logger, version: version +, aries: true, exportable: true} return &msp, nil } @@ -192,11 +245,47 @@ func (msp *Idemixmsp) Setup(conf1 *m.MSPConfig) error { msp.name = conf.Name msp.logger.Debugf("Setting up Idemix MSP instance %s", msp.name) - switch conf1.Type { - case int32(IDEMIX): - case int32(IDEMIX_ARIES): - default: - return errors.New("setup error: config is not of type IDEMIX") + // Enforce that the config type matches the constructor used. + if msp.aries { + if conf1.Type != int32(IDEMIX_ARIES) { + return fmt.Errorf("setup error: aries MSP requires config of type IDEMIX_ARIES, got %d", conf1.Type) + } + } else { + if conf1.Type != int32(IDEMIX) { + return errors.New("setup error: dlog MSP requires config of type IDEMIX, got %d", conf1.Type) + } + } + + // Determine the curve from config.CurveId. + curveID := conf.CurveId + if msp.aries { + switch curveID { + case "", curveIDBLS12_381_BBS: + curveID = curveIDBLS12_381_BBS + case curveIDBLS12_381_BBS_GURVY: + // accepted + default: + return fmt.Errorf("setup error: aries MSP requires a BBS curve, got %q", curveID) + } + } else { + if curveID == "" { + curveID = curveIDFP256BN_AMCL + } + } + + curve, tr, err := curveAndTranslator(curveID) + if err != nil { + return fmt.Errorf("setup error: %w", err) + } + + // Build the BCCSP using the curve selected from config. + if msp.aries { + msp.csp, err = idemix.NewAries(&keystore.Dummy{}, curve, tr, msp.exportable) + } else { + msp.csp, err = idemix.New(&keystore.Dummy{}, curve, tr, msp.exportable) + } + if err != nil { + return fmt.Errorf("setup error: failed to create BCCSP: %w", err) } // Import Issuer Public Key diff --git a/msp/idemixmsp_test.go b/msp/idemixmsp_test.go index c8170c5..e04a7f1 100644 --- a/msp/idemixmsp_test.go +++ b/msp/idemixmsp_test.go @@ -103,7 +103,7 @@ func TestSetupBad(t *testing.T) { conf := &msp.MSPConfig{Type: 1234, Config: nil} err = msp1.Setup(conf) require.Error(t, err) - require.Contains(t, err.Error(), "setup error: config is not of type IDEMIX") + require.Contains(t, err.Error(), "setup error:") // Setup with bad idemix config bytes conf = &msp.MSPConfig{Type: int32(IDEMIX), Config: []byte("barf")} From 3b5152179dfe45e7773f782022296be0a5f10ef9 Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Wed, 8 Jul 2026 18:54:37 +0200 Subject: [PATCH 2/2] fix Signed-off-by: Angelo De Caro --- AGENTS.md | 4 +--- README.md | 20 ++++++++++---------- go.mod | 2 +- go.sum | 4 ++-- msp/idemixmsp.go | 18 +++++++++++++----- 5 files changed, 27 insertions(+), 21 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index cc9dfba..8f295ef 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,4 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +This file provides guidance to agents when working with code in this repository. ## What this project is diff --git a/README.md b/README.md index 60cb0c0..afe9643 100644 --- a/README.md +++ b/README.md @@ -34,16 +34,16 @@ The elliptic curve is no longer hardcoded at construction time. Instead, `Setup` Supported `curve_id` values and their backends: -| `curve_id` | Backend | Notes | -|---|---|---| -| `FP256BN_AMCL` | AMCL | Default for dlog | -| `BN254` | Gurvy | | -| `FP256BN_AMCL_MIRACL` | AMCL | Legacy compatibility | -| `BLS12_377_GURVY` | Gurvy | | -| `BLS12_381_GURVY` | Gurvy | | -| `BLS12_381` | Kilic | | -| `BLS12_381_BBS` | Kilic | Default for Aries | -| `BLS12_381_BBS_GURVY` | Gurvy | | +| `curve_id` | Backend | Notes | +|---|-----------------------|---| +| `FP256BN_AMCL` | AMCL | Default for dlog | +| `BN254` | Gurvy | | +| `FP256BN_AMCL_MIRACL` | AMCL | Legacy compatibility | +| `BLS12_377_GURVY` | Gurvy | | +| `BLS12_381_GURVY` | Gurvy | | +| `BLS12_381` | Gurvy (it was Kilic) | | +| `BLS12_381_BBS` | Gurvy (it was Kilic) | Default for Aries | +| `BLS12_381_BBS_GURVY` | Gurvy | | The `curve_id` value is written into the MSP config by `idemixgen --curve `. An empty `curve_id` triggers the per-scheme default (backward-compatible). `Setup` errors if the config type (`IDEMIX` vs `IDEMIX_ARIES`) does not match the constructor used, or if an Aries MSP is configured with a non-BBS curve. diff --git a/go.mod b/go.mod index 7dd345f..805a7ec 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/IBM/idemix go 1.26.3 require ( - github.com/IBM/mathlib v0.2.1-0.20260708043658-e8f8fcd199a4 + github.com/IBM/mathlib v0.3.0 github.com/alecthomas/kingpin/v2 v2.4.0 github.com/hyperledger/fabric-protos-go-apiv2 v0.3.7 github.com/onsi/ginkgo/v2 v2.32.0 diff --git a/go.sum b/go.sum index bb5bd26..91a2f22 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/IBM/mathlib v0.2.1-0.20260708043658-e8f8fcd199a4 h1:HRDczSMpaugF7rq8AirQX7dvL9iK7TCyPnI+QkrqCX8= -github.com/IBM/mathlib v0.2.1-0.20260708043658-e8f8fcd199a4/go.mod h1:r5/+9SWcYCm1TSZE9HRXq9/ejjdtS0Ab2MjLYBuF9S4= +github.com/IBM/mathlib v0.3.0 h1:lYmtMn43OZRzuw8yVnKqPPZD4MnWV34fffFM/X4apvk= +github.com/IBM/mathlib v0.3.0/go.mod h1:r5/+9SWcYCm1TSZE9HRXq9/ejjdtS0Ab2MjLYBuF9S4= github.com/Masterminds/semver/v3 v3.5.0 h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAwZ/2OOE= github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY= diff --git a/msp/idemixmsp.go b/msp/idemixmsp.go index 9de777d..81c5f8c 100644 --- a/msp/idemixmsp.go +++ b/msp/idemixmsp.go @@ -88,27 +88,35 @@ func curveAndTranslator(curveID string) (*math.Curve, idemixcrypto.Translator, e switch curveID { case curveIDFP256BN_AMCL: c := math.Curves[math.FP256BN_AMCL] + return c, &amcl.Fp256bn{C: c}, nil case curveIDBN254: c := math.Curves[math.BN254] + return c, &amcl.Gurvy{C: c}, nil case curveIDFP256BN_AMCL_MIRACL: c := math.Curves[math.FP256BN_AMCL_MIRACL] + return c, &amcl.Fp256bnMiracl{C: c}, nil case curveIDBLS12_377_GURVY: c := math.Curves[math.BLS12_377_GURVY] + return c, &amcl.Gurvy{C: c}, nil case curveIDBLS12_381_GURVY: c := math.Curves[math.BLS12_381_GURVY] + return c, &amcl.Gurvy{C: c}, nil case curveIDBLS12_381: c := math.Curves[math.BLS12_381] + return c, &amcl.Gurvy{C: c}, nil case curveIDBLS12_381_BBS: c := math.Curves[math.BLS12_381_BBS] + return c, &amcl.Gurvy{C: c}, nil case curveIDBLS12_381_BBS_GURVY: c := math.Curves[math.BLS12_381_BBS_GURVY] + return c, &amcl.Gurvy{C: c}, nil default: return nil, nil, fmt.Errorf("unknown curve id %q", curveID) @@ -203,8 +211,8 @@ func NewIdemixMspWithLogger(version MSPVersion, logger Logger) (MSP, error) { logger = newDefaultLogger("idemix") } logger.Debugf("Creating Idemix-based MSP instance") - msp := Idemixmsp{logger: logger, version: version -, aries: false, exportable: true} + msp := Idemixmsp{logger: logger, version: version, aries: false, exportable: true} + return &msp, nil } @@ -224,8 +232,8 @@ func NewIdemixMspAriesWithLogger(version MSPVersion, logger Logger) (MSP, error) logger = newDefaultLogger("idemix") } logger.Debugf("Creating Idemix-based MSP instance") - msp := Idemixmsp{logger: logger, version: version -, aries: true, exportable: true} + msp := Idemixmsp{logger: logger, version: version, aries: true, exportable: true} + return &msp, nil } @@ -252,7 +260,7 @@ func (msp *Idemixmsp) Setup(conf1 *m.MSPConfig) error { } } else { if conf1.Type != int32(IDEMIX) { - return errors.New("setup error: dlog MSP requires config of type IDEMIX, got %d", conf1.Type) + return fmt.Errorf("setup error: dlog MSP requires config of type IDEMIX, got %d", conf1.Type) } }